@dittolive/ditto 4.4.0 → 4.4.2-alpha1
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 +395 -78
- 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 +139 -41
- package/web/ditto.es6.js +1 -1
- package/web/ditto.umd.js +1 -1
- package/web/ditto.wasm +0 -0
package/node/ditto.cjs.js
CHANGED
|
@@ -70,10 +70,14 @@ KeepAlive.finalizationRegistry = new FinalizationRegistry(clearInterval);
|
|
|
70
70
|
* alive, you have to keep a reference to the corresponding observer.
|
|
71
71
|
*/
|
|
72
72
|
class Observer {
|
|
73
|
+
/** @internal */
|
|
74
|
+
get token() {
|
|
75
|
+
return this._token;
|
|
76
|
+
}
|
|
73
77
|
/** @internal */
|
|
74
78
|
constructor(observerManager, token, options = {}) {
|
|
75
79
|
this.observerManager = observerManager;
|
|
76
|
-
this.
|
|
80
|
+
this._token = token;
|
|
77
81
|
this.options = options;
|
|
78
82
|
if (options.stopsWhenFinalized) {
|
|
79
83
|
Observer.finalizationRegistry.register(this, { observerManager, token }, this);
|
|
@@ -84,7 +88,7 @@ class Observer {
|
|
|
84
88
|
* method. Otherwise returns `false`.
|
|
85
89
|
*/
|
|
86
90
|
get isStopped() {
|
|
87
|
-
return this.observerManager.hasObserver(this.token);
|
|
91
|
+
return this.token !== undefined && this.observerManager.hasObserver(this.token);
|
|
88
92
|
}
|
|
89
93
|
/**
|
|
90
94
|
* Stops the observation. Calling this method multiple times has no effect.
|
|
@@ -92,7 +96,7 @@ class Observer {
|
|
|
92
96
|
stop() {
|
|
93
97
|
const token = this.token;
|
|
94
98
|
if (token) {
|
|
95
|
-
delete this
|
|
99
|
+
delete this._token;
|
|
96
100
|
Observer.finalizationRegistry.unregister(this);
|
|
97
101
|
this.observerManager.removeObserver(token);
|
|
98
102
|
}
|
|
@@ -121,24 +125,29 @@ class Value {
|
|
|
121
125
|
// NOTE: we use a token to detect private invocation of the constructor. This is
|
|
122
126
|
// not secure and just to prevent accidental private invocation on the client
|
|
123
127
|
// side.
|
|
124
|
-
const privateToken$1 = '
|
|
128
|
+
const privateToken$1 = Symbol('privateConstructorToken');
|
|
125
129
|
/**
|
|
126
130
|
* Represents a CRDT counter that can be upserted as part of a document or
|
|
127
131
|
* assigned to a property during an update of a document.
|
|
128
132
|
*/
|
|
129
133
|
class Counter {
|
|
134
|
+
/** The value of the counter. */
|
|
135
|
+
get value() {
|
|
136
|
+
return this._value;
|
|
137
|
+
}
|
|
130
138
|
/**
|
|
131
139
|
* Creates a new counter that can be used as part of a document's content.
|
|
132
140
|
*/
|
|
133
141
|
constructor() {
|
|
134
|
-
this.
|
|
142
|
+
this._value = 0.0;
|
|
135
143
|
}
|
|
136
144
|
/** @internal */
|
|
137
145
|
static '@ditto.create'(mutDoc, path, value) {
|
|
146
|
+
// @ts-expect-error - using hidden argument
|
|
138
147
|
const counter = mutDoc ? new MutableCounter(privateToken$1) : new Counter();
|
|
139
148
|
counter.mutDoc = mutDoc;
|
|
140
149
|
counter.path = path;
|
|
141
|
-
counter
|
|
150
|
+
counter._value = value;
|
|
142
151
|
return counter;
|
|
143
152
|
}
|
|
144
153
|
}
|
|
@@ -160,15 +169,15 @@ class MutableCounter extends Counter {
|
|
|
160
169
|
* otherwise an exception is thrown.
|
|
161
170
|
*/
|
|
162
171
|
increment(amount) {
|
|
163
|
-
const mutDoc = this
|
|
164
|
-
const path = this
|
|
172
|
+
const mutDoc = this.mutDoc;
|
|
173
|
+
const path = this.path;
|
|
165
174
|
if (!mutDoc) {
|
|
166
175
|
throw new Error(`Can't increment counter, only possible within the closure of a collection's update() method.`);
|
|
167
176
|
}
|
|
168
177
|
mutDoc.at(path)['@ditto.increment'](amount);
|
|
169
178
|
// We also increment the local value to make sure that the change is
|
|
170
179
|
// reflected locally as well as in the underlying document
|
|
171
|
-
this
|
|
180
|
+
this._value += amount;
|
|
172
181
|
}
|
|
173
182
|
/** @internal */
|
|
174
183
|
constructor() {
|
|
@@ -324,9 +333,11 @@ function ditto_document_set_cbor(...args) { return ditto.ditto_document_set_cbor
|
|
|
324
333
|
function ditto_document_set_cbor_with_timestamp(...args) { return ditto.ditto_document_set_cbor_with_timestamp(...args) }
|
|
325
334
|
function ditto_documents_hash(...args) { return ditto.ditto_documents_hash(...args) }
|
|
326
335
|
function ditto_documents_hash_mnemonic(...args) { return ditto.ditto_documents_hash_mnemonic(...args) }
|
|
336
|
+
function ditto_dql_response_free(...args) { return ditto.ditto_dql_response_free(...args) }
|
|
337
|
+
function ditto_dql_response_results(...args) { return ditto.ditto_dql_response_results(...args) }
|
|
338
|
+
function ditto_dql_result_free(...args) { return ditto.ditto_dql_result_free(...args) }
|
|
327
339
|
function ditto_error_message(...args) { return ditto.ditto_error_message(...args) }
|
|
328
340
|
function ditto_experimental_add_dql_subscription(...args) { return ditto.ditto_experimental_add_dql_subscription(...args) }
|
|
329
|
-
function ditto_experimental_exec_statement_str(...args) { return ditto.ditto_experimental_exec_statement_str(...args) }
|
|
330
341
|
function ditto_experimental_register_dql_live_query_str_detached(...args) { return ditto.ditto_experimental_register_dql_live_query_str_detached(...args) }
|
|
331
342
|
function ditto_experimental_remove_dql_subscription(...args) { return ditto.ditto_experimental_remove_dql_subscription(...args) }
|
|
332
343
|
function ditto_experimental_webhook_register_dql_live_query_str(...args) { return ditto.ditto_experimental_webhook_register_dql_live_query_str(...args) }
|
|
@@ -369,6 +380,7 @@ function ditto_register_transport_condition_changed_callback(...args) { return d
|
|
|
369
380
|
function ditto_remove_multicast_transport(...args) { return ditto.ditto_remove_multicast_transport(...args) }
|
|
370
381
|
function ditto_remove_subscription(...args) { return ditto.ditto_remove_subscription(...args) }
|
|
371
382
|
function ditto_resolve_attachment(...args) { return ditto.ditto_resolve_attachment(...args) }
|
|
383
|
+
function ditto_result_cbor(...args) { return ditto.ditto_result_cbor(...args) }
|
|
372
384
|
function ditto_run_garbage_collection(...args) { return ditto.ditto_run_garbage_collection(...args) }
|
|
373
385
|
function ditto_set_connect_retry_interval(...args) { return ditto.ditto_set_connect_retry_interval(...args) }
|
|
374
386
|
function ditto_set_device_name(...args) { return ditto.ditto_set_device_name(...args) }
|
|
@@ -385,6 +397,9 @@ function ditto_validate_document_id(...args) { return ditto.ditto_validate_docum
|
|
|
385
397
|
function ditto_write_transaction(...args) { return ditto.ditto_write_transaction(...args) }
|
|
386
398
|
function ditto_write_transaction_commit(...args) { return ditto.ditto_write_transaction_commit(...args) }
|
|
387
399
|
function ditto_write_transaction_rollback(...args) { return ditto.ditto_write_transaction_rollback(...args) }
|
|
400
|
+
function dittoffi_error_description(...args) { return ditto.dittoffi_error_description(...args) }
|
|
401
|
+
function dittoffi_error_free(...args) { return ditto.dittoffi_error_free(...args) }
|
|
402
|
+
function dittoffi_try_experimental_exec_statement_str(...args) { return ditto.dittoffi_try_experimental_exec_statement_str(...args) }
|
|
388
403
|
function getDeadlockTimeout$1(...args) { return ditto.getDeadlockTimeout(...args) }
|
|
389
404
|
function jsDocsToCDocs(...args) { return ditto.jsDocsToCDocs(...args) }
|
|
390
405
|
function mdns_client_free_handle(...args) { return ditto.mdns_client_free_handle(...args) }
|
|
@@ -636,13 +651,27 @@ async function collectionEvictQueryStr(ditto, collectionName, writeTransaction,
|
|
|
636
651
|
const queryX = bytesFromString(query);
|
|
637
652
|
return await ditto_collection_evict_query_str(ditto, collectionNameX, writeTransaction, queryX, queryArgsCBOR, orderBy, limit, offset);
|
|
638
653
|
}
|
|
639
|
-
/**
|
|
654
|
+
/**
|
|
655
|
+
* This FFI can error:
|
|
656
|
+
* - DQL parser error
|
|
657
|
+
* - Incorrect arguments to query parameters
|
|
658
|
+
* - Collection is not found.
|
|
659
|
+
*
|
|
660
|
+
* @internal
|
|
661
|
+
*/
|
|
640
662
|
async function experimentalExecQueryStr(ditto, writeTransaction, query, queryArgsCBOR) {
|
|
641
663
|
ensureInitialized();
|
|
642
664
|
const queryBytesPointer = bytesFromString(query);
|
|
643
665
|
// This doesn't need to convert error return codes into thrown js errors
|
|
644
666
|
// because the ffi implementation already does that.
|
|
645
|
-
|
|
667
|
+
const result = await dittoffi_try_experimental_exec_statement_str(ditto, writeTransaction, queryBytesPointer, queryArgsCBOR);
|
|
668
|
+
if (result.error !== null) {
|
|
669
|
+
// TODO: idiomatic js error class shim around `Pointer<FFIError>`.
|
|
670
|
+
const errorMsg = errorMessage(result.error);
|
|
671
|
+
dittoffi_error_free(result.error);
|
|
672
|
+
throw new Error(errorMsg);
|
|
673
|
+
}
|
|
674
|
+
return result.success;
|
|
646
675
|
}
|
|
647
676
|
/** @internal */
|
|
648
677
|
function addSubscription(ditto, collectionName, query, queryArgsCBOR, orderBy, limit, offset) {
|
|
@@ -674,6 +703,49 @@ function experimentalRemoveDqlSubscription(ditto, query, queryArgsCBOR) {
|
|
|
674
703
|
if (statusCode !== 0)
|
|
675
704
|
throw new Error(errorMessage() || `ditto_experimental_remove_dql_subscription() failed with error code: ${statusCode}`);
|
|
676
705
|
}
|
|
706
|
+
// ----------------------------------------------------------- DqlResponse ------
|
|
707
|
+
/**
|
|
708
|
+
* Doesn't error
|
|
709
|
+
*
|
|
710
|
+
* @internal
|
|
711
|
+
*/
|
|
712
|
+
function experimentalDqlResponseFree(self) {
|
|
713
|
+
ensureInitialized();
|
|
714
|
+
ditto_dql_response_free(self);
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Doesn't error
|
|
718
|
+
*
|
|
719
|
+
* @internal
|
|
720
|
+
*/
|
|
721
|
+
function experimentalDqlResultFree(self) {
|
|
722
|
+
ensureInitialized();
|
|
723
|
+
ditto_dql_result_free(self);
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Can error only on internal bug.
|
|
727
|
+
*
|
|
728
|
+
* @internal */
|
|
729
|
+
function experimentalDqlResponseResults(self) {
|
|
730
|
+
ensureInitialized();
|
|
731
|
+
return ditto_dql_response_results(self);
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* The result CBOR contains a map/object with fields and values.
|
|
735
|
+
* No CRDTs are present there as they are not needed. Currently
|
|
736
|
+
* only values from registers are returned and non-register fields are
|
|
737
|
+
* ignored. Reading values from other CRDTs will be supported with
|
|
738
|
+
* definition support.
|
|
739
|
+
*
|
|
740
|
+
* Doesn't error
|
|
741
|
+
*
|
|
742
|
+
* @internal
|
|
743
|
+
*/
|
|
744
|
+
function experimentalDqlResultCBOR(self) {
|
|
745
|
+
ensureInitialized();
|
|
746
|
+
const cborBytes = ditto_result_cbor(self);
|
|
747
|
+
return boxCBytesIntoBuffer(cborBytes);
|
|
748
|
+
}
|
|
677
749
|
// ------------------------------------------------------------ LiveQuery ------
|
|
678
750
|
/** @internal */
|
|
679
751
|
function liveQueryRegister(ditto, collectionName, query, queryArgsCBOR, orderBy, limit, offset, eventHandler,
|
|
@@ -1342,9 +1414,12 @@ function bytesFromString(jsString) {
|
|
|
1342
1414
|
return textEncoder.encode(`${jsString}\0`);
|
|
1343
1415
|
}
|
|
1344
1416
|
/** @internal */
|
|
1345
|
-
function errorMessage() {
|
|
1417
|
+
function errorMessage(ffiError) {
|
|
1346
1418
|
ensureInitialized();
|
|
1347
|
-
|
|
1419
|
+
// eslint-disable-next-line
|
|
1420
|
+
const errorMessageCString = (typeof ffiError === 'undefined'
|
|
1421
|
+
? ditto_error_message()
|
|
1422
|
+
: dittoffi_error_description(ffiError));
|
|
1348
1423
|
return boxCStringIntoString(errorMessageCString);
|
|
1349
1424
|
}
|
|
1350
1425
|
/** @internal */
|
|
@@ -1521,7 +1596,7 @@ function awdlDestroy(awdl) {
|
|
|
1521
1596
|
|
|
1522
1597
|
// NOTE: this is patched up with the actual build version by Jake task
|
|
1523
1598
|
// build:package and has to be a valid semantic version as defined here: https://semver.org.
|
|
1524
|
-
const fullBuildVersionString = '4.4.
|
|
1599
|
+
const fullBuildVersionString = '4.4.2-alpha1';
|
|
1525
1600
|
|
|
1526
1601
|
//
|
|
1527
1602
|
// Copyright © 2021 DittoLive Incorporated. All rights reserved.
|
|
@@ -1563,6 +1638,13 @@ async function init(options = {}) {
|
|
|
1563
1638
|
* log messages with the Ditto logging infrastructure.
|
|
1564
1639
|
*/
|
|
1565
1640
|
class Logger {
|
|
1641
|
+
/**
|
|
1642
|
+
* Registers a file path where logs will be written to, whenever Ditto wants
|
|
1643
|
+
* to issue a log (on _top_ of emitting the log to the console).
|
|
1644
|
+
*/
|
|
1645
|
+
static get logFile() {
|
|
1646
|
+
return this._logFile;
|
|
1647
|
+
}
|
|
1566
1648
|
/**
|
|
1567
1649
|
* On Node, registers a file path where logs will be written to, whenever
|
|
1568
1650
|
* Ditto wants to issue a log (on _top_ of emitting the log to the console).
|
|
@@ -1575,11 +1657,11 @@ class Logger {
|
|
|
1575
1657
|
static setLogFile(path) {
|
|
1576
1658
|
if (path) {
|
|
1577
1659
|
loggerSetLogFile(path);
|
|
1578
|
-
this
|
|
1660
|
+
this._logFile = path;
|
|
1579
1661
|
}
|
|
1580
1662
|
else {
|
|
1581
|
-
loggerSetLogFile(
|
|
1582
|
-
delete this
|
|
1663
|
+
loggerSetLogFile(undefined);
|
|
1664
|
+
delete this._logFile;
|
|
1583
1665
|
}
|
|
1584
1666
|
}
|
|
1585
1667
|
/**
|
|
@@ -1587,7 +1669,8 @@ class Logger {
|
|
|
1587
1669
|
* {@link setLogFile | setLogFile()} with it.
|
|
1588
1670
|
*/
|
|
1589
1671
|
static setLogFileURL(url) {
|
|
1590
|
-
|
|
1672
|
+
var _a;
|
|
1673
|
+
this.setLogFile((_a = url === null || url === void 0 ? void 0 : url.pathname) !== null && _a !== void 0 ? _a : null);
|
|
1591
1674
|
}
|
|
1592
1675
|
/** Whether the logger is currently enabled. */
|
|
1593
1676
|
static get enabled() {
|
|
@@ -1629,6 +1712,14 @@ class Logger {
|
|
|
1629
1712
|
static set minimumLogLevel(minimumLogLevel) {
|
|
1630
1713
|
loggerMinimumLogLevel(minimumLogLevel);
|
|
1631
1714
|
}
|
|
1715
|
+
/**
|
|
1716
|
+
* Returns the current custom log callback, `undefined` by default. See
|
|
1717
|
+
* {@link setCustomLogCallback | setCustomLogCallback()} for a detailed
|
|
1718
|
+
* description.
|
|
1719
|
+
*/
|
|
1720
|
+
static get customLogCallback() {
|
|
1721
|
+
return this._customLogCallback;
|
|
1722
|
+
}
|
|
1632
1723
|
/**
|
|
1633
1724
|
* Registers a custom callback that will be called to report each log entry.
|
|
1634
1725
|
*
|
|
@@ -1639,11 +1730,11 @@ class Logger {
|
|
|
1639
1730
|
static async setCustomLogCallback(callback) {
|
|
1640
1731
|
if (callback) {
|
|
1641
1732
|
await loggerSetCustomLogCb(callback);
|
|
1642
|
-
this
|
|
1733
|
+
this._customLogCallback = callback;
|
|
1643
1734
|
}
|
|
1644
1735
|
else {
|
|
1645
1736
|
await loggerSetCustomLogCb(null);
|
|
1646
|
-
delete this
|
|
1737
|
+
delete this._customLogCallback;
|
|
1647
1738
|
}
|
|
1648
1739
|
}
|
|
1649
1740
|
/**
|
|
@@ -1693,7 +1784,6 @@ class Logger {
|
|
|
1693
1784
|
static verbose(message) {
|
|
1694
1785
|
this.log('Verbose', message);
|
|
1695
1786
|
}
|
|
1696
|
-
// ------------------------------------------------------------ Private ------
|
|
1697
1787
|
constructor() {
|
|
1698
1788
|
throw new Error("Logger can't be instantiated, use its static properties & methods directly instead.");
|
|
1699
1789
|
}
|
|
@@ -2839,7 +2929,7 @@ function validateDocumentIDCBOR(idCBOR) {
|
|
|
2839
2929
|
}
|
|
2840
2930
|
|
|
2841
2931
|
//
|
|
2842
|
-
// Copyright ©
|
|
2932
|
+
// Copyright © 2023 DittoLive Incorporated. All rights reserved.
|
|
2843
2933
|
//
|
|
2844
2934
|
// REFACTOR: tweak the API to use Rust concepts of ownership. For example,
|
|
2845
2935
|
// register() could be named something like yieldOwnership() while unregister()
|
|
@@ -3284,6 +3374,10 @@ Bridge.document = new Bridge(documentFree);
|
|
|
3284
3374
|
/** @internal */
|
|
3285
3375
|
Bridge.mutableDocument = new Bridge(documentFree);
|
|
3286
3376
|
/** @internal */
|
|
3377
|
+
Bridge.dqlResponse = new Bridge(experimentalDqlResponseFree);
|
|
3378
|
+
/** @internal */
|
|
3379
|
+
Bridge.dqlResult = new Bridge(experimentalDqlResultFree);
|
|
3380
|
+
/** @internal */
|
|
3287
3381
|
Bridge.staticTCPClient = new Bridge(staticTCPClientFreeHandle);
|
|
3288
3382
|
/** @internal */
|
|
3289
3383
|
Bridge.websocketClient = new Bridge(websocketClientFreeHandle);
|
|
@@ -4584,6 +4678,12 @@ class ObserverManager {
|
|
|
4584
4678
|
* `OnlineWithAuthentication` or an `Online` identity.
|
|
4585
4679
|
*/
|
|
4586
4680
|
class Authenticator {
|
|
4681
|
+
/**
|
|
4682
|
+
Returns the current authentication status.
|
|
4683
|
+
*/
|
|
4684
|
+
get status() {
|
|
4685
|
+
return this._status;
|
|
4686
|
+
}
|
|
4587
4687
|
/**
|
|
4588
4688
|
* Log in to Ditto with a third-party token. Throws if authentication is not
|
|
4589
4689
|
* available, which can be checked with {@link loginSupported}.
|
|
@@ -4632,7 +4732,7 @@ class Authenticator {
|
|
|
4632
4732
|
/** @internal */
|
|
4633
4733
|
constructor(keepAlive) {
|
|
4634
4734
|
this.keepAlive = keepAlive;
|
|
4635
|
-
this.
|
|
4735
|
+
this._status = { isAuthenticated: false, userID: null };
|
|
4636
4736
|
this.loginSupported = false;
|
|
4637
4737
|
this.observerManager = new ObserverManager('AuthenticationStatusObservation', { keepAlive });
|
|
4638
4738
|
}
|
|
@@ -4693,13 +4793,13 @@ class OnlineAuthenticator extends Authenticator {
|
|
|
4693
4793
|
return ditto.deferCloseAsync(async () => {
|
|
4694
4794
|
await dittoAuthClientLogout(dittoPointer);
|
|
4695
4795
|
ditto.stopSync();
|
|
4696
|
-
cleanupFn === null || cleanupFn === void 0 ? void 0 : cleanupFn(
|
|
4796
|
+
cleanupFn === null || cleanupFn === void 0 ? void 0 : cleanupFn(ditto);
|
|
4697
4797
|
});
|
|
4698
4798
|
}
|
|
4699
4799
|
constructor(keepAlive, ditto, authenticationHandler) {
|
|
4700
4800
|
super(keepAlive);
|
|
4701
|
-
this
|
|
4702
|
-
this
|
|
4801
|
+
this.loginSupported = true;
|
|
4802
|
+
this._status = { isAuthenticated: false, userID: null };
|
|
4703
4803
|
this.ditto = new WeakRef(ditto);
|
|
4704
4804
|
this.authenticationHandler = authenticationHandler;
|
|
4705
4805
|
this.updateAndNotify(false);
|
|
@@ -4734,7 +4834,7 @@ class OnlineAuthenticator extends Authenticator {
|
|
|
4734
4834
|
const isAuthenticated = dittoAuthClientIsWebValid(dittoPointer);
|
|
4735
4835
|
const userID = dittoAuthClientUserID(dittoPointer);
|
|
4736
4836
|
const status = { isAuthenticated, userID };
|
|
4737
|
-
this
|
|
4837
|
+
this._status = status;
|
|
4738
4838
|
if (shouldNotify) {
|
|
4739
4839
|
const sameStatus = !!wasAuthenticated === !!isAuthenticated && previousUserID === userID;
|
|
4740
4840
|
if (!sameStatus) {
|
|
@@ -4927,6 +5027,7 @@ class TransportConfig {
|
|
|
4927
5027
|
* Create a new transport config initialized with the default settings.
|
|
4928
5028
|
*/
|
|
4929
5029
|
constructor() {
|
|
5030
|
+
this._isFrozen = false;
|
|
4930
5031
|
this.peerToPeer = {
|
|
4931
5032
|
bluetoothLE: { isEnabled: false },
|
|
4932
5033
|
awdl: { isEnabled: false },
|
|
@@ -4963,13 +5064,20 @@ class TransportConfig {
|
|
|
4963
5064
|
this.peerToPeer.lan.isEnabled = enabled;
|
|
4964
5065
|
this.peerToPeer.awdl.isEnabled = enabled;
|
|
4965
5066
|
}
|
|
5067
|
+
/**
|
|
5068
|
+
* Returns `true` if the transport configuration is frozen, otherwise
|
|
5069
|
+
* returns `false`.
|
|
5070
|
+
*/
|
|
5071
|
+
get isFrozen() {
|
|
5072
|
+
return this._isFrozen;
|
|
5073
|
+
}
|
|
4966
5074
|
/**
|
|
4967
5075
|
* (Deep) freezes the receiver such that it can't be modified anymore.
|
|
4968
5076
|
*/
|
|
4969
5077
|
freeze() {
|
|
4970
5078
|
if (this.isFrozen)
|
|
4971
5079
|
return this;
|
|
4972
|
-
this
|
|
5080
|
+
this._isFrozen = true;
|
|
4973
5081
|
Object.freeze(this.peerToPeer.bluetoothLE);
|
|
4974
5082
|
Object.freeze(this.peerToPeer.awdl);
|
|
4975
5083
|
Object.freeze(this.peerToPeer.lan);
|
|
@@ -4996,8 +5104,8 @@ class TransportConfig {
|
|
|
4996
5104
|
copy.connect.tcpServers = this.connect.tcpServers.slice();
|
|
4997
5105
|
copy.connect.websocketURLs = this.connect.websocketURLs.slice();
|
|
4998
5106
|
copy.connect.retryInterval = this.connect.retryInterval;
|
|
4999
|
-
copy.listen
|
|
5000
|
-
copy.listen
|
|
5107
|
+
copy.listen.tcp = { ...this.listen.tcp };
|
|
5108
|
+
copy.listen.http = { ...this.listen.http };
|
|
5001
5109
|
copy.global.syncGroup = this.global.syncGroup;
|
|
5002
5110
|
copy.global.routingHint = this.global.routingHint;
|
|
5003
5111
|
return copy;
|
|
@@ -5008,6 +5116,7 @@ class TransportConfig {
|
|
|
5008
5116
|
*/
|
|
5009
5117
|
static areListenTCPsEqual(left, right) {
|
|
5010
5118
|
/* eslint-disable */
|
|
5119
|
+
// prettier-ignore
|
|
5011
5120
|
return (left.isEnabled === right.isEnabled &&
|
|
5012
5121
|
left.interfaceIP === right.interfaceIP &&
|
|
5013
5122
|
left.port === right.port);
|
|
@@ -5019,6 +5128,7 @@ class TransportConfig {
|
|
|
5019
5128
|
*/
|
|
5020
5129
|
static areListenHTTPsEqual(left, right) {
|
|
5021
5130
|
/* eslint-disable */
|
|
5131
|
+
// prettier-ignore
|
|
5022
5132
|
return (left.isEnabled === right.isEnabled &&
|
|
5023
5133
|
left.interfaceIP === right.interfaceIP &&
|
|
5024
5134
|
left.port === right.port &&
|
|
@@ -5042,6 +5152,13 @@ class TransportConfig {
|
|
|
5042
5152
|
* try to be kept up-to-date with the latest changes from remote peers.
|
|
5043
5153
|
*/
|
|
5044
5154
|
class Subscription {
|
|
5155
|
+
/**
|
|
5156
|
+
* Returns `true` if subscription has been explicitly cancelled, `false`
|
|
5157
|
+
* otherwise.
|
|
5158
|
+
*/
|
|
5159
|
+
get isCancelled() {
|
|
5160
|
+
return this._isCancelled;
|
|
5161
|
+
}
|
|
5045
5162
|
/**
|
|
5046
5163
|
* The name of the collection that the subscription is based on.
|
|
5047
5164
|
*/
|
|
@@ -5053,18 +5170,14 @@ class Subscription {
|
|
|
5053
5170
|
*/
|
|
5054
5171
|
cancel() {
|
|
5055
5172
|
if (!this.isCancelled) {
|
|
5056
|
-
this
|
|
5173
|
+
this._isCancelled = true;
|
|
5057
5174
|
this.manager.remove(this);
|
|
5058
5175
|
}
|
|
5059
5176
|
}
|
|
5060
5177
|
// ----------------------------------------------------------- Internal ------
|
|
5061
5178
|
/** @internal */
|
|
5062
5179
|
constructor(collection, query, queryArgsCBOR, orderBys, limit, offset) {
|
|
5063
|
-
|
|
5064
|
-
* Returns `true` if subscription has been explicitly cancelled, `false`
|
|
5065
|
-
* otherwise.
|
|
5066
|
-
*/
|
|
5067
|
-
this.isCancelled = false;
|
|
5180
|
+
this._isCancelled = false;
|
|
5068
5181
|
// Query should be validated at this point.
|
|
5069
5182
|
this.query = query;
|
|
5070
5183
|
this.queryArgsCBOR = queryArgsCBOR;
|
|
@@ -5230,6 +5343,9 @@ class LiveQuery {
|
|
|
5230
5343
|
this.liveQueryManager.stopLiveQuery(this);
|
|
5231
5344
|
}
|
|
5232
5345
|
}
|
|
5346
|
+
get liveQueryID() {
|
|
5347
|
+
return this._liveQueryID;
|
|
5348
|
+
}
|
|
5233
5349
|
/** @internal */
|
|
5234
5350
|
constructor(query, queryArgs, queryArgsCBOR, orderBys, limit, offset, collection, handler) {
|
|
5235
5351
|
// Query should be validated at this point.
|
|
@@ -5277,11 +5393,11 @@ class LiveQuery {
|
|
|
5277
5393
|
}
|
|
5278
5394
|
handler(documents, event, signalNext);
|
|
5279
5395
|
});
|
|
5280
|
-
if (!liveQueryID) {
|
|
5281
|
-
throw new Error("Internal inconsistency, couldn't create a valid live query ID.");
|
|
5282
|
-
}
|
|
5283
|
-
this['liveQueryID'] = liveQueryID;
|
|
5284
5396
|
});
|
|
5397
|
+
if (!liveQueryID) {
|
|
5398
|
+
throw new Error("Internal inconsistency, couldn't create a valid live query ID.");
|
|
5399
|
+
}
|
|
5400
|
+
this._liveQueryID = liveQueryID;
|
|
5285
5401
|
}
|
|
5286
5402
|
/** @internal */
|
|
5287
5403
|
async signalNext() {
|
|
@@ -6360,26 +6476,29 @@ class ExperimentalReplicationSubscriptionManager {
|
|
|
6360
6476
|
* @internal
|
|
6361
6477
|
*/
|
|
6362
6478
|
class ExperimentalReplicationSubscription {
|
|
6479
|
+
/**
|
|
6480
|
+
* `true` when {@link cancel | cancel()} has been called or the Ditto instance
|
|
6481
|
+
* managing this replication subscription has been closed.
|
|
6482
|
+
*/
|
|
6483
|
+
// Recording this information on the replication subscription instance itself
|
|
6484
|
+
// allows working with it even after Ditto has been closed and the replication
|
|
6485
|
+
// subscription manager can not be accessed anymore.
|
|
6486
|
+
get isCancelled() {
|
|
6487
|
+
return this._isCancelled;
|
|
6488
|
+
}
|
|
6363
6489
|
/**
|
|
6364
6490
|
* Cancels a replication subscription and releases all associated resources.
|
|
6365
6491
|
*/
|
|
6366
6492
|
cancel() {
|
|
6367
6493
|
if (!this.isCancelled) {
|
|
6368
|
-
this
|
|
6494
|
+
this._isCancelled = true;
|
|
6369
6495
|
this.manager.removeSubscription(this);
|
|
6370
6496
|
}
|
|
6371
6497
|
}
|
|
6372
6498
|
// --------------------------- Internal --------------------------------------
|
|
6373
6499
|
/** @internal */
|
|
6374
6500
|
constructor(manager, query, queryArgsCBOR) {
|
|
6375
|
-
|
|
6376
|
-
* `true` when {@link cancel | cancel()} has been called or the Ditto instance
|
|
6377
|
-
* managing this replication subscription has been closed.
|
|
6378
|
-
*/
|
|
6379
|
-
// Recording this information on the replication subscription instance itself
|
|
6380
|
-
// allows working with it even after Ditto has been closed and the replication
|
|
6381
|
-
// subscription manager can not be accessed anymore.
|
|
6382
|
-
this.isCancelled = false;
|
|
6501
|
+
this._isCancelled = false;
|
|
6383
6502
|
this.query = query;
|
|
6384
6503
|
this.queryArgsCBOR = queryArgsCBOR;
|
|
6385
6504
|
this.contextInfo = {
|
|
@@ -6480,14 +6599,23 @@ class ExperimentalStore {
|
|
|
6480
6599
|
* The returned {@link ExperimentalReplicationSubscription} object must be kept in scope
|
|
6481
6600
|
* for as long as you want to keep receiving updates.
|
|
6482
6601
|
*
|
|
6602
|
+
* Use placeholders to incorporate values from the optional `queryArguments`
|
|
6603
|
+
* parameter into the query. The keys of the `queryArguments` object must
|
|
6604
|
+
* match the placeholders used within the query. You can not use placeholders
|
|
6605
|
+
* in the `FROM` clause.
|
|
6606
|
+
*
|
|
6483
6607
|
* @internal
|
|
6484
6608
|
* @param query a Ditto Query Language query string of the form SELECT * FROM
|
|
6485
6609
|
* collection WHERE expression
|
|
6610
|
+
* @param queryArguments an object with values to be incorporated into a
|
|
6611
|
+
* query. The keys of the object must match the placeholders used within the
|
|
6612
|
+
* query.
|
|
6486
6613
|
* @returns {@link ExperimentalReplicationSubscription} object
|
|
6487
6614
|
*/
|
|
6488
|
-
addReplicationSubscription(query) {
|
|
6615
|
+
addReplicationSubscription(query, queryArguments) {
|
|
6489
6616
|
Logger.debug(`ExperimentalStore.subscribe(query = ${query})`);
|
|
6490
|
-
|
|
6617
|
+
const queryArgumentsCBOR = queryArguments ? CBOR.encode(queryArguments) : null;
|
|
6618
|
+
return new ExperimentalReplicationSubscription(this.replicationSubscriptionManager, query, queryArgumentsCBOR);
|
|
6491
6619
|
}
|
|
6492
6620
|
/** @internal */
|
|
6493
6621
|
close() {
|
|
@@ -6497,18 +6625,25 @@ class ExperimentalStore {
|
|
|
6497
6625
|
/**
|
|
6498
6626
|
* Executes the given query in the local store and returns the result.
|
|
6499
6627
|
*
|
|
6628
|
+
* Use placeholders to incorporate values from the optional `queryArguments`
|
|
6629
|
+
* parameter into the query. The keys of the `queryArguments` object must
|
|
6630
|
+
* match the placeholders used within the query. You can not use placeholders
|
|
6631
|
+
* in the `FROM` clause.
|
|
6632
|
+
*
|
|
6500
6633
|
* Limitations:
|
|
6501
6634
|
*
|
|
6502
6635
|
* - Supports `SELECT * FROM <collection name>` with optional `WHERE
|
|
6503
6636
|
* <expression>`
|
|
6504
|
-
* - No query parameters as function arguments yet
|
|
6505
6637
|
* - No transactions
|
|
6506
6638
|
*
|
|
6507
6639
|
* @internal
|
|
6508
6640
|
* @param query a Ditto Query Language query string
|
|
6641
|
+
* @param queryArguments an object with values to be incorporated into a
|
|
6642
|
+
* query. The keys of the object must match the placeholders used within the
|
|
6643
|
+
* query.
|
|
6509
6644
|
* @returns a promise for an array of Ditto documents matching the query
|
|
6510
6645
|
*/
|
|
6511
|
-
async execute(query) {
|
|
6646
|
+
async execute(query, queryArguments) {
|
|
6512
6647
|
Logger.debug(`ExperimentalStore.execute(query = ${query})`);
|
|
6513
6648
|
const ditto = this.ditto;
|
|
6514
6649
|
const dittoHandle = Bridge.ditto.handleFor(ditto);
|
|
@@ -6516,12 +6651,11 @@ class ExperimentalStore {
|
|
|
6516
6651
|
// A one-off query execution uses a transaction internally but a
|
|
6517
6652
|
// transaction API is not implemented yet.
|
|
6518
6653
|
const writeTransaction = null;
|
|
6519
|
-
|
|
6520
|
-
const
|
|
6521
|
-
|
|
6522
|
-
return await experimentalExecQueryStr(dittoHandle.deref(), writeTransaction, query, queryParameters);
|
|
6654
|
+
const queryArgumentsCBOR = queryArguments ? CBOR.encode(queryArguments) : null;
|
|
6655
|
+
const responsePointer = await performAsyncToWorkaroundNonAsyncFFIAPI(async () => {
|
|
6656
|
+
return await experimentalExecQueryStr(dittoHandle.deref(), writeTransaction, query, queryArgumentsCBOR);
|
|
6523
6657
|
});
|
|
6524
|
-
return
|
|
6658
|
+
return Bridge.dqlResponse.bridge(responsePointer);
|
|
6525
6659
|
});
|
|
6526
6660
|
}
|
|
6527
6661
|
}
|
|
@@ -7303,6 +7437,9 @@ class TransportConditionsManager extends ObserverManager {
|
|
|
7303
7437
|
//
|
|
7304
7438
|
/** @internal */
|
|
7305
7439
|
class Sync {
|
|
7440
|
+
get parameters() {
|
|
7441
|
+
return this._parameters;
|
|
7442
|
+
}
|
|
7306
7443
|
constructor(ditto) {
|
|
7307
7444
|
this.bluetoothLETransportPointer = null;
|
|
7308
7445
|
this.awdlTransportPointer = null;
|
|
@@ -7313,11 +7450,11 @@ class Sync {
|
|
|
7313
7450
|
const transportConfig = new TransportConfig();
|
|
7314
7451
|
const parameters = { identity, transportConfig, ditto, isWebValid: false, isX509Valid: false, isSyncActive: false };
|
|
7315
7452
|
this.ditto = ditto;
|
|
7316
|
-
this.
|
|
7453
|
+
this._parameters = parameters;
|
|
7317
7454
|
this.state = stateFrom(parameters);
|
|
7318
7455
|
}
|
|
7319
7456
|
update(parameters) {
|
|
7320
|
-
this
|
|
7457
|
+
this._parameters = { ...parameters };
|
|
7321
7458
|
// NOTE: updating is a two-step process. Given all parameters, we
|
|
7322
7459
|
// first compute the final "state" we want the transports stuff to
|
|
7323
7460
|
// be in via the `stateFrom()` function. We then take that "desired" state
|
|
@@ -7897,6 +8034,32 @@ class Ditto {
|
|
|
7897
8034
|
Logger.warning("⚠️ Deprecation Warning: The 'Ditto.path' property is deprecated. Please update your code to use the new 'Ditto.persistenceDirectory' property instead.");
|
|
7898
8035
|
return this.persistenceDirectory;
|
|
7899
8036
|
}
|
|
8037
|
+
/**
|
|
8038
|
+
* Returns `true` if an offline license token has been set, otherwise returns `false`.
|
|
8039
|
+
*
|
|
8040
|
+
* @see {@link setOfflineOnlyLicenseToken | setOfflineOnlyLicenseToken()}
|
|
8041
|
+
*/
|
|
8042
|
+
get isActivated() {
|
|
8043
|
+
var _a;
|
|
8044
|
+
return (_a = this._isActivated) !== null && _a !== void 0 ? _a : false;
|
|
8045
|
+
}
|
|
8046
|
+
/**
|
|
8047
|
+
* `true` once {@link close | Ditto.close()} has been called, otherwise
|
|
8048
|
+
* `false`.
|
|
8049
|
+
*/
|
|
8050
|
+
get isClosed() {
|
|
8051
|
+
var _a;
|
|
8052
|
+
return (_a = this._isClosed) !== null && _a !== void 0 ? _a : false;
|
|
8053
|
+
}
|
|
8054
|
+
/**
|
|
8055
|
+
* Returns `true` if sync is active, otherwise returns `false`. Use
|
|
8056
|
+
* {@link startSync | startSync()} to activate and
|
|
8057
|
+
* {@link stopSync | stopSync()} to deactivate sync.
|
|
8058
|
+
*/
|
|
8059
|
+
get isSyncActive() {
|
|
8060
|
+
var _a;
|
|
8061
|
+
return (_a = this._isSyncActive) !== null && _a !== void 0 ? _a : false;
|
|
8062
|
+
}
|
|
7900
8063
|
/**
|
|
7901
8064
|
* Initializes a new `Ditto` instance.
|
|
7902
8065
|
*
|
|
@@ -7908,20 +8071,18 @@ class Ditto {
|
|
|
7908
8071
|
* `offlinePlayground` with `appID` being the empty string `''`.
|
|
7909
8072
|
*
|
|
7910
8073
|
* @param persistenceDirectory optional string containing a directory path
|
|
7911
|
-
* that Ditto will use for persistence. Defaults to `"ditto"`.
|
|
8074
|
+
* that Ditto will use for persistence. Defaults to `"ditto"`. On Windows,
|
|
8075
|
+
* the path will be automatically normalized.
|
|
7912
8076
|
*
|
|
7913
8077
|
* @see {@link Ditto.identity}
|
|
7914
8078
|
* @see {@link Ditto.persistenceDirectory}
|
|
7915
8079
|
*/
|
|
7916
8080
|
constructor(identity, persistenceDirectory) {
|
|
7917
|
-
/**
|
|
7918
|
-
* `true` once {@link close | Ditto.close()} has been called, otherwise
|
|
7919
|
-
* `false`.
|
|
7920
|
-
*/
|
|
7921
|
-
this.isClosed = false;
|
|
7922
8081
|
this.deferCloseAllowed = true;
|
|
7923
8082
|
this.isWebValid = false;
|
|
7924
8083
|
this.isX509Valid = false;
|
|
8084
|
+
this._isSyncActive = false;
|
|
8085
|
+
this._isClosed = false;
|
|
7925
8086
|
/** Set of pending operations that need to complete before the Ditto instance can be closed in a safe manner. */
|
|
7926
8087
|
this.pendingOperations = new Set();
|
|
7927
8088
|
if (!Ditto.isEnvironmentSupported()) {
|
|
@@ -8035,14 +8196,14 @@ class Ditto {
|
|
|
8035
8196
|
//
|
|
8036
8197
|
this.appID = appID;
|
|
8037
8198
|
this.siteID = siteID;
|
|
8038
|
-
this.
|
|
8199
|
+
this._transportConfig = transportConfig.copy().freeze();
|
|
8039
8200
|
this.isX509Valid = isX509Valid;
|
|
8040
8201
|
this.isWebValid = isWebValid;
|
|
8041
8202
|
this.sync = new Sync(this);
|
|
8042
8203
|
this.sync.update({ isSyncActive: false, isX509Valid, isWebValid, identity: validIdentity, ditto: this, transportConfig });
|
|
8043
8204
|
// We don't need a license when running in the browser, so we
|
|
8044
8205
|
// mark the instance as activated from the get-go in that case.
|
|
8045
|
-
this.
|
|
8206
|
+
this._isActivated = !IdentityTypesRequiringOfflineLicenseToken.includes(validIdentity.type);
|
|
8046
8207
|
this.store = new Store(this);
|
|
8047
8208
|
this.presence = new Presence(this);
|
|
8048
8209
|
this.presenceManager = new PresenceManager(this);
|
|
@@ -8152,11 +8313,42 @@ class Ditto {
|
|
|
8152
8313
|
}
|
|
8153
8314
|
{
|
|
8154
8315
|
const fs = require('fs');
|
|
8316
|
+
const path = require('path');
|
|
8317
|
+
if (process.platform === 'win32') {
|
|
8318
|
+
// Normalize the path on Windows to prevent issues with its max path
|
|
8319
|
+
// length. Windows has a hard limit at 260 characters for file paths
|
|
8320
|
+
// [1]. When this limit is exceeded reads and writes may fail.
|
|
8321
|
+
//
|
|
8322
|
+
// [1]: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation
|
|
8323
|
+
validatedPath = `\\\\?\\${path.resolve(validatedPath)}`;
|
|
8324
|
+
}
|
|
8325
|
+
const absolutePath = path.resolve(validatedPath);
|
|
8326
|
+
// We check if the directory exists before creating it to be able to
|
|
8327
|
+
// provide a more helpful error message in case the directory is not
|
|
8328
|
+
// writable.
|
|
8329
|
+
let isDirectoryExisting = false;
|
|
8155
8330
|
try {
|
|
8156
|
-
fs.
|
|
8331
|
+
fs.statSync(absolutePath);
|
|
8332
|
+
isDirectoryExisting = true;
|
|
8157
8333
|
}
|
|
8158
8334
|
catch (error) {
|
|
8159
|
-
|
|
8335
|
+
// On Windows, permissions can prevent us from checking if the directory
|
|
8336
|
+
// exists: https://github.com/nodejs/node/issues/35853
|
|
8337
|
+
if (error.code === 'EPERM') {
|
|
8338
|
+
throw new Error(`Missing read or write permissions for the persistence directory path '${absolutePath}'. Please update the permissions or use a different path.`);
|
|
8339
|
+
}
|
|
8340
|
+
}
|
|
8341
|
+
if (!isDirectoryExisting) {
|
|
8342
|
+
try {
|
|
8343
|
+
fs.mkdirSync(absolutePath, { recursive: true });
|
|
8344
|
+
}
|
|
8345
|
+
catch (error) {
|
|
8346
|
+
throw new Error(`Failed to create persistence directory at path '${absolutePath}'.\n\n${error}`);
|
|
8347
|
+
}
|
|
8348
|
+
}
|
|
8349
|
+
// Caveat: It is still possible that these permissions are revoked during runtime.
|
|
8350
|
+
if (!isDirectoryWritable(absolutePath)) {
|
|
8351
|
+
throw new Error(`Missing read or write permissions for the persistence directory path '${absolutePath}'. Please update the permissions or use a different path.`);
|
|
8160
8352
|
}
|
|
8161
8353
|
}
|
|
8162
8354
|
return validatedPath;
|
|
@@ -8175,11 +8367,11 @@ class Ditto {
|
|
|
8175
8367
|
if (IdentityTypesRequiringOfflineLicenseToken.includes(this.identity.type)) {
|
|
8176
8368
|
const { result, errorMessage } = verifyLicense(licenseToken);
|
|
8177
8369
|
if (result !== 'LicenseOk') {
|
|
8178
|
-
this
|
|
8370
|
+
this._isActivated = false;
|
|
8179
8371
|
throw new Error(errorMessage);
|
|
8180
8372
|
}
|
|
8181
8373
|
else {
|
|
8182
|
-
this
|
|
8374
|
+
this._isActivated = true;
|
|
8183
8375
|
}
|
|
8184
8376
|
}
|
|
8185
8377
|
else {
|
|
@@ -8187,6 +8379,20 @@ class Ditto {
|
|
|
8187
8379
|
}
|
|
8188
8380
|
}
|
|
8189
8381
|
}
|
|
8382
|
+
/**
|
|
8383
|
+
* Returns the current transport configuration, frozen. If you want to modify
|
|
8384
|
+
* the transport config, make a {@link TransportConfig.copy | copy} first. Or
|
|
8385
|
+
* use the {@link updateTransportConfig | updateTransportConfig()}
|
|
8386
|
+
* convenience method. By default peer-to-peer transports (Bluetooth, WiFi,
|
|
8387
|
+
* and AWDL) are enabled if available in the current environment
|
|
8388
|
+
* (Web, Node, OS, etc.).
|
|
8389
|
+
*
|
|
8390
|
+
* @see {@link setTransportConfig | setTransportConfig()}
|
|
8391
|
+
* @see {@link updateTransportConfig | updateTransportConfig()}
|
|
8392
|
+
*/
|
|
8393
|
+
get transportConfig() {
|
|
8394
|
+
return this._transportConfig;
|
|
8395
|
+
}
|
|
8190
8396
|
/**
|
|
8191
8397
|
* Assigns a new transports configuration. By default peer-to-peer transports
|
|
8192
8398
|
* (Bluetooth, WiFi, and AWDL) are enabled. You may use this method to alter
|
|
@@ -8197,7 +8403,7 @@ class Ditto {
|
|
|
8197
8403
|
* @see {@link updateTransportConfig | updateTransportConfig()}
|
|
8198
8404
|
*/
|
|
8199
8405
|
setTransportConfig(transportConfig) {
|
|
8200
|
-
this
|
|
8406
|
+
this._transportConfig = transportConfig.copy().freeze();
|
|
8201
8407
|
const transportConfigNew = this.transportConfig;
|
|
8202
8408
|
const identity = this.identity;
|
|
8203
8409
|
const isWebValid = this.isWebValid;
|
|
@@ -8334,7 +8540,7 @@ class Ditto {
|
|
|
8334
8540
|
if (this.isClosed) {
|
|
8335
8541
|
return;
|
|
8336
8542
|
}
|
|
8337
|
-
this
|
|
8543
|
+
this._isClosed = true;
|
|
8338
8544
|
this.stopSync();
|
|
8339
8545
|
this.store.close();
|
|
8340
8546
|
this.presence.close();
|
|
@@ -8424,8 +8630,8 @@ class Ditto {
|
|
|
8424
8630
|
}
|
|
8425
8631
|
validateIdentity(identity) {
|
|
8426
8632
|
const validIdentity = { ...identity };
|
|
8427
|
-
|
|
8428
|
-
const appID = identity
|
|
8633
|
+
// @ts-expect-error we validate the existence of the value below.
|
|
8634
|
+
const appID = identity.appID;
|
|
8429
8635
|
if (!['offlinePlayground', 'sharedKey', 'manual', 'onlinePlayground', 'onlineWithAuthentication'].includes(identity.type)) {
|
|
8430
8636
|
throw new Error(`Can't create Ditto instance, unknown identity type: ${identity.type}`);
|
|
8431
8637
|
}
|
|
@@ -8471,7 +8677,7 @@ class Ditto {
|
|
|
8471
8677
|
if (this.isSyncActive && !flag) {
|
|
8472
8678
|
this.keepAlive.release('sync');
|
|
8473
8679
|
}
|
|
8474
|
-
this
|
|
8680
|
+
this._isSyncActive = flag;
|
|
8475
8681
|
const isWebValid = this.isWebValid;
|
|
8476
8682
|
const isX509Valid = this.isX509Valid;
|
|
8477
8683
|
const identity = this.identity;
|
|
@@ -8535,6 +8741,54 @@ const disableDeadlockTimeoutWhenDebugging = () => {
|
|
|
8535
8741
|
}
|
|
8536
8742
|
}
|
|
8537
8743
|
};
|
|
8744
|
+
/**
|
|
8745
|
+
* Return true if we have read and write permissions for the given directory.
|
|
8746
|
+
*
|
|
8747
|
+
* Always returns `true` in the browser.
|
|
8748
|
+
*
|
|
8749
|
+
* Uses `fs.accessSync()` on all platforms except Windows, where ACLs are not
|
|
8750
|
+
* checked by that method [1]. On Windows, we try writing and removing a temp
|
|
8751
|
+
* file to the given path instead.
|
|
8752
|
+
*
|
|
8753
|
+
* [1]:
|
|
8754
|
+
* https://nodejs.org/docs/latest-v18.x/api/fs.html#fsaccesspath-mode-callback
|
|
8755
|
+
*
|
|
8756
|
+
* @internal
|
|
8757
|
+
*/
|
|
8758
|
+
const isDirectoryWritable = (directoryPath) => {
|
|
8759
|
+
{
|
|
8760
|
+
const fs = require('fs');
|
|
8761
|
+
const path = require('path');
|
|
8762
|
+
if (process.platform === 'win32') {
|
|
8763
|
+
const persistenceDirectory = path.resolve(directoryPath);
|
|
8764
|
+
const testFilePath = path.join(persistenceDirectory, 'ditto-permissions-test.txt');
|
|
8765
|
+
const testFileContents = 'permissions test';
|
|
8766
|
+
try {
|
|
8767
|
+
fs.writeFileSync(testFilePath, testFileContents);
|
|
8768
|
+
const fileContents = fs.readFileSync(testFilePath, 'utf8');
|
|
8769
|
+
if (fileContents !== testFileContents) {
|
|
8770
|
+
Logger.debug(`Failed to read back test file contents, expected '${testFileContents}' but got '${fileContents}'`);
|
|
8771
|
+
return false;
|
|
8772
|
+
}
|
|
8773
|
+
fs.unlinkSync(testFilePath);
|
|
8774
|
+
}
|
|
8775
|
+
catch (error) {
|
|
8776
|
+
Logger.debug(`Failed to access persistence directory: ${error === null || error === void 0 ? void 0 : error.message}`);
|
|
8777
|
+
return false;
|
|
8778
|
+
}
|
|
8779
|
+
}
|
|
8780
|
+
else {
|
|
8781
|
+
try {
|
|
8782
|
+
fs.accessSync(directoryPath, fs.constants.W_OK | fs.constants.R_OK);
|
|
8783
|
+
}
|
|
8784
|
+
catch (error) {
|
|
8785
|
+
Logger.debug(`Failed to access persistence directory: ${error === null || error === void 0 ? void 0 : error.message}`);
|
|
8786
|
+
return false;
|
|
8787
|
+
}
|
|
8788
|
+
}
|
|
8789
|
+
}
|
|
8790
|
+
return true;
|
|
8791
|
+
};
|
|
8538
8792
|
|
|
8539
8793
|
/**
|
|
8540
8794
|
* Get a count of bridged objects binned by bridge type.
|
|
@@ -8575,6 +8829,67 @@ class StaticTCPClient {
|
|
|
8575
8829
|
class WebsocketClient {
|
|
8576
8830
|
}
|
|
8577
8831
|
|
|
8832
|
+
//
|
|
8833
|
+
// Copyright © 2023 DittoLive Incorporated. All rights reserved.
|
|
8834
|
+
//
|
|
8835
|
+
/**
|
|
8836
|
+
* Represents single result object.
|
|
8837
|
+
* This API needs to be completed to navigate in the result object.
|
|
8838
|
+
* The result object is retrieved via FFI.dqlResultCBOR(Pointer<FFIDqlResult>).
|
|
8839
|
+
*
|
|
8840
|
+
* Currently experimental.
|
|
8841
|
+
*
|
|
8842
|
+
* @internal
|
|
8843
|
+
*/
|
|
8844
|
+
class ExperimentalDqlResult {
|
|
8845
|
+
get value() {
|
|
8846
|
+
if (this.storedValue === undefined) {
|
|
8847
|
+
const result = Bridge.dqlResult.handleFor(this);
|
|
8848
|
+
const cbor = experimentalDqlResultCBOR(result.deref());
|
|
8849
|
+
this.storedValue = CBOR.decode(cbor);
|
|
8850
|
+
}
|
|
8851
|
+
return this.storedValue;
|
|
8852
|
+
}
|
|
8853
|
+
/**
|
|
8854
|
+
* FIXME: warn users who try to instantiate this class themselves
|
|
8855
|
+
*
|
|
8856
|
+
* @internal
|
|
8857
|
+
*/
|
|
8858
|
+
constructor() { }
|
|
8859
|
+
}
|
|
8860
|
+
|
|
8861
|
+
//
|
|
8862
|
+
// Copyright © 2023 DittoLive Incorporated. All rights reserved.
|
|
8863
|
+
//
|
|
8864
|
+
/**
|
|
8865
|
+
* Represents the response from executing DQL statements.
|
|
8866
|
+
* Currently experimental.
|
|
8867
|
+
*
|
|
8868
|
+
* The memory management is quite complex. Every time when Rust is called
|
|
8869
|
+
* it will clone the result object. Rust mirror of DqlResponse contains
|
|
8870
|
+
* the collection of result objects. During the getting results call
|
|
8871
|
+
* Rust will copy DqlResult obejcts, thus JS here gets complete control
|
|
8872
|
+
* of their lifetimes.
|
|
8873
|
+
*
|
|
8874
|
+
* @internal
|
|
8875
|
+
*/
|
|
8876
|
+
class ExperimentalDqlResponse {
|
|
8877
|
+
get results() {
|
|
8878
|
+
if (this.storedResults === undefined) {
|
|
8879
|
+
const response = Bridge.dqlResponse.handleFor(this);
|
|
8880
|
+
const results = experimentalDqlResponseResults(response.deref());
|
|
8881
|
+
this.storedResults = results.map((r) => Bridge.dqlResult.bridge(r));
|
|
8882
|
+
}
|
|
8883
|
+
return this.storedResults;
|
|
8884
|
+
}
|
|
8885
|
+
/**
|
|
8886
|
+
* FIXME: warn users who try to instantiate this class themselves
|
|
8887
|
+
*
|
|
8888
|
+
* @internal
|
|
8889
|
+
*/
|
|
8890
|
+
constructor() { }
|
|
8891
|
+
}
|
|
8892
|
+
|
|
8578
8893
|
//
|
|
8579
8894
|
// Copyright © 2021 DittoLive Incorporated. All rights reserved.
|
|
8580
8895
|
//
|
|
@@ -8586,6 +8901,8 @@ class WebsocketClient {
|
|
|
8586
8901
|
// immediately require the bridged type, oftentimes leading to an import cycle.
|
|
8587
8902
|
Bridge.attachment.registerType(Attachment);
|
|
8588
8903
|
Bridge.document.registerType(Document);
|
|
8904
|
+
Bridge.dqlResult.registerType(ExperimentalDqlResult);
|
|
8905
|
+
Bridge.dqlResponse.registerType(ExperimentalDqlResponse);
|
|
8589
8906
|
Bridge.mutableDocument.registerType(MutableDocument);
|
|
8590
8907
|
Bridge.staticTCPClient.registerType(StaticTCPClient);
|
|
8591
8908
|
Bridge.websocketClient.registerType(WebsocketClient);
|