@dittolive/ditto 4.1.1 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,10 +3,10 @@
3
3
  *Ditto is a cross platform SDK that allows mobile, web, and IoT apps to sync
4
4
  with and even without connectivity.*
5
5
 
6
- Version: **4.1.1**
6
+ Version: **4.2.1**
7
7
 
8
8
  Please visit [ditto.live](https://ditto.live) for more info as well as the
9
- [API Reference](https://software.ditto.live/js/Ditto/4.1.1/api-reference/) for this particular version.
9
+ [API Reference](https://software.ditto.live/js/Ditto/4.2.1/api-reference/) for this particular version.
10
10
 
11
11
  --------------------------------------------------------------------------------
12
12
 
package/node/ditto.cjs.js CHANGED
@@ -289,9 +289,7 @@ function ditto_add_internal_ble_server_transport(...args) { return ditto.ditto_a
289
289
  function ditto_add_internal_mdns_client_transport(...args) { return ditto.ditto_add_internal_mdns_client_transport(...args) }
290
290
  function ditto_add_internal_mdns_server_transport(...args) { return ditto.ditto_add_internal_mdns_server_transport(...args) }
291
291
  function ditto_add_multicast_transport(...args) { return ditto.ditto_add_multicast_transport(...args) }
292
- function ditto_add_static_tcp_client(...args) { return ditto.ditto_add_static_tcp_client(...args) }
293
292
  function ditto_add_subscription(...args) { return ditto.ditto_add_subscription(...args) }
294
- function ditto_add_websocket_client(...args) { return ditto.ditto_add_websocket_client(...args) }
295
293
  function ditto_auth_client_get_site_id(...args) { return ditto.ditto_auth_client_get_site_id(...args) }
296
294
  function ditto_auth_client_is_web_valid(...args) { return ditto.ditto_auth_client_is_web_valid(...args) }
297
295
  function ditto_auth_client_is_x509_valid(...args) { return ditto.ditto_auth_client_is_x509_valid(...args) }
@@ -326,7 +324,9 @@ function ditto_document_set_cbor_with_timestamp(...args) { return ditto.ditto_do
326
324
  function ditto_documents_hash(...args) { return ditto.ditto_documents_hash(...args) }
327
325
  function ditto_documents_hash_mnemonic(...args) { return ditto.ditto_documents_hash_mnemonic(...args) }
328
326
  function ditto_error_message(...args) { return ditto.ditto_error_message(...args) }
327
+ function ditto_experimental_add_dql_subscription(...args) { return ditto.ditto_experimental_add_dql_subscription(...args) }
329
328
  function ditto_experimental_exec_query_str(...args) { return ditto.ditto_experimental_exec_query_str(...args) }
329
+ function ditto_experimental_remove_dql_subscription(...args) { return ditto.ditto_experimental_remove_dql_subscription(...args) }
330
330
  function ditto_free(...args) { return ditto.ditto_free(...args) }
331
331
  function ditto_free_attachment_handle(...args) { return ditto.ditto_free_attachment_handle(...args) }
332
332
  function ditto_get_collection_names(...args) { return ditto.ditto_get_collection_names(...args) }
@@ -369,6 +369,8 @@ function ditto_resolve_attachment(...args) { return ditto.ditto_resolve_attachme
369
369
  function ditto_run_garbage_collection(...args) { return ditto.ditto_run_garbage_collection(...args) }
370
370
  function ditto_set_connect_retry_interval(...args) { return ditto.ditto_set_connect_retry_interval(...args) }
371
371
  function ditto_set_device_name(...args) { return ditto.ditto_set_device_name(...args) }
372
+ function ditto_set_static_tcp_clients(...args) { return ditto.ditto_set_static_tcp_clients(...args) }
373
+ function ditto_set_static_websocket_clients(...args) { return ditto.ditto_set_static_websocket_clients(...args) }
372
374
  function ditto_set_sync_group(...args) { return ditto.ditto_set_sync_group(...args) }
373
375
  function ditto_shutdown(...args) { return ditto.ditto_shutdown(...args) }
374
376
  function ditto_start_http_server(...args) { return ditto.ditto_start_http_server(...args) }
@@ -406,9 +408,9 @@ var DittoCRDTType;
406
408
  DittoCRDTType[DittoCRDTType["rga"] = 3] = "rga";
407
409
  DittoCRDTType[DittoCRDTType["rwMap"] = 4] = "rwMap";
408
410
  })(DittoCRDTType || (DittoCRDTType = {}));
409
- // ------------------------------------------------------- Linux BLE Hack ------
411
+ // -------------------------------------- Linux & Windows Transports Hack ------
410
412
  // HACK: quick and dirty, wrap the internal BLE functions which
411
- // are currently only used by the Node Linux build. See comment
413
+ // are currently only used by the Node Linux & Windows build. See comment
412
414
  // in `sync.ts` -> `Ditto.applyPeerToPeerBluetoothLE()` for details.
413
415
  function dittoAddInternalBLEClientTransport(ditto) {
414
416
  {
@@ -643,6 +645,22 @@ function removeSubscription(ditto, collectionName, query, queryArgsCBOR, orderBy
643
645
  const queryX = bytesFromString(query);
644
646
  return ditto_remove_subscription(ditto, collectionNameX, queryX, queryArgsCBOR, orderBy, limit, offset);
645
647
  }
648
+ /** @internal */
649
+ function experimentalAddDqlSubscription(ditto, query, queryArgsCBOR) {
650
+ ensureInitialized();
651
+ const queryBuffer = bytesFromString(query);
652
+ const statusCode = ditto_experimental_add_dql_subscription(ditto, queryBuffer, queryArgsCBOR);
653
+ if (statusCode !== 0)
654
+ throw new Error(errorMessage() || `ditto_experimental_add_dql_subscription() failed with error code: ${statusCode}`);
655
+ }
656
+ /** @internal */
657
+ function experimentalRemoveDqlSubscription(ditto, query, queryArgsCBOR) {
658
+ ensureInitialized();
659
+ const queryBuffer = bytesFromString(query);
660
+ const statusCode = ditto_experimental_remove_dql_subscription(ditto, queryBuffer, queryArgsCBOR);
661
+ if (statusCode !== 0)
662
+ throw new Error(errorMessage() || `ditto_experimental_remove_dql_subscription() failed with error code: ${statusCode}`);
663
+ }
646
664
  // ------------------------------------------------------------ LiveQuery ------
647
665
  /** @internal */
648
666
  function liveQueryRegister(ditto, collectionName, query, queryArgsCBOR, orderBy, limit, offset, eventHandler,
@@ -729,24 +747,10 @@ async function writeTransactionRollback(ditto, transaction) {
729
747
  if (errorCode !== 0)
730
748
  throw new Error(errorMessage() || `ditto_write_transaction_rollback() failed with error code: ${errorCode}`);
731
749
  }
732
- // ------------------------------------------------------ StaticTCPClient ------
733
- /** @internal */
734
- function addStaticTCPClient(ditto, address) {
735
- ensureInitialized();
736
- const addressBuffer = bytesFromString(address);
737
- return ditto_add_static_tcp_client(ditto, addressBuffer);
738
- }
739
750
  /** @internal */
740
751
  function staticTCPClientFreeHandle(self) {
741
752
  static_tcp_client_free_handle(self);
742
753
  }
743
- // ------------------------------------------------------ WebsocketClient ------
744
- /** @internal */
745
- function addWebsocketClient(ditto, address, routingHint) {
746
- ensureInitialized();
747
- const addressBuffer = bytesFromString(address);
748
- return ditto_add_websocket_client(ditto, addressBuffer, routingHint);
749
- }
750
754
  /** @internal */
751
755
  function websocketClientFreeHandle(self) {
752
756
  ensureInitialized();
@@ -1140,6 +1144,20 @@ async function dittoDisableSyncWithV3(dittoPointer) {
1140
1144
  if (errorCode !== 0)
1141
1145
  throw new Error(errorMessage() || `ditto_disable_sync_with_v3() failed with error code: ${errorCode}`);
1142
1146
  }
1147
+ function dittoSetStaticTCPClients(ditto, listOfServers) {
1148
+ ensureInitialized();
1149
+ // TODO(Vincent): add error handling.
1150
+ {
1151
+ const listOfServersBytes = listOfServers.map((server) => bytesFromString(server));
1152
+ ditto_set_static_tcp_clients(ditto, listOfServersBytes);
1153
+ }
1154
+ }
1155
+ function dittoSetStaticWebsocketClients(ditto, listOfServers, routingHint) {
1156
+ ensureInitialized();
1157
+ // TODO(Vincent): add error handling.
1158
+ const listOfServersBytes = listOfServers.map((server) => bytesFromString(server));
1159
+ ditto_set_static_websocket_clients(ditto, listOfServersBytes, routingHint);
1160
+ }
1143
1161
  // ------------------------------------------------------ Hash & Mnemonic ------
1144
1162
  /** @internal */
1145
1163
  function documentsHash(documents) {
@@ -1452,7 +1470,7 @@ function awdlDestroy(awdl) {
1452
1470
 
1453
1471
  // NOTE: this is patched up with the actual build version by Jake task
1454
1472
  // build:package and has to be a valid semantic version as defined here: https://semver.org.
1455
- const fullBuildVersionString = '4.1.1';
1473
+ const fullBuildVersionString = '4.2.1';
1456
1474
 
1457
1475
  //
1458
1476
  /**
@@ -5221,6 +5239,125 @@ function collectionsFromDocuments(documents, store) {
5221
5239
  return collections;
5222
5240
  }
5223
5241
 
5242
+ //
5243
+ /**
5244
+ * Manages `ExperimentalSubscription` instances and removes them when Ditto is
5245
+ * closed.
5246
+ *
5247
+ * @internal
5248
+ */
5249
+ class ExperimentalSubscriptionManager {
5250
+ constructor(ditto) {
5251
+ this.subscriptions = {};
5252
+ this.finalizationRegistry = new FinalizationRegistry(this.removeWithContextInfo.bind(this));
5253
+ this.ditto = ditto;
5254
+ }
5255
+ /**
5256
+ * Begin tracking a subscription instance and start it.
5257
+ *
5258
+ * @internal
5259
+ */
5260
+ addSubscription(subscription) {
5261
+ if (this.subscriptions[subscription.contextInfo.id] != null) {
5262
+ throw new Error(`Internal inconsistency, tried to add a subscription that is already tracked: ${subscription.contextInfo.id}`);
5263
+ }
5264
+ const dittoHandle = Bridge.ditto.handleFor(this.ditto);
5265
+ this.ditto.deferClose(() => {
5266
+ experimentalAddDqlSubscription(dittoHandle.deref(), subscription.query, subscription.queryArgsCBOR);
5267
+ });
5268
+ // Only track the subscription after it has been successfully added to Ditto
5269
+ // to avoid tracking a subscription that failed to start.
5270
+ this.subscriptions[subscription.contextInfo.id] = new WeakRef(subscription);
5271
+ this.finalizationRegistry.register(subscription, subscription.contextInfo, subscription);
5272
+ }
5273
+ /**
5274
+ * Stop tracking a subscription instance and cancel it.
5275
+ *
5276
+ * @internal
5277
+ */
5278
+ removeSubscription(subscription) {
5279
+ if (this.subscriptions[subscription.contextInfo.id] == null) {
5280
+ throw new Error(`Internal inconsistency, tried to remove a subscription that is not tracked: ${subscription.contextInfo.id}`);
5281
+ }
5282
+ this.finalizationRegistry.unregister(subscription);
5283
+ delete this.subscriptions[subscription.contextInfo.id];
5284
+ this.removeWithContextInfo(subscription.contextInfo);
5285
+ }
5286
+ /**
5287
+ * Stop tracking all subscriptions and cancel them.
5288
+ *
5289
+ * @internal
5290
+ */
5291
+ close() {
5292
+ Logger.debug(`ExperimentalSubscriptionManager.close() with ${this.subscriptions.length} queries`);
5293
+ this.ditto.deferClose(() => {
5294
+ var _a;
5295
+ for (const subscriptionWeakRef of Object.values(this.subscriptions)) {
5296
+ // If this deref fails, the garbage collector has already removed the
5297
+ // subscription and will issue a finalization callback to
5298
+ // `removeWithContextInfo()`.
5299
+ (_a = subscriptionWeakRef.deref()) === null || _a === void 0 ? void 0 : _a.cancel();
5300
+ }
5301
+ });
5302
+ }
5303
+ /**
5304
+ * Remove tracked subscription without unregistering from finalization
5305
+ * registry.
5306
+ *
5307
+ * @internal
5308
+ */
5309
+ removeWithContextInfo(contextInfo) {
5310
+ const dittoHandle = Bridge.ditto.handleFor(this.ditto);
5311
+ this.ditto.deferClose(() => {
5312
+ experimentalRemoveDqlSubscription(dittoHandle.deref(), contextInfo.query, contextInfo.queryArgsCBOR);
5313
+ delete this.subscriptions[contextInfo.id];
5314
+ });
5315
+ }
5316
+ }
5317
+
5318
+ //
5319
+ /**
5320
+ * Create a subscription to receive updates from remote peers about documents
5321
+ * matching the subscription's query.
5322
+ *
5323
+ * The subscription will remain active until {@link cancel | cancel()} is called
5324
+ * or the subscription object goes out of scope and is garbage collected.
5325
+ *
5326
+ * @internal
5327
+ */
5328
+ class ExperimentalSubscription {
5329
+ /**
5330
+ * Cancels a subscription and releases all associated resources.
5331
+ */
5332
+ cancel() {
5333
+ if (!this.isCancelled) {
5334
+ this['isCancelled'] = true;
5335
+ this.manager.removeSubscription(this);
5336
+ }
5337
+ }
5338
+ // --------------------------- Internal --------------------------------------
5339
+ /** @internal */
5340
+ constructor(manager, query, queryArgsCBOR) {
5341
+ /**
5342
+ * `true` when {@link cancel | cancel()} has been called or the Ditto instance
5343
+ * managing this subscription has been closed.
5344
+ */
5345
+ // Recording this information on the subscription instance itself allows
5346
+ // working with it even after Ditto has been closed and the subscription
5347
+ // manager can not be accessed anymore.
5348
+ this.isCancelled = false;
5349
+ this.query = query;
5350
+ this.queryArgsCBOR = queryArgsCBOR;
5351
+ this.contextInfo = {
5352
+ id: generateEphemeralToken(),
5353
+ query,
5354
+ queryArgsCBOR,
5355
+ };
5356
+ this.manager = manager;
5357
+ manager.addSubscription(this);
5358
+ }
5359
+ }
5360
+
5224
5361
  //
5225
5362
  /**
5226
5363
  * An addon for the Ditto store that contains experimental features. Accessible
@@ -5232,21 +5369,28 @@ class ExperimentalStore {
5232
5369
  /** @internal */
5233
5370
  constructor(ditto) {
5234
5371
  this.ditto = ditto;
5372
+ this.subscriptionManager = new ExperimentalSubscriptionManager(this.ditto);
5373
+ }
5374
+ /** @internal */
5375
+ close() {
5376
+ this.subscriptionManager.close();
5235
5377
  }
5236
5378
  /**
5237
5379
  * Executes the given query in the local store and returns the result.
5238
5380
  *
5239
5381
  * Limitations:
5240
5382
  *
5241
- * - Only supports `SELECT * FROM <collection name>`
5242
- * - No query parameters
5383
+ * - Supports `SELECT * FROM <collection name>` with optional `WHERE
5384
+ * <expression>`
5385
+ * - No query parameters as function arguments yet
5243
5386
  * - No transactions
5244
5387
  *
5245
5388
  * @internal
5246
5389
  * @param query a Ditto Query Language query string
5247
5390
  * @returns a promise for an array of Ditto documents matching the query
5248
- **/
5391
+ */
5249
5392
  async execute(query) {
5393
+ Logger.debug(`ExperimentalStore.execute(query = ${query})`);
5250
5394
  const ditto = this.ditto;
5251
5395
  const dittoHandle = Bridge.ditto.handleFor(ditto);
5252
5396
  return ditto.deferCloseAsync(async () => {
@@ -5261,9 +5405,21 @@ class ExperimentalStore {
5261
5405
  return documentPointers.map((documentPointer) => Bridge.document.bridge(documentPointer));
5262
5406
  });
5263
5407
  }
5264
- /** @internal */
5265
- close() {
5266
- // Nothing to do yet.
5408
+ /**
5409
+ * Subscribes the device to updates specified by a DQL query to collections
5410
+ * that other devices know about.
5411
+ *
5412
+ * The returned {@link ExperimentalSubscription} object must be kept in scope
5413
+ * for as long as you want to keep receiving updates.
5414
+ *
5415
+ * @internal
5416
+ * @param query a Ditto Query Language query string of the form SELECT * FROM
5417
+ * collection WHERE expression
5418
+ * @returns {@link ExperimentalSubscription} object
5419
+ */
5420
+ subscribe(query) {
5421
+ Logger.debug(`ExperimentalStore.subscribe(query = ${query})`);
5422
+ return new ExperimentalSubscription(this.subscriptionManager, query, null);
5267
5423
  }
5268
5424
  }
5269
5425
 
@@ -6031,8 +6187,6 @@ class Sync {
6031
6187
  this.ditto = ditto;
6032
6188
  this.parameters = parameters;
6033
6189
  this.state = stateFrom(parameters);
6034
- this.staticTCPClientsByAddress = {};
6035
- this.websocketClientsByURL = {};
6036
6190
  }
6037
6191
  update(parameters) {
6038
6192
  this['parameters'] = { ...parameters };
@@ -6216,70 +6370,17 @@ class Sync {
6216
6370
  const ditto = this.ditto;
6217
6371
  const dittoHandle = Bridge.ditto.handleFor(ditto);
6218
6372
  ditto.deferClose(() => {
6219
- // REFACTOR: same "algorithm" as for Websockets below, DRY up.
6220
- const currentTCPServers = Object.getOwnPropertyNames(this.staticTCPClientsByAddress);
6221
- const desiredTCPServers = stateNew.effectiveTransportConfig.connect.tcpServers;
6222
- const tcpServersToConnectToSet = new Set(desiredTCPServers);
6223
- for (const tcpServer of currentTCPServers)
6224
- tcpServersToConnectToSet.delete(tcpServer);
6225
- const tcpServersToDisconnectFromSet = new Set(currentTCPServers);
6226
- for (const tcpServer of desiredTCPServers)
6227
- tcpServersToDisconnectFromSet.delete(tcpServer);
6228
- const tcpServersToConnectTo = tcpServersToConnectToSet.values();
6229
- const tcpServersToDisconnectFrom = tcpServersToDisconnectFromSet.values();
6230
- for (const tcpServer of tcpServersToConnectTo) {
6231
- const staticTCPClientPointer = addStaticTCPClient(dittoHandle.deref(), tcpServer);
6232
- const staticTCPClient = Bridge.staticTCPClient.bridge(staticTCPClientPointer);
6233
- this.staticTCPClientsByAddress[tcpServer] = staticTCPClient;
6234
- }
6235
- for (const tcpServer of tcpServersToDisconnectFrom) {
6236
- // REFACTOR: we have to make sure freeing the tcpServer handle is done
6237
- // BEFORE the Ditto instance itself is freed.
6238
- const staticTCPClient = this.staticTCPClientsByAddress[tcpServer];
6239
- if (!staticTCPClient)
6240
- throw new Error(`Internal inconsistency, can't disconnect from TCP address '${tcpServer}', no staticTCPClient found.`);
6241
- const staticTCPClientPointer = Bridge.staticTCPClient.handleFor(staticTCPClient).deref();
6242
- // Unregister to avoid double-free for this pointer, then free manually:
6243
- Bridge.staticTCPClient.unregister(staticTCPClient);
6244
- staticTCPClientFreeHandle(staticTCPClientPointer);
6245
- delete this.staticTCPClientsByAddress[tcpServer];
6246
- }
6373
+ const tcpServers = stateNew.effectiveTransportConfig.connect.tcpServers;
6374
+ dittoSetStaticTCPClients(dittoHandle.deref(), tcpServers);
6247
6375
  });
6248
6376
  }
6249
6377
  updateConnectWebsocketURLs(stateOld, stateNew) {
6250
6378
  const ditto = this.ditto;
6251
6379
  const dittoHandle = Bridge.ditto.handleFor(ditto);
6252
6380
  ditto.deferClose(() => {
6253
- // REFACTOR: same "algorithm" as for TCP servers above, DRY up.
6254
- // IDEA: normalize URLs so that we don't connect to the same URL twice?
6255
- const currentWebsocketURLs = Object.getOwnPropertyNames(this.websocketClientsByURL);
6256
- const desiredWebsocketURLs = stateNew.effectiveTransportConfig.connect.websocketURLs.slice();
6257
- const websocketURLsToConnectToSet = new Set(desiredWebsocketURLs);
6258
- for (const websocketURL of currentWebsocketURLs)
6259
- websocketURLsToConnectToSet.delete(websocketURL);
6260
- const websocketURLsToDisconnectFromSet = new Set(currentWebsocketURLs);
6261
- for (const websocketURL of desiredWebsocketURLs)
6262
- websocketURLsToDisconnectFromSet.delete(websocketURL);
6263
- const websocketURLsToConnectTo = websocketURLsToConnectToSet.values();
6264
- const websocketURLsToDisconnectFrom = websocketURLsToDisconnectFromSet.values();
6381
+ const websocketURLs = stateNew.effectiveTransportConfig.connect.websocketURLs;
6265
6382
  const routingHint = stateNew.effectiveTransportConfig.global.routingHint;
6266
- for (const websocketURL of websocketURLsToConnectTo) {
6267
- const websocketClientPointer = addWebsocketClient(dittoHandle.deref(), websocketURL, routingHint);
6268
- const websocketClient = Bridge.websocketClient.bridge(websocketClientPointer);
6269
- this.websocketClientsByURL[websocketURL] = websocketClient;
6270
- }
6271
- for (const websocketURL of websocketURLsToDisconnectFrom) {
6272
- // REFACTOR: we have to make sure freeing the websocket handle is done
6273
- // BEFORE the Ditto instance itself is freed.
6274
- const websocketClient = this.websocketClientsByURL[websocketURL];
6275
- if (!websocketClient)
6276
- throw new Error(`Internal inconsistency, can't disconnect from websocket URL '${websocketURL}', no websocketClient found.`);
6277
- const websocketClientPointer = Bridge.websocketClient.handleFor(websocketClient).deref();
6278
- // Unregister to avoid double-free for this pointer, then free manually:
6279
- Bridge.websocketClient.unregister(websocketClient);
6280
- websocketClientFreeHandle(websocketClientPointer);
6281
- delete this.websocketClientsByURL[websocketURL];
6282
- }
6383
+ dittoSetStaticWebsocketClients(dittoHandle.deref(), websocketURLs, routingHint);
6283
6384
  });
6284
6385
  }
6285
6386
  updateGlobal(stateOld, stateNew) {
@@ -6381,7 +6482,7 @@ class SubscriptionManager {
6381
6482
  constructor(ditto) {
6382
6483
  this.ditto = ditto;
6383
6484
  this.subscriptions = {};
6384
- this.finalizationRegistry = new FinalizationRegistry(this.removeWithContextinfo.bind(this));
6485
+ this.finalizationRegistry = new FinalizationRegistry(this.removeWithContextInfo.bind(this));
6385
6486
  }
6386
6487
  /**
6387
6488
  * Begin tracking a subscription instance and start it.
@@ -6406,7 +6507,7 @@ class SubscriptionManager {
6406
6507
  throw new Error(`Internal inconsistency, tried to remove a subscription that is not tracked: ${subscription.contextInfo.id}`);
6407
6508
  }
6408
6509
  this.finalizationRegistry.unregister(subscription);
6409
- this.removeWithContextinfo(subscription.contextInfo);
6510
+ this.removeWithContextInfo(subscription.contextInfo);
6410
6511
  }
6411
6512
  /**
6412
6513
  * Stop tracking all subscriptions and cancel them.
@@ -6429,7 +6530,7 @@ class SubscriptionManager {
6429
6530
  * registry.
6430
6531
  *
6431
6532
  * @internal */
6432
- removeWithContextinfo(contextInfo) {
6533
+ removeWithContextInfo(contextInfo) {
6433
6534
  const ditto = this.ditto;
6434
6535
  const dittoHandle = Bridge.ditto.handleFor(ditto);
6435
6536
  ditto.deferClose(() => {
@@ -6732,8 +6833,13 @@ class Ditto {
6732
6833
  })();
6733
6834
  const dittoPointer = dittoMake(uninitializedDittoX, identityConfig);
6734
6835
  dittoAuthClientSetValidityListener(dittoPointer, function (...args) {
6735
- var _a;
6736
- (_a = weakThis.deref()) === null || _a === void 0 ? void 0 : _a.authClientValidityChanged(...args);
6836
+ const ditto = weakThis.deref();
6837
+ if (ditto == null || ditto.isClosed) {
6838
+ Logger.info('Ditto is closed, ignoring auth client validity change');
6839
+ }
6840
+ else {
6841
+ ditto.authClientValidityChanged(...args);
6842
+ }
6737
6843
  });
6738
6844
  const isWebValid = dittoAuthClientIsWebValid(dittoPointer);
6739
6845
  const isX509Valid = dittoAuthClientIsX509Valid(dittoPointer);
@@ -7244,7 +7350,8 @@ const disableDeadlockTimeoutWhenDebugging = () => {
7244
7350
  *
7245
7351
  * Use this in testing to ensure that all objects are properly garbage collected at the end of tests.
7246
7352
  *
7247
- * @returns an object with a key per bridge type, containing the number of registered objects.
7353
+ * @returns an object with a key per bridge type, set to the count of registered
7354
+ * objects.
7248
7355
  */
7249
7356
  function getBridgeLoad() {
7250
7357
  const countsByType = {};
@@ -7300,6 +7407,8 @@ exports.Document = Document;
7300
7407
  exports.DocumentID = DocumentID;
7301
7408
  exports.DocumentPath = DocumentPath;
7302
7409
  exports.ExperimentalStore = ExperimentalStore;
7410
+ exports.ExperimentalSubscription = ExperimentalSubscription;
7411
+ exports.ExperimentalSubscriptionManager = ExperimentalSubscriptionManager;
7303
7412
  exports.IdentityTypesRequiringOfflineLicenseToken = IdentityTypesRequiringOfflineLicenseToken;
7304
7413
  exports.KeepAlive = KeepAlive;
7305
7414
  exports.LiveQuery = LiveQuery;
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dittolive/ditto",
3
- "version": "4.1.1",
3
+ "version": "4.2.1",
4
4
  "description": "Ditto is a cross-platform embeddable NoSQL database that can sync with or without an internet connection.",
5
5
  "homepage": "https://ditto.live",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
package/types/ditto.d.ts CHANGED
@@ -1348,7 +1348,7 @@ declare class SubscriptionManager {
1348
1348
  * registry.
1349
1349
  *
1350
1350
  * @internal */
1351
- private removeWithContextinfo;
1351
+ private removeWithContextInfo;
1352
1352
  }
1353
1353
 
1354
1354
  /**
@@ -2638,6 +2638,94 @@ declare class PendingCollectionsOperation implements PromiseLike<Collection[]> {
2638
2638
  private readonly pendingCursorOperation;
2639
2639
  }
2640
2640
 
2641
+ /**
2642
+ * Manages `ExperimentalSubscription` instances and removes them when Ditto is
2643
+ * closed.
2644
+ *
2645
+ * @internal
2646
+ */
2647
+ declare class ExperimentalSubscriptionManager {
2648
+ constructor(ditto: Ditto);
2649
+ /**
2650
+ * Begin tracking a subscription instance and start it.
2651
+ *
2652
+ * @internal
2653
+ */
2654
+ addSubscription(subscription: ExperimentalSubscription): void;
2655
+ /**
2656
+ * Stop tracking a subscription instance and cancel it.
2657
+ *
2658
+ * @internal
2659
+ */
2660
+ removeSubscription(subscription: ExperimentalSubscription): void;
2661
+ /**
2662
+ * Stop tracking all subscriptions and cancel them.
2663
+ *
2664
+ * @internal
2665
+ */
2666
+ close(): void;
2667
+ private readonly ditto;
2668
+ private subscriptions;
2669
+ private finalizationRegistry;
2670
+ /**
2671
+ * Remove tracked subscription without unregistering from finalization
2672
+ * registry.
2673
+ *
2674
+ * @internal
2675
+ */
2676
+ private removeWithContextInfo;
2677
+ }
2678
+
2679
+ /**
2680
+ * Contains all information required to deregister a subscription
2681
+ * in Ditto Core, even when the subscription instance itself has
2682
+ * already been garbage collected.
2683
+ *
2684
+ * @internal
2685
+ */
2686
+ type ExperimentalSubscriptionContextInfo = {
2687
+ /** Unique ID per `ExperimentalSubscription` instance. */
2688
+ id: string;
2689
+ query: string;
2690
+ queryArgsCBOR: Uint8Array | null;
2691
+ };
2692
+ /**
2693
+ * Create a subscription to receive updates from remote peers about documents
2694
+ * matching the subscription's query.
2695
+ *
2696
+ * The subscription will remain active until {@link cancel | cancel()} is called
2697
+ * or the subscription object goes out of scope and is garbage collected.
2698
+ *
2699
+ * @internal
2700
+ */
2701
+ declare class ExperimentalSubscription {
2702
+ /**
2703
+ * Documents matching this query will be included in the subscription.
2704
+ */
2705
+ readonly query: string;
2706
+ /**
2707
+ * `true` when {@link cancel | cancel()} has been called or the Ditto instance
2708
+ * managing this subscription has been closed.
2709
+ */
2710
+ readonly isCancelled = false;
2711
+ /**
2712
+ * Cancels a subscription and releases all associated resources.
2713
+ */
2714
+ cancel(): void;
2715
+ /** @internal */
2716
+ constructor(manager: ExperimentalSubscriptionManager, query: string, queryArgsCBOR: Uint8Array | null);
2717
+ /** @internal */
2718
+ contextInfo: ExperimentalSubscriptionContextInfo;
2719
+ /**
2720
+ * The corresponding named arguments for {@link query}, if any.
2721
+ *
2722
+ * @internal
2723
+ */
2724
+ readonly queryArgsCBOR: Uint8Array | null;
2725
+ /** @internal */
2726
+ private manager;
2727
+ }
2728
+
2641
2729
  /**
2642
2730
  * An addon for the Ditto store that contains experimental features. Accessible
2643
2731
  * on `ditto.store.experimental`.
@@ -2652,22 +2740,37 @@ declare class ExperimentalStore {
2652
2740
  readonly ditto: Ditto;
2653
2741
  /** @internal */
2654
2742
  constructor(ditto: Ditto);
2743
+ /** @internal */
2744
+ close(): void;
2655
2745
  /**
2656
2746
  * Executes the given query in the local store and returns the result.
2657
2747
  *
2658
2748
  * Limitations:
2659
2749
  *
2660
- * - Only supports `SELECT * FROM <collection name>`
2661
- * - No query parameters
2750
+ * - Supports `SELECT * FROM <collection name>` with optional `WHERE
2751
+ * <expression>`
2752
+ * - No query parameters as function arguments yet
2662
2753
  * - No transactions
2663
2754
  *
2664
2755
  * @internal
2665
2756
  * @param query a Ditto Query Language query string
2666
2757
  * @returns a promise for an array of Ditto documents matching the query
2667
- **/
2758
+ */
2668
2759
  execute(query: string): Promise<Document[]>;
2669
- /** @internal */
2670
- close(): void;
2760
+ /**
2761
+ * Subscribes the device to updates specified by a DQL query to collections
2762
+ * that other devices know about.
2763
+ *
2764
+ * The returned {@link ExperimentalSubscription} object must be kept in scope
2765
+ * for as long as you want to keep receiving updates.
2766
+ *
2767
+ * @internal
2768
+ * @param query a Ditto Query Language query string of the form SELECT * FROM
2769
+ * collection WHERE expression
2770
+ * @returns {@link ExperimentalSubscription} object
2771
+ */
2772
+ subscribe(query: string): ExperimentalSubscription;
2773
+ private subscriptionManager;
2671
2774
  }
2672
2775
 
2673
2776
  /**
@@ -3048,7 +3151,8 @@ declare abstract class BasePendingCursorOperation implements PromiseLike<Documen
3048
3151
  *
3049
3152
  * Use this in testing to ensure that all objects are properly garbage collected at the end of tests.
3050
3153
  *
3051
- * @returns an object with a key per bridge type, containing the number of registered objects.
3154
+ * @returns an object with a key per bridge type, set to the count of registered
3155
+ * objects.
3052
3156
  */
3053
3157
  declare function getBridgeLoad(): {
3054
3158
  [bridgeName: string]: number;
@@ -3062,5 +3166,5 @@ declare class CBOR {
3062
3166
  static decode(data: Uint8Array): any;
3063
3167
  }
3064
3168
 
3065
- export { Address, Attachment, AttachmentFetchEvent, AttachmentFetchEventCompleted, AttachmentFetchEventDeleted, AttachmentFetchEventProgress, AttachmentFetchEventType, AttachmentFetcher, AttachmentToken, AuthenticationHandler, AuthenticationStatus, Authenticator, BasePendingCursorOperation, BasePendingIDSpecificOperation, CBOR, Collection, CollectionInterface, CollectionsEvent, CollectionsEventParams, CollectionsObservationHandler, ConditionSource, Connection, ConnectionType, Counter, CustomLogCallback, Ditto, Document, DocumentID, DocumentIDValue, DocumentPath, DocumentValue, ExperimentalStore, Identity, IdentityManual, IdentityOfflinePlayground, IdentityOnlinePlayground, IdentityOnlineWithAuthentication, IdentitySharedKey, IdentityTypesRequiringOfflineLicenseToken, InitOptions, KeepAlive, LiveQuery, LiveQueryEvent, LiveQueryEventInitial, LiveQueryEventUpdate, LiveQueryEventUpdateParams, LiveQueryMove, LogLevel, Logger, MutableCounter, MutableDocument, MutableDocumentPath, MutableRegister, NotAvailableAuthenticator, Observer, ObserverOptions, OnlineAuthenticator, Peer, PendingCollectionsOperation, PendingCursorOperation, PendingIDSpecificOperation, Presence, PresenceConnectionType, PresenceGraph, QueryArguments, QueryObservationHandler, Register, RemotePeer, SingleDocumentLiveQueryEvent, SingleObservationHandler, SortDirection, Store, Subscription, TransportCondition, TransportConfig, TransportConfigConnect, TransportConfigGlobal, TransportConfigLan, TransportConfigListen, TransportConfigListenHTTP, TransportConfigListenTCP, TransportConfigPeerToPeer, UpdateResult, UpdateResultType, UpdateResultsMap, UpsertOptions, Value, WebAssemblyModule, WriteStrategy, WriteTransaction, WriteTransactionCollection, WriteTransactionPendingCursorOperation, WriteTransactionPendingIDSpecificOperation, WriteTransactionResult, addressToString, checkAPIs, disableDeadlockTimeoutWhenDebugging, getBridgeLoad, init, validateDocumentIDCBOR, validateDocumentIDValue };
3169
+ export { Address, Attachment, AttachmentFetchEvent, AttachmentFetchEventCompleted, AttachmentFetchEventDeleted, AttachmentFetchEventProgress, AttachmentFetchEventType, AttachmentFetcher, AttachmentToken, AuthenticationHandler, AuthenticationStatus, Authenticator, BasePendingCursorOperation, BasePendingIDSpecificOperation, CBOR, Collection, CollectionInterface, CollectionsEvent, CollectionsEventParams, CollectionsObservationHandler, ConditionSource, Connection, ConnectionType, Counter, CustomLogCallback, Ditto, Document, DocumentID, DocumentIDValue, DocumentPath, DocumentValue, ExperimentalStore, ExperimentalSubscription, ExperimentalSubscriptionContextInfo, ExperimentalSubscriptionManager, Identity, IdentityManual, IdentityOfflinePlayground, IdentityOnlinePlayground, IdentityOnlineWithAuthentication, IdentitySharedKey, IdentityTypesRequiringOfflineLicenseToken, InitOptions, KeepAlive, LiveQuery, LiveQueryEvent, LiveQueryEventInitial, LiveQueryEventUpdate, LiveQueryEventUpdateParams, LiveQueryMove, LogLevel, Logger, MutableCounter, MutableDocument, MutableDocumentPath, MutableRegister, NotAvailableAuthenticator, Observer, ObserverOptions, OnlineAuthenticator, Peer, PendingCollectionsOperation, PendingCursorOperation, PendingIDSpecificOperation, Presence, PresenceConnectionType, PresenceGraph, QueryArguments, QueryObservationHandler, Register, RemotePeer, SingleDocumentLiveQueryEvent, SingleObservationHandler, SortDirection, Store, Subscription, TransportCondition, TransportConfig, TransportConfigConnect, TransportConfigGlobal, TransportConfigLan, TransportConfigListen, TransportConfigListenHTTP, TransportConfigListenTCP, TransportConfigPeerToPeer, UpdateResult, UpdateResultType, UpdateResultsMap, UpsertOptions, Value, WebAssemblyModule, WriteStrategy, WriteTransaction, WriteTransactionCollection, WriteTransactionPendingCursorOperation, WriteTransactionPendingIDSpecificOperation, WriteTransactionResult, addressToString, checkAPIs, disableDeadlockTimeoutWhenDebugging, getBridgeLoad, init, validateDocumentIDCBOR, validateDocumentIDValue };
3066
3170
  //# sourceMappingURL=ditto.d.ts.map