@dittolive/ditto 4.1.0 → 4.2.0
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 +2 -2
- package/node/ditto.cjs.js +311 -94
- package/node/ditto.darwin-arm64.node +0 -0
- package/node/ditto.darwin-x64.node +0 -0
- package/node/ditto.linux-arm.node +0 -0
- package/node/ditto.linux-x64.node +0 -0
- package/node/ditto.win32-x64.node +0 -0
- package/node/transports.darwin-arm64.node +0 -0
- package/node/transports.darwin-x64.node +0 -0
- package/package.json +1 -1
- package/types/ditto.d.ts +152 -9
- package/web/ditto.es6.js +1 -1
- package/web/ditto.umd.js +1 -1
- package/web/ditto.wasm +0 -0
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.
|
|
6
|
+
Version: **4.2.0**
|
|
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.
|
|
9
|
+
[API Reference](https://software.ditto.live/js/Ditto/4.2.0/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) }
|
|
@@ -379,10 +381,12 @@ function ditto_validate_document_id(...args) { return ditto.ditto_validate_docum
|
|
|
379
381
|
function ditto_write_transaction(...args) { return ditto.ditto_write_transaction(...args) }
|
|
380
382
|
function ditto_write_transaction_commit(...args) { return ditto.ditto_write_transaction_commit(...args) }
|
|
381
383
|
function ditto_write_transaction_rollback(...args) { return ditto.ditto_write_transaction_rollback(...args) }
|
|
384
|
+
function getDeadlockTimeout$1(...args) { return ditto.getDeadlockTimeout(...args) }
|
|
382
385
|
function jsDocsToCDocs(...args) { return ditto.jsDocsToCDocs(...args) }
|
|
383
386
|
function mdns_client_free_handle(...args) { return ditto.mdns_client_free_handle(...args) }
|
|
384
387
|
function mdns_server_free_handle(...args) { return ditto.mdns_server_free_handle(...args) }
|
|
385
388
|
function refCStringToString(...args) { return ditto.refCStringToString(...args) }
|
|
389
|
+
function setDeadlockTimeout$1(...args) { return ditto.setDeadlockTimeout(...args) }
|
|
386
390
|
function static_tcp_client_free_handle(...args) { return ditto.static_tcp_client_free_handle(...args) }
|
|
387
391
|
function uninitialized_ditto_make(...args) { return ditto.uninitialized_ditto_make(...args) }
|
|
388
392
|
function verify_license(...args) { return ditto.verify_license(...args) }
|
|
@@ -404,9 +408,9 @@ var DittoCRDTType;
|
|
|
404
408
|
DittoCRDTType[DittoCRDTType["rga"] = 3] = "rga";
|
|
405
409
|
DittoCRDTType[DittoCRDTType["rwMap"] = 4] = "rwMap";
|
|
406
410
|
})(DittoCRDTType || (DittoCRDTType = {}));
|
|
407
|
-
//
|
|
411
|
+
// -------------------------------------- Linux & Windows Transports Hack ------
|
|
408
412
|
// HACK: quick and dirty, wrap the internal BLE functions which
|
|
409
|
-
// are currently only used by the Node Linux build. See comment
|
|
413
|
+
// are currently only used by the Node Linux & Windows build. See comment
|
|
410
414
|
// in `sync.ts` -> `Ditto.applyPeerToPeerBluetoothLE()` for details.
|
|
411
415
|
function dittoAddInternalBLEClientTransport(ditto) {
|
|
412
416
|
{
|
|
@@ -641,6 +645,22 @@ function removeSubscription(ditto, collectionName, query, queryArgsCBOR, orderBy
|
|
|
641
645
|
const queryX = bytesFromString(query);
|
|
642
646
|
return ditto_remove_subscription(ditto, collectionNameX, queryX, queryArgsCBOR, orderBy, limit, offset);
|
|
643
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
|
+
}
|
|
644
664
|
// ------------------------------------------------------------ LiveQuery ------
|
|
645
665
|
/** @internal */
|
|
646
666
|
function liveQueryRegister(ditto, collectionName, query, queryArgsCBOR, orderBy, limit, offset, eventHandler,
|
|
@@ -650,9 +670,10 @@ onError) {
|
|
|
650
670
|
ensureInitialized();
|
|
651
671
|
const collectionNameBuffer = bytesFromString(collectionName);
|
|
652
672
|
const queryBuffer = bytesFromString(query);
|
|
653
|
-
// Note(Daniel): the callback is now registered to be called in a detached
|
|
654
|
-
// if the FFI / Rust does `cb()`, then when that call returns, the js
|
|
655
|
-
// have completed. This is fine, since `signalNext()`
|
|
673
|
+
// Note(Daniel): the callback is now registered to be called in a detached
|
|
674
|
+
// manner: if the FFI / Rust does `cb()`, then when that call returns, the js
|
|
675
|
+
// callback itself may not have completed. This is fine, since `signalNext()`
|
|
676
|
+
// shall be the proper way to achieve this.
|
|
656
677
|
const { status_code: errorCode, i64: id } = ditto_live_query_register_str_detached(ditto, collectionNameBuffer, queryBuffer, queryArgsCBOR, orderBy, limit, offset, wrapBackgroundCbForFFI(onError, eventHandler));
|
|
657
678
|
if (errorCode !== 0)
|
|
658
679
|
throw new Error(errorMessage() || `\`ditto_live_query_register_str()\` failed with error code: ${errorCode}`);
|
|
@@ -726,24 +747,10 @@ async function writeTransactionRollback(ditto, transaction) {
|
|
|
726
747
|
if (errorCode !== 0)
|
|
727
748
|
throw new Error(errorMessage() || `ditto_write_transaction_rollback() failed with error code: ${errorCode}`);
|
|
728
749
|
}
|
|
729
|
-
// ------------------------------------------------------ StaticTCPClient ------
|
|
730
|
-
/** @internal */
|
|
731
|
-
function addStaticTCPClient(ditto, address) {
|
|
732
|
-
ensureInitialized();
|
|
733
|
-
const addressBuffer = bytesFromString(address);
|
|
734
|
-
return ditto_add_static_tcp_client(ditto, addressBuffer);
|
|
735
|
-
}
|
|
736
750
|
/** @internal */
|
|
737
751
|
function staticTCPClientFreeHandle(self) {
|
|
738
752
|
static_tcp_client_free_handle(self);
|
|
739
753
|
}
|
|
740
|
-
// ------------------------------------------------------ WebsocketClient ------
|
|
741
|
-
/** @internal */
|
|
742
|
-
function addWebsocketClient(ditto, address, routingHint) {
|
|
743
|
-
ensureInitialized();
|
|
744
|
-
const addressBuffer = bytesFromString(address);
|
|
745
|
-
return ditto_add_websocket_client(ditto, addressBuffer, routingHint);
|
|
746
|
-
}
|
|
747
754
|
/** @internal */
|
|
748
755
|
function websocketClientFreeHandle(self) {
|
|
749
756
|
ensureInitialized();
|
|
@@ -948,6 +955,16 @@ function dittoFree(self) {
|
|
|
948
955
|
return ditto_free(self);
|
|
949
956
|
}
|
|
950
957
|
/** @internal */
|
|
958
|
+
function getDeadlockTimeout() {
|
|
959
|
+
ensureInitialized();
|
|
960
|
+
return getDeadlockTimeout$1();
|
|
961
|
+
}
|
|
962
|
+
/** @internal */
|
|
963
|
+
function setDeadlockTimeout(duration) {
|
|
964
|
+
ensureInitialized();
|
|
965
|
+
setDeadlockTimeout$1(duration);
|
|
966
|
+
}
|
|
967
|
+
/** @internal */
|
|
951
968
|
async function dittoRegisterPresenceV1Callback(self, cb) {
|
|
952
969
|
ensureInitialized();
|
|
953
970
|
ditto_register_presence_v1_callback(self, wrapBackgroundCbForFFI((err) => console.error(`The registered presence callback v1 errored with ${err}`), (cJsonStr) => {
|
|
@@ -1127,6 +1144,20 @@ async function dittoDisableSyncWithV3(dittoPointer) {
|
|
|
1127
1144
|
if (errorCode !== 0)
|
|
1128
1145
|
throw new Error(errorMessage() || `ditto_disable_sync_with_v3() failed with error code: ${errorCode}`);
|
|
1129
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
|
+
}
|
|
1130
1161
|
// ------------------------------------------------------ Hash & Mnemonic ------
|
|
1131
1162
|
/** @internal */
|
|
1132
1163
|
function documentsHash(documents) {
|
|
@@ -1439,7 +1470,7 @@ function awdlDestroy(awdl) {
|
|
|
1439
1470
|
|
|
1440
1471
|
// NOTE: this is patched up with the actual build version by Jake task
|
|
1441
1472
|
// build:package and has to be a valid semantic version as defined here: https://semver.org.
|
|
1442
|
-
const fullBuildVersionString = '4.
|
|
1473
|
+
const fullBuildVersionString = '4.2.0';
|
|
1443
1474
|
|
|
1444
1475
|
//
|
|
1445
1476
|
/**
|
|
@@ -2749,6 +2780,11 @@ function augmentJSONValue(json, mutDoc, workingPath) {
|
|
|
2749
2780
|
return json;
|
|
2750
2781
|
}
|
|
2751
2782
|
}
|
|
2783
|
+
/**
|
|
2784
|
+
* Converts objects that may contain instances of classes of this SDK, i.e.
|
|
2785
|
+
* `DocumentID`, `Counter`, `Register` and `Attachment`, into plain JS objects
|
|
2786
|
+
* that can be passed to the FFI layer.
|
|
2787
|
+
*/
|
|
2752
2788
|
function desugarJSObject(jsObj, atRoot = false) {
|
|
2753
2789
|
if (jsObj && typeof jsObj === 'object') {
|
|
2754
2790
|
if (Array.isArray(jsObj)) {
|
|
@@ -2786,8 +2822,25 @@ function desugarJSObject(jsObj, atRoot = false) {
|
|
|
2786
2822
|
}
|
|
2787
2823
|
}
|
|
2788
2824
|
else {
|
|
2825
|
+
checkForUnsupportedValues(jsObj);
|
|
2789
2826
|
return jsObj;
|
|
2790
2827
|
}
|
|
2828
|
+
}
|
|
2829
|
+
/**
|
|
2830
|
+
* Throws an error if input is a non-finite float value.
|
|
2831
|
+
*
|
|
2832
|
+
* Workaround while we don't have a reliable way of receiving error messages
|
|
2833
|
+
* from `dittoCore.ditto_collection_insert_value()`.
|
|
2834
|
+
*
|
|
2835
|
+
* See https://github.com/getditto/ditto/issues/8657 for details.
|
|
2836
|
+
*
|
|
2837
|
+
* @param jsObj The object to check.
|
|
2838
|
+
* @throws {Error} If `jsObj` is a non-finite float value.
|
|
2839
|
+
*/
|
|
2840
|
+
function checkForUnsupportedValues(jsObj) {
|
|
2841
|
+
if (Number.isNaN(jsObj) || jsObj === Infinity || jsObj === -Infinity) {
|
|
2842
|
+
throw new Error('Non-finite float values are not supported');
|
|
2843
|
+
}
|
|
2791
2844
|
}
|
|
2792
2845
|
|
|
2793
2846
|
//
|
|
@@ -5186,6 +5239,125 @@ function collectionsFromDocuments(documents, store) {
|
|
|
5186
5239
|
return collections;
|
|
5187
5240
|
}
|
|
5188
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
|
+
|
|
5189
5361
|
//
|
|
5190
5362
|
/**
|
|
5191
5363
|
* An addon for the Ditto store that contains experimental features. Accessible
|
|
@@ -5197,21 +5369,28 @@ class ExperimentalStore {
|
|
|
5197
5369
|
/** @internal */
|
|
5198
5370
|
constructor(ditto) {
|
|
5199
5371
|
this.ditto = ditto;
|
|
5372
|
+
this.subscriptionManager = new ExperimentalSubscriptionManager(this.ditto);
|
|
5373
|
+
}
|
|
5374
|
+
/** @internal */
|
|
5375
|
+
close() {
|
|
5376
|
+
this.subscriptionManager.close();
|
|
5200
5377
|
}
|
|
5201
5378
|
/**
|
|
5202
5379
|
* Executes the given query in the local store and returns the result.
|
|
5203
5380
|
*
|
|
5204
5381
|
* Limitations:
|
|
5205
5382
|
*
|
|
5206
|
-
* -
|
|
5207
|
-
*
|
|
5383
|
+
* - Supports `SELECT * FROM <collection name>` with optional `WHERE
|
|
5384
|
+
* <expression>`
|
|
5385
|
+
* - No query parameters as function arguments yet
|
|
5208
5386
|
* - No transactions
|
|
5209
5387
|
*
|
|
5210
5388
|
* @internal
|
|
5211
5389
|
* @param query a Ditto Query Language query string
|
|
5212
5390
|
* @returns a promise for an array of Ditto documents matching the query
|
|
5213
|
-
|
|
5391
|
+
*/
|
|
5214
5392
|
async execute(query) {
|
|
5393
|
+
Logger.debug(`ExperimentalStore.execute(query = ${query})`);
|
|
5215
5394
|
const ditto = this.ditto;
|
|
5216
5395
|
const dittoHandle = Bridge.ditto.handleFor(ditto);
|
|
5217
5396
|
return ditto.deferCloseAsync(async () => {
|
|
@@ -5226,9 +5405,21 @@ class ExperimentalStore {
|
|
|
5226
5405
|
return documentPointers.map((documentPointer) => Bridge.document.bridge(documentPointer));
|
|
5227
5406
|
});
|
|
5228
5407
|
}
|
|
5229
|
-
/**
|
|
5230
|
-
|
|
5231
|
-
|
|
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);
|
|
5232
5423
|
}
|
|
5233
5424
|
}
|
|
5234
5425
|
|
|
@@ -5996,8 +6187,6 @@ class Sync {
|
|
|
5996
6187
|
this.ditto = ditto;
|
|
5997
6188
|
this.parameters = parameters;
|
|
5998
6189
|
this.state = stateFrom(parameters);
|
|
5999
|
-
this.staticTCPClientsByAddress = {};
|
|
6000
|
-
this.websocketClientsByURL = {};
|
|
6001
6190
|
}
|
|
6002
6191
|
update(parameters) {
|
|
6003
6192
|
this['parameters'] = { ...parameters };
|
|
@@ -6181,70 +6370,17 @@ class Sync {
|
|
|
6181
6370
|
const ditto = this.ditto;
|
|
6182
6371
|
const dittoHandle = Bridge.ditto.handleFor(ditto);
|
|
6183
6372
|
ditto.deferClose(() => {
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
const desiredTCPServers = stateNew.effectiveTransportConfig.connect.tcpServers;
|
|
6187
|
-
const tcpServersToConnectToSet = new Set(desiredTCPServers);
|
|
6188
|
-
for (const tcpServer of currentTCPServers)
|
|
6189
|
-
tcpServersToConnectToSet.delete(tcpServer);
|
|
6190
|
-
const tcpServersToDisconnectFromSet = new Set(currentTCPServers);
|
|
6191
|
-
for (const tcpServer of desiredTCPServers)
|
|
6192
|
-
tcpServersToDisconnectFromSet.delete(tcpServer);
|
|
6193
|
-
const tcpServersToConnectTo = tcpServersToConnectToSet.values();
|
|
6194
|
-
const tcpServersToDisconnectFrom = tcpServersToDisconnectFromSet.values();
|
|
6195
|
-
for (const tcpServer of tcpServersToConnectTo) {
|
|
6196
|
-
const staticTCPClientPointer = addStaticTCPClient(dittoHandle.deref(), tcpServer);
|
|
6197
|
-
const staticTCPClient = Bridge.staticTCPClient.bridge(staticTCPClientPointer);
|
|
6198
|
-
this.staticTCPClientsByAddress[tcpServer] = staticTCPClient;
|
|
6199
|
-
}
|
|
6200
|
-
for (const tcpServer of tcpServersToDisconnectFrom) {
|
|
6201
|
-
// REFACTOR: we have to make sure freeing the tcpServer handle is done
|
|
6202
|
-
// BEFORE the Ditto instance itself is freed.
|
|
6203
|
-
const staticTCPClient = this.staticTCPClientsByAddress[tcpServer];
|
|
6204
|
-
if (!staticTCPClient)
|
|
6205
|
-
throw new Error(`Internal inconsistency, can't disconnect from TCP address '${tcpServer}', no staticTCPClient found.`);
|
|
6206
|
-
const staticTCPClientPointer = Bridge.staticTCPClient.handleFor(staticTCPClient).deref();
|
|
6207
|
-
// Unregister to avoid double-free for this pointer, then free manually:
|
|
6208
|
-
Bridge.staticTCPClient.unregister(staticTCPClient);
|
|
6209
|
-
staticTCPClientFreeHandle(staticTCPClientPointer);
|
|
6210
|
-
delete this.staticTCPClientsByAddress[tcpServer];
|
|
6211
|
-
}
|
|
6373
|
+
const tcpServers = stateNew.effectiveTransportConfig.connect.tcpServers;
|
|
6374
|
+
dittoSetStaticTCPClients(dittoHandle.deref(), tcpServers);
|
|
6212
6375
|
});
|
|
6213
6376
|
}
|
|
6214
6377
|
updateConnectWebsocketURLs(stateOld, stateNew) {
|
|
6215
6378
|
const ditto = this.ditto;
|
|
6216
6379
|
const dittoHandle = Bridge.ditto.handleFor(ditto);
|
|
6217
6380
|
ditto.deferClose(() => {
|
|
6218
|
-
|
|
6219
|
-
// IDEA: normalize URLs so that we don't connect to the same URL twice?
|
|
6220
|
-
const currentWebsocketURLs = Object.getOwnPropertyNames(this.websocketClientsByURL);
|
|
6221
|
-
const desiredWebsocketURLs = stateNew.effectiveTransportConfig.connect.websocketURLs.slice();
|
|
6222
|
-
const websocketURLsToConnectToSet = new Set(desiredWebsocketURLs);
|
|
6223
|
-
for (const websocketURL of currentWebsocketURLs)
|
|
6224
|
-
websocketURLsToConnectToSet.delete(websocketURL);
|
|
6225
|
-
const websocketURLsToDisconnectFromSet = new Set(currentWebsocketURLs);
|
|
6226
|
-
for (const websocketURL of desiredWebsocketURLs)
|
|
6227
|
-
websocketURLsToDisconnectFromSet.delete(websocketURL);
|
|
6228
|
-
const websocketURLsToConnectTo = websocketURLsToConnectToSet.values();
|
|
6229
|
-
const websocketURLsToDisconnectFrom = websocketURLsToDisconnectFromSet.values();
|
|
6381
|
+
const websocketURLs = stateNew.effectiveTransportConfig.connect.websocketURLs;
|
|
6230
6382
|
const routingHint = stateNew.effectiveTransportConfig.global.routingHint;
|
|
6231
|
-
|
|
6232
|
-
const websocketClientPointer = addWebsocketClient(dittoHandle.deref(), websocketURL, routingHint);
|
|
6233
|
-
const websocketClient = Bridge.websocketClient.bridge(websocketClientPointer);
|
|
6234
|
-
this.websocketClientsByURL[websocketURL] = websocketClient;
|
|
6235
|
-
}
|
|
6236
|
-
for (const websocketURL of websocketURLsToDisconnectFrom) {
|
|
6237
|
-
// REFACTOR: we have to make sure freeing the websocket handle is done
|
|
6238
|
-
// BEFORE the Ditto instance itself is freed.
|
|
6239
|
-
const websocketClient = this.websocketClientsByURL[websocketURL];
|
|
6240
|
-
if (!websocketClient)
|
|
6241
|
-
throw new Error(`Internal inconsistency, can't disconnect from websocket URL '${websocketURL}', no websocketClient found.`);
|
|
6242
|
-
const websocketClientPointer = Bridge.websocketClient.handleFor(websocketClient).deref();
|
|
6243
|
-
// Unregister to avoid double-free for this pointer, then free manually:
|
|
6244
|
-
Bridge.websocketClient.unregister(websocketClient);
|
|
6245
|
-
websocketClientFreeHandle(websocketClientPointer);
|
|
6246
|
-
delete this.websocketClientsByURL[websocketURL];
|
|
6247
|
-
}
|
|
6383
|
+
dittoSetStaticWebsocketClients(dittoHandle.deref(), websocketURLs, routingHint);
|
|
6248
6384
|
});
|
|
6249
6385
|
}
|
|
6250
6386
|
updateGlobal(stateOld, stateNew) {
|
|
@@ -6346,7 +6482,7 @@ class SubscriptionManager {
|
|
|
6346
6482
|
constructor(ditto) {
|
|
6347
6483
|
this.ditto = ditto;
|
|
6348
6484
|
this.subscriptions = {};
|
|
6349
|
-
this.finalizationRegistry = new FinalizationRegistry(this.
|
|
6485
|
+
this.finalizationRegistry = new FinalizationRegistry(this.removeWithContextInfo.bind(this));
|
|
6350
6486
|
}
|
|
6351
6487
|
/**
|
|
6352
6488
|
* Begin tracking a subscription instance and start it.
|
|
@@ -6371,7 +6507,7 @@ class SubscriptionManager {
|
|
|
6371
6507
|
throw new Error(`Internal inconsistency, tried to remove a subscription that is not tracked: ${subscription.contextInfo.id}`);
|
|
6372
6508
|
}
|
|
6373
6509
|
this.finalizationRegistry.unregister(subscription);
|
|
6374
|
-
this.
|
|
6510
|
+
this.removeWithContextInfo(subscription.contextInfo);
|
|
6375
6511
|
}
|
|
6376
6512
|
/**
|
|
6377
6513
|
* Stop tracking all subscriptions and cancel them.
|
|
@@ -6394,7 +6530,7 @@ class SubscriptionManager {
|
|
|
6394
6530
|
* registry.
|
|
6395
6531
|
*
|
|
6396
6532
|
* @internal */
|
|
6397
|
-
|
|
6533
|
+
removeWithContextInfo(contextInfo) {
|
|
6398
6534
|
const ditto = this.ditto;
|
|
6399
6535
|
const dittoHandle = Bridge.ditto.handleFor(ditto);
|
|
6400
6536
|
ditto.deferClose(() => {
|
|
@@ -6697,8 +6833,13 @@ class Ditto {
|
|
|
6697
6833
|
})();
|
|
6698
6834
|
const dittoPointer = dittoMake(uninitializedDittoX, identityConfig);
|
|
6699
6835
|
dittoAuthClientSetValidityListener(dittoPointer, function (...args) {
|
|
6700
|
-
|
|
6701
|
-
|
|
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
|
+
}
|
|
6702
6843
|
});
|
|
6703
6844
|
const isWebValid = dittoAuthClientIsWebValid(dittoPointer);
|
|
6704
6845
|
const isX509Valid = dittoAuthClientIsX509Valid(dittoPointer);
|
|
@@ -6762,12 +6903,59 @@ class Ditto {
|
|
|
6762
6903
|
this.attachmentFetcherManager = new AttachmentFetcherManager(this);
|
|
6763
6904
|
this.transportConditionsManager = new TransportConditionsManager(this);
|
|
6764
6905
|
this.subscriptionManager = new SubscriptionManager(this);
|
|
6906
|
+
disableDeadlockTimeoutWhenDebugging();
|
|
6765
6907
|
// WORKAROUND: see description above where the
|
|
6766
6908
|
// secondsRemainingUntilAuthenticationExpires variable is declared.
|
|
6767
6909
|
if (secondsRemainingUntilAuthenticationExpires !== null) {
|
|
6768
6910
|
this.auth['@ditto.authenticationExpiring'](secondsRemainingUntilAuthenticationExpires);
|
|
6769
6911
|
}
|
|
6770
6912
|
}
|
|
6913
|
+
/**
|
|
6914
|
+
* Don't terminate the process when callbacks are pending for a long time.
|
|
6915
|
+
*
|
|
6916
|
+
* Some methods in the Ditto library accept asynchronous functions as callback
|
|
6917
|
+
* parameters. If these asynchronous functions do not resolve within a certain
|
|
6918
|
+
* period of time after having been invoked by Ditto, deadlock detection gets
|
|
6919
|
+
* triggered, resulting in the termination of the process.
|
|
6920
|
+
*
|
|
6921
|
+
* When Ditto is executed in a Node.js environment with an interactive
|
|
6922
|
+
* debugger attached, this deadlock detection might get activated upon
|
|
6923
|
+
* encountering a breakpoint. Calling `Ditto.disableDeadlockDetection()`
|
|
6924
|
+
* disables this behavior, thus allowing the use of an interactive debugger
|
|
6925
|
+
* without triggering the deadlock detection.
|
|
6926
|
+
*
|
|
6927
|
+
* This feature is not available in the browser.
|
|
6928
|
+
*/
|
|
6929
|
+
static disableDeadlockDetection() {
|
|
6930
|
+
{
|
|
6931
|
+
const current = getDeadlockTimeout();
|
|
6932
|
+
if (current !== 0) {
|
|
6933
|
+
try {
|
|
6934
|
+
setDeadlockTimeout(0);
|
|
6935
|
+
}
|
|
6936
|
+
catch (e) {
|
|
6937
|
+
throw new Error(`Failed to disable deadlock detection: ${e === null || e === void 0 ? void 0 : e.message}`);
|
|
6938
|
+
}
|
|
6939
|
+
}
|
|
6940
|
+
}
|
|
6941
|
+
}
|
|
6942
|
+
/**
|
|
6943
|
+
* Returns `true` if deadlock detection is enabled.
|
|
6944
|
+
*
|
|
6945
|
+
* See
|
|
6946
|
+
* {@link Ditto.disableDeadlockDetection | Ditto.disableDeadlockDetection()}
|
|
6947
|
+
* for more information.
|
|
6948
|
+
*
|
|
6949
|
+
* This method always returns `false` in the browser where deadlock detection
|
|
6950
|
+
* is not available.
|
|
6951
|
+
*
|
|
6952
|
+
* @returns `true` if deadlock detection is enabled
|
|
6953
|
+
*/
|
|
6954
|
+
static hasDeadlockDetection() {
|
|
6955
|
+
{
|
|
6956
|
+
return getDeadlockTimeout() !== 0;
|
|
6957
|
+
}
|
|
6958
|
+
}
|
|
6771
6959
|
/**
|
|
6772
6960
|
* Check if the current environment supports running Ditto.
|
|
6773
6961
|
*
|
|
@@ -6779,7 +6967,7 @@ class Ditto {
|
|
|
6779
6967
|
*
|
|
6780
6968
|
* Internet Explorer is not supported.
|
|
6781
6969
|
*
|
|
6782
|
-
* @returns true if the environment is supported
|
|
6970
|
+
* @returns `true` if the environment is supported
|
|
6783
6971
|
*/
|
|
6784
6972
|
static isEnvironmentSupported() {
|
|
6785
6973
|
// From https://stackoverflow.com/questions/21825157/internet-explorer-11-detection
|
|
@@ -7130,6 +7318,31 @@ const checkAPIs = (_globalObject) => {
|
|
|
7130
7318
|
const requiredBrowserAPIs = ['BigInt', 'WeakRef', 'FinalizationRegistry'];
|
|
7131
7319
|
const globalObject = _globalObject || globalThis || global || window;
|
|
7132
7320
|
return requiredBrowserAPIs.every((apiName) => !!globalObject[apiName]);
|
|
7321
|
+
};
|
|
7322
|
+
/**
|
|
7323
|
+
* Disable deadlock timeout when Node.js is running with `--inspect` parameter.
|
|
7324
|
+
*
|
|
7325
|
+
* This heuristic is not failsafe as debugging mode can also be enabled by
|
|
7326
|
+
* sending a `SIGUSR1` signal to the process.
|
|
7327
|
+
*
|
|
7328
|
+
* @internal
|
|
7329
|
+
*/
|
|
7330
|
+
const disableDeadlockTimeoutWhenDebugging = () => {
|
|
7331
|
+
var _a, _b;
|
|
7332
|
+
{
|
|
7333
|
+
const hasInspector = process && ((_a = process === null || process === void 0 ? void 0 : process.execArgv) === null || _a === void 0 ? void 0 : _a.some((arg) => arg.includes('--inspect') || arg.includes('--debug')));
|
|
7334
|
+
// @ts-ignore - v8debug may be undefined
|
|
7335
|
+
const hasLegacyDebugMode = (process && ((_b = process === null || process === void 0 ? void 0 : process.execArgv) === null || _b === void 0 ? void 0 : _b.some((arg) => arg.includes('--debug')))) || typeof v8debug === 'object';
|
|
7336
|
+
if (hasInspector || hasLegacyDebugMode) {
|
|
7337
|
+
try {
|
|
7338
|
+
setDeadlockTimeout(0);
|
|
7339
|
+
Logger.warning('Detected Node running with inspector enabled, disabling deadlock timeout.');
|
|
7340
|
+
}
|
|
7341
|
+
catch (e) {
|
|
7342
|
+
Logger.error(`Detected Node running with inspector enabled but failed to disable deadlock timeout: ${e === null || e === void 0 ? void 0 : e.message}`);
|
|
7343
|
+
}
|
|
7344
|
+
}
|
|
7345
|
+
}
|
|
7133
7346
|
};
|
|
7134
7347
|
|
|
7135
7348
|
/**
|
|
@@ -7137,7 +7350,8 @@ const checkAPIs = (_globalObject) => {
|
|
|
7137
7350
|
*
|
|
7138
7351
|
* Use this in testing to ensure that all objects are properly garbage collected at the end of tests.
|
|
7139
7352
|
*
|
|
7140
|
-
* @returns an object with a key per bridge type,
|
|
7353
|
+
* @returns an object with a key per bridge type, set to the count of registered
|
|
7354
|
+
* objects.
|
|
7141
7355
|
*/
|
|
7142
7356
|
function getBridgeLoad() {
|
|
7143
7357
|
const countsByType = {};
|
|
@@ -7193,6 +7407,8 @@ exports.Document = Document;
|
|
|
7193
7407
|
exports.DocumentID = DocumentID;
|
|
7194
7408
|
exports.DocumentPath = DocumentPath;
|
|
7195
7409
|
exports.ExperimentalStore = ExperimentalStore;
|
|
7410
|
+
exports.ExperimentalSubscription = ExperimentalSubscription;
|
|
7411
|
+
exports.ExperimentalSubscriptionManager = ExperimentalSubscriptionManager;
|
|
7196
7412
|
exports.IdentityTypesRequiringOfflineLicenseToken = IdentityTypesRequiringOfflineLicenseToken;
|
|
7197
7413
|
exports.KeepAlive = KeepAlive;
|
|
7198
7414
|
exports.LiveQuery = LiveQuery;
|
|
@@ -7224,6 +7440,7 @@ exports.WriteTransactionPendingCursorOperation = WriteTransactionPendingCursorOp
|
|
|
7224
7440
|
exports.WriteTransactionPendingIDSpecificOperation = WriteTransactionPendingIDSpecificOperation;
|
|
7225
7441
|
exports.addressToString = addressToString;
|
|
7226
7442
|
exports.checkAPIs = checkAPIs;
|
|
7443
|
+
exports.disableDeadlockTimeoutWhenDebugging = disableDeadlockTimeoutWhenDebugging;
|
|
7227
7444
|
exports.getBridgeLoad = getBridgeLoad;
|
|
7228
7445
|
exports.init = init;
|
|
7229
7446
|
exports.validateDocumentIDCBOR = validateDocumentIDCBOR;
|
|
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.
|
|
3
|
+
"version": "4.2.0",
|
|
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",
|