@dittolive/ditto 4.8.2 → 4.9.0-alpha.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/DittoReactNative.podspec +0 -2
- package/README.md +2 -2
- package/node/ditto.cjs.js +952 -532
- 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-arm64.node +0 -0
- package/node/ditto.linux-x64.node +0 -0
- package/node/ditto.win32-x64.node +0 -0
- package/package.json +3 -3
- package/react-native/android/CMakeLists.txt +8 -10
- package/react-native/android/build.gradle +5 -5
- package/react-native/android/dittoffi/src/attachment_observer_interfaces.cpp +1 -0
- package/react-native/android/dittoffi/src/attachment_observer_interfaces.h +75 -0
- package/react-native/android/dittoffi/src/connection_request.cpp +1 -0
- package/react-native/android/dittoffi/src/connection_request.h +56 -0
- package/react-native/android/dittoffi/src/crash_reporter.cpp +1 -0
- package/react-native/android/dittoffi/src/crash_reporter.h +40 -0
- package/react-native/android/dittoffi/src/dittoffi_java.i +42 -0
- package/react-native/android/dittoffi/src/dittomesh_java.i +16 -0
- package/react-native/android/dittoffi/src/dittostore_java.i +430 -0
- package/react-native/android/dittoffi/src/live_query_java_interfaces.cpp +1 -0
- package/react-native/android/dittoffi/src/live_query_java_interfaces.h +64 -0
- package/react-native/android/dittoffi/src/logger_cb.cpp +3 -0
- package/react-native/android/dittoffi/src/logger_cb.h +47 -0
- package/react-native/android/dittoffi/src/logger_export.cpp +1 -0
- package/react-native/android/dittoffi/src/logger_export.h +37 -0
- package/react-native/android/dittoffi/src/mesh_java_interfaces.cpp +1 -0
- package/react-native/android/dittoffi/src/mesh_java_interfaces.h +664 -0
- package/react-native/android/dittoffi/src/presence.cpp +1 -0
- package/react-native/android/dittoffi/src/presence.h +82 -0
- package/react-native/android/dittoffi/src/retainable.cpp +1 -0
- package/react-native/android/dittoffi/src/retainable.h +47 -0
- package/react-native/android/dittoffi/src/store_observer.cpp +1 -0
- package/react-native/android/dittoffi/src/store_observer.h +57 -0
- package/react-native/android/src/main/java/com/dittolive/rnsdk/DittoRNSDKModule.java +1 -1
- package/react-native/cpp/include/Authentication.h +1 -0
- package/react-native/cpp/include/Logger.h +1 -0
- package/react-native/cpp/src/Authentication.cpp +25 -0
- package/react-native/cpp/src/Logger.cpp +50 -6
- package/react-native/cpp/src/main.cpp +2 -0
- package/react-native/ditto.es6.js +1 -1
- package/types/ditto.d.ts +3010 -2763
- package/web/ditto.es6.js +1 -1
- package/web/ditto.umd.js +1 -1
- package/web/ditto.wasm +0 -0
- package/react-native/dittoffi/dittoffi.h +0 -4872
- /package/react-native/{dittoffi → android/dittoffi/src}/ifaddrs.cpp +0 -0
- /package/react-native/{dittoffi → android/dittoffi/src}/ifaddrs.h +0 -0
package/node/ditto.cjs.js
CHANGED
|
@@ -16,9 +16,8 @@ class KeepAlive {
|
|
|
16
16
|
}
|
|
17
17
|
/** @internal */
|
|
18
18
|
retain(id) {
|
|
19
|
-
if (typeof this.countsByID[id] === 'undefined')
|
|
19
|
+
if (typeof this.countsByID[id] === 'undefined')
|
|
20
20
|
this.countsByID[id] = 0;
|
|
21
|
-
}
|
|
22
21
|
this.countsByID[id] += 1;
|
|
23
22
|
if (this.intervalID === null) {
|
|
24
23
|
// Keep the process alive as long as there is at least one ID being
|
|
@@ -37,9 +36,8 @@ class KeepAlive {
|
|
|
37
36
|
throw new Error(`Internal inconsistency, trying to release a keep-alive ID that hasn't been retained before or isn't tracked anymore: ${id}`);
|
|
38
37
|
}
|
|
39
38
|
this.countsByID[id] -= 1;
|
|
40
|
-
if (this.countsByID[id] === 0)
|
|
39
|
+
if (this.countsByID[id] === 0)
|
|
41
40
|
delete this.countsByID[id];
|
|
42
|
-
}
|
|
43
41
|
if (Object.keys(this.countsByID).length === 0) {
|
|
44
42
|
// Nothing is tracked anymore, it's safe to clear the interval
|
|
45
43
|
// and let the process do what it wants.
|
|
@@ -88,7 +86,7 @@ class Observer {
|
|
|
88
86
|
* method. Otherwise returns `false`.
|
|
89
87
|
*/
|
|
90
88
|
get isStopped() {
|
|
91
|
-
return this.token !== undefined && this.observerManager.hasObserver(this.token);
|
|
89
|
+
return (this.token !== undefined && this.observerManager.hasObserver(this.token));
|
|
92
90
|
}
|
|
93
91
|
/**
|
|
94
92
|
* Stops the observation. Calling this method multiple times has no effect.
|
|
@@ -108,6 +106,8 @@ class Observer {
|
|
|
108
106
|
}
|
|
109
107
|
Observer.finalizationRegistry = new FinalizationRegistry(Observer.finalize);
|
|
110
108
|
|
|
109
|
+
const isWebBuild = false;
|
|
110
|
+
|
|
111
111
|
//
|
|
112
112
|
// Copyright © 2021 DittoLive Incorporated. All rights reserved.
|
|
113
113
|
//
|
|
@@ -118,6 +118,8 @@ const privateToken$1 = Symbol('privateConstructorToken');
|
|
|
118
118
|
/**
|
|
119
119
|
* Represents a CRDT counter that can be upserted as part of a document or
|
|
120
120
|
* assigned to a property during an update of a document.
|
|
121
|
+
*
|
|
122
|
+
* Not available in React Native environments.
|
|
121
123
|
*/
|
|
122
124
|
class Counter {
|
|
123
125
|
/** The value of the counter. */
|
|
@@ -156,6 +158,8 @@ class MutableCounter extends Counter {
|
|
|
156
158
|
* {@link PendingCursorOperation.update | PendingCursorOperation.update()} and
|
|
157
159
|
* {@link PendingIDSpecificOperation.update | PendingIDSpecificOperation.update()},
|
|
158
160
|
* otherwise an exception is thrown.
|
|
161
|
+
*
|
|
162
|
+
* @throws {Error} when called in a React Native environment.
|
|
159
163
|
*/
|
|
160
164
|
increment(amount) {
|
|
161
165
|
const mutDoc = this.mutDoc;
|
|
@@ -170,12 +174,10 @@ class MutableCounter extends Counter {
|
|
|
170
174
|
}
|
|
171
175
|
/** @internal */
|
|
172
176
|
constructor() {
|
|
173
|
-
if (arguments[0] === privateToken$1)
|
|
177
|
+
if (arguments[0] === privateToken$1)
|
|
174
178
|
super();
|
|
175
|
-
|
|
176
|
-
else {
|
|
179
|
+
else
|
|
177
180
|
throw new Error(`MutableCounter constructor is for internal use only.`);
|
|
178
|
-
}
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
|
|
@@ -189,6 +191,8 @@ const privateToken = '@ditto.ff82dae89821c5ab822a8b539056bce4';
|
|
|
189
191
|
/**
|
|
190
192
|
* Represents a CRDT register that can be upserted as part of a document or
|
|
191
193
|
* assigned to a property during an update of a document.
|
|
194
|
+
*
|
|
195
|
+
* Not available in React Native environments.
|
|
192
196
|
*/
|
|
193
197
|
class Register {
|
|
194
198
|
/** Returns the value of the register. */
|
|
@@ -203,7 +207,9 @@ class Register {
|
|
|
203
207
|
}
|
|
204
208
|
/** @internal */
|
|
205
209
|
static '@ditto.create'(mutableDocument, path, value) {
|
|
206
|
-
const register = mutableDocument
|
|
210
|
+
const register = mutableDocument
|
|
211
|
+
? new MutableRegister(value, privateToken)
|
|
212
|
+
: new Register(value);
|
|
207
213
|
register['@ditto.mutableDocument'] = mutableDocument;
|
|
208
214
|
register['@ditto.path'] = path;
|
|
209
215
|
register['@ditto.value'] = value;
|
|
@@ -216,15 +222,24 @@ class Register {
|
|
|
216
222
|
* updating a document.
|
|
217
223
|
*
|
|
218
224
|
* This class can't be instantiated directly, it's returned automatically for
|
|
219
|
-
* any register property of a document within an update block via
|
|
225
|
+
* any register property of a document within an update block via
|
|
226
|
+
* {@link MutableDocumentPath.register}.
|
|
227
|
+
*
|
|
228
|
+
* Not available in React Native environments.
|
|
220
229
|
*/
|
|
221
230
|
class MutableRegister extends Register {
|
|
222
|
-
/**
|
|
231
|
+
/**
|
|
232
|
+
* Returns the value of the register.
|
|
233
|
+
*
|
|
234
|
+
* Not available in React Native environments.
|
|
235
|
+
*/
|
|
223
236
|
get value() {
|
|
224
237
|
return super.value;
|
|
225
238
|
}
|
|
226
239
|
/**
|
|
227
240
|
* Convenience setter, equivalent to {@link set | set()}.
|
|
241
|
+
*
|
|
242
|
+
* Not available in React Native environments.
|
|
228
243
|
*/
|
|
229
244
|
set value(value) {
|
|
230
245
|
this.set(value);
|
|
@@ -236,6 +251,8 @@ class MutableRegister extends Register {
|
|
|
236
251
|
* {@link PendingCursorOperation.update | PendingCursorOperation.update()} and
|
|
237
252
|
* {@link PendingIDSpecificOperation.update | PendingIDSpecificOperation.update()},
|
|
238
253
|
* otherwise an exception is thrown.
|
|
254
|
+
*
|
|
255
|
+
* Not available in React Native environments.
|
|
239
256
|
*/
|
|
240
257
|
set(value) {
|
|
241
258
|
const mutableDocument = this['@ditto.mutableDocument'];
|
|
@@ -247,12 +264,10 @@ class MutableRegister extends Register {
|
|
|
247
264
|
}
|
|
248
265
|
/** @internal */
|
|
249
266
|
constructor(value) {
|
|
250
|
-
if (arguments[1] === privateToken)
|
|
267
|
+
if (arguments[1] === privateToken)
|
|
251
268
|
super(value);
|
|
252
|
-
|
|
253
|
-
else {
|
|
269
|
+
else
|
|
254
270
|
throw new Error(`MutableRegister constructor is for internal use only.`);
|
|
255
|
-
}
|
|
256
271
|
}
|
|
257
272
|
}
|
|
258
273
|
|
|
@@ -295,6 +310,7 @@ function ditto_auth_client_is_web_valid(...args) { return ditto.ditto_auth_clien
|
|
|
295
310
|
function ditto_auth_client_is_x509_valid(...args) { return ditto.ditto_auth_client_is_x509_valid(...args) }
|
|
296
311
|
function ditto_auth_client_login_with_credentials(...args) { return ditto.ditto_auth_client_login_with_credentials(...args) }
|
|
297
312
|
function ditto_auth_client_login_with_token(...args) { return ditto.ditto_auth_client_login_with_token(...args) }
|
|
313
|
+
function ditto_auth_client_login_with_token_and_feedback(...args) { return ditto.ditto_auth_client_login_with_token_and_feedback(...args) }
|
|
298
314
|
function ditto_auth_client_logout(...args) { return ditto.ditto_auth_client_logout(...args) }
|
|
299
315
|
function ditto_auth_client_make_login_provider(...args) { return ditto.ditto_auth_client_make_login_provider(...args) }
|
|
300
316
|
function ditto_auth_client_set_validity_listener(...args) { return ditto.ditto_auth_client_set_validity_listener(...args) }
|
|
@@ -438,8 +454,6 @@ function refCStringToString(...args) { return ditto.refCStringToString(...args)
|
|
|
438
454
|
function setDeadlockTimeout$1(...args) { return ditto.setDeadlockTimeout(...args) }
|
|
439
455
|
function withOutBoxCBytes(...args) { return ditto.withOutBoxCBytes(...args) }
|
|
440
456
|
|
|
441
|
-
const isWebBuild = false;
|
|
442
|
-
|
|
443
457
|
//
|
|
444
458
|
// Copyright © 2023 DittoLive Incorporated. All rights reserved.
|
|
445
459
|
//
|
|
@@ -557,44 +571,28 @@ var DittoCRDTType;
|
|
|
557
571
|
// are currently only used by the Node Linux & Windows build. See comment
|
|
558
572
|
// in `sync.ts` -> `Ditto.applyPeerToPeerBluetoothLE()` for details.
|
|
559
573
|
function dittoAddInternalBLEClientTransport(ditto) {
|
|
560
|
-
|
|
561
|
-
return ditto_add_internal_ble_client_transport(ditto);
|
|
562
|
-
}
|
|
574
|
+
return ditto_add_internal_ble_client_transport(ditto);
|
|
563
575
|
}
|
|
564
576
|
function dittoAddInternalBLEServerTransport(ditto) {
|
|
565
|
-
|
|
566
|
-
return ditto_add_internal_ble_server_transport(ditto);
|
|
567
|
-
}
|
|
577
|
+
return ditto_add_internal_ble_server_transport(ditto);
|
|
568
578
|
}
|
|
569
579
|
function bleClientFreeHandle(handle) {
|
|
570
|
-
|
|
571
|
-
return ble_client_free_handle(handle);
|
|
572
|
-
}
|
|
580
|
+
return ble_client_free_handle(handle);
|
|
573
581
|
}
|
|
574
582
|
function bleServerFreeHandle(handle) {
|
|
575
|
-
|
|
576
|
-
return ble_server_free_handle(handle);
|
|
577
|
-
}
|
|
583
|
+
return ble_server_free_handle(handle);
|
|
578
584
|
}
|
|
579
585
|
function dittoAddInternalMdnsTransport(ditto) {
|
|
580
|
-
|
|
581
|
-
return ditto_add_internal_mdns_client_transport(ditto);
|
|
582
|
-
}
|
|
586
|
+
return ditto_add_internal_mdns_client_transport(ditto);
|
|
583
587
|
}
|
|
584
588
|
function mdnsClientFreeHandle(handle) {
|
|
585
|
-
|
|
586
|
-
return mdns_client_free_handle(handle);
|
|
587
|
-
}
|
|
589
|
+
return mdns_client_free_handle(handle);
|
|
588
590
|
}
|
|
589
591
|
function dittoAddInternalMdnsAdvertiser(ditto) {
|
|
590
|
-
|
|
591
|
-
return ditto_add_internal_mdns_server_transport(ditto);
|
|
592
|
-
}
|
|
592
|
+
return ditto_add_internal_mdns_server_transport(ditto);
|
|
593
593
|
}
|
|
594
594
|
function mdnsServerFreeHandle(handle) {
|
|
595
|
-
|
|
596
|
-
return mdns_server_free_handle(handle);
|
|
597
|
-
}
|
|
595
|
+
return mdns_server_free_handle(handle);
|
|
598
596
|
}
|
|
599
597
|
// ------------------------------------------------------------- Document ------
|
|
600
598
|
/** @internal */
|
|
@@ -602,8 +600,10 @@ function documentSetCBORWithTimestamp(document, path, cbor, timestamp) {
|
|
|
602
600
|
ensureInitialized();
|
|
603
601
|
const pathX = bytesFromString(path);
|
|
604
602
|
const errorCode = ditto_document_set_cbor_with_timestamp(document, pathX, cbor, timestamp);
|
|
605
|
-
if (errorCode !== 0)
|
|
606
|
-
throw new Error(errorMessage() ||
|
|
603
|
+
if (errorCode !== 0) {
|
|
604
|
+
throw new Error(errorMessage() ||
|
|
605
|
+
`ditto_document_set_cbor_with_timestamp() failed with error code: ${errorCode}`);
|
|
606
|
+
}
|
|
607
607
|
}
|
|
608
608
|
/** @internal */
|
|
609
609
|
function documentSetCBOR(document, path, cbor) {
|
|
@@ -611,8 +611,10 @@ function documentSetCBOR(document, path, cbor) {
|
|
|
611
611
|
// NOTE: not sure if this should be async or not.
|
|
612
612
|
const pathX = bytesFromString(path);
|
|
613
613
|
const errorCode = ditto_document_set_cbor(document, pathX, cbor);
|
|
614
|
-
if (errorCode !== 0)
|
|
615
|
-
throw new Error(errorMessage() ||
|
|
614
|
+
if (errorCode !== 0) {
|
|
615
|
+
throw new Error(errorMessage() ||
|
|
616
|
+
`ditto_document_set_cbor() failed with error code: ${errorCode}`);
|
|
617
|
+
}
|
|
616
618
|
}
|
|
617
619
|
/** @internal */
|
|
618
620
|
function documentID(self) {
|
|
@@ -637,16 +639,20 @@ function documentRemove(document, path) {
|
|
|
637
639
|
ensureInitialized();
|
|
638
640
|
const pathBytes = bytesFromString(path);
|
|
639
641
|
const errorCode = ditto_document_remove(document, pathBytes);
|
|
640
|
-
if (errorCode !== 0)
|
|
641
|
-
throw new Error(errorMessage() ||
|
|
642
|
+
if (errorCode !== 0) {
|
|
643
|
+
throw new Error(errorMessage() ||
|
|
644
|
+
`ditto_document_remove() failed with error code: ${errorCode}`);
|
|
645
|
+
}
|
|
642
646
|
}
|
|
643
647
|
/** @internal */
|
|
644
648
|
function documentIncrementCounter(document, path, amount) {
|
|
645
649
|
ensureInitialized();
|
|
646
650
|
const pathBytes = bytesFromString(path);
|
|
647
651
|
const errorCode = ditto_document_increment_counter(document, pathBytes, amount);
|
|
648
|
-
if (errorCode !== 0)
|
|
649
|
-
throw new Error(errorMessage() ||
|
|
652
|
+
if (errorCode !== 0) {
|
|
653
|
+
throw new Error(errorMessage() ||
|
|
654
|
+
`ditto_document_increment_counter() failed with error code: ${errorCode}`);
|
|
655
|
+
}
|
|
650
656
|
}
|
|
651
657
|
/** @internal */
|
|
652
658
|
function documentFree(self) {
|
|
@@ -666,8 +672,10 @@ function validateDocumentID(docID) {
|
|
|
666
672
|
ensureInitialized();
|
|
667
673
|
const cborCBytes = withOutBoxCBytes((outCBOR) => {
|
|
668
674
|
const errorCode = ditto_validate_document_id(docID, outCBOR);
|
|
669
|
-
if (errorCode !== 0)
|
|
670
|
-
throw new Error(errorMessage() ||
|
|
675
|
+
if (errorCode !== 0) {
|
|
676
|
+
throw new Error(errorMessage() ||
|
|
677
|
+
`ditto_validate_document_id() failed with error code: ${errorCode}`);
|
|
678
|
+
}
|
|
671
679
|
return outCBOR;
|
|
672
680
|
});
|
|
673
681
|
return boxCBytesIntoBuffer(cborCBytes);
|
|
@@ -681,8 +689,10 @@ async function collectionGet(ditto, collectionName, documentID, readTransaction)
|
|
|
681
689
|
const { status_code: errorCode, document } = await ditto_collection_get(ditto, collectionNameX, documentID, readTransaction);
|
|
682
690
|
if (errorCode === NOT_FOUND_ERROR_CODE)
|
|
683
691
|
return null;
|
|
684
|
-
if (errorCode !== 0)
|
|
685
|
-
throw new Error(errorMessage() ||
|
|
692
|
+
if (errorCode !== 0) {
|
|
693
|
+
throw new Error(errorMessage() ||
|
|
694
|
+
`ditto_collection_get() failed with error code: ${errorCode}`);
|
|
695
|
+
}
|
|
686
696
|
return document;
|
|
687
697
|
}
|
|
688
698
|
/** @internal */
|
|
@@ -708,8 +718,10 @@ async function collectionInsertValue(ditto, collectionName, doc_cbor, writeStrat
|
|
|
708
718
|
throw new Error(`Unsupported write strategy '${writeStrategy}' provided.`);
|
|
709
719
|
}
|
|
710
720
|
const { status_code: errorCode, id } = await ditto_collection_insert_value(ditto, collectionNameX, doc_cbor, strategy, null, writeTransaction !== null && writeTransaction !== void 0 ? writeTransaction : null);
|
|
711
|
-
if (errorCode !== 0)
|
|
712
|
-
throw new Error(errorMessage() ||
|
|
721
|
+
if (errorCode !== 0) {
|
|
722
|
+
throw new Error(errorMessage() ||
|
|
723
|
+
`ditto_collection_insert_value() failed with error code: ${errorCode}`);
|
|
724
|
+
}
|
|
713
725
|
return boxCBytesIntoBuffer(id);
|
|
714
726
|
}
|
|
715
727
|
/** @internal */
|
|
@@ -718,8 +730,10 @@ async function collectionRemove(ditto, collectionName, writeTransaction, documen
|
|
|
718
730
|
// REFACTOR: add proper error handling.
|
|
719
731
|
const collectionNameX = bytesFromString(collectionName);
|
|
720
732
|
const { status_code: errorCode, bool_value: didRemove } = await ditto_collection_remove(ditto, collectionNameX, writeTransaction, documentID);
|
|
721
|
-
if (errorCode !== 0)
|
|
722
|
-
throw new Error(errorMessage() ||
|
|
733
|
+
if (errorCode !== 0) {
|
|
734
|
+
throw new Error(errorMessage() ||
|
|
735
|
+
`ditto_collection_remove() failed with error code: ${errorCode}`);
|
|
736
|
+
}
|
|
723
737
|
return didRemove;
|
|
724
738
|
}
|
|
725
739
|
/** @internal */
|
|
@@ -728,8 +742,10 @@ async function collectionEvict(ditto, collectionName, writeTransaction, document
|
|
|
728
742
|
// REFACTOR: add proper error handling.
|
|
729
743
|
const collectionNameX = bytesFromString(collectionName);
|
|
730
744
|
const { status_code: errorCode, bool_value: didEvict } = await ditto_collection_evict(ditto, collectionNameX, writeTransaction, documentID);
|
|
731
|
-
if (errorCode !== 0)
|
|
732
|
-
throw new Error(errorMessage() ||
|
|
745
|
+
if (errorCode !== 0) {
|
|
746
|
+
throw new Error(errorMessage() ||
|
|
747
|
+
`ditto_collection_evict() failed with error code: ${errorCode}`);
|
|
748
|
+
}
|
|
733
749
|
return didEvict;
|
|
734
750
|
}
|
|
735
751
|
/** @internal */
|
|
@@ -737,8 +753,10 @@ async function collectionUpdate(ditto, collectionName, writeTransaction, documen
|
|
|
737
753
|
ensureInitialized();
|
|
738
754
|
const collectionNameX = bytesFromString(collectionName);
|
|
739
755
|
const errorCode = await ditto_collection_update(ditto, collectionNameX, writeTransaction, document);
|
|
740
|
-
if (errorCode !== 0)
|
|
741
|
-
throw new Error(errorMessage() ||
|
|
756
|
+
if (errorCode !== 0) {
|
|
757
|
+
throw new Error(errorMessage() ||
|
|
758
|
+
`ditto_collection_update() failed with error code: ${errorCode}`);
|
|
759
|
+
}
|
|
742
760
|
}
|
|
743
761
|
/** @internal */
|
|
744
762
|
async function collectionUpdateMultiple(ditto, collectionName, writeTransaction, documents) {
|
|
@@ -746,8 +764,10 @@ async function collectionUpdateMultiple(ditto, collectionName, writeTransaction,
|
|
|
746
764
|
const collectionNameX = bytesFromString(collectionName);
|
|
747
765
|
const cDocuments = jsDocsToCDocs(documents);
|
|
748
766
|
const errorCode = await ditto_collection_update_multiple(ditto, collectionNameX, writeTransaction, cDocuments);
|
|
749
|
-
if (errorCode !== 0)
|
|
750
|
-
throw new Error(errorMessage() ||
|
|
767
|
+
if (errorCode !== 0) {
|
|
768
|
+
throw new Error(errorMessage() ||
|
|
769
|
+
`ditto_collection_update_multiple() failed with error code: ${errorCode}`);
|
|
770
|
+
}
|
|
751
771
|
}
|
|
752
772
|
/** @internal */
|
|
753
773
|
async function collectionExecQueryStr(ditto, collectionName, writeTransaction, query, queryArgsCBOR, orderBy, limit, offset) {
|
|
@@ -791,8 +811,10 @@ function addSubscription(ditto, collectionName, query, queryArgsCBOR, orderBy, l
|
|
|
791
811
|
const collectionNameX = bytesFromString(collectionName);
|
|
792
812
|
const queryX = bytesFromString(query);
|
|
793
813
|
const statusCode = ditto_add_subscription(ditto, collectionNameX, queryX, queryArgsCBOR, orderBy, limit, offset);
|
|
794
|
-
if (statusCode !== 0)
|
|
795
|
-
throw new Error(errorMessage() ||
|
|
814
|
+
if (statusCode !== 0) {
|
|
815
|
+
throw new Error(errorMessage() ||
|
|
816
|
+
`ditto_add_subscription() failed with error code: ${statusCode}`);
|
|
817
|
+
}
|
|
796
818
|
}
|
|
797
819
|
/** @internal */
|
|
798
820
|
function removeSubscription(ditto, collectionName, query, queryArgsCBOR, orderBy, limit, offset) {
|
|
@@ -800,8 +822,10 @@ function removeSubscription(ditto, collectionName, query, queryArgsCBOR, orderBy
|
|
|
800
822
|
const collectionNameX = bytesFromString(collectionName);
|
|
801
823
|
const queryX = bytesFromString(query);
|
|
802
824
|
const statusCode = ditto_remove_subscription(ditto, collectionNameX, queryX, queryArgsCBOR, orderBy, limit, offset);
|
|
803
|
-
if (statusCode !== 0)
|
|
804
|
-
throw new Error(errorMessage() ||
|
|
825
|
+
if (statusCode !== 0) {
|
|
826
|
+
throw new Error(errorMessage() ||
|
|
827
|
+
`ditto_remove_subscription() failed with error code: ${statusCode}`);
|
|
828
|
+
}
|
|
805
829
|
}
|
|
806
830
|
/** @internal */
|
|
807
831
|
function tryAddSyncSubscription(dittoPointer, query, queryArgsCBOR) {
|
|
@@ -844,9 +868,8 @@ function queryResultItems(queryResultPointer) {
|
|
|
844
868
|
ensureInitialized();
|
|
845
869
|
const rv = [];
|
|
846
870
|
const resultCount = dittoffi_query_result_item_count(queryResultPointer);
|
|
847
|
-
for (let i = 0; i < resultCount; i++)
|
|
871
|
+
for (let i = 0; i < resultCount; i++)
|
|
848
872
|
rv.push(dittoffi_query_result_item_at(queryResultPointer, i));
|
|
849
|
-
}
|
|
850
873
|
return rv;
|
|
851
874
|
}
|
|
852
875
|
/**
|
|
@@ -906,8 +929,10 @@ onError) {
|
|
|
906
929
|
// callback itself may not have completed. This is fine, since `signalNext()`
|
|
907
930
|
// shall be the proper way to let the Rust core know the FFI call completed.
|
|
908
931
|
const { status_code: errorCode, i64: id } = ditto_live_query_register_str_detached(ditto, collectionNameBuffer, queryBuffer, queryArgsCBOR, orderBy, limit, offset, wrapBackgroundCbForFFI(onError, eventHandler));
|
|
909
|
-
if (errorCode !== 0)
|
|
910
|
-
throw new Error(errorMessage() ||
|
|
932
|
+
if (errorCode !== 0) {
|
|
933
|
+
throw new Error(errorMessage() ||
|
|
934
|
+
`\`ditto_live_query_register_str()\` failed with error code: ${errorCode}`);
|
|
935
|
+
}
|
|
911
936
|
return id;
|
|
912
937
|
}
|
|
913
938
|
/** @internal */
|
|
@@ -928,8 +953,10 @@ function tryExperimentalRegisterChangeObserver(ditto, query, queryArgsCBOR, chan
|
|
|
928
953
|
async function liveQueryStart(ditto, liveQueryID) {
|
|
929
954
|
ensureInitialized();
|
|
930
955
|
const errorCode = await ditto_live_query_start(ditto, liveQueryID);
|
|
931
|
-
if (errorCode !== 0)
|
|
932
|
-
throw new Error(errorMessage() ||
|
|
956
|
+
if (errorCode !== 0) {
|
|
957
|
+
throw new Error(errorMessage() ||
|
|
958
|
+
`\`ditto_live_query_start()\` failed with error code: ${errorCode}`);
|
|
959
|
+
}
|
|
933
960
|
}
|
|
934
961
|
/** @internal */
|
|
935
962
|
function liveQueryStop(ditto, liveQueryID) {
|
|
@@ -949,8 +976,10 @@ async function liveQueryWebhookRegister(ditto, collectionName, query, orderBy, l
|
|
|
949
976
|
const queryBuffer = bytesFromString(query);
|
|
950
977
|
const urlBuffer = bytesFromString(url);
|
|
951
978
|
const { status_code: errorCode, id } = await ditto_live_query_webhook_register_str(ditto, collectionNameBuffer, queryBuffer, orderBy, limit, offset, urlBuffer);
|
|
952
|
-
if (errorCode !== 0)
|
|
953
|
-
throw new Error(errorMessage() ||
|
|
979
|
+
if (errorCode !== 0) {
|
|
980
|
+
throw new Error(errorMessage() ||
|
|
981
|
+
`\`ditto_live_query_webhook_register_str()\` failed with error code: ${errorCode}`);
|
|
982
|
+
}
|
|
954
983
|
return boxCBytesIntoBuffer(id);
|
|
955
984
|
}
|
|
956
985
|
/** @internal */
|
|
@@ -969,8 +998,10 @@ async function readTransaction(ditto) {
|
|
|
969
998
|
ensureInitialized();
|
|
970
999
|
// REFACTOR: add proper error handling.
|
|
971
1000
|
const { status_code: errorCode, txn: readTransaction } = await ditto_read_transaction(ditto);
|
|
972
|
-
if (errorCode !== 0)
|
|
973
|
-
throw new Error(errorMessage() ||
|
|
1001
|
+
if (errorCode !== 0) {
|
|
1002
|
+
throw new Error(errorMessage() ||
|
|
1003
|
+
`\`ditto_read_transaction()\` failed with error code: ${errorCode}`);
|
|
1004
|
+
}
|
|
974
1005
|
return readTransaction;
|
|
975
1006
|
}
|
|
976
1007
|
/** @internal */
|
|
@@ -985,23 +1016,29 @@ async function writeTransaction(ditto) {
|
|
|
985
1016
|
ensureInitialized();
|
|
986
1017
|
// REFACTOR: add proper error handling.
|
|
987
1018
|
const { status_code: errorCode, txn: writeTransaction } = await ditto_write_transaction(ditto, null);
|
|
988
|
-
if (errorCode !== 0)
|
|
989
|
-
throw new Error(errorMessage() ||
|
|
1019
|
+
if (errorCode !== 0) {
|
|
1020
|
+
throw new Error(errorMessage() ||
|
|
1021
|
+
`ditto_write_transaction() failed with error code: ${errorCode}`);
|
|
1022
|
+
}
|
|
990
1023
|
return writeTransaction;
|
|
991
1024
|
}
|
|
992
1025
|
/** @internal */
|
|
993
1026
|
async function writeTransactionCommit(ditto, self) {
|
|
994
1027
|
ensureInitialized();
|
|
995
1028
|
const errorCode = await ditto_write_transaction_commit(ditto, self);
|
|
996
|
-
if (errorCode !== 0)
|
|
997
|
-
throw new Error(errorMessage() ||
|
|
1029
|
+
if (errorCode !== 0) {
|
|
1030
|
+
throw new Error(errorMessage() ||
|
|
1031
|
+
`ditto_write_transaction_commit() failed with error code: ${errorCode}`);
|
|
1032
|
+
}
|
|
998
1033
|
}
|
|
999
1034
|
/** @internal */
|
|
1000
1035
|
async function writeTransactionRollback(ditto, transaction) {
|
|
1001
1036
|
ensureInitialized();
|
|
1002
1037
|
const errorCode = await ditto_write_transaction_rollback(ditto, transaction);
|
|
1003
|
-
if (errorCode !== 0)
|
|
1004
|
-
throw new Error(errorMessage() ||
|
|
1038
|
+
if (errorCode !== 0) {
|
|
1039
|
+
throw new Error(errorMessage() ||
|
|
1040
|
+
`ditto_write_transaction_rollback() failed with error code: ${errorCode}`);
|
|
1041
|
+
}
|
|
1005
1042
|
}
|
|
1006
1043
|
// --------------------------------------------------------------- Logger ------
|
|
1007
1044
|
/** @internal */
|
|
@@ -1094,8 +1131,10 @@ function dittoIdentityConfigMakeOnlinePlayground(appID, sharedToken, baseURL) {
|
|
|
1094
1131
|
const sharedTokenX = bytesFromString(sharedToken);
|
|
1095
1132
|
const baseURLX = bytesFromString(baseURL);
|
|
1096
1133
|
const { status_code: errorCode, identity_config: identityConfig } = ditto_identity_config_make_online_playground(appIDX, sharedTokenX, baseURLX);
|
|
1097
|
-
if (errorCode !== 0)
|
|
1098
|
-
throw new Error(errorMessage() ||
|
|
1134
|
+
if (errorCode !== 0) {
|
|
1135
|
+
throw new Error(errorMessage() ||
|
|
1136
|
+
`ditto_identity_config_make_online_playground() failed with error code: ${errorCode}`);
|
|
1137
|
+
}
|
|
1099
1138
|
return identityConfig;
|
|
1100
1139
|
}
|
|
1101
1140
|
/** @internal */
|
|
@@ -1104,8 +1143,10 @@ function dittoIdentityConfigMakeOnlineWithAuthentication(appID, baseURL) {
|
|
|
1104
1143
|
const appIDX = bytesFromString(appID);
|
|
1105
1144
|
const baseURLX = bytesFromString(baseURL);
|
|
1106
1145
|
const { status_code: errorCode, identity_config: identityConfig } = ditto_identity_config_make_online_with_authentication(appIDX, baseURLX);
|
|
1107
|
-
if (errorCode !== 0)
|
|
1108
|
-
throw new Error(errorMessage() ||
|
|
1146
|
+
if (errorCode !== 0) {
|
|
1147
|
+
throw new Error(errorMessage() ||
|
|
1148
|
+
`ditto_identity_config_make_online_with_authentication() failed with error code: ${errorCode}`);
|
|
1149
|
+
}
|
|
1109
1150
|
return identityConfig;
|
|
1110
1151
|
}
|
|
1111
1152
|
/** @internal */
|
|
@@ -1114,8 +1155,10 @@ function dittoIdentityConfigMakeOfflinePlayground(appId, siteID) {
|
|
|
1114
1155
|
const appIdX = bytesFromString(appId);
|
|
1115
1156
|
const siteIDX = Number(siteID);
|
|
1116
1157
|
const { status_code: errorCode, identity_config: identityConfig } = ditto_identity_config_make_offline_playground(appIdX, siteIDX);
|
|
1117
|
-
if (errorCode !== 0)
|
|
1118
|
-
throw new Error(errorMessage() ||
|
|
1158
|
+
if (errorCode !== 0) {
|
|
1159
|
+
throw new Error(errorMessage() ||
|
|
1160
|
+
`ditto_identity_config_make_offline_playground() failed with error code: ${errorCode}`);
|
|
1161
|
+
}
|
|
1119
1162
|
return identityConfig;
|
|
1120
1163
|
}
|
|
1121
1164
|
/** @internal */
|
|
@@ -1125,8 +1168,10 @@ function dittoIdentityConfigMakeSharedKey(appId, sharedKey, siteID) {
|
|
|
1125
1168
|
const sharedKeyX = bytesFromString(sharedKey);
|
|
1126
1169
|
const siteIDX = Number(siteID);
|
|
1127
1170
|
const { status_code: errorCode, identity_config: identityConfig } = ditto_identity_config_make_shared_key(appIdX, sharedKeyX, siteIDX);
|
|
1128
|
-
if (errorCode !== 0)
|
|
1129
|
-
throw new Error(errorMessage() ||
|
|
1171
|
+
if (errorCode !== 0) {
|
|
1172
|
+
throw new Error(errorMessage() ||
|
|
1173
|
+
`ditto_identity_config_make_shared_key() failed with error code: ${errorCode}`);
|
|
1174
|
+
}
|
|
1130
1175
|
return identityConfig;
|
|
1131
1176
|
}
|
|
1132
1177
|
/** @internal */
|
|
@@ -1134,8 +1179,10 @@ function dittoIdentityConfigMakeManual(configCBORBase64) {
|
|
|
1134
1179
|
ensureInitialized();
|
|
1135
1180
|
const configCBORBase64X = bytesFromString(configCBORBase64);
|
|
1136
1181
|
const { status_code: errorCode, identity_config: identityConfig } = ditto_identity_config_make_manual_v0(configCBORBase64X);
|
|
1137
|
-
if (errorCode !== 0)
|
|
1138
|
-
throw new Error(errorMessage() ||
|
|
1182
|
+
if (errorCode !== 0) {
|
|
1183
|
+
throw new Error(errorMessage() ||
|
|
1184
|
+
`ditto_identity_config_make_manual_v0() failed with error code: ${errorCode}`);
|
|
1185
|
+
}
|
|
1139
1186
|
return identityConfig;
|
|
1140
1187
|
}
|
|
1141
1188
|
/** @internal */
|
|
@@ -1163,13 +1210,34 @@ function dittoAuthClientIsX509Valid(ditto) {
|
|
|
1163
1210
|
ensureInitialized();
|
|
1164
1211
|
return ditto_auth_client_is_x509_valid(ditto) !== 0;
|
|
1165
1212
|
}
|
|
1213
|
+
async function dittoAuthClientLoginWithTokenAndFeedback(ditto, token, provider) {
|
|
1214
|
+
ensureInitialized();
|
|
1215
|
+
const tokenBytes = bytesFromString(token);
|
|
1216
|
+
const providerBytes = bytesFromString(provider);
|
|
1217
|
+
const result = await ditto_auth_client_login_with_token_and_feedback(ditto, tokenBytes, providerBytes);
|
|
1218
|
+
// Our `login_with_token_and_feedback()` API returns the `clientInfo` string
|
|
1219
|
+
// even when authentication has failed, so this function does not throw an
|
|
1220
|
+
// error when the status code is non-zero.
|
|
1221
|
+
const error = result.status_code === 0
|
|
1222
|
+
? null
|
|
1223
|
+
: new DittoFFIError(result.status_code, undefined, 'Ditto failed to authenticate.');
|
|
1224
|
+
const clientInfo = result.c_string
|
|
1225
|
+
? boxCStringIntoString(result.c_string)
|
|
1226
|
+
: null;
|
|
1227
|
+
return {
|
|
1228
|
+
error,
|
|
1229
|
+
clientInfo,
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1166
1232
|
async function dittoAuthClientLoginWithToken(ditto, token, provider) {
|
|
1167
1233
|
ensureInitialized();
|
|
1168
1234
|
const tokenBytes = bytesFromString(token);
|
|
1169
1235
|
const providerBytes = bytesFromString(provider);
|
|
1170
1236
|
const errorCode = await ditto_auth_client_login_with_token(ditto, tokenBytes, providerBytes);
|
|
1171
|
-
if (errorCode !== 0)
|
|
1172
|
-
throw new Error(errorMessage() ||
|
|
1237
|
+
if (errorCode !== 0) {
|
|
1238
|
+
throw new Error(errorMessage() ||
|
|
1239
|
+
`Ditto failed to authenticate (error code: ${errorCode}).`);
|
|
1240
|
+
}
|
|
1173
1241
|
}
|
|
1174
1242
|
async function dittoAuthClientLoginWithUsernameAndPassword(ditto, username, password, provider) {
|
|
1175
1243
|
ensureInitialized();
|
|
@@ -1177,14 +1245,17 @@ async function dittoAuthClientLoginWithUsernameAndPassword(ditto, username, pass
|
|
|
1177
1245
|
const passwordBytes = bytesFromString(password);
|
|
1178
1246
|
const providerBytes = bytesFromString(provider);
|
|
1179
1247
|
const errorCode = await ditto_auth_client_login_with_credentials(ditto, usernameBytes, passwordBytes, providerBytes);
|
|
1180
|
-
if (errorCode !== 0)
|
|
1181
|
-
throw new Error(errorMessage() ||
|
|
1248
|
+
if (errorCode !== 0) {
|
|
1249
|
+
throw new Error(errorMessage() ||
|
|
1250
|
+
`Ditto failed to authenticate (error code: ${errorCode}).`);
|
|
1251
|
+
}
|
|
1182
1252
|
}
|
|
1183
1253
|
async function dittoAuthClientLogout(ditto) {
|
|
1184
1254
|
ensureInitialized();
|
|
1185
1255
|
const errorCode = await ditto_auth_client_logout(ditto);
|
|
1186
|
-
if (errorCode !== 0)
|
|
1256
|
+
if (errorCode !== 0) {
|
|
1187
1257
|
throw new Error(errorMessage() || `Ditto failed to logout (error code: ${errorCode}).`);
|
|
1258
|
+
}
|
|
1188
1259
|
}
|
|
1189
1260
|
// ---------------------------------------------------------------- Ditto ------
|
|
1190
1261
|
/** @internal */
|
|
@@ -1199,8 +1270,10 @@ async function dittoGetCollectionNames(self) {
|
|
|
1199
1270
|
const result = await ditto_get_collection_names(self);
|
|
1200
1271
|
const errorCode = result.status_code;
|
|
1201
1272
|
const cStringVec = result.names;
|
|
1202
|
-
if (errorCode !== 0)
|
|
1203
|
-
throw new Error(errorMessage() ||
|
|
1273
|
+
if (errorCode !== 0) {
|
|
1274
|
+
throw new Error(errorMessage() ||
|
|
1275
|
+
`ditto_get_collection_names() failed with error code: ${errorCode}`);
|
|
1276
|
+
}
|
|
1204
1277
|
// WORKAROUND: cStringVecToStringArray() returns an `object` for the Wasm
|
|
1205
1278
|
// variant, but it really is an array. We therefore force-cast here. Remove as
|
|
1206
1279
|
// soon the exported function has proper type.
|
|
@@ -1361,7 +1434,8 @@ function dittoSmallPeerInfoSetMetadata(dittoPointer, metadata) {
|
|
|
1361
1434
|
case 3:
|
|
1362
1435
|
throw new Error(`Validation error, ${errorMessage() || `'${metadata}' is not a valid JSON object`}`);
|
|
1363
1436
|
default:
|
|
1364
|
-
throw new Error(errorMessage() ||
|
|
1437
|
+
throw new Error(errorMessage() ||
|
|
1438
|
+
`Internal inconsistency, ditto_small_peer_info_set_metadata() returned an unknown error code: ${statusCode}`);
|
|
1365
1439
|
}
|
|
1366
1440
|
}
|
|
1367
1441
|
/** @internal */
|
|
@@ -1446,8 +1520,10 @@ onError) {
|
|
|
1446
1520
|
function dittoCancelResolveAttachment(dittoPointer, id, cancelToken) {
|
|
1447
1521
|
ensureInitialized();
|
|
1448
1522
|
const errorCode = ditto_cancel_resolve_attachment(dittoPointer, id, cancelToken);
|
|
1449
|
-
if (errorCode !== 0)
|
|
1450
|
-
throw new Error(errorMessage() ||
|
|
1523
|
+
if (errorCode !== 0) {
|
|
1524
|
+
throw new Error(errorMessage() ||
|
|
1525
|
+
`ditto_cancel_resolve_attachment() failed with error code: ${errorCode}`);
|
|
1526
|
+
}
|
|
1451
1527
|
}
|
|
1452
1528
|
/** @internal */
|
|
1453
1529
|
function freeAttachmentHandle(attachmentHandlePointer) {
|
|
@@ -1528,15 +1604,19 @@ function dittoStopHTTPServer(dittoPointer) {
|
|
|
1528
1604
|
async function dittoRunGarbageCollection(dittoPointer) {
|
|
1529
1605
|
ensureInitialized();
|
|
1530
1606
|
const statusCode = await ditto_run_garbage_collection(dittoPointer);
|
|
1531
|
-
if (statusCode !== 0)
|
|
1532
|
-
throw new Error(errorMessage() ||
|
|
1607
|
+
if (statusCode !== 0) {
|
|
1608
|
+
throw new Error(errorMessage() ||
|
|
1609
|
+
`ditto_run_garbage_collection() failed with error code: ${statusCode}`);
|
|
1610
|
+
}
|
|
1533
1611
|
}
|
|
1534
1612
|
/** @internal */
|
|
1535
1613
|
async function dittoDisableSyncWithV3(dittoPointer) {
|
|
1536
1614
|
ensureInitialized();
|
|
1537
1615
|
const errorCode = await ditto_disable_sync_with_v3(dittoPointer);
|
|
1538
|
-
if (errorCode !== 0)
|
|
1539
|
-
throw new Error(errorMessage() ||
|
|
1616
|
+
if (errorCode !== 0) {
|
|
1617
|
+
throw new Error(errorMessage() ||
|
|
1618
|
+
`ditto_disable_sync_with_v3() failed with error code: ${errorCode}`);
|
|
1619
|
+
}
|
|
1540
1620
|
}
|
|
1541
1621
|
function dittoSetStaticTCPClients(ditto, listOfServers) {
|
|
1542
1622
|
ensureInitialized();
|
|
@@ -1557,8 +1637,10 @@ function dittoSetStaticWebsocketClients(ditto, listOfServers, routingHint) {
|
|
|
1557
1637
|
function documentsHash(documents) {
|
|
1558
1638
|
ensureInitialized();
|
|
1559
1639
|
const { status_code: errorCode, u64: hash } = ditto_documents_hash(documents);
|
|
1560
|
-
if (errorCode !== 0)
|
|
1561
|
-
throw new Error(errorMessage() ||
|
|
1640
|
+
if (errorCode !== 0) {
|
|
1641
|
+
throw new Error(errorMessage() ||
|
|
1642
|
+
`\`ditto_documents_hash()\` failed with error code: ${errorCode}`);
|
|
1643
|
+
}
|
|
1562
1644
|
// `hash` is of type `number | BigInt`, let's unify it to `BigInt` to keep it simple.
|
|
1563
1645
|
return BigInt(hash);
|
|
1564
1646
|
}
|
|
@@ -1566,8 +1648,10 @@ function documentsHash(documents) {
|
|
|
1566
1648
|
function documentsHashMnemonic(documents) {
|
|
1567
1649
|
ensureInitialized();
|
|
1568
1650
|
const { status_code: errorCode, c_string } = ditto_documents_hash_mnemonic(documents);
|
|
1569
|
-
if (errorCode !== 0)
|
|
1570
|
-
throw new Error(errorMessage() ||
|
|
1651
|
+
if (errorCode !== 0) {
|
|
1652
|
+
throw new Error(errorMessage() ||
|
|
1653
|
+
`\`ditto_documents_hash_mnemonic()\` failed with error code: ${errorCode}`);
|
|
1654
|
+
}
|
|
1571
1655
|
return boxCStringIntoString(c_string);
|
|
1572
1656
|
}
|
|
1573
1657
|
// ------------------------------------------------------------- base64 --------
|
|
@@ -1582,7 +1666,7 @@ function base64encode(bytes, paddingMode) {
|
|
|
1582
1666
|
*/
|
|
1583
1667
|
function tryBase64Decode(base64, paddingMode) {
|
|
1584
1668
|
const base64BytesPointer = bytesFromString(base64);
|
|
1585
|
-
const result = dittoffi_try_base64_decode(base64BytesPointer, paddingMode
|
|
1669
|
+
const result = dittoffi_try_base64_decode(base64BytesPointer, paddingMode);
|
|
1586
1670
|
throwOnErrorResult(result.error, 'dittoffi_try_base64_decode');
|
|
1587
1671
|
return boxCBytesIntoBuffer(result.success);
|
|
1588
1672
|
}
|
|
@@ -1613,28 +1697,23 @@ onError) {
|
|
|
1613
1697
|
function transportsInit() {
|
|
1614
1698
|
ensureInitialized();
|
|
1615
1699
|
const { output: wasInitialized, errorType } = withTransportsError(ditto_sdk_transports_init);
|
|
1616
|
-
if (wasInitialized === false)
|
|
1700
|
+
if (wasInitialized === false)
|
|
1617
1701
|
throw new Error(`Failed to initialize transports (${errorType} error)`);
|
|
1618
|
-
}
|
|
1619
1702
|
}
|
|
1620
1703
|
/** @internal */
|
|
1621
1704
|
function transportsBLEIsAvailable(ditto) {
|
|
1622
1705
|
ensureInitialized();
|
|
1623
|
-
|
|
1624
|
-
return ditto_sdk_transports_ble_is_available(ditto);
|
|
1625
|
-
}
|
|
1706
|
+
return ditto_sdk_transports_ble_is_available(ditto);
|
|
1626
1707
|
}
|
|
1627
1708
|
/** @internal */
|
|
1628
1709
|
function transportsBLECreate(ditto) {
|
|
1629
1710
|
ensureInitialized();
|
|
1630
1711
|
{
|
|
1631
1712
|
const { output: blePointer, errorType } = withTransportsError(ditto_sdk_transports_ble_create, ditto);
|
|
1632
|
-
if (blePointer != null)
|
|
1713
|
+
if (blePointer != null)
|
|
1633
1714
|
log('Info', `Bluetooth transport created.`);
|
|
1634
|
-
|
|
1635
|
-
else {
|
|
1715
|
+
else
|
|
1636
1716
|
log('Error', `Can't create bluetooth transport (${errorType} error).`);
|
|
1637
|
-
}
|
|
1638
1717
|
return blePointer;
|
|
1639
1718
|
}
|
|
1640
1719
|
}
|
|
@@ -1655,21 +1734,17 @@ function transportsBLEDestroy(ble) {
|
|
|
1655
1734
|
/** @internal */
|
|
1656
1735
|
function transportsLANIsAvailable(ditto) {
|
|
1657
1736
|
ensureInitialized();
|
|
1658
|
-
|
|
1659
|
-
return ditto_sdk_transports_lan_is_available(ditto);
|
|
1660
|
-
}
|
|
1737
|
+
return ditto_sdk_transports_lan_is_available(ditto);
|
|
1661
1738
|
}
|
|
1662
1739
|
/** @internal */
|
|
1663
1740
|
function transportsLANCreate(ditto) {
|
|
1664
1741
|
ensureInitialized();
|
|
1665
1742
|
{
|
|
1666
1743
|
const { output: lanPointer, errorType } = withTransportsError(ditto_sdk_transports_lan_create, ditto);
|
|
1667
|
-
if (lanPointer != null)
|
|
1744
|
+
if (lanPointer != null)
|
|
1668
1745
|
log('Info', `LAN transport created.`);
|
|
1669
|
-
|
|
1670
|
-
else {
|
|
1746
|
+
else
|
|
1671
1747
|
log('Error', `Can't create LAN transport (${errorType} error).`);
|
|
1672
|
-
}
|
|
1673
1748
|
return lanPointer;
|
|
1674
1749
|
}
|
|
1675
1750
|
}
|
|
@@ -1678,33 +1753,27 @@ function transportsLANDestroy(lan) {
|
|
|
1678
1753
|
ensureInitialized();
|
|
1679
1754
|
{
|
|
1680
1755
|
const { output: wasDestroyed, errorType } = withTransportsError(ditto_sdk_transports_lan_destroy, lan);
|
|
1681
|
-
if (wasDestroyed === true)
|
|
1756
|
+
if (wasDestroyed === true)
|
|
1682
1757
|
log('Info', 'LAN transport disabled.');
|
|
1683
|
-
|
|
1684
|
-
else {
|
|
1758
|
+
else
|
|
1685
1759
|
log('Error', `LAN transport could not be disabled (${errorType} error).`);
|
|
1686
|
-
}
|
|
1687
1760
|
return wasDestroyed;
|
|
1688
1761
|
}
|
|
1689
1762
|
}
|
|
1690
1763
|
/** @internal */
|
|
1691
1764
|
function transportsAWDLIsAvailable(ditto) {
|
|
1692
1765
|
ensureInitialized();
|
|
1693
|
-
|
|
1694
|
-
return ditto_sdk_transports_awdl_is_available(ditto);
|
|
1695
|
-
}
|
|
1766
|
+
return ditto_sdk_transports_awdl_is_available(ditto);
|
|
1696
1767
|
}
|
|
1697
1768
|
/** @internal */
|
|
1698
1769
|
function transportsAWDLCreate(ditto) {
|
|
1699
1770
|
ensureInitialized();
|
|
1700
1771
|
{
|
|
1701
1772
|
const { output: awdlPointer, errorType } = withTransportsError(ditto_sdk_transports_awdl_create, ditto);
|
|
1702
|
-
if (awdlPointer != null)
|
|
1773
|
+
if (awdlPointer != null)
|
|
1703
1774
|
log('Info', `AWDL transport created.`);
|
|
1704
|
-
|
|
1705
|
-
else {
|
|
1775
|
+
else
|
|
1706
1776
|
log('Error', `Can't create AWDL transport (${errorType} error).`);
|
|
1707
|
-
}
|
|
1708
1777
|
return awdlPointer;
|
|
1709
1778
|
}
|
|
1710
1779
|
}
|
|
@@ -1746,9 +1815,7 @@ function withTransportsError(ffiFunction, ...args) {
|
|
|
1746
1815
|
// ---------------------------------------------------------------- Other ------
|
|
1747
1816
|
/** @internal */
|
|
1748
1817
|
let isInitialized = false;
|
|
1749
|
-
|
|
1750
|
-
isInitialized = true;
|
|
1751
|
-
}
|
|
1818
|
+
isInitialized = true;
|
|
1752
1819
|
/** @internal */
|
|
1753
1820
|
function initSDKVersion(platform, language, semVer) {
|
|
1754
1821
|
ensureInitialized();
|
|
@@ -1756,8 +1823,10 @@ function initSDKVersion(platform, language, semVer) {
|
|
|
1756
1823
|
bytesFromString(language);
|
|
1757
1824
|
const semVerCString = bytesFromString(semVer);
|
|
1758
1825
|
const errorCode = ditto_init_sdk_version(platform, language, semVerCString);
|
|
1759
|
-
if (typeof errorCode !== 'undefined' && errorCode !== 0)
|
|
1760
|
-
throw new Error(errorMessage() ||
|
|
1826
|
+
if (typeof errorCode !== 'undefined' && errorCode !== 0) {
|
|
1827
|
+
throw new Error(errorMessage() ||
|
|
1828
|
+
`ditto_init_sdk_version() failed with error code: ${errorCode}`);
|
|
1829
|
+
}
|
|
1761
1830
|
}
|
|
1762
1831
|
/** @internal */
|
|
1763
1832
|
function tryVerifyLicense(license) {
|
|
@@ -1774,7 +1843,6 @@ function tryVerifyLicense(license) {
|
|
|
1774
1843
|
/** @internal */
|
|
1775
1844
|
const NOT_FOUND_ERROR_CODE = -30798;
|
|
1776
1845
|
/** @internal */
|
|
1777
|
-
// prettier-ignore
|
|
1778
1846
|
function wrapBackgroundCbForFFI(onError, cb) {
|
|
1779
1847
|
const errorHandler = onError !== null && onError !== void 0 ? onError : ((err) => log('Error', `The registered callback failed with ${err}`));
|
|
1780
1848
|
return (ret_sender, ...args) => {
|
|
@@ -1794,7 +1862,6 @@ function wrapBackgroundCbForFFI(onError, cb) {
|
|
|
1794
1862
|
};
|
|
1795
1863
|
}
|
|
1796
1864
|
/** @internal */
|
|
1797
|
-
// prettier-ignore
|
|
1798
1865
|
function wrapAsyncBackgroundCbForFFI(onError, cb) {
|
|
1799
1866
|
const errorHandler = onError !== null && onError !== void 0 ? onError : ((err) => log('Error', `The registered callback failed with ${err}`));
|
|
1800
1867
|
return async (ret_sender, ...args) => {
|
|
@@ -1819,8 +1886,9 @@ function bytesFromString(jsString) {
|
|
|
1819
1886
|
return undefined;
|
|
1820
1887
|
if (jsString === null)
|
|
1821
1888
|
return null;
|
|
1822
|
-
if (typeof jsString !== 'string')
|
|
1889
|
+
if (typeof jsString !== 'string') {
|
|
1823
1890
|
throw new Error(`Can't convert string to Uint8Array, not a string: ${jsString}`);
|
|
1891
|
+
}
|
|
1824
1892
|
const textEncoder = new TextEncoder();
|
|
1825
1893
|
return textEncoder.encode(`${jsString}\0`);
|
|
1826
1894
|
}
|
|
@@ -1869,6 +1937,11 @@ const ERROR_CODES = {
|
|
|
1869
1937
|
// REFACTOR: May be replaced by `unsupported` in v5. Tracked in #12755.
|
|
1870
1938
|
'sdk/unsupported': 'The feature is not supported by the current environment.',
|
|
1871
1939
|
//
|
|
1940
|
+
// Authentication errors
|
|
1941
|
+
//
|
|
1942
|
+
/** Error when authentication failed */
|
|
1943
|
+
'authentication/failed-to-authenticate': 'Ditto failed to authenticate.',
|
|
1944
|
+
//
|
|
1872
1945
|
// IO errors
|
|
1873
1946
|
//
|
|
1874
1947
|
/** Error when a file or directory already exists */
|
|
@@ -1947,8 +2020,12 @@ const DEFAULT_STATUS_CODE_MAPPING = {
|
|
|
1947
2020
|
// Activation errors
|
|
1948
2021
|
//
|
|
1949
2022
|
ActivationLicenseTokenExpired: ['activation/license-token-expired'],
|
|
1950
|
-
ActivationLicenseTokenInvalid: [
|
|
1951
|
-
|
|
2023
|
+
ActivationLicenseTokenInvalid: [
|
|
2024
|
+
'activation/license-token-verification-failed',
|
|
2025
|
+
],
|
|
2026
|
+
ActivationLicenseUnsupportedFutureVersion: [
|
|
2027
|
+
'activation/license-token-unsupported-future-version',
|
|
2028
|
+
],
|
|
1952
2029
|
ActivationNotActivated: ['activation/not-activated'],
|
|
1953
2030
|
//
|
|
1954
2031
|
// Input/output errors
|
|
@@ -1960,7 +2037,10 @@ const DEFAULT_STATUS_CODE_MAPPING = {
|
|
|
1960
2037
|
//
|
|
1961
2038
|
// SDK-specific errors
|
|
1962
2039
|
//
|
|
1963
|
-
JsFloatingStoreOperation: [
|
|
2040
|
+
JsFloatingStoreOperation: [
|
|
2041
|
+
'internal',
|
|
2042
|
+
'Internal inconsistency, an outstanding store operation was not awaited.',
|
|
2043
|
+
],
|
|
1964
2044
|
//
|
|
1965
2045
|
// DQL errors
|
|
1966
2046
|
//
|
|
@@ -2024,9 +2104,8 @@ class DittoError extends Error {
|
|
|
2024
2104
|
* @throws {@link DittoError} `internal`: when supplied with an invalid error code
|
|
2025
2105
|
*/
|
|
2026
2106
|
constructor(code, message, context = {}) {
|
|
2027
|
-
if (ERROR_CODES[code] == null)
|
|
2107
|
+
if (ERROR_CODES[code] == null)
|
|
2028
2108
|
throw new DittoError('internal', `Invalid error code: ${code}`);
|
|
2029
|
-
}
|
|
2030
2109
|
super(message || ERROR_CODES[code]);
|
|
2031
2110
|
/**
|
|
2032
2111
|
* Use the error code to identify a specific error programatically.
|
|
@@ -2063,7 +2142,7 @@ class DittoError extends Error {
|
|
|
2063
2142
|
};
|
|
2064
2143
|
const err = new DittoError(code, message, errorContext);
|
|
2065
2144
|
if (ffiError.stack != null) {
|
|
2066
|
-
// @ts-
|
|
2145
|
+
// @ts-expect-error assigning to a readonly property
|
|
2067
2146
|
err.stack = ffiError.stack;
|
|
2068
2147
|
}
|
|
2069
2148
|
return err;
|
|
@@ -2092,9 +2171,8 @@ function mapFFIErrors(closure, statusCodeMapping, context) {
|
|
|
2092
2171
|
return closure();
|
|
2093
2172
|
}
|
|
2094
2173
|
catch (error) {
|
|
2095
|
-
if (error instanceof DittoFFIError)
|
|
2174
|
+
if (error instanceof DittoFFIError)
|
|
2096
2175
|
throw translateFFIError(error, statusCodeMapping, context);
|
|
2097
|
-
}
|
|
2098
2176
|
throw error;
|
|
2099
2177
|
}
|
|
2100
2178
|
}
|
|
@@ -2121,9 +2199,8 @@ async function mapFFIErrorsAsync(closure, statusCodeMapping, context) {
|
|
|
2121
2199
|
return await closure();
|
|
2122
2200
|
}
|
|
2123
2201
|
catch (error) {
|
|
2124
|
-
if (error instanceof DittoFFIError)
|
|
2202
|
+
if (error instanceof DittoFFIError)
|
|
2125
2203
|
throw translateFFIError(error, statusCodeMapping, context);
|
|
2126
|
-
}
|
|
2127
2204
|
throw error;
|
|
2128
2205
|
}
|
|
2129
2206
|
}
|
|
@@ -2133,11 +2210,13 @@ const translateFFIError = (ffiError, customStatusCodeMapping, context) => {
|
|
|
2133
2210
|
// which can't be used as an index into an object.
|
|
2134
2211
|
const statusCode = ffiError.code.toString();
|
|
2135
2212
|
let code, message;
|
|
2136
|
-
if (customStatusCodeMapping != null &&
|
|
2213
|
+
if (customStatusCodeMapping != null &&
|
|
2214
|
+
customStatusCodeMapping[statusCode] != null) {
|
|
2137
2215
|
[code, message] = customStatusCodeMapping[statusCode];
|
|
2138
2216
|
}
|
|
2139
2217
|
else {
|
|
2140
|
-
[code, message] =
|
|
2218
|
+
[code, message] =
|
|
2219
|
+
(_a = DEFAULT_STATUS_CODE_MAPPING[statusCode]) !== null && _a !== void 0 ? _a : DEFAULT_STATUS_CODE_MAPPING.default;
|
|
2141
2220
|
}
|
|
2142
2221
|
return DittoError.fromFFIError(ffiError, code, message, context);
|
|
2143
2222
|
};
|
|
@@ -2182,21 +2261,18 @@ class AttachmentToken {
|
|
|
2182
2261
|
*/
|
|
2183
2262
|
static validateTypedInput(jsObj) {
|
|
2184
2263
|
const type = jsObj[DittoCRDTTypeKey];
|
|
2185
|
-
if (type !== DittoCRDTType.attachment)
|
|
2264
|
+
if (type !== DittoCRDTType.attachment)
|
|
2186
2265
|
throw new Error('Invalid attachment token');
|
|
2187
|
-
}
|
|
2188
2266
|
const id = jsObj['_id'];
|
|
2189
|
-
if (!(id instanceof Uint8Array))
|
|
2267
|
+
if (!(id instanceof Uint8Array))
|
|
2190
2268
|
throw new Error('Invalid attachment token id');
|
|
2191
|
-
}
|
|
2192
2269
|
const len = jsObj['_len'];
|
|
2193
2270
|
if ((typeof len !== 'number' && typeof len !== 'bigint') || len < 0) {
|
|
2194
2271
|
throw new Error('Invalid attachment token length, must be a non-negative number or bigint');
|
|
2195
2272
|
}
|
|
2196
2273
|
const meta = jsObj['_meta'];
|
|
2197
|
-
if (typeof meta !== 'object')
|
|
2274
|
+
if (typeof meta !== 'object')
|
|
2198
2275
|
throw new Error('Invalid attachment token meta');
|
|
2199
|
-
}
|
|
2200
2276
|
return { id, len, meta };
|
|
2201
2277
|
}
|
|
2202
2278
|
/**
|
|
@@ -2211,11 +2287,13 @@ class AttachmentToken {
|
|
|
2211
2287
|
*/
|
|
2212
2288
|
static validateUntypedInput(jsObj) {
|
|
2213
2289
|
const idBase64 = jsObj['id'];
|
|
2214
|
-
if (typeof idBase64 !== 'string')
|
|
2290
|
+
if (typeof idBase64 !== 'string')
|
|
2215
2291
|
throw new Error('Invalid attachment token id');
|
|
2216
|
-
}
|
|
2217
2292
|
const id = mapFFIErrors(() => tryBase64Decode(idBase64, 'Unpadded'), {
|
|
2218
|
-
Base64Invalid: [
|
|
2293
|
+
Base64Invalid: [
|
|
2294
|
+
'store/attachment-token-invalid',
|
|
2295
|
+
'Failed to decode attachment token id from base64 input',
|
|
2296
|
+
],
|
|
2219
2297
|
}, {
|
|
2220
2298
|
attachmentTokenID: idBase64,
|
|
2221
2299
|
});
|
|
@@ -2224,16 +2302,15 @@ class AttachmentToken {
|
|
|
2224
2302
|
throw new Error('Invalid attachment token length, must be a non-negative number or bigint');
|
|
2225
2303
|
}
|
|
2226
2304
|
const meta = jsObj['metadata'];
|
|
2227
|
-
if (typeof meta !== 'object')
|
|
2305
|
+
if (typeof meta !== 'object')
|
|
2228
2306
|
throw new Error('Invalid attachment token meta');
|
|
2229
|
-
}
|
|
2230
2307
|
return { id, len, meta };
|
|
2231
2308
|
}
|
|
2232
2309
|
}
|
|
2233
2310
|
|
|
2234
2311
|
// NOTE: this is patched up with the actual build version by Jake task
|
|
2235
2312
|
// build:package and has to be a valid semantic version as defined here: https://semver.org.
|
|
2236
|
-
const fullBuildVersionString = '4.
|
|
2313
|
+
const fullBuildVersionString = '4.9.0-alpha.1';
|
|
2237
2314
|
|
|
2238
2315
|
//
|
|
2239
2316
|
// Copyright © 2021 DittoLive Incorporated. All rights reserved.
|
|
@@ -2383,7 +2460,7 @@ class Logger {
|
|
|
2383
2460
|
* callbacks.
|
|
2384
2461
|
*
|
|
2385
2462
|
* @throws {TypeError} if `callback` is not a function or `undefined`.
|
|
2386
|
-
* @throws {Error}
|
|
2463
|
+
* @throws {Error} when called in a React Native environment.
|
|
2387
2464
|
*/
|
|
2388
2465
|
static async setCustomLogCallback(callback) {
|
|
2389
2466
|
if (callback != null && typeof callback !== 'function') {
|
|
@@ -2446,10 +2523,10 @@ class Logger {
|
|
|
2446
2523
|
throw new TypeError(`Expected parameter 'path' to be a string, but got ${typeof path}.`);
|
|
2447
2524
|
}
|
|
2448
2525
|
let normalizedPath = path;
|
|
2449
|
-
if (process.platform === 'win32' &&
|
|
2450
|
-
|
|
2526
|
+
if (process.platform === 'win32' &&
|
|
2527
|
+
!path.startsWith('\\\\?\\')) {
|
|
2528
|
+
if (path.length === 0)
|
|
2451
2529
|
throw new DittoError('io/not-found', 'The path must not be empty.');
|
|
2452
|
-
}
|
|
2453
2530
|
// Normalize the path on Windows to prevent issues with long paths.
|
|
2454
2531
|
// Windows has a hard limit of 260 characters for paths.
|
|
2455
2532
|
// c.f. httpes://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation
|
|
@@ -3581,22 +3658,17 @@ class DocumentID {
|
|
|
3581
3658
|
equals(documentID) {
|
|
3582
3659
|
const left = this['@ditto.cbor'];
|
|
3583
3660
|
const right = documentID['@ditto.cbor'];
|
|
3584
|
-
if (left === right)
|
|
3661
|
+
if (left === right)
|
|
3585
3662
|
return true;
|
|
3586
|
-
|
|
3587
|
-
if (!(left instanceof Uint8Array)) {
|
|
3663
|
+
if (!(left instanceof Uint8Array))
|
|
3588
3664
|
return false;
|
|
3589
|
-
|
|
3590
|
-
if (!(right instanceof Uint8Array)) {
|
|
3665
|
+
if (!(right instanceof Uint8Array))
|
|
3591
3666
|
return false;
|
|
3592
|
-
|
|
3593
|
-
if (left.length !== right.length) {
|
|
3667
|
+
if (left.length !== right.length)
|
|
3594
3668
|
return false;
|
|
3595
|
-
|
|
3596
|
-
for (let i = 0; i < left.length; i += 1) {
|
|
3669
|
+
for (let i = 0; i < left.length; i += 1)
|
|
3597
3670
|
if (left[i] !== right[i])
|
|
3598
3671
|
return false;
|
|
3599
|
-
}
|
|
3600
3672
|
return true;
|
|
3601
3673
|
}
|
|
3602
3674
|
/**
|
|
@@ -3640,9 +3712,8 @@ class DocumentID {
|
|
|
3640
3712
|
// -----------------------------------------------------------------------------
|
|
3641
3713
|
/** @internal */
|
|
3642
3714
|
function validateDocumentIDValue(id) {
|
|
3643
|
-
if (typeof id === 'undefined')
|
|
3715
|
+
if (typeof id === 'undefined')
|
|
3644
3716
|
throw new Error(`Invalid document ID: ${id}`);
|
|
3645
|
-
}
|
|
3646
3717
|
return id;
|
|
3647
3718
|
}
|
|
3648
3719
|
/** @internal */
|
|
@@ -3713,15 +3784,12 @@ class Handle {
|
|
|
3713
3784
|
*/
|
|
3714
3785
|
derefOrNull() {
|
|
3715
3786
|
var _b;
|
|
3716
|
-
if (this.isClosed)
|
|
3787
|
+
if (this.isClosed)
|
|
3717
3788
|
return null;
|
|
3718
|
-
|
|
3719
|
-
if (this.isFinalized) {
|
|
3789
|
+
if (this.isFinalized)
|
|
3720
3790
|
return null;
|
|
3721
|
-
|
|
3722
|
-
if (this.isUnregistered) {
|
|
3791
|
+
if (this.isUnregistered)
|
|
3723
3792
|
return null;
|
|
3724
|
-
}
|
|
3725
3793
|
return (_b = this.pointer) !== null && _b !== void 0 ? _b : null;
|
|
3726
3794
|
}
|
|
3727
3795
|
/**
|
|
@@ -3853,9 +3921,8 @@ class Bridge {
|
|
|
3853
3921
|
*
|
|
3854
3922
|
* @internal */
|
|
3855
3923
|
get type() {
|
|
3856
|
-
if (this.internalType == null)
|
|
3924
|
+
if (this.internalType == null)
|
|
3857
3925
|
throw new Error('Bridge type has not been registered yet.');
|
|
3858
|
-
}
|
|
3859
3926
|
return this.internalType;
|
|
3860
3927
|
}
|
|
3861
3928
|
/**
|
|
@@ -3908,8 +3975,9 @@ class Bridge {
|
|
|
3908
3975
|
const handle = this.handlesByAddress[pointer.addr];
|
|
3909
3976
|
if (!handle)
|
|
3910
3977
|
return undefined;
|
|
3911
|
-
if (handle.type !== this.type)
|
|
3978
|
+
if (handle.type !== this.type) {
|
|
3912
3979
|
throw new Error(`Can't return object for pointer, pointer is associated with an object of type ${handle.type} but this bridge is configured for ${this.type}`);
|
|
3980
|
+
}
|
|
3913
3981
|
// This throws an error if the object has been garbage collected but the
|
|
3914
3982
|
// finalizer has not been called yet.
|
|
3915
3983
|
return handle.object();
|
|
@@ -3928,9 +3996,8 @@ class Bridge {
|
|
|
3928
3996
|
*/
|
|
3929
3997
|
bridge(pointer, objectOrCreate) {
|
|
3930
3998
|
const existingObject = this.objectFor(pointer);
|
|
3931
|
-
if (existingObject)
|
|
3999
|
+
if (existingObject)
|
|
3932
4000
|
return existingObject;
|
|
3933
|
-
}
|
|
3934
4001
|
if (!objectOrCreate) {
|
|
3935
4002
|
objectOrCreate = () => {
|
|
3936
4003
|
return Reflect.construct(this.type, []);
|
|
@@ -3961,17 +4028,21 @@ class Bridge {
|
|
|
3961
4028
|
* @private */
|
|
3962
4029
|
register(object, pointer) {
|
|
3963
4030
|
const objectType = object.constructor;
|
|
3964
|
-
if (objectType !== this.type)
|
|
4031
|
+
if (objectType !== this.type) {
|
|
3965
4032
|
throw new Error(`Can't register, bridge is configured for type ${this.type.name} but passed in object is of type ${objectType.name}`);
|
|
4033
|
+
}
|
|
3966
4034
|
const existingHandle = this.handlesByObject.get(object);
|
|
3967
4035
|
const existingPointer = existingHandle ? existingHandle.pointer : null;
|
|
3968
4036
|
// Check that both pointer and handle are undefined at this point.
|
|
3969
|
-
if (existingPointer != null && existingHandle != null)
|
|
4037
|
+
if (existingPointer != null && existingHandle != null) {
|
|
3970
4038
|
throw new Error(`Can't register, an object for the passed in pointer has previously been registered: ${existingHandle.object()}`);
|
|
3971
|
-
|
|
4039
|
+
}
|
|
4040
|
+
if (existingPointer != null && existingHandle == null) {
|
|
3972
4041
|
throw new Error(`Internal inconsistency, trying to register an object which has an associated pointer but no handle entry: ${objectType.name} at ${existingPointer.type} ${existingPointer.addr}`);
|
|
3973
|
-
|
|
4042
|
+
}
|
|
4043
|
+
if (existingPointer == null && existingHandle != null) {
|
|
3974
4044
|
throw new Error(`Internal inconsistency, trying to register an object which has a handle entry but no associated pointer: ${objectType.name} ${object}`);
|
|
4045
|
+
}
|
|
3975
4046
|
const handle = new Handle(this, object, pointer);
|
|
3976
4047
|
this.handlesByAddress[pointer.addr] = handle;
|
|
3977
4048
|
this.handlesByObject.set(object, handle);
|
|
@@ -3991,21 +4062,28 @@ class Bridge {
|
|
|
3991
4062
|
unregister(object) {
|
|
3992
4063
|
const objectType = object.constructor;
|
|
3993
4064
|
const bridgeType = this.type;
|
|
3994
|
-
if (objectType !== bridgeType)
|
|
4065
|
+
if (objectType !== bridgeType) {
|
|
3995
4066
|
throw new Error(`Can't unregister, bridge is configured for type ${bridgeType.name} but passed in object is of type ${objectType.name}`);
|
|
4067
|
+
}
|
|
3996
4068
|
const handle = this.handlesByObject.get(object);
|
|
3997
|
-
if (handle == null)
|
|
4069
|
+
if (handle == null) {
|
|
3998
4070
|
throw new Error(`Can't unregister, object has not been registered before: ${object}`);
|
|
3999
|
-
|
|
4071
|
+
}
|
|
4072
|
+
if (handle.type !== bridgeType) {
|
|
4000
4073
|
throw new Error(`Internal inconsistency, trying to unregister an object that has a handle with a different type than that of the bridge: ${handle}`);
|
|
4001
|
-
|
|
4074
|
+
}
|
|
4075
|
+
if (handle.objectOrNull() !== object) {
|
|
4002
4076
|
throw new Error(`Internal inconsistency, trying to unregister an object whose associated handle holds a different object: ${handle}`);
|
|
4003
|
-
|
|
4077
|
+
}
|
|
4078
|
+
if (handle.isClosed) {
|
|
4004
4079
|
throw new Error(`Can't unregister, object has been closed before: ${object}`);
|
|
4005
|
-
|
|
4080
|
+
}
|
|
4081
|
+
if (handle.isFinalized) {
|
|
4006
4082
|
throw new Error(`Can't unregister, object has been finalized before: ${object}`);
|
|
4007
|
-
|
|
4083
|
+
}
|
|
4084
|
+
if (handle.isUnregistered) {
|
|
4008
4085
|
throw new Error(`Can't unregister, object has been unregistered already: ${object}`);
|
|
4086
|
+
}
|
|
4009
4087
|
handle.bridgeWillUnregister();
|
|
4010
4088
|
this.finalizationRegistry.unregister(object);
|
|
4011
4089
|
delete this.handlesByAddress[handle.pointer.addr];
|
|
@@ -4022,9 +4100,8 @@ class Bridge {
|
|
|
4022
4100
|
}
|
|
4023
4101
|
for (const handle of Object.values(this.handlesByAddress)) {
|
|
4024
4102
|
const object = handle.object();
|
|
4025
|
-
if (object)
|
|
4103
|
+
if (object)
|
|
4026
4104
|
this.unregister(object);
|
|
4027
|
-
}
|
|
4028
4105
|
}
|
|
4029
4106
|
}
|
|
4030
4107
|
/**
|
|
@@ -4035,22 +4112,27 @@ class Bridge {
|
|
|
4035
4112
|
async close(object) {
|
|
4036
4113
|
const objectType = object.constructor;
|
|
4037
4114
|
const bridgeType = this.type;
|
|
4038
|
-
if (objectType !== bridgeType)
|
|
4115
|
+
if (objectType !== bridgeType) {
|
|
4039
4116
|
throw new Error(`Can't close, bridge is configured for type ${bridgeType.name} but passed in object is of type ${objectType.name}`);
|
|
4117
|
+
}
|
|
4040
4118
|
const handle = this.handlesByObject.get(object);
|
|
4041
|
-
if (handle == null)
|
|
4119
|
+
if (handle == null) {
|
|
4042
4120
|
throw new Error(`Can't close an object that has not been registered before: ${object}`);
|
|
4043
|
-
|
|
4121
|
+
}
|
|
4122
|
+
if (handle.type !== bridgeType) {
|
|
4044
4123
|
throw new Error(`Internal inconsistency, trying to close an object that has a handle with a different type than that of the bridge: ${handle}`);
|
|
4124
|
+
}
|
|
4045
4125
|
if (handle.isUnregistered)
|
|
4046
4126
|
throw new Error(`Can't close object, object has been unregistered.`);
|
|
4047
|
-
if (handle.isFinalized)
|
|
4127
|
+
if (handle.isFinalized) {
|
|
4048
4128
|
throw new Error(`Internal inconsistency, trying to close an object that has already been finalized.`);
|
|
4129
|
+
}
|
|
4049
4130
|
if (handle.isClosed)
|
|
4050
4131
|
return;
|
|
4051
4132
|
const pointer = handle.pointer;
|
|
4052
|
-
if (!pointer)
|
|
4133
|
+
if (!pointer) {
|
|
4053
4134
|
throw new Error(`Internal inconsistency, trying to close an object whose pointer is null.`);
|
|
4135
|
+
}
|
|
4054
4136
|
handle.bridgeWillClose();
|
|
4055
4137
|
delete this.handlesByAddress[pointer.addr];
|
|
4056
4138
|
await this.release(pointer);
|
|
@@ -4064,15 +4146,18 @@ class Bridge {
|
|
|
4064
4146
|
return Object.keys(this.handlesByAddress).length;
|
|
4065
4147
|
}
|
|
4066
4148
|
async finalize(handle) {
|
|
4067
|
-
if (handle.isFinalized)
|
|
4149
|
+
if (handle.isFinalized) {
|
|
4068
4150
|
throw new Error(`Internal inconsistency, trying to finalize an object that has already been finalized.`);
|
|
4069
|
-
|
|
4151
|
+
}
|
|
4152
|
+
if (handle.isUnregistered) {
|
|
4070
4153
|
throw new Error(`Internal inconsistency, trying to finalize an object that has been unregistered before.`);
|
|
4154
|
+
}
|
|
4071
4155
|
handle.bridgeWillFinalize();
|
|
4072
4156
|
if (!handle.isClosed) {
|
|
4073
4157
|
const pointer = handle.pointer;
|
|
4074
|
-
if (!pointer)
|
|
4158
|
+
if (!pointer) {
|
|
4075
4159
|
throw new Error(`Internal inconsistency, trying to finalize an object whose pointer is null.`);
|
|
4160
|
+
}
|
|
4076
4161
|
delete this.handlesByAddress[pointer.addr];
|
|
4077
4162
|
await this.release(pointer);
|
|
4078
4163
|
}
|
|
@@ -4229,9 +4314,8 @@ function augmentJSONValue(json, mutDoc, workingPath) {
|
|
|
4229
4314
|
return new AttachmentToken(json);
|
|
4230
4315
|
}
|
|
4231
4316
|
else {
|
|
4232
|
-
for (const [key, value] of Object.entries(json))
|
|
4317
|
+
for (const [key, value] of Object.entries(json))
|
|
4233
4318
|
json[key] = augmentJSONValue(value, mutDoc, `${workingPath}['${key}']`);
|
|
4234
|
-
}
|
|
4235
4319
|
return json;
|
|
4236
4320
|
}
|
|
4237
4321
|
}
|
|
@@ -4281,9 +4365,8 @@ function desugarJSObject(jsObj) {
|
|
|
4281
4365
|
else {
|
|
4282
4366
|
// Create a copy to not mutate the original object
|
|
4283
4367
|
const jsObjJSON = {};
|
|
4284
|
-
for (const [key, value] of Object.entries(jsObj))
|
|
4368
|
+
for (const [key, value] of Object.entries(jsObj))
|
|
4285
4369
|
jsObjJSON[key] = desugarJSObject(value);
|
|
4286
|
-
}
|
|
4287
4370
|
return jsObjJSON;
|
|
4288
4371
|
}
|
|
4289
4372
|
}
|
|
@@ -4304,9 +4387,8 @@ function desugarJSObject(jsObj) {
|
|
|
4304
4387
|
* @throws {Error} If `jsObj` is a non-finite float value.
|
|
4305
4388
|
*/
|
|
4306
4389
|
function checkForUnsupportedValues(jsObj) {
|
|
4307
|
-
if (Number.isNaN(jsObj) || jsObj === Infinity || jsObj === -Infinity)
|
|
4390
|
+
if (Number.isNaN(jsObj) || jsObj === Infinity || jsObj === -Infinity)
|
|
4308
4391
|
throw new Error('Non-finite float values are not supported');
|
|
4309
|
-
}
|
|
4310
4392
|
}
|
|
4311
4393
|
|
|
4312
4394
|
//
|
|
@@ -4319,6 +4401,8 @@ function checkForUnsupportedValues(jsObj) {
|
|
|
4319
4401
|
* - `set`
|
|
4320
4402
|
* - `removed`
|
|
4321
4403
|
* - `incremented`
|
|
4404
|
+
*
|
|
4405
|
+
* Not available in React Native environments.
|
|
4322
4406
|
*/
|
|
4323
4407
|
class UpdateResult {
|
|
4324
4408
|
// ----------------------------------------------------------- Internal ------
|
|
@@ -4408,8 +4492,9 @@ function validateQuery(query, options = {}) {
|
|
|
4408
4492
|
if (query === '')
|
|
4409
4493
|
throw new Error(`${errorMessagePrefix} query is an empty string.`);
|
|
4410
4494
|
const validatedQuery = query.trim();
|
|
4411
|
-
if (validatedQuery === '')
|
|
4495
|
+
if (validatedQuery === '') {
|
|
4412
4496
|
throw new Error(`${errorMessagePrefix} query contains only whitespace characters.`);
|
|
4497
|
+
}
|
|
4413
4498
|
return validatedQuery;
|
|
4414
4499
|
}
|
|
4415
4500
|
// -------------------------------------------------------------- Helpers ------
|
|
@@ -4423,9 +4508,8 @@ function validateQuery(query, options = {}) {
|
|
|
4423
4508
|
* See https://nodejs.org/api/util.html#custom-inspection-functions-on-objects
|
|
4424
4509
|
*/
|
|
4425
4510
|
function customInspectRepresentation(documentLike, inspect) {
|
|
4426
|
-
if (inspect === undefined)
|
|
4511
|
+
if (inspect === undefined)
|
|
4427
4512
|
return `${documentLike.constructor.name} ${JSON.stringify({ value: documentLike.value }, null, 2)}`;
|
|
4428
|
-
}
|
|
4429
4513
|
return `${documentLike.constructor.name} ${inspect({ value: documentLike.value })}`;
|
|
4430
4514
|
}
|
|
4431
4515
|
// --------------------------------------------------------------- System ------
|
|
@@ -4482,9 +4566,8 @@ class KeyPath {
|
|
|
4482
4566
|
* starts with a character or an underscore.
|
|
4483
4567
|
*/
|
|
4484
4568
|
static withLeadingDot(keyPath) {
|
|
4485
|
-
if (typeof keyPath === 'number')
|
|
4569
|
+
if (typeof keyPath === 'number')
|
|
4486
4570
|
return keyPath;
|
|
4487
|
-
}
|
|
4488
4571
|
if (typeof keyPath !== 'string') {
|
|
4489
4572
|
throw new Error(`Key-path must be a string or a number but is ${typeof keyPath}: ${keyPath}`);
|
|
4490
4573
|
}
|
|
@@ -4495,9 +4578,8 @@ class KeyPath {
|
|
|
4495
4578
|
* Returns the key-path by removing the leading dot if it starts with one.
|
|
4496
4579
|
*/
|
|
4497
4580
|
static withoutLeadingDot(keyPath) {
|
|
4498
|
-
if (typeof keyPath === 'number')
|
|
4581
|
+
if (typeof keyPath === 'number')
|
|
4499
4582
|
return keyPath;
|
|
4500
|
-
}
|
|
4501
4583
|
if (typeof keyPath !== 'string') {
|
|
4502
4584
|
throw new Error(`Key-path must be a string or a number but is ${typeof keyPath}: ${keyPath}`);
|
|
4503
4585
|
}
|
|
@@ -4511,16 +4593,14 @@ class KeyPath {
|
|
|
4511
4593
|
static validate(keyPath, options = {}) {
|
|
4512
4594
|
var _a;
|
|
4513
4595
|
const isInitial = (_a = options.isInitial) !== null && _a !== void 0 ? _a : false;
|
|
4514
|
-
if (typeof keyPath === 'number')
|
|
4596
|
+
if (typeof keyPath === 'number')
|
|
4515
4597
|
return Math.floor(Math.abs(keyPath));
|
|
4516
|
-
}
|
|
4517
4598
|
if (typeof keyPath !== 'string') {
|
|
4518
4599
|
throw new Error(`Key-path must be a string or a number but is ${typeof keyPath}: ${keyPath}`);
|
|
4519
4600
|
}
|
|
4520
4601
|
const keyPathWithLeadingDot = this.withLeadingDot(keyPath);
|
|
4521
|
-
if (!validKeyPathRegExp.test(keyPathWithLeadingDot))
|
|
4602
|
+
if (!validKeyPathRegExp.test(keyPathWithLeadingDot))
|
|
4522
4603
|
throw new Error(`Key-path is not valid: ${keyPath}`);
|
|
4523
|
-
}
|
|
4524
4604
|
return isInitial ? keyPath : keyPathWithLeadingDot;
|
|
4525
4605
|
}
|
|
4526
4606
|
/**
|
|
@@ -4544,7 +4624,11 @@ class KeyPath {
|
|
|
4544
4624
|
};
|
|
4545
4625
|
function advance(keyPath) {
|
|
4546
4626
|
if (typeof keyPath === 'number') {
|
|
4547
|
-
return {
|
|
4627
|
+
return {
|
|
4628
|
+
nextPathComponentRaw: keyPath,
|
|
4629
|
+
nextPathComponent: keyPath,
|
|
4630
|
+
remainingPath: '',
|
|
4631
|
+
};
|
|
4548
4632
|
}
|
|
4549
4633
|
if (typeof keyPath !== 'string') {
|
|
4550
4634
|
throw new Error(`Can't return value at given keyPath, expected keyPath to be a string or a number but got ${typeof keyPath}: ${keyPath}`);
|
|
@@ -4592,12 +4676,11 @@ class KeyPath {
|
|
|
4592
4676
|
const { nextPathComponentRaw, nextPathComponent, remainingPath } = advance(keyPath);
|
|
4593
4677
|
evaluationResult.nextPathComponent = nextPathComponent;
|
|
4594
4678
|
evaluationResult.remainingPath = remainingPath;
|
|
4595
|
-
if (evaluationResult.coveredPath === null ||
|
|
4679
|
+
if (evaluationResult.coveredPath === null ||
|
|
4680
|
+
typeof nextPathComponentRaw === 'number')
|
|
4596
4681
|
evaluationResult.coveredPath = nextPathComponentRaw;
|
|
4597
|
-
|
|
4598
|
-
else {
|
|
4682
|
+
else
|
|
4599
4683
|
evaluationResult.coveredPath += nextPathComponentRaw;
|
|
4600
|
-
}
|
|
4601
4684
|
if (remainingPath === '' && stopAtLastContainer) {
|
|
4602
4685
|
evaluationResult.value = object;
|
|
4603
4686
|
return evaluationResult;
|
|
@@ -4623,6 +4706,8 @@ class KeyPath {
|
|
|
4623
4706
|
* type. You don't create a `DocumentPath` directly but obtain one via the
|
|
4624
4707
|
* {@link Document.path | path} property or the {@link Document.at | at()}
|
|
4625
4708
|
* method of {@link Document}.
|
|
4709
|
+
*
|
|
4710
|
+
* Not available in React Native environments.
|
|
4626
4711
|
*/
|
|
4627
4712
|
class DocumentPath {
|
|
4628
4713
|
/**
|
|
@@ -4643,6 +4728,8 @@ class DocumentPath {
|
|
|
4643
4728
|
* - `documentPath.at('passengers[2]')`
|
|
4644
4729
|
* - `documentPath.at('passengers[2].belongings[1].kind')`
|
|
4645
4730
|
* - `documentPath.at('.mileage')`
|
|
4731
|
+
*
|
|
4732
|
+
* @throws {Error} when called in a React Native environment.
|
|
4646
4733
|
*/
|
|
4647
4734
|
at(keyPathOrIndex) {
|
|
4648
4735
|
if (typeof keyPathOrIndex === 'string') {
|
|
@@ -4654,7 +4741,11 @@ class DocumentPath {
|
|
|
4654
4741
|
}
|
|
4655
4742
|
if (typeof keyPathOrIndex === 'number') {
|
|
4656
4743
|
const index = keyPathOrIndex;
|
|
4657
|
-
const validatedIndex = validateNumber(index, {
|
|
4744
|
+
const validatedIndex = validateNumber(index, {
|
|
4745
|
+
integer: true,
|
|
4746
|
+
min: 0,
|
|
4747
|
+
errorMessagePrefix: 'DocumentPath.at() validation failed index:',
|
|
4748
|
+
});
|
|
4658
4749
|
return new DocumentPath(this.document, `${this.path}[${validatedIndex.toString()}]`, false);
|
|
4659
4750
|
}
|
|
4660
4751
|
throw new Error(`Can't return document path at key-path or index, string or number expected but got ${typeof keyPathOrIndex}: ${keyPathOrIndex}`);
|
|
@@ -4662,6 +4753,8 @@ class DocumentPath {
|
|
|
4662
4753
|
/**
|
|
4663
4754
|
* Traverses the document with the key-path represented by the receiver and
|
|
4664
4755
|
* returns the corresponding object or value.
|
|
4756
|
+
*
|
|
4757
|
+
* @throws {Error} when called in a React Native environment.
|
|
4665
4758
|
*/
|
|
4666
4759
|
get value() {
|
|
4667
4760
|
return this.underlyingValueForPathType('Any');
|
|
@@ -4669,26 +4762,38 @@ class DocumentPath {
|
|
|
4669
4762
|
/**
|
|
4670
4763
|
* Returns the value at the previously specified key in the document as a
|
|
4671
4764
|
* {@link Counter} if possible, otherwise returns `null`.
|
|
4765
|
+
*
|
|
4766
|
+
* @throws {Error} when called in a React Native environment.
|
|
4672
4767
|
*/
|
|
4673
4768
|
get counter() {
|
|
4674
4769
|
const underlyingValue = this.underlyingValueForPathType('Counter');
|
|
4675
|
-
return typeof underlyingValue !== 'undefined'
|
|
4770
|
+
return typeof underlyingValue !== 'undefined'
|
|
4771
|
+
? Counter['@ditto.create'](null, this.path, underlyingValue)
|
|
4772
|
+
: null;
|
|
4676
4773
|
}
|
|
4677
4774
|
/**
|
|
4678
4775
|
* Returns the value at the previously specified key in the document as a
|
|
4679
4776
|
* {@link Register} if possible, otherwise returns `null`.
|
|
4777
|
+
*
|
|
4778
|
+
* @throws {Error} when called in a React Native environment.
|
|
4680
4779
|
*/
|
|
4681
4780
|
get register() {
|
|
4682
4781
|
const underlyingValue = this.underlyingValueForPathType('Register');
|
|
4683
|
-
return typeof underlyingValue !== 'undefined'
|
|
4782
|
+
return typeof underlyingValue !== 'undefined'
|
|
4783
|
+
? Register['@ditto.create'](null, this.path, underlyingValue)
|
|
4784
|
+
: null;
|
|
4684
4785
|
}
|
|
4685
4786
|
/**
|
|
4686
4787
|
* Returns the value at the previously specified key in the document as an
|
|
4687
4788
|
* {@link AttachmentToken} if possible, otherwise returns `null`.
|
|
4789
|
+
*
|
|
4790
|
+
* @throws {Error} when called in a React Native environment.
|
|
4688
4791
|
*/
|
|
4689
4792
|
get attachmentToken() {
|
|
4690
4793
|
const underlyingValue = this.underlyingValueForPathType('Attachment');
|
|
4691
|
-
return typeof underlyingValue !== 'undefined'
|
|
4794
|
+
return typeof underlyingValue !== 'undefined'
|
|
4795
|
+
? new AttachmentToken(underlyingValue)
|
|
4796
|
+
: null;
|
|
4692
4797
|
}
|
|
4693
4798
|
/** @internal */
|
|
4694
4799
|
constructor(document, path, validate) {
|
|
@@ -4701,7 +4806,9 @@ class DocumentPath {
|
|
|
4701
4806
|
const document = this.document;
|
|
4702
4807
|
const documentHandle = Bridge.document.handleFor(document);
|
|
4703
4808
|
const cborPathResult = documentGetCBORWithPathType(documentHandle.deref(), path, pathType);
|
|
4704
|
-
return cborPathResult.cbor !== null
|
|
4809
|
+
return cborPathResult.cbor !== null
|
|
4810
|
+
? CBOR.decode(cborPathResult.cbor)
|
|
4811
|
+
: undefined;
|
|
4705
4812
|
}
|
|
4706
4813
|
}
|
|
4707
4814
|
// -----------------------------------------------------------------------------
|
|
@@ -4710,6 +4817,8 @@ class DocumentPath {
|
|
|
4710
4817
|
* a specific key-path. You don't create a `MutableDocumentPath` directly but
|
|
4711
4818
|
* obtain one via the {@link MutableDocument.path | path} property or the
|
|
4712
4819
|
* {@link MutableDocument.at | at()} method of {@link MutableDocument}.
|
|
4820
|
+
*
|
|
4821
|
+
* Not available in React Native environments.
|
|
4713
4822
|
*/
|
|
4714
4823
|
class MutableDocumentPath {
|
|
4715
4824
|
/**
|
|
@@ -4730,6 +4839,8 @@ class MutableDocumentPath {
|
|
|
4730
4839
|
* - `mutableDocumentPath.at('passengers[2]')`
|
|
4731
4840
|
* - `mutableDocumentPath.at('passengers[2].belongings[1].kind')`
|
|
4732
4841
|
* - `mutableDocumentPath.at('.mileage')`
|
|
4842
|
+
*
|
|
4843
|
+
* @throws {Error} when called in a React Native environment.
|
|
4733
4844
|
*/
|
|
4734
4845
|
at(keyPathOrIndex) {
|
|
4735
4846
|
if (typeof keyPathOrIndex === 'string') {
|
|
@@ -4741,7 +4852,11 @@ class MutableDocumentPath {
|
|
|
4741
4852
|
}
|
|
4742
4853
|
if (typeof keyPathOrIndex === 'number') {
|
|
4743
4854
|
const index = keyPathOrIndex;
|
|
4744
|
-
const validatedIndex = validateNumber(index, {
|
|
4855
|
+
const validatedIndex = validateNumber(index, {
|
|
4856
|
+
integer: true,
|
|
4857
|
+
min: 0,
|
|
4858
|
+
errorMessagePrefix: 'MutableDocumentPath.at() validation failed index:',
|
|
4859
|
+
});
|
|
4745
4860
|
return new MutableDocumentPath(this.mutableDocument, `${this.path}[${validatedIndex.toString()}]`, false);
|
|
4746
4861
|
}
|
|
4747
4862
|
throw new Error(`Can't return mutable document path at key-path or index, string or number expected but got ${typeof keyPathOrIndex}: ${keyPathOrIndex}`);
|
|
@@ -4749,6 +4864,8 @@ class MutableDocumentPath {
|
|
|
4749
4864
|
/**
|
|
4750
4865
|
* Traverses the document with the key-path represented by the receiver and
|
|
4751
4866
|
* returns the corresponding object or value.
|
|
4867
|
+
*
|
|
4868
|
+
* @throws {Error} when called in a React Native environment.
|
|
4752
4869
|
*/
|
|
4753
4870
|
get value() {
|
|
4754
4871
|
return this.underlyingValueForPathType('Any');
|
|
@@ -4756,26 +4873,38 @@ class MutableDocumentPath {
|
|
|
4756
4873
|
/**
|
|
4757
4874
|
* Returns the value at the previously specified key in the document as a
|
|
4758
4875
|
* {@link MutableCounter} if possible, otherwise returns `null`.
|
|
4876
|
+
*
|
|
4877
|
+
* @throws {Error} when called in a React Native environment.
|
|
4759
4878
|
*/
|
|
4760
4879
|
get counter() {
|
|
4761
4880
|
const underlyingValue = this.underlyingValueForPathType('Counter');
|
|
4762
|
-
return typeof underlyingValue !== 'undefined'
|
|
4881
|
+
return typeof underlyingValue !== 'undefined'
|
|
4882
|
+
? Counter['@ditto.create'](this.mutableDocument, this.path, underlyingValue)
|
|
4883
|
+
: null;
|
|
4763
4884
|
}
|
|
4764
4885
|
/**
|
|
4765
4886
|
* Returns the value at the previously specified key in the document as a
|
|
4766
4887
|
* {@link MutableRegister} if possible, otherwise returns `null`.
|
|
4888
|
+
*
|
|
4889
|
+
* @throws {Error} when called in a React Native environment.
|
|
4767
4890
|
*/
|
|
4768
4891
|
get register() {
|
|
4769
4892
|
const underlyingValue = this.underlyingValueForPathType('Register');
|
|
4770
|
-
return typeof underlyingValue !== 'undefined'
|
|
4893
|
+
return typeof underlyingValue !== 'undefined'
|
|
4894
|
+
? Register['@ditto.create'](this.mutableDocument, this.path, underlyingValue)
|
|
4895
|
+
: null;
|
|
4771
4896
|
}
|
|
4772
4897
|
/**
|
|
4773
4898
|
* Returns the value at the previously specified key in the document as a
|
|
4774
4899
|
* {@link AttachmentToken} if possible, otherwise returns `null`.
|
|
4900
|
+
*
|
|
4901
|
+
* @throws {Error} when called in a React Native environment.
|
|
4775
4902
|
*/
|
|
4776
4903
|
get attachmentToken() {
|
|
4777
4904
|
const underlyingValue = this.underlyingValueForPathType('Attachment');
|
|
4778
|
-
return typeof underlyingValue !== 'undefined'
|
|
4905
|
+
return typeof underlyingValue !== 'undefined'
|
|
4906
|
+
? new AttachmentToken(underlyingValue)
|
|
4907
|
+
: null;
|
|
4779
4908
|
}
|
|
4780
4909
|
/**
|
|
4781
4910
|
* Sets a value at the document's key-path defined by the receiver.
|
|
@@ -4784,12 +4913,16 @@ class MutableDocumentPath {
|
|
|
4784
4913
|
* default value. Set this to `true` if you want to set a default value that
|
|
4785
4914
|
* you expect to be overwritten by other devices in the network. The default
|
|
4786
4915
|
* value is `false`.
|
|
4916
|
+
*
|
|
4917
|
+
* @throws {Error} when called in a React Native environment.
|
|
4787
4918
|
*/
|
|
4788
4919
|
set(value, isDefault) {
|
|
4789
4920
|
return this['@ditto.set'](value, isDefault);
|
|
4790
4921
|
}
|
|
4791
4922
|
/**
|
|
4792
4923
|
* Removes a value at the document's key-path defined by the receiver.
|
|
4924
|
+
*
|
|
4925
|
+
* @throws {Error} when called in a React Native environment.
|
|
4793
4926
|
*/
|
|
4794
4927
|
remove() {
|
|
4795
4928
|
return this['@ditto.remove']();
|
|
@@ -4805,7 +4938,9 @@ class MutableDocumentPath {
|
|
|
4805
4938
|
const document = this.mutableDocument;
|
|
4806
4939
|
const documentHandle = Bridge.mutableDocument.handleFor(document);
|
|
4807
4940
|
const cborPathResult = documentGetCBORWithPathType(documentHandle.deref(), path, pathType);
|
|
4808
|
-
return cborPathResult.cbor !== null
|
|
4941
|
+
return cborPathResult.cbor !== null
|
|
4942
|
+
? CBOR.decode(cborPathResult.cbor)
|
|
4943
|
+
: undefined;
|
|
4809
4944
|
}
|
|
4810
4945
|
/** @internal */
|
|
4811
4946
|
'@ditto.increment'(amount) {
|
|
@@ -4842,12 +4977,10 @@ class MutableDocumentPath {
|
|
|
4842
4977
|
const documentHandle = Bridge.mutableDocument.handleFor(this.mutableDocument);
|
|
4843
4978
|
documentRemove(documentHandle.deref(), this.path);
|
|
4844
4979
|
this.updateInMemory((container, lastPathComponent) => {
|
|
4845
|
-
if (Array.isArray(container) && typeof lastPathComponent === 'number')
|
|
4980
|
+
if (Array.isArray(container) && typeof lastPathComponent === 'number')
|
|
4846
4981
|
container.splice(lastPathComponent, 1);
|
|
4847
|
-
|
|
4848
|
-
else {
|
|
4982
|
+
else
|
|
4849
4983
|
delete container[lastPathComponent];
|
|
4850
|
-
}
|
|
4851
4984
|
});
|
|
4852
4985
|
const updateResult = UpdateResult.removed(this.mutableDocument.id, this.path);
|
|
4853
4986
|
this.recordUpdateResult(updateResult);
|
|
@@ -4855,7 +4988,9 @@ class MutableDocumentPath {
|
|
|
4855
4988
|
/** @private */
|
|
4856
4989
|
updateInMemory(block) {
|
|
4857
4990
|
const mutableDocumentValue = this.mutableDocument.value;
|
|
4858
|
-
const evaluationResult = KeyPath.evaluate(this.path, mutableDocumentValue, {
|
|
4991
|
+
const evaluationResult = KeyPath.evaluate(this.path, mutableDocumentValue, {
|
|
4992
|
+
stopAtLastContainer: true,
|
|
4993
|
+
});
|
|
4859
4994
|
block(evaluationResult.value, evaluationResult.nextPathComponent);
|
|
4860
4995
|
}
|
|
4861
4996
|
/** @private */
|
|
@@ -4876,10 +5011,16 @@ class MutableDocumentPath {
|
|
|
4876
5011
|
// the object is inspected with console.log() or util.inspect().
|
|
4877
5012
|
const CUSTOM_INSPECT_SYMBOL$1 = Symbol.for('nodejs.util.inspect.custom');
|
|
4878
5013
|
// -----------------------------------------------------------------------------
|
|
4879
|
-
/**
|
|
5014
|
+
/**
|
|
5015
|
+
* A document belonging to a {@link Collection} with an inner value.
|
|
5016
|
+
*
|
|
5017
|
+
* Not available in React Native environments.
|
|
5018
|
+
*/
|
|
4880
5019
|
class Document {
|
|
4881
5020
|
/**
|
|
4882
5021
|
* Returns a hash that represents the passed in document(s).
|
|
5022
|
+
*
|
|
5023
|
+
* @throws {Error} when called in a React Native environment.
|
|
4883
5024
|
*/
|
|
4884
5025
|
static hash(documentOrMany) {
|
|
4885
5026
|
const documents = documentsFrom(documentOrMany);
|
|
@@ -4889,6 +5030,8 @@ class Document {
|
|
|
4889
5030
|
/**
|
|
4890
5031
|
* Returns a pattern of words that together create a mnemonic, which
|
|
4891
5032
|
* represents the passed in document(s).
|
|
5033
|
+
*
|
|
5034
|
+
* @throws {Error} when called in a React Native environment.
|
|
4892
5035
|
*/
|
|
4893
5036
|
static hashMnemonic(documentOrMany) {
|
|
4894
5037
|
const documents = documentsFrom(documentOrMany);
|
|
@@ -4897,6 +5040,8 @@ class Document {
|
|
|
4897
5040
|
}
|
|
4898
5041
|
/**
|
|
4899
5042
|
* Returns the document ID.
|
|
5043
|
+
*
|
|
5044
|
+
* @throws {Error} when called in a React Native environment.
|
|
4900
5045
|
*/
|
|
4901
5046
|
get id() {
|
|
4902
5047
|
let id = this['@ditto.id'];
|
|
@@ -4910,6 +5055,8 @@ class Document {
|
|
|
4910
5055
|
}
|
|
4911
5056
|
/**
|
|
4912
5057
|
* Returns the document path at the root of the document.
|
|
5058
|
+
*
|
|
5059
|
+
* @throws {Error} when called in a React Native environment.
|
|
4913
5060
|
*/
|
|
4914
5061
|
get path() {
|
|
4915
5062
|
return new DocumentPath(this, '', false);
|
|
@@ -4918,6 +5065,8 @@ class Document {
|
|
|
4918
5065
|
* Convenience property, same as calling `path.value`. The value is cached on
|
|
4919
5066
|
* first access and returned on subsequent calls without calling `path.value`
|
|
4920
5067
|
* again.
|
|
5068
|
+
*
|
|
5069
|
+
* @throws {Error} when called in a React Native environment.
|
|
4921
5070
|
*/
|
|
4922
5071
|
get value() {
|
|
4923
5072
|
let value = this['@ditto.value'];
|
|
@@ -4929,6 +5078,8 @@ class Document {
|
|
|
4929
5078
|
}
|
|
4930
5079
|
/**
|
|
4931
5080
|
* Convenience method, same as calling `path.at()`.
|
|
5081
|
+
*
|
|
5082
|
+
* @throws {Error} when called in a React Native environment.
|
|
4932
5083
|
*/
|
|
4933
5084
|
at(keyPathOrIndex) {
|
|
4934
5085
|
return this.path.at(keyPathOrIndex);
|
|
@@ -4978,10 +5129,14 @@ class Document {
|
|
|
4978
5129
|
* `MutableDocument` directly but rather through the `update()` methods of
|
|
4979
5130
|
* {@link PendingCursorOperation.update | PendingCursorOperation} and
|
|
4980
5131
|
* {@link PendingIDSpecificOperation.update | PendingIDSpecificOperation}.
|
|
5132
|
+
*
|
|
5133
|
+
* Not available in React Native environments.
|
|
4981
5134
|
*/
|
|
4982
5135
|
class MutableDocument {
|
|
4983
5136
|
/**
|
|
4984
5137
|
* Returns the ID of the document.
|
|
5138
|
+
*
|
|
5139
|
+
* @throws {Error} when called in a React Native environment.
|
|
4985
5140
|
*/
|
|
4986
5141
|
get id() {
|
|
4987
5142
|
let id = this['@ditto.id'];
|
|
@@ -4995,18 +5150,24 @@ class MutableDocument {
|
|
|
4995
5150
|
}
|
|
4996
5151
|
/**
|
|
4997
5152
|
* Returns the document path at the root of the document.
|
|
5153
|
+
*
|
|
5154
|
+
* @throws {Error} when called in a React Native environment.
|
|
4998
5155
|
*/
|
|
4999
5156
|
get path() {
|
|
5000
5157
|
return new MutableDocumentPath(this, '', false);
|
|
5001
5158
|
}
|
|
5002
5159
|
/**
|
|
5003
5160
|
* Convenience property, same as `path.value`.
|
|
5161
|
+
*
|
|
5162
|
+
* @throws {Error} when called in a React Native environment.
|
|
5004
5163
|
*/
|
|
5005
5164
|
get value() {
|
|
5006
5165
|
return this.path.value;
|
|
5007
5166
|
}
|
|
5008
5167
|
/**
|
|
5009
5168
|
* Convenience method, same as calling `path.at()`.
|
|
5169
|
+
*
|
|
5170
|
+
* @throws {Error} when called in a React Native environment.
|
|
5010
5171
|
*/
|
|
5011
5172
|
at(keyPathOrIndex) {
|
|
5012
5173
|
return this.path.at(keyPathOrIndex);
|
|
@@ -5042,15 +5203,12 @@ MutableDocument.isIDCBORCanonical = Document.isIDCBORCanonical;
|
|
|
5042
5203
|
// -----------------------------------------------------------------------------
|
|
5043
5204
|
/** @private */
|
|
5044
5205
|
function documentsFrom(documentOrMany) {
|
|
5045
|
-
if (!documentOrMany)
|
|
5206
|
+
if (!documentOrMany)
|
|
5046
5207
|
return [];
|
|
5047
|
-
|
|
5048
|
-
if (documentOrMany instanceof Document) {
|
|
5208
|
+
if (documentOrMany instanceof Document)
|
|
5049
5209
|
return [documentOrMany];
|
|
5050
|
-
|
|
5051
|
-
if (documentOrMany instanceof Array) {
|
|
5210
|
+
if (documentOrMany instanceof Array)
|
|
5052
5211
|
return documentOrMany;
|
|
5053
|
-
}
|
|
5054
5212
|
throw new Error(`Expected null, a single document, or an array of documents but got value of type ${typeof documentOrMany}: ${documentOrMany}`);
|
|
5055
5213
|
}
|
|
5056
5214
|
|
|
@@ -5062,6 +5220,8 @@ function documentsFrom(documentOrMany) {
|
|
|
5062
5220
|
* {@link UpdateResult | update results}. This is the data structure you get
|
|
5063
5221
|
* when {@link PendingCursorOperation.update | updating} a set of documents
|
|
5064
5222
|
* with detailed info about the performed updates.
|
|
5223
|
+
*
|
|
5224
|
+
* Not available in React Native environments.
|
|
5065
5225
|
*/
|
|
5066
5226
|
class UpdateResultsMap {
|
|
5067
5227
|
/**
|
|
@@ -5069,7 +5229,9 @@ class UpdateResultsMap {
|
|
|
5069
5229
|
* the `documentID` or undefined if not found.
|
|
5070
5230
|
*/
|
|
5071
5231
|
get(documentIDOrValue) {
|
|
5072
|
-
const documentID = documentIDOrValue instanceof DocumentID
|
|
5232
|
+
const documentID = documentIDOrValue instanceof DocumentID
|
|
5233
|
+
? documentIDOrValue
|
|
5234
|
+
: new DocumentID(documentIDOrValue);
|
|
5073
5235
|
const documentIDString = documentID.toString();
|
|
5074
5236
|
return this.updateResultsByDocumentIDString[documentIDString];
|
|
5075
5237
|
}
|
|
@@ -5094,18 +5256,25 @@ class UpdateResultsMap {
|
|
|
5094
5256
|
.map((documentID) => documentID.toString())
|
|
5095
5257
|
.sort()
|
|
5096
5258
|
.join(', ');
|
|
5097
|
-
const updateResultsKeys = Object.keys(updateResultsByDocumentIDString)
|
|
5259
|
+
const updateResultsKeys = Object.keys(updateResultsByDocumentIDString)
|
|
5260
|
+
.sort()
|
|
5261
|
+
.join(', ');
|
|
5098
5262
|
if (documentIDStrings !== updateResultsKeys) {
|
|
5099
5263
|
throw new Error("Internal inconsistency, can't construct update results map, documentIDs must all be keys in update results (by document ID string)");
|
|
5100
5264
|
}
|
|
5101
5265
|
this.documentIDs = documentIDs.slice();
|
|
5102
|
-
this.updateResultsByDocumentIDString = {
|
|
5266
|
+
this.updateResultsByDocumentIDString = {
|
|
5267
|
+
...updateResultsByDocumentIDString,
|
|
5268
|
+
};
|
|
5103
5269
|
}
|
|
5104
5270
|
}
|
|
5105
5271
|
|
|
5106
5272
|
//
|
|
5107
5273
|
// Copyright © 2023 DittoLive Incorporated. All rights reserved.
|
|
5108
5274
|
//
|
|
5275
|
+
/**
|
|
5276
|
+
* Not available in React Native environments.
|
|
5277
|
+
*/
|
|
5109
5278
|
class BasePendingCursorOperation {
|
|
5110
5279
|
/**
|
|
5111
5280
|
* Sorts the documents that match the query provided in the preceding
|
|
@@ -5119,6 +5288,7 @@ class BasePendingCursorOperation {
|
|
|
5119
5288
|
* @param direction Specify whether you want the sorting order to be
|
|
5120
5289
|
* `ascending` or `descending`. Defaults to `ascending`.
|
|
5121
5290
|
*
|
|
5291
|
+
* @throws {Error} when called in a React Native environment.
|
|
5122
5292
|
* @return A cursor that you can chain further function calls and then either
|
|
5123
5293
|
* get the matching documents immediately or get updates about them over time.
|
|
5124
5294
|
*/
|
|
@@ -5141,6 +5311,7 @@ class BasePendingCursorOperation {
|
|
|
5141
5311
|
* @param offset The number of matching documents that you want the eventual
|
|
5142
5312
|
* resulting set of matching documents to be offset by (and thus not include).
|
|
5143
5313
|
*
|
|
5314
|
+
* @throws {Error} when called in a React Native environment.
|
|
5144
5315
|
* @return A cursor that you can chain further function calls and then either
|
|
5145
5316
|
* get the matching documents immediately or get updates about them over time.
|
|
5146
5317
|
*/
|
|
@@ -5148,13 +5319,16 @@ class BasePendingCursorOperation {
|
|
|
5148
5319
|
// REFACTOR: factor out parameter validation.
|
|
5149
5320
|
if (offset < 0)
|
|
5150
5321
|
throw new Error(`Can't offset by '${offset}', offset must be >= 0`);
|
|
5151
|
-
if (!Number.isFinite(offset))
|
|
5322
|
+
if (!Number.isFinite(offset)) {
|
|
5152
5323
|
throw new Error(`Can't offset by '${offset}', offset must be a finite number`);
|
|
5153
|
-
|
|
5324
|
+
}
|
|
5325
|
+
if (Number.isNaN(offset)) {
|
|
5154
5326
|
throw new Error(`Can't offset by '${offset}', offset must be a valid number`);
|
|
5327
|
+
}
|
|
5155
5328
|
const integerOffset = Math.round(offset);
|
|
5156
|
-
if (offset !== integerOffset)
|
|
5329
|
+
if (offset !== integerOffset) {
|
|
5157
5330
|
throw new Error(`Can't offset by '${offset}', offset must be an integer number`);
|
|
5331
|
+
}
|
|
5158
5332
|
this.currentOffset = offset;
|
|
5159
5333
|
return this;
|
|
5160
5334
|
}
|
|
@@ -5164,20 +5338,25 @@ class BasePendingCursorOperation {
|
|
|
5164
5338
|
*
|
|
5165
5339
|
* @param limit The maximum number of documents that will be returned.
|
|
5166
5340
|
*
|
|
5341
|
+
* @throws {Error} when called in a React Native environment.
|
|
5167
5342
|
* @return A cursor that you can chain further function calls and then either
|
|
5168
5343
|
* get the matching documents immediately or get updates about them over time.
|
|
5169
5344
|
*/
|
|
5170
5345
|
limit(limit) {
|
|
5171
5346
|
// REFACTOR: factor out parameter validation.
|
|
5172
|
-
if (limit < -1)
|
|
5347
|
+
if (limit < -1) {
|
|
5173
5348
|
throw new Error(`Can't limit to '${limit}', limit must be >= -1 (where -1 means unlimited)`);
|
|
5174
|
-
|
|
5349
|
+
}
|
|
5350
|
+
if (!Number.isFinite(limit)) {
|
|
5175
5351
|
throw new Error(`Can't limit to '${limit}', limit must be a finite number`);
|
|
5176
|
-
|
|
5352
|
+
}
|
|
5353
|
+
if (Number.isNaN(limit)) {
|
|
5177
5354
|
throw new Error(`Can't limit to '${limit}', limit must be a valid number`);
|
|
5355
|
+
}
|
|
5178
5356
|
const integerLimit = Math.round(limit);
|
|
5179
|
-
if (limit !== integerLimit)
|
|
5357
|
+
if (limit !== integerLimit) {
|
|
5180
5358
|
throw new Error(`Can't limit to '${limit}', limit must be an integer number`);
|
|
5359
|
+
}
|
|
5181
5360
|
this.currentLimit = limit;
|
|
5182
5361
|
return this;
|
|
5183
5362
|
}
|
|
@@ -5185,6 +5364,7 @@ class BasePendingCursorOperation {
|
|
|
5185
5364
|
* Executes the query generated by the preceding function chaining and return
|
|
5186
5365
|
* the list of matching documents.
|
|
5187
5366
|
*
|
|
5367
|
+
* @throws {Error} when called in a React Native environment.
|
|
5188
5368
|
* @returns An array promise containing {@link Document | documents} matching
|
|
5189
5369
|
* the query generated by the preceding function chaining.
|
|
5190
5370
|
*/
|
|
@@ -5206,25 +5386,33 @@ class BasePendingCursorOperation {
|
|
|
5206
5386
|
* chaining.
|
|
5207
5387
|
*
|
|
5208
5388
|
* @param closure A closure that gets called with all of the documents
|
|
5209
|
-
*
|
|
5210
|
-
* so you can call update-related functions on them.
|
|
5211
|
-
* @param
|
|
5389
|
+
* matching the query. The documents are instances of
|
|
5390
|
+
* {@link MutableDocument} so you can call update-related functions on them.
|
|
5391
|
+
* @param publicAPIName the name of the public API function that was called
|
|
5392
|
+
* to perform the operation. Used in error messages and warnings.
|
|
5393
|
+
* @param writeTransactionPointer a transaction to perform the operation in.
|
|
5394
|
+
* If not provided, a new transaction will be created.
|
|
5212
5395
|
*
|
|
5213
5396
|
* @returns An {@link UpdateResultsMap} promise mapping document IDs to lists
|
|
5214
|
-
*
|
|
5215
|
-
*
|
|
5397
|
+
* of {@link UpdateResult | update results} that describe the updates that
|
|
5398
|
+
* were performed for each document.
|
|
5399
|
+
*
|
|
5216
5400
|
* @internal
|
|
5217
5401
|
*/
|
|
5218
|
-
async
|
|
5402
|
+
async updateWithClosure(closure, publicAPIName, writeTransactionPointer) {
|
|
5219
5403
|
const ditto = this.collection.store.ditto;
|
|
5220
5404
|
return ditto.deferCloseAsync(async (dittoHandle) => {
|
|
5221
5405
|
return await performAsyncToWorkaroundNonAsyncFFIAPI(async () => {
|
|
5222
5406
|
const query = this.query;
|
|
5223
|
-
const
|
|
5407
|
+
const transactionPointer = writeTransactionPointer !== null && writeTransactionPointer !== void 0 ? writeTransactionPointer : (await writeTransaction(dittoHandle.deref()));
|
|
5408
|
+
const documentsX = await collectionExecQueryStr(dittoHandle.deref(), this.collection.name, transactionPointer, query, this.queryArgsCBOR, this.orderBys, this.currentLimit, this.currentOffset);
|
|
5224
5409
|
const mutableDocuments = documentsX.map((documentX) => {
|
|
5225
5410
|
return Bridge.mutableDocument.bridge(documentX, () => new MutableDocument());
|
|
5226
5411
|
});
|
|
5227
|
-
closure(mutableDocuments)
|
|
5412
|
+
if (closure(mutableDocuments) instanceof Promise) {
|
|
5413
|
+
Logger.warning(`Expected ${publicAPIName} to be called with a synchronous closure but ` +
|
|
5414
|
+
'it was called with an async closure.');
|
|
5415
|
+
}
|
|
5228
5416
|
const updateResultsDocumentIDs = [];
|
|
5229
5417
|
const updateResultsByDocumentIDString = {};
|
|
5230
5418
|
for (const mutableDocument of mutableDocuments) {
|
|
@@ -5246,7 +5434,10 @@ class BasePendingCursorOperation {
|
|
|
5246
5434
|
}
|
|
5247
5435
|
// NOTE: ownership of documentsX (and contained documents)
|
|
5248
5436
|
// is transferred to Rust at this point.
|
|
5249
|
-
await collectionUpdateMultiple(dittoHandle.deref(), this.collection.name,
|
|
5437
|
+
await collectionUpdateMultiple(dittoHandle.deref(), this.collection.name, transactionPointer, documentsX);
|
|
5438
|
+
if (!writeTransactionPointer) {
|
|
5439
|
+
await writeTransactionCommit(dittoHandle.deref(), transactionPointer);
|
|
5440
|
+
}
|
|
5250
5441
|
return new UpdateResultsMap(updateResultsDocumentIDs, updateResultsByDocumentIDString);
|
|
5251
5442
|
});
|
|
5252
5443
|
});
|
|
@@ -5288,11 +5479,14 @@ class BasePendingCursorOperation {
|
|
|
5288
5479
|
* return value, or chain calls to update, evict or remove the document.
|
|
5289
5480
|
*
|
|
5290
5481
|
* Live queries and subscriptions are only available outside of a transaction.
|
|
5482
|
+
*
|
|
5483
|
+
* Not available in React Native environments.
|
|
5291
5484
|
*/
|
|
5292
5485
|
class BasePendingIDSpecificOperation {
|
|
5293
5486
|
/**
|
|
5294
5487
|
* Executes the find operation to return the document with the matching ID.
|
|
5295
5488
|
*
|
|
5489
|
+
* @throws {Error} when called in a React Native environment.
|
|
5296
5490
|
* @returns The {@link Document} promise with the ID provided in the
|
|
5297
5491
|
* {@link Collection.findByID | findByID()} call or `undefined` if the document was
|
|
5298
5492
|
* not found.
|
|
@@ -5326,6 +5520,64 @@ class BasePendingIDSpecificOperation {
|
|
|
5326
5520
|
get query() {
|
|
5327
5521
|
return `_id == ${this.documentID.toQueryCompatibleString()}`;
|
|
5328
5522
|
}
|
|
5523
|
+
/**
|
|
5524
|
+
* Commit changes made by the given closure to the current document.
|
|
5525
|
+
*
|
|
5526
|
+
* @param closure
|
|
5527
|
+
* @param throwOnAsyncClosure if true, throw an error when passed an async
|
|
5528
|
+
* closure, otherwise log a warning
|
|
5529
|
+
* @param throwOnDocumentNotFound if true, throw an error if the document is
|
|
5530
|
+
* not found, otherwise call the closure with `undefined` and return an
|
|
5531
|
+
* empty array
|
|
5532
|
+
* @param publicAPIName the name of the public API that was called, used in
|
|
5533
|
+
* error messages and warnings
|
|
5534
|
+
* @param transaction the transaction to use, if not provided a new one will
|
|
5535
|
+
* be created and committed
|
|
5536
|
+
* @internal
|
|
5537
|
+
*/
|
|
5538
|
+
async updateWithClosure(closure, throwOnAsyncClosure, throwOnDocumentNotFound, publicAPIName, transaction) {
|
|
5539
|
+
const ditto = this.collection.store.ditto;
|
|
5540
|
+
return ditto.deferCloseAsync(async (dittoHandle) => {
|
|
5541
|
+
var _a;
|
|
5542
|
+
const readTransactionPointer = await readTransaction(dittoHandle.deref());
|
|
5543
|
+
let documentPointer = null;
|
|
5544
|
+
try {
|
|
5545
|
+
documentPointer = await collectionGet(dittoHandle.deref(), this.collection.name, this.documentIDCBOR, readTransactionPointer);
|
|
5546
|
+
}
|
|
5547
|
+
finally {
|
|
5548
|
+
readTransactionFree(readTransactionPointer);
|
|
5549
|
+
}
|
|
5550
|
+
const errorMessageAsyncClosure = `Expected ${publicAPIName} to be called with a synchronous ` +
|
|
5551
|
+
'closure but it was called with an async closure';
|
|
5552
|
+
if (!documentPointer) {
|
|
5553
|
+
if (throwOnDocumentNotFound) {
|
|
5554
|
+
throw new Error(`Can't update, document with ID '${this.documentID.toString()}' not found in collection named '${this.collection.name}'`);
|
|
5555
|
+
}
|
|
5556
|
+
if (closure() instanceof Promise) {
|
|
5557
|
+
if (throwOnAsyncClosure)
|
|
5558
|
+
throw new TypeError(errorMessageAsyncClosure);
|
|
5559
|
+
else
|
|
5560
|
+
Logger.warning(errorMessageAsyncClosure);
|
|
5561
|
+
}
|
|
5562
|
+
return [];
|
|
5563
|
+
}
|
|
5564
|
+
const mutableDocument = Bridge.mutableDocument.bridge(documentPointer, () => new MutableDocument());
|
|
5565
|
+
if (closure(mutableDocument) instanceof Promise) {
|
|
5566
|
+
if (throwOnAsyncClosure)
|
|
5567
|
+
throw new TypeError(errorMessageAsyncClosure);
|
|
5568
|
+
Logger.warning(errorMessageAsyncClosure);
|
|
5569
|
+
}
|
|
5570
|
+
const writeTransactionPointer = (_a = transaction === null || transaction === void 0 ? void 0 : transaction.writeTransactionPointer) !== null && _a !== void 0 ? _a : (await writeTransaction(dittoHandle.deref()));
|
|
5571
|
+
// Ownership is transferred back to the FFI layer via collectionUpdate(),
|
|
5572
|
+
// we therefore need to explicitly unregister the instance.
|
|
5573
|
+
Bridge.mutableDocument.unregister(mutableDocument);
|
|
5574
|
+
await collectionUpdate(dittoHandle.deref(), this.collection.name, writeTransactionPointer, documentPointer);
|
|
5575
|
+
if (!transaction) {
|
|
5576
|
+
await writeTransactionCommit(dittoHandle.deref(), writeTransactionPointer);
|
|
5577
|
+
}
|
|
5578
|
+
return mutableDocument['@ditto.updateResults'].slice();
|
|
5579
|
+
});
|
|
5580
|
+
}
|
|
5329
5581
|
}
|
|
5330
5582
|
|
|
5331
5583
|
//
|
|
@@ -5445,15 +5697,12 @@ class ObserverManager {
|
|
|
5445
5697
|
this.isClosed = false;
|
|
5446
5698
|
this.isRegistered = false;
|
|
5447
5699
|
this.callbacksByToken = {};
|
|
5448
|
-
if (register !== null)
|
|
5700
|
+
if (register !== null)
|
|
5449
5701
|
this.register = register;
|
|
5450
|
-
|
|
5451
|
-
if (unregister !== null) {
|
|
5702
|
+
if (unregister !== null)
|
|
5452
5703
|
this.unregister = unregister;
|
|
5453
|
-
|
|
5454
|
-
if (process !== null) {
|
|
5704
|
+
if (process !== null)
|
|
5455
5705
|
this.process = process;
|
|
5456
|
-
}
|
|
5457
5706
|
}
|
|
5458
5707
|
/** @internal */
|
|
5459
5708
|
addObserver(callback) {
|
|
@@ -5511,9 +5760,8 @@ class ObserverManager {
|
|
|
5511
5760
|
/** @internal */
|
|
5512
5761
|
close() {
|
|
5513
5762
|
this.isClosed = true;
|
|
5514
|
-
for (const token in this.callbacksByToken)
|
|
5763
|
+
for (const token in this.callbacksByToken)
|
|
5515
5764
|
this.removeObserver(token);
|
|
5516
|
-
}
|
|
5517
5765
|
}
|
|
5518
5766
|
/**
|
|
5519
5767
|
* Can be injected and replaced via constructor options.
|
|
@@ -5550,9 +5798,8 @@ class ObserverManager {
|
|
|
5550
5798
|
this.isRegistered = true;
|
|
5551
5799
|
this.register(function (...args) {
|
|
5552
5800
|
const strongThis = weakThis.deref();
|
|
5553
|
-
if (!strongThis)
|
|
5801
|
+
if (!strongThis)
|
|
5554
5802
|
return;
|
|
5555
|
-
}
|
|
5556
5803
|
strongThis.notify(...args);
|
|
5557
5804
|
});
|
|
5558
5805
|
}
|
|
@@ -5581,12 +5828,35 @@ class Authenticator {
|
|
|
5581
5828
|
get status() {
|
|
5582
5829
|
return this._status;
|
|
5583
5830
|
}
|
|
5831
|
+
/**
|
|
5832
|
+
* Log in to Ditto with a third-party token.
|
|
5833
|
+
*
|
|
5834
|
+
* Returns a promise that resolves to a `LoginResult` object. When the login
|
|
5835
|
+
* attempt is successful, the `error` property of the response will be `null`,
|
|
5836
|
+
* otherwise it will contain a `DittoError` object with details about the
|
|
5837
|
+
* error.
|
|
5838
|
+
*
|
|
5839
|
+
* If the authentication service provides additional client info, it will be
|
|
5840
|
+
* returned in the `clientInfo` property of the response, whether the login
|
|
5841
|
+
* attempt was successful or not.
|
|
5842
|
+
*
|
|
5843
|
+
* @param token The authentication token required to log in.
|
|
5844
|
+
* @param provider The name of the authentication provider.
|
|
5845
|
+
* @throws {@link DittoError} `authentication/failed-to-authenticate` if the
|
|
5846
|
+
* Ditto instance is closed.
|
|
5847
|
+
* @returns A promise that resolves to a `LoginResult` object.
|
|
5848
|
+
*/
|
|
5849
|
+
async login(token, provider) {
|
|
5850
|
+
throw new Error(`Authenticator.login() is abstract and must be implemented by subclasses.`);
|
|
5851
|
+
}
|
|
5584
5852
|
/**
|
|
5585
5853
|
* Log in to Ditto with a third-party token. Throws if authentication is not
|
|
5586
5854
|
* available, which can be checked with {@link loginSupported}.
|
|
5587
5855
|
*
|
|
5588
5856
|
* @param token the authentication token required to log in.
|
|
5589
5857
|
* @param provider the name of the authentication provider.
|
|
5858
|
+
* @deprecated Use {@link login} instead, which provides access to client info
|
|
5859
|
+
* provided by the authentication service.
|
|
5590
5860
|
*/
|
|
5591
5861
|
loginWithToken(token, provider) {
|
|
5592
5862
|
throw new Error(`Authenticator.loginWithToken() is abstract and must be implemented by subclasses.`);
|
|
@@ -5624,7 +5894,9 @@ class Authenticator {
|
|
|
5624
5894
|
*/
|
|
5625
5895
|
observeStatus(callback) {
|
|
5626
5896
|
const token = this.observerManager.addObserver(callback);
|
|
5627
|
-
return new Observer(this.observerManager, token, {
|
|
5897
|
+
return new Observer(this.observerManager, token, {
|
|
5898
|
+
stopsWhenFinalized: true,
|
|
5899
|
+
});
|
|
5628
5900
|
}
|
|
5629
5901
|
/** @internal */
|
|
5630
5902
|
constructor(keepAlive) {
|
|
@@ -5649,6 +5921,19 @@ class Authenticator {
|
|
|
5649
5921
|
// -----------------------------------------------------------------------------
|
|
5650
5922
|
/** @internal */
|
|
5651
5923
|
class OnlineAuthenticator extends Authenticator {
|
|
5924
|
+
async login(token, provider) {
|
|
5925
|
+
const ditto = this.ditto.deref();
|
|
5926
|
+
if (!ditto || ditto.isClosed) {
|
|
5927
|
+
throw new DittoError('authentication/failed-to-authenticate', 'Ditto instance is closed');
|
|
5928
|
+
}
|
|
5929
|
+
return ditto.deferCloseAsync(async (dittoHandle) => {
|
|
5930
|
+
const { clientInfo, error: ffiError } = await dittoAuthClientLoginWithTokenAndFeedback(dittoHandle.deref(), token, provider);
|
|
5931
|
+
const error = ffiError != null
|
|
5932
|
+
? DittoError.fromFFIError(ffiError, 'authentication/failed-to-authenticate')
|
|
5933
|
+
: null;
|
|
5934
|
+
return { clientInfo, error };
|
|
5935
|
+
});
|
|
5936
|
+
}
|
|
5652
5937
|
async loginWithToken(token, provider) {
|
|
5653
5938
|
const ditto = this.ditto.deref();
|
|
5654
5939
|
if (!ditto || ditto.isClosed)
|
|
@@ -5685,12 +5970,10 @@ class OnlineAuthenticator extends Authenticator {
|
|
|
5685
5970
|
}
|
|
5686
5971
|
'@ditto.authenticationExpiring'(secondsRemaining) {
|
|
5687
5972
|
const authenticationHandler = this.authenticationHandler;
|
|
5688
|
-
if (secondsRemaining > 0)
|
|
5973
|
+
if (secondsRemaining > 0)
|
|
5689
5974
|
authenticationHandler.authenticationExpiringSoon(this, secondsRemaining);
|
|
5690
|
-
|
|
5691
|
-
else {
|
|
5975
|
+
else
|
|
5692
5976
|
authenticationHandler.authenticationRequired(this);
|
|
5693
|
-
}
|
|
5694
5977
|
}
|
|
5695
5978
|
'@ditto.authClientValidityChanged'(isWebValid, isX509Valid) {
|
|
5696
5979
|
this.updateAndNotify(true);
|
|
@@ -5726,6 +6009,9 @@ class OnlineAuthenticator extends Authenticator {
|
|
|
5726
6009
|
// -----------------------------------------------------------------------------
|
|
5727
6010
|
/** @internal */
|
|
5728
6011
|
class NotAvailableAuthenticator extends Authenticator {
|
|
6012
|
+
async login(token, provider) {
|
|
6013
|
+
throw new Error(`Can't login, authentication is not supported for the identity in use, please use an onlineWithAuthentication identity.`);
|
|
6014
|
+
}
|
|
5729
6015
|
async loginWithToken(token, provider) {
|
|
5730
6016
|
throw new Error(`Can't login, authentication is not supported for the identity in use, please use an onlineWithAuthentication identity.`);
|
|
5731
6017
|
}
|
|
@@ -5747,7 +6033,11 @@ class NotAvailableAuthenticator extends Authenticator {
|
|
|
5747
6033
|
// Copyright © 2021 DittoLive Incorporated. All rights reserved.
|
|
5748
6034
|
//
|
|
5749
6035
|
/** The list of identity types that require activation through an offlineLicenseToken */
|
|
5750
|
-
const IdentityTypesRequiringOfflineLicenseToken = [
|
|
6036
|
+
const IdentityTypesRequiringOfflineLicenseToken = [
|
|
6037
|
+
'manual',
|
|
6038
|
+
'sharedKey',
|
|
6039
|
+
'offlinePlayground',
|
|
6040
|
+
];
|
|
5751
6041
|
|
|
5752
6042
|
//
|
|
5753
6043
|
// Copyright © 2021 DittoLive Incorporated. All rights reserved.
|
|
@@ -5862,13 +6152,14 @@ function transportConfigToSerializable(config) {
|
|
|
5862
6152
|
};
|
|
5863
6153
|
// Only set the optional properties if they are not undefined
|
|
5864
6154
|
if (listen.http.staticContentPath) {
|
|
5865
|
-
serialized.listen.http['static_content_path'] =
|
|
6155
|
+
serialized.listen.http['static_content_path'] =
|
|
6156
|
+
listen.http.staticContentPath;
|
|
5866
6157
|
}
|
|
5867
|
-
if (listen.http.tlsKeyPath)
|
|
6158
|
+
if (listen.http.tlsKeyPath)
|
|
5868
6159
|
serialized.listen.http['tls_key_path'] = listen.http.tlsKeyPath;
|
|
5869
|
-
}
|
|
5870
6160
|
if (listen.http.tlsCertificatePath) {
|
|
5871
|
-
serialized.listen.http['tls_certificate_path'] =
|
|
6161
|
+
serialized.listen.http['tls_certificate_path'] =
|
|
6162
|
+
listen.http.tlsCertificatePath;
|
|
5872
6163
|
}
|
|
5873
6164
|
return serialized;
|
|
5874
6165
|
}
|
|
@@ -5975,11 +6266,13 @@ class TransportConfig {
|
|
|
5975
6266
|
*/
|
|
5976
6267
|
copy() {
|
|
5977
6268
|
const copy = new TransportConfig();
|
|
5978
|
-
copy.peerToPeer.bluetoothLE.isEnabled =
|
|
6269
|
+
copy.peerToPeer.bluetoothLE.isEnabled =
|
|
6270
|
+
this.peerToPeer.bluetoothLE.isEnabled;
|
|
5979
6271
|
copy.peerToPeer.awdl.isEnabled = this.peerToPeer.awdl.isEnabled;
|
|
5980
6272
|
copy.peerToPeer.lan.isEnabled = this.peerToPeer.lan.isEnabled;
|
|
5981
6273
|
copy.peerToPeer.lan.isMdnsEnabled = this.peerToPeer.lan.isMdnsEnabled;
|
|
5982
|
-
copy.peerToPeer.lan.isMulticastEnabled =
|
|
6274
|
+
copy.peerToPeer.lan.isMulticastEnabled =
|
|
6275
|
+
this.peerToPeer.lan.isMulticastEnabled;
|
|
5983
6276
|
copy.connect.tcpServers = this.connect.tcpServers.slice();
|
|
5984
6277
|
copy.connect.websocketURLs = this.connect.websocketURLs.slice();
|
|
5985
6278
|
copy.connect.retryInterval = this.connect.retryInterval;
|
|
@@ -5994,28 +6287,23 @@ class TransportConfig {
|
|
|
5994
6287
|
* returns `false`.
|
|
5995
6288
|
*/
|
|
5996
6289
|
static areListenTCPsEqual(left, right) {
|
|
5997
|
-
/* eslint-disable */
|
|
5998
|
-
// prettier-ignore
|
|
5999
6290
|
return (left.isEnabled === right.isEnabled &&
|
|
6000
6291
|
left.interfaceIP === right.interfaceIP &&
|
|
6001
6292
|
left.port === right.port);
|
|
6002
|
-
/* eslint-enable */
|
|
6003
6293
|
}
|
|
6004
6294
|
/**
|
|
6005
6295
|
* Returns `true` if passed in HTTP configurations are equal, otherwise
|
|
6006
6296
|
* returns `false`.
|
|
6007
6297
|
*/
|
|
6008
6298
|
static areListenHTTPsEqual(left, right) {
|
|
6009
|
-
/* eslint-disable */
|
|
6010
|
-
// prettier-ignore
|
|
6011
6299
|
return (left.isEnabled === right.isEnabled &&
|
|
6012
6300
|
left.interfaceIP === right.interfaceIP &&
|
|
6013
6301
|
left.port === right.port &&
|
|
6302
|
+
// Optional properties
|
|
6014
6303
|
left.staticContentPath === right.staticContentPath &&
|
|
6015
6304
|
left.websocketSync === right.websocketSync &&
|
|
6016
6305
|
left.tlsKeyPath === right.tlsKeyPath &&
|
|
6017
6306
|
left.tlsCertificatePath === right.tlsCertificatePath);
|
|
6018
|
-
/* eslint-enable */
|
|
6019
6307
|
}
|
|
6020
6308
|
}
|
|
6021
6309
|
|
|
@@ -6103,7 +6391,11 @@ class AttachmentFetcher {
|
|
|
6103
6391
|
resolve(attachment);
|
|
6104
6392
|
};
|
|
6105
6393
|
const onProgress = (downloaded, toDownload) => {
|
|
6106
|
-
eventHandlerOrNoOp({
|
|
6394
|
+
eventHandlerOrNoOp({
|
|
6395
|
+
type: 'Progress',
|
|
6396
|
+
totalBytes: toDownload,
|
|
6397
|
+
downloadedBytes: downloaded,
|
|
6398
|
+
});
|
|
6107
6399
|
};
|
|
6108
6400
|
const onDelete = () => {
|
|
6109
6401
|
eventHandlerOrNoOp({ type: 'Deleted' });
|
|
@@ -6124,7 +6416,9 @@ class AttachmentFetcher {
|
|
|
6124
6416
|
// cancelled through `this.stop()` so we use this function to reject the
|
|
6125
6417
|
// promise from the outside.
|
|
6126
6418
|
this.rejectPendingFetch = () => {
|
|
6127
|
-
const err = this.manager != null
|
|
6419
|
+
const err = this.manager != null
|
|
6420
|
+
? new Error('Attachment fetch was canceled')
|
|
6421
|
+
: new DittoError('store/failed-to-fetch-attachment', 'Attachment fetch was canceled');
|
|
6128
6422
|
reject(err);
|
|
6129
6423
|
};
|
|
6130
6424
|
// `cancelTokenPromise` resolves once the fetcher has been initialised in
|
|
@@ -6144,9 +6438,18 @@ class AttachmentFetcher {
|
|
|
6144
6438
|
this.cancelTokenPromise = (async () => {
|
|
6145
6439
|
try {
|
|
6146
6440
|
return await mapFFIErrorsAsync(async () => dittoResolveAttachment(dittoHandle.deref(), token.idBytes, { onComplete, onProgress, onDelete }, onError), {
|
|
6147
|
-
1: [
|
|
6148
|
-
|
|
6149
|
-
|
|
6441
|
+
1: [
|
|
6442
|
+
'store/failed-to-fetch-attachment',
|
|
6443
|
+
'Failed to fetch the attachment.',
|
|
6444
|
+
],
|
|
6445
|
+
2: [
|
|
6446
|
+
'store/attachment-token-invalid',
|
|
6447
|
+
'The attachment token was invalid.',
|
|
6448
|
+
],
|
|
6449
|
+
3: [
|
|
6450
|
+
'store/attachment-not-found',
|
|
6451
|
+
'The attachment was not found.',
|
|
6452
|
+
],
|
|
6150
6453
|
});
|
|
6151
6454
|
}
|
|
6152
6455
|
catch (e) {
|
|
@@ -6154,7 +6457,8 @@ class AttachmentFetcher {
|
|
|
6154
6457
|
// Legacy behavior: when the attachment is deleted before the fetch is
|
|
6155
6458
|
// started, the fetcher is stopped and the promise is resolved to
|
|
6156
6459
|
// `null`.
|
|
6157
|
-
if (e instanceof DittoError &&
|
|
6460
|
+
if (e instanceof DittoError &&
|
|
6461
|
+
e.code === 'store/attachment-not-found') {
|
|
6158
6462
|
isDeleted = true;
|
|
6159
6463
|
eventHandlerOrNoOp({ type: 'Deleted' });
|
|
6160
6464
|
}
|
|
@@ -6168,19 +6472,16 @@ class AttachmentFetcher {
|
|
|
6168
6472
|
}
|
|
6169
6473
|
// When this is called from legacy `Collection.fetchAttachment()`, we
|
|
6170
6474
|
// convert the DittoError to a regular Error.
|
|
6171
|
-
if (strongThis.manager != null && e instanceof DittoError)
|
|
6475
|
+
if (strongThis.manager != null && e instanceof DittoError)
|
|
6172
6476
|
e = new Error(e.message);
|
|
6173
|
-
}
|
|
6174
6477
|
strongThis.rejectPendingFetch = null;
|
|
6175
6478
|
// Reject the `attachment` promise to signal that the fetch has
|
|
6176
6479
|
// failed, unless this is a legacy fetcher and the attachment was
|
|
6177
6480
|
// deleted.
|
|
6178
|
-
if (strongThis.manager != null && isDeleted)
|
|
6481
|
+
if (strongThis.manager != null && isDeleted)
|
|
6179
6482
|
resolve(null);
|
|
6180
|
-
|
|
6181
|
-
else {
|
|
6483
|
+
else
|
|
6182
6484
|
reject(e);
|
|
6183
|
-
}
|
|
6184
6485
|
// Set cancelTokenPromise to null to indicate that there is nothing to
|
|
6185
6486
|
// cancel anymore.
|
|
6186
6487
|
return null;
|
|
@@ -6220,6 +6521,11 @@ class AttachmentFetcher {
|
|
|
6220
6521
|
* While {@link Subscription} objects remain in scope they ensure that
|
|
6221
6522
|
* documents in the collection specified and that match the query provided will
|
|
6222
6523
|
* try to be kept up-to-date with the latest changes from remote peers.
|
|
6524
|
+
*
|
|
6525
|
+
* This class is used by Ditto's query builder APIs.
|
|
6526
|
+
* @see {@link SyncSubscription} for the DQL equivalent.
|
|
6527
|
+
*
|
|
6528
|
+
* Not available in React Native environments.
|
|
6223
6529
|
*/
|
|
6224
6530
|
class Subscription {
|
|
6225
6531
|
/**
|
|
@@ -6273,6 +6579,8 @@ class Subscription {
|
|
|
6273
6579
|
/**
|
|
6274
6580
|
* First event fired immediately after registering a live query without any
|
|
6275
6581
|
* mutations. All subsequent events are of type {@link LiveQueryEventUpdate}.
|
|
6582
|
+
*
|
|
6583
|
+
* Not available in React Native environments.
|
|
6276
6584
|
*/
|
|
6277
6585
|
class LiveQueryEventInitial {
|
|
6278
6586
|
constructor() {
|
|
@@ -6308,11 +6616,14 @@ class LiveQueryEventInitial {
|
|
|
6308
6616
|
/**
|
|
6309
6617
|
* Represents an update event describing all changes that occured for documents
|
|
6310
6618
|
* covered by a (live) query.
|
|
6619
|
+
*
|
|
6620
|
+
* Not available in React Native environments.
|
|
6311
6621
|
*/
|
|
6312
6622
|
class LiveQueryEventUpdate {
|
|
6313
6623
|
/**
|
|
6314
6624
|
* Returns a hash that represents the set of matching documents.
|
|
6315
6625
|
*
|
|
6626
|
+
* @throws {Error} when called in a React Native environment.
|
|
6316
6627
|
* @deprecated use {@link Document.hash | Document.hash()} instead.
|
|
6317
6628
|
*/
|
|
6318
6629
|
hash(documents) {
|
|
@@ -6324,6 +6635,7 @@ class LiveQueryEventUpdate {
|
|
|
6324
6635
|
* Returns a pattern of words that together create a mnemonic, which
|
|
6325
6636
|
* represents the set of matching documents.
|
|
6326
6637
|
*
|
|
6638
|
+
* @throws {Error} when called in a React Native environment.
|
|
6327
6639
|
* @deprecated use {@link Document.hashMnemonic | Document.hashMnemonic()} instead.
|
|
6328
6640
|
*/
|
|
6329
6641
|
hashMnemonic(documents) {
|
|
@@ -6395,6 +6707,8 @@ class SingleDocumentLiveQueryEvent {
|
|
|
6395
6707
|
* your event handler be called when there is an update to a document matching
|
|
6396
6708
|
* the query you provide. When you no longer want to receive updates about
|
|
6397
6709
|
* documents matching a query then you must call {@link stop | stop()}.
|
|
6710
|
+
*
|
|
6711
|
+
* Not available in React Native environments.
|
|
6398
6712
|
*/
|
|
6399
6713
|
class LiveQuery {
|
|
6400
6714
|
/** The name of the collection that the live query is based on. */
|
|
@@ -6407,11 +6721,12 @@ class LiveQuery {
|
|
|
6407
6721
|
}
|
|
6408
6722
|
/**
|
|
6409
6723
|
* Stop the live query from delivering updates.
|
|
6724
|
+
*
|
|
6725
|
+
* @throws {Error} when called in a React Native environment.
|
|
6410
6726
|
*/
|
|
6411
6727
|
stop() {
|
|
6412
|
-
if (!this.isStopped)
|
|
6728
|
+
if (!this.isStopped)
|
|
6413
6729
|
this.liveQueryManager.stopLiveQuery(this);
|
|
6414
|
-
}
|
|
6415
6730
|
}
|
|
6416
6731
|
get liveQueryID() {
|
|
6417
6732
|
return this._liveQueryID;
|
|
@@ -6450,10 +6765,16 @@ class LiveQuery {
|
|
|
6450
6765
|
else {
|
|
6451
6766
|
event = new LiveQueryEventUpdate({
|
|
6452
6767
|
oldDocuments: cCBParams.old_documents.map((ptr) => Bridge.document.bridge(ptr)),
|
|
6768
|
+
// We don't need to bridge these indices which are returned from FFI
|
|
6769
|
+
// as JS values and not as pointers. c.f.
|
|
6770
|
+
// https://dittolive.slack.com/archives/C01NLL95095/p1720609380387539
|
|
6453
6771
|
insertions: cCBParams.insertions,
|
|
6454
6772
|
deletions: cCBParams.deletions,
|
|
6455
6773
|
updates: cCBParams.updates,
|
|
6456
|
-
moves: cCBParams.moves.map((move) => ({
|
|
6774
|
+
moves: cCBParams.moves.map((move) => ({
|
|
6775
|
+
from: move[0],
|
|
6776
|
+
to: move[1],
|
|
6777
|
+
})),
|
|
6457
6778
|
});
|
|
6458
6779
|
}
|
|
6459
6780
|
// We discard the return promise because error-handling is not supported.
|
|
@@ -6495,6 +6816,8 @@ class LiveQuery {
|
|
|
6495
6816
|
* preceding `find`-like call.
|
|
6496
6817
|
*
|
|
6497
6818
|
* Update and remove functionality is also exposed through this object.
|
|
6819
|
+
*
|
|
6820
|
+
* Not available in React Native environments.
|
|
6498
6821
|
*/
|
|
6499
6822
|
class PendingCursorOperation extends BasePendingCursorOperation {
|
|
6500
6823
|
sort(propertyPath, direction = 'ascending') {
|
|
@@ -6540,10 +6863,7 @@ class PendingCursorOperation extends BasePendingCursorOperation {
|
|
|
6540
6863
|
const ditto = this.collection.store.ditto;
|
|
6541
6864
|
return ditto.deferCloseAsync(async (dittoHandle) => {
|
|
6542
6865
|
return await performAsyncToWorkaroundNonAsyncFFIAPI(async () => {
|
|
6543
|
-
|
|
6544
|
-
const results = await super.updateWithTransaction(closure, writeTransactionX);
|
|
6545
|
-
await writeTransactionCommit(dittoHandle.deref(), writeTransactionX);
|
|
6546
|
-
return results;
|
|
6866
|
+
return await super.updateWithClosure(closure, 'cursor operation update()');
|
|
6547
6867
|
});
|
|
6548
6868
|
});
|
|
6549
6869
|
}
|
|
@@ -6558,6 +6878,8 @@ class PendingCursorOperation extends BasePendingCursorOperation {
|
|
|
6558
6878
|
* The returned {@link Subscription} object must be kept in scope for as long
|
|
6559
6879
|
* as you want to keep receiving updates.
|
|
6560
6880
|
*
|
|
6881
|
+
* @throws {Error} when called in a React Native environment.
|
|
6882
|
+
*
|
|
6561
6883
|
* @returns A {@link Subscription} object that must be kept in scope for as
|
|
6562
6884
|
* long as you want to keep receiving updates for documents that match the
|
|
6563
6885
|
* query specified in the preceding chain.
|
|
@@ -6588,6 +6910,8 @@ class PendingCursorOperation extends BasePendingCursorOperation {
|
|
|
6588
6910
|
* transaction committed to the store that involves modifications to documents
|
|
6589
6911
|
* matching the query in the collection this method was called on.
|
|
6590
6912
|
*
|
|
6913
|
+
* @throws {Error} when called in a React Native environment.
|
|
6914
|
+
*
|
|
6591
6915
|
* @return A {@link LiveQuery} object that must be kept in scope for as long
|
|
6592
6916
|
* as you want to keep receiving updates.
|
|
6593
6917
|
*/
|
|
@@ -6617,6 +6941,8 @@ class PendingCursorOperation extends BasePendingCursorOperation {
|
|
|
6617
6941
|
* documents matching the query in the collection that this method was called
|
|
6618
6942
|
* on.
|
|
6619
6943
|
*
|
|
6944
|
+
* @throws {Error} when called in a React Native environment.
|
|
6945
|
+
*
|
|
6620
6946
|
* @return A {@link LiveQuery} object that must be kept in scope for as long
|
|
6621
6947
|
* as you want to keep receiving updates.
|
|
6622
6948
|
*/
|
|
@@ -6638,7 +6964,9 @@ class PendingCursorOperation extends BasePendingCursorOperation {
|
|
|
6638
6964
|
nextSignal();
|
|
6639
6965
|
}
|
|
6640
6966
|
}
|
|
6641
|
-
const handlerOrWrapped = waitForNextSignal
|
|
6967
|
+
const handlerOrWrapped = waitForNextSignal
|
|
6968
|
+
? handler
|
|
6969
|
+
: wrappedHandler;
|
|
6642
6970
|
const liveQuery = new LiveQuery(this.query, this.queryArgs, this.queryArgsCBOR, this.orderBys, this.currentLimit, this.currentOffset, this.collection, handlerOrWrapped);
|
|
6643
6971
|
this.collection.store.ditto.liveQueryManager.startLiveQuery(liveQuery);
|
|
6644
6972
|
return liveQuery;
|
|
@@ -6693,23 +7021,10 @@ class PendingIDSpecificOperation extends BasePendingIDSpecificOperation {
|
|
|
6693
7021
|
});
|
|
6694
7022
|
}
|
|
6695
7023
|
async update(closure) {
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
readTransactionFree(readTransactionX);
|
|
6701
|
-
if (!documentX)
|
|
6702
|
-
throw new Error(`Can't update, document with ID '${this.documentID.toString()}' not found in collection named '${this.collection.name}'`);
|
|
6703
|
-
const mutableDocument = Bridge.mutableDocument.bridge(documentX, () => new MutableDocument());
|
|
6704
|
-
closure(mutableDocument);
|
|
6705
|
-
// Ownership is transferred back to the FFI layer via collectionUpdate(),
|
|
6706
|
-
// we therefore need to explicitly unregister the instance.
|
|
6707
|
-
Bridge.mutableDocument.unregister(mutableDocument);
|
|
6708
|
-
const writeTransactionX = await writeTransaction(dittoHandle.deref());
|
|
6709
|
-
await collectionUpdate(dittoHandle.deref(), this.collection.name, writeTransactionX, documentX);
|
|
6710
|
-
await writeTransactionCommit(dittoHandle.deref(), writeTransactionX);
|
|
6711
|
-
return mutableDocument['@ditto.updateResults'].slice();
|
|
6712
|
-
});
|
|
7024
|
+
return this.updateWithClosure(closure, false, true, 'ID-specific cursor operation update()');
|
|
7025
|
+
}
|
|
7026
|
+
async updateV2(closure) {
|
|
7027
|
+
return this.updateWithClosure(closure, true, false, 'ID-specific cursor operation updateV2()');
|
|
6713
7028
|
}
|
|
6714
7029
|
/**
|
|
6715
7030
|
* Enables you to subscribe to changes that occur in relation to a document
|
|
@@ -6791,19 +7106,29 @@ class PendingIDSpecificOperation extends BasePendingIDSpecificOperation {
|
|
|
6791
7106
|
if (documents.length > 1) {
|
|
6792
7107
|
throw new Error(`Internal inconsistency, single document live query returned more than one document. Query: ${this.query}}.`);
|
|
6793
7108
|
}
|
|
6794
|
-
if (event.isInitial === false && event.oldDocuments.length > 1)
|
|
7109
|
+
if (event.isInitial === false && event.oldDocuments.length > 1) {
|
|
6795
7110
|
throw new Error(`Internal inconsistency, single document live query returned an update event with more than one old documents. Query ${this.query}.`);
|
|
6796
|
-
|
|
7111
|
+
}
|
|
7112
|
+
if (event.isInitial === false && event.insertions.length > 1) {
|
|
6797
7113
|
throw new Error(`Internal inconsistency, single document live query returned an update event with more than one insertion, which doesn't make sense for single document observations. Query ${this.query}.`);
|
|
6798
|
-
|
|
7114
|
+
}
|
|
7115
|
+
if (event.isInitial === false && event.deletions.length > 1) {
|
|
6799
7116
|
throw new Error(`Internal inconsistency, single document live query returned an update event with more than one deletion, which doesn't make sense for single document observations. Query ${this.query}.`);
|
|
6800
|
-
|
|
7117
|
+
}
|
|
7118
|
+
if (event.isInitial === false && event.updates.length > 1) {
|
|
6801
7119
|
throw new Error(`Internal inconsistency, single document live query returned an update event with more than one update, which doesn't make sense for single document observations. Query ${this.query}.`);
|
|
6802
|
-
|
|
7120
|
+
}
|
|
7121
|
+
if (event.isInitial === false && event.moves.length > 0) {
|
|
6803
7122
|
throw new Error(`Internal inconsistency, single document live query returned an update event with moves, which doesn't make sense for single document observations. Query ${this.query}.`);
|
|
6804
|
-
|
|
6805
|
-
|
|
7123
|
+
}
|
|
7124
|
+
const totalNumberOfManipulations = event.isInitial === true
|
|
7125
|
+
? 0
|
|
7126
|
+
: event.insertions.length +
|
|
7127
|
+
event.deletions.length +
|
|
7128
|
+
event.updates.length;
|
|
7129
|
+
if (totalNumberOfManipulations > 1) {
|
|
6806
7130
|
throw new Error(`Internal inconsistency, single document live query returned a combination of inserts, updates, and/or deletes, which doesn't make sense for single document observation. Query ${this.query}.`);
|
|
7131
|
+
}
|
|
6807
7132
|
// IDEA: use `undefined` instead of `null` and
|
|
6808
7133
|
// adapt Wasm variant plus API definition.
|
|
6809
7134
|
const document = documents[0] || null;
|
|
@@ -6836,6 +7161,8 @@ class PendingIDSpecificOperation extends BasePendingIDSpecificOperation {
|
|
|
6836
7161
|
* querying a collection. You can get a collection by calling
|
|
6837
7162
|
* {@link Store.collection | collection()} on a {@link Store} of a {@link Ditto}
|
|
6838
7163
|
* object.
|
|
7164
|
+
*
|
|
7165
|
+
* Not available in React Native environments.
|
|
6839
7166
|
*/
|
|
6840
7167
|
class Collection {
|
|
6841
7168
|
/**
|
|
@@ -6856,6 +7183,7 @@ class Collection {
|
|
|
6856
7183
|
* Find more information about the query string format in the documentation's
|
|
6857
7184
|
* section on {@link https://docs.ditto.live/javascript/common/concepts/querying Querying}
|
|
6858
7185
|
*
|
|
7186
|
+
* @throws {Error} when called in a React Native environment.
|
|
6859
7187
|
* @param query The query to run against the collection.
|
|
6860
7188
|
* @param queryArgs The arguments to use to replace placeholders in the
|
|
6861
7189
|
* provided query.
|
|
@@ -6878,6 +7206,7 @@ class Collection {
|
|
|
6878
7206
|
* remove or evict the document.
|
|
6879
7207
|
*
|
|
6880
7208
|
* @param id The ID of the document to find.
|
|
7209
|
+
* @throws {Error} when called in a React Native environment.
|
|
6881
7210
|
*/
|
|
6882
7211
|
findByID(id) {
|
|
6883
7212
|
const documentID = id instanceof DocumentID ? id : new DocumentID(id);
|
|
@@ -6918,9 +7247,10 @@ class Collection {
|
|
|
6918
7247
|
*
|
|
6919
7248
|
* @param pathOrData The path to the file that you want to create an
|
|
6920
7249
|
* attachment with or the raw data.
|
|
6921
|
-
*
|
|
6922
7250
|
* @param metadata Metadata relating to the attachment.
|
|
6923
7251
|
*
|
|
7252
|
+
* @throws {Error} when called in a React Native environment.
|
|
7253
|
+
*
|
|
6924
7254
|
* @deprecated Use {@link Store.newAttachment | ditto.store.newAttachment() }
|
|
6925
7255
|
* instead.
|
|
6926
7256
|
*/
|
|
@@ -6938,7 +7268,11 @@ class Collection {
|
|
|
6938
7268
|
}
|
|
6939
7269
|
throw new Error(`Can't create new attachment, only file path as string or raw data as Uint8Array are supported, but got: ${typeof pathOrData}, ${pathOrData}`);
|
|
6940
7270
|
})();
|
|
6941
|
-
const attachmentTokenJSON = {
|
|
7271
|
+
const attachmentTokenJSON = {
|
|
7272
|
+
_id: id,
|
|
7273
|
+
_len: len,
|
|
7274
|
+
_meta: { ...metadata },
|
|
7275
|
+
};
|
|
6942
7276
|
attachmentTokenJSON[DittoCRDTTypeKey] = DittoCRDTType.attachment;
|
|
6943
7277
|
const attachmentToken = new AttachmentToken(attachmentTokenJSON);
|
|
6944
7278
|
const attachment = new Attachment(ditto, attachmentToken);
|
|
@@ -6961,10 +7295,11 @@ class Collection {
|
|
|
6961
7295
|
*
|
|
6962
7296
|
* @param token The {@link AttachmentToken} relevant to the attachment that
|
|
6963
7297
|
* you wish to download and observe. Throws if token is invalid.
|
|
6964
|
-
*
|
|
6965
7298
|
* @param eventHandler An optional callback that will be called when there is
|
|
6966
7299
|
* an update to the status of the attachment fetch attempt.
|
|
6967
7300
|
*
|
|
7301
|
+
* @throws {Error} when called in a React Native environment.
|
|
7302
|
+
*
|
|
6968
7303
|
* @return An `AttachmentFetcher` object, which must be kept alive for the
|
|
6969
7304
|
* fetch request to proceed and for you to be notified about the attachment's
|
|
6970
7305
|
* fetch status changes.
|
|
@@ -6973,9 +7308,8 @@ class Collection {
|
|
|
6973
7308
|
* {@link Store.fetchAttachment | ditto.store.fetchAttachment() } instead.
|
|
6974
7309
|
*/
|
|
6975
7310
|
fetchAttachment(token, eventHandler) {
|
|
6976
|
-
if (token == null || !(token instanceof AttachmentToken))
|
|
7311
|
+
if (token == null || !(token instanceof AttachmentToken))
|
|
6977
7312
|
throw new Error(`Invalid attachment token: ${token}`);
|
|
6978
|
-
}
|
|
6979
7313
|
const ditto = this.store.ditto;
|
|
6980
7314
|
return ditto.deferClose(() => {
|
|
6981
7315
|
return ditto.attachmentFetcherManager.startAttachmentFetcher(token, eventHandler);
|
|
@@ -7084,7 +7418,9 @@ class StoreObserver {
|
|
|
7084
7418
|
*/
|
|
7085
7419
|
this._isCancelled = false;
|
|
7086
7420
|
this.queryString = query;
|
|
7087
|
-
this.queryArguments = queryArguments
|
|
7421
|
+
this.queryArguments = queryArguments
|
|
7422
|
+
? Object.freeze({ ...queryArguments })
|
|
7423
|
+
: undefined;
|
|
7088
7424
|
this.ditto = ditto;
|
|
7089
7425
|
let queryArgumentsCBOR = null;
|
|
7090
7426
|
if (queryArguments != null) {
|
|
@@ -7149,6 +7485,8 @@ class StoreObserver {
|
|
|
7149
7485
|
* the collections that were previously known about in the previous event, along
|
|
7150
7486
|
* with information about what collections have been inserted, deleted, updated,
|
|
7151
7487
|
* or moved since the last event.
|
|
7488
|
+
*
|
|
7489
|
+
* Not available in React Native environments.
|
|
7152
7490
|
*/
|
|
7153
7491
|
class CollectionsEvent {
|
|
7154
7492
|
/** @internal */
|
|
@@ -7196,6 +7534,8 @@ class CollectionsEvent {
|
|
|
7196
7534
|
* A subscription, established by calling {@link subscribe | subscribe()}, will
|
|
7197
7535
|
* act as a signal to other peers that the device connects to that you would
|
|
7198
7536
|
* like to receive updates from them about the collections that they know about.
|
|
7537
|
+
*
|
|
7538
|
+
* Not available in React Native environments.
|
|
7199
7539
|
*/
|
|
7200
7540
|
class PendingCollectionsOperation {
|
|
7201
7541
|
/**
|
|
@@ -7207,6 +7547,8 @@ class PendingCollectionsOperation {
|
|
|
7207
7547
|
* @param direction Specify whether you want the sorting order to be
|
|
7208
7548
|
* `Ascending` or `Descending`.
|
|
7209
7549
|
*
|
|
7550
|
+
* @throws {Error} when called in a React Native environment.
|
|
7551
|
+
*
|
|
7210
7552
|
* @return A {@link PendingCollectionsOperation} that you can chain further
|
|
7211
7553
|
* function calls to.
|
|
7212
7554
|
*/
|
|
@@ -7227,6 +7569,8 @@ class PendingCollectionsOperation {
|
|
|
7227
7569
|
* @param offset The number of collections that you want the eventual
|
|
7228
7570
|
* resulting set of collections to be offset by (and thus not include).
|
|
7229
7571
|
*
|
|
7572
|
+
* @throws {Error} when called in a React Native environment.
|
|
7573
|
+
*
|
|
7230
7574
|
* @return A {@link PendingCollectionsOperation} that you can chain further
|
|
7231
7575
|
* function calls to.
|
|
7232
7576
|
*/
|
|
@@ -7241,6 +7585,8 @@ class PendingCollectionsOperation {
|
|
|
7241
7585
|
*
|
|
7242
7586
|
* @param limit The maximum number of collections that will be returned.
|
|
7243
7587
|
*
|
|
7588
|
+
* @throws {Error} when called in a React Native environment.
|
|
7589
|
+
*
|
|
7244
7590
|
* @return A {@link PendingCollectionsOperation} that you can chain further
|
|
7245
7591
|
* function calls to.
|
|
7246
7592
|
*/
|
|
@@ -7257,6 +7603,8 @@ class PendingCollectionsOperation {
|
|
|
7257
7603
|
* The returned {@link Subscription} object must be kept in scope for as long
|
|
7258
7604
|
* as you want to keep receiving updates.
|
|
7259
7605
|
*
|
|
7606
|
+
* @throws {Error} when called in a React Native environment.
|
|
7607
|
+
*
|
|
7260
7608
|
* @return A {@link Subscription} object that must be kept in scope for as
|
|
7261
7609
|
* long as you want to keep receiving updates from other devices about the
|
|
7262
7610
|
* collections that they know about.
|
|
@@ -7279,6 +7627,8 @@ class PendingCollectionsOperation {
|
|
|
7279
7627
|
* @param handler A closure that will be called every time there is an update
|
|
7280
7628
|
* about the list of known about collections.
|
|
7281
7629
|
*
|
|
7630
|
+
* @throws {Error} when called in a React Native environment.
|
|
7631
|
+
*
|
|
7282
7632
|
* @return A {@link LiveQuery} object that must be kept in scope for as long
|
|
7283
7633
|
* as you want to keep receiving updates.
|
|
7284
7634
|
*/
|
|
@@ -7300,6 +7650,8 @@ class PendingCollectionsOperation {
|
|
|
7300
7650
|
* @param handler A closure that will be called every time there is an update
|
|
7301
7651
|
* about the list of known about collections.
|
|
7302
7652
|
*
|
|
7653
|
+
* @throws {Error} when called in a React Native environment.
|
|
7654
|
+
*
|
|
7303
7655
|
* @return A {@link LiveQuery} object that must be kept in scope for as long
|
|
7304
7656
|
* as you want to keep receiving updates.
|
|
7305
7657
|
*/
|
|
@@ -7310,6 +7662,8 @@ class PendingCollectionsOperation {
|
|
|
7310
7662
|
* Return the list of collections requested based on the preceding function
|
|
7311
7663
|
* chaining.
|
|
7312
7664
|
*
|
|
7665
|
+
* @throws {Error} when called in a React Native environment.
|
|
7666
|
+
*
|
|
7313
7667
|
* @return A list of {@link Collection}s based on the preceding function
|
|
7314
7668
|
* chaining.
|
|
7315
7669
|
*/
|
|
@@ -7332,9 +7686,8 @@ class PendingCollectionsOperation {
|
|
|
7332
7686
|
const weakStore = new WeakRef(this.store);
|
|
7333
7687
|
const collectionsObservationHandler = function (documents, event, nextSignal) {
|
|
7334
7688
|
const strongStore = weakStore.deref();
|
|
7335
|
-
if (!strongStore)
|
|
7689
|
+
if (!strongStore)
|
|
7336
7690
|
return;
|
|
7337
|
-
}
|
|
7338
7691
|
const collections = collectionsFromDocuments(documents, strongStore);
|
|
7339
7692
|
let collEvent;
|
|
7340
7693
|
if (event.isInitial === true) {
|
|
@@ -7354,12 +7707,10 @@ class PendingCollectionsOperation {
|
|
|
7354
7707
|
}
|
|
7355
7708
|
// The handler return promises are ignored here because we are not
|
|
7356
7709
|
// handling errors during handler execution.
|
|
7357
|
-
if (waitForNextSignal)
|
|
7710
|
+
if (waitForNextSignal)
|
|
7358
7711
|
void handler(collEvent, nextSignal);
|
|
7359
|
-
|
|
7360
|
-
else {
|
|
7712
|
+
else
|
|
7361
7713
|
void handler(collEvent);
|
|
7362
|
-
}
|
|
7363
7714
|
};
|
|
7364
7715
|
return this.pendingCursorOperation._observe(collectionsObservationHandler, waitForNextSignal);
|
|
7365
7716
|
}
|
|
@@ -7369,9 +7720,8 @@ function collectionsFromDocuments(documents, store) {
|
|
|
7369
7720
|
const collections = [];
|
|
7370
7721
|
for (const document of documents) {
|
|
7371
7722
|
const collectionName = document.at('name').value;
|
|
7372
|
-
if (collectionName !== undefined && typeof collectionName === 'string')
|
|
7723
|
+
if (collectionName !== undefined && typeof collectionName === 'string')
|
|
7373
7724
|
collections.push(new Collection(collectionName, store));
|
|
7374
|
-
}
|
|
7375
7725
|
}
|
|
7376
7726
|
return collections;
|
|
7377
7727
|
}
|
|
@@ -7392,6 +7742,8 @@ function collectionsFromDocuments(documents, store) {
|
|
|
7392
7742
|
* or evicting any matching documents.
|
|
7393
7743
|
*
|
|
7394
7744
|
* Live queries and subscriptions are only available outside of transactions.
|
|
7745
|
+
*
|
|
7746
|
+
* Not available in React Native environments.
|
|
7395
7747
|
*/
|
|
7396
7748
|
class WriteTransactionPendingCursorOperation extends BasePendingCursorOperation {
|
|
7397
7749
|
sort(propertyPath, direction = 'ascending') {
|
|
@@ -7407,7 +7759,8 @@ class WriteTransactionPendingCursorOperation extends BasePendingCursorOperation
|
|
|
7407
7759
|
const ditto = this.collection.store.ditto;
|
|
7408
7760
|
return ditto.deferCloseAsync(async (dittoHandle) => {
|
|
7409
7761
|
const query = this.query;
|
|
7410
|
-
const transaction = this.collection
|
|
7762
|
+
const transaction = this.collection
|
|
7763
|
+
.writeTransaction;
|
|
7411
7764
|
const documentsX = await performAsyncToWorkaroundNonAsyncFFIAPI(async () => collectionRemoveQueryStr(dittoHandle.deref(), this.collection.name, transaction.writeTransactionPointer, query, this.queryArgsCBOR, this.orderBys, this.currentLimit, this.currentOffset));
|
|
7412
7765
|
const results = documentsX.map((idCBOR) => {
|
|
7413
7766
|
return new DocumentID(idCBOR, true);
|
|
@@ -7422,7 +7775,8 @@ class WriteTransactionPendingCursorOperation extends BasePendingCursorOperation
|
|
|
7422
7775
|
const ditto = this.collection.store.ditto;
|
|
7423
7776
|
return ditto.deferCloseAsync(async (dittoHandle) => {
|
|
7424
7777
|
const query = this.query;
|
|
7425
|
-
const transaction = this.collection
|
|
7778
|
+
const transaction = this.collection
|
|
7779
|
+
.writeTransaction;
|
|
7426
7780
|
const documentsX = await performAsyncToWorkaroundNonAsyncFFIAPI(async () => collectionEvictQueryStr(dittoHandle.deref(), this.collection.name, transaction.writeTransactionPointer, query, this.queryArgsCBOR, this.orderBys, this.currentLimit, this.currentOffset));
|
|
7427
7781
|
const results = documentsX.map((idCBOR) => {
|
|
7428
7782
|
return new DocumentID(idCBOR, true);
|
|
@@ -7434,8 +7788,9 @@ class WriteTransactionPendingCursorOperation extends BasePendingCursorOperation
|
|
|
7434
7788
|
});
|
|
7435
7789
|
}
|
|
7436
7790
|
async update(closure) {
|
|
7437
|
-
const transaction = this.collection
|
|
7438
|
-
|
|
7791
|
+
const transaction = this.collection
|
|
7792
|
+
.writeTransaction;
|
|
7793
|
+
const results = await super.updateWithClosure(closure, 'write transaction cursor operation update()', transaction.writeTransactionPointer);
|
|
7439
7794
|
results.keys().forEach((documentId) => {
|
|
7440
7795
|
transaction.addResult('updated', documentId, this.collection.name);
|
|
7441
7796
|
});
|
|
@@ -7455,7 +7810,8 @@ class WriteTransactionPendingIDSpecificOperation extends BasePendingIDSpecificOp
|
|
|
7455
7810
|
async remove() {
|
|
7456
7811
|
const ditto = this.collection.store.ditto;
|
|
7457
7812
|
return ditto.deferCloseAsync(async (dittoHandle) => {
|
|
7458
|
-
const transaction = this.collection
|
|
7813
|
+
const transaction = this.collection
|
|
7814
|
+
.writeTransaction;
|
|
7459
7815
|
const result = await performAsyncToWorkaroundNonAsyncFFIAPI(async () => collectionRemove(dittoHandle.deref(), this.collection.name, transaction.writeTransactionPointer, this.documentIDCBOR));
|
|
7460
7816
|
transaction.addResult('removed', this.documentID, this.collection.name);
|
|
7461
7817
|
return result;
|
|
@@ -7464,30 +7820,26 @@ class WriteTransactionPendingIDSpecificOperation extends BasePendingIDSpecificOp
|
|
|
7464
7820
|
async evict() {
|
|
7465
7821
|
const ditto = this.collection.store.ditto;
|
|
7466
7822
|
return ditto.deferCloseAsync(async (dittoHandle) => {
|
|
7467
|
-
const transaction = this.collection
|
|
7823
|
+
const transaction = this.collection
|
|
7824
|
+
.writeTransaction;
|
|
7468
7825
|
const result = await performAsyncToWorkaroundNonAsyncFFIAPI(async () => await collectionEvict(dittoHandle.deref(), this.collection.name, transaction.writeTransactionPointer, this.documentIDCBOR));
|
|
7469
7826
|
transaction.addResult('evicted', this.documentID, this.collection.name);
|
|
7470
7827
|
return result;
|
|
7471
7828
|
});
|
|
7472
7829
|
}
|
|
7473
7830
|
async update(closure) {
|
|
7474
|
-
const
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
Bridge.mutableDocument.unregister(mutableDocument);
|
|
7487
|
-
await collectionUpdate(dittoHandle.deref(), this.collection.name, transaction.writeTransactionPointer, documentX);
|
|
7488
|
-
transaction.addResult('updated', this.documentID, this.collection.name);
|
|
7489
|
-
return mutableDocument['@ditto.updateResults'].slice();
|
|
7490
|
-
});
|
|
7831
|
+
const transaction = this.collection
|
|
7832
|
+
.writeTransaction;
|
|
7833
|
+
const updateResults = this.updateWithClosure(closure, false, true, 'write transaction cursor operation update()', transaction);
|
|
7834
|
+
transaction.addResult('updated', this.documentID, this.collection.name);
|
|
7835
|
+
return updateResults;
|
|
7836
|
+
}
|
|
7837
|
+
async updateV2(closure) {
|
|
7838
|
+
const transaction = this.collection
|
|
7839
|
+
.writeTransaction;
|
|
7840
|
+
const updateResults = this.updateWithClosure(closure, true, false, 'write transaction cursor operation updateV2()', transaction);
|
|
7841
|
+
transaction.addResult('updated', this.documentID, this.collection.name);
|
|
7842
|
+
return updateResults;
|
|
7491
7843
|
}
|
|
7492
7844
|
// ----------------------------------------------------------- Internal ------
|
|
7493
7845
|
/** @internal */
|
|
@@ -7509,6 +7861,8 @@ class WriteTransactionPendingIDSpecificOperation extends BasePendingIDSpecificOp
|
|
|
7509
7861
|
*
|
|
7510
7862
|
* Create a `WriteTransactionCollection` by starting a {@link WriteTransaction}
|
|
7511
7863
|
* and using its `scoped` method.
|
|
7864
|
+
*
|
|
7865
|
+
* Not available in React Native environments.
|
|
7512
7866
|
*/
|
|
7513
7867
|
class WriteTransactionCollection {
|
|
7514
7868
|
/**
|
|
@@ -7520,6 +7874,7 @@ class WriteTransactionCollection {
|
|
|
7520
7874
|
* @param query The query to run against the collection.
|
|
7521
7875
|
* @param queryArgs These arguments replace placeholders in the provided
|
|
7522
7876
|
* query.
|
|
7877
|
+
* @throws {Error} when called in a React Native environment.
|
|
7523
7878
|
*/
|
|
7524
7879
|
find(query, queryArgs) {
|
|
7525
7880
|
return new WriteTransactionPendingCursorOperation(query, queryArgs !== null && queryArgs !== void 0 ? queryArgs : null, this);
|
|
@@ -7527,6 +7882,8 @@ class WriteTransactionCollection {
|
|
|
7527
7882
|
/**
|
|
7528
7883
|
* Convenience method, equivalent to calling {@link find | find()} and passing
|
|
7529
7884
|
* the query `"true"`.
|
|
7885
|
+
*
|
|
7886
|
+
* @throws {Error} when called in a React Native environment.
|
|
7530
7887
|
*/
|
|
7531
7888
|
findAll() {
|
|
7532
7889
|
return this.find('true');
|
|
@@ -7538,6 +7895,7 @@ class WriteTransactionCollection {
|
|
|
7538
7895
|
* The returned object can be used to find and return the document. It can also be used to update, remove or evict the document.
|
|
7539
7896
|
*
|
|
7540
7897
|
* @param id The ID of the document to find.
|
|
7898
|
+
* @throws {Error} when called in a React Native environment.
|
|
7541
7899
|
*/
|
|
7542
7900
|
findByID(id) {
|
|
7543
7901
|
const documentID = id instanceof DocumentID ? id : new DocumentID(id);
|
|
@@ -7594,11 +7952,12 @@ class WriteTransaction {
|
|
|
7594
7952
|
* provided to this function. You can create many
|
|
7595
7953
|
* {@link WriteTransactionCollection | collection} objects per
|
|
7596
7954
|
* {@link WriteTransaction} object.
|
|
7955
|
+
*
|
|
7956
|
+
* @throws {Error} when called in a React Native environment.
|
|
7597
7957
|
* */
|
|
7598
7958
|
scoped(toCollectionNamed) {
|
|
7599
|
-
if (typeof toCollectionNamed !== 'string')
|
|
7959
|
+
if (typeof toCollectionNamed !== 'string')
|
|
7600
7960
|
throw new Error('Collection name must be a string');
|
|
7601
|
-
}
|
|
7602
7961
|
return new WriteTransactionCollection(toCollectionNamed, this.ditto.store, this);
|
|
7603
7962
|
}
|
|
7604
7963
|
/** @internal */
|
|
@@ -7756,7 +8115,6 @@ class Store {
|
|
|
7756
8115
|
return new Promise((resolve) => {
|
|
7757
8116
|
void step(async () => {
|
|
7758
8117
|
try {
|
|
7759
|
-
// prettier-ignore
|
|
7760
8118
|
await mapFFIErrorsAsync(async () => await liveQueryStart(dittoHandle.deref(), storeObserver.liveQueryID));
|
|
7761
8119
|
}
|
|
7762
8120
|
catch (error) {
|
|
@@ -7779,6 +8137,8 @@ class Store {
|
|
|
7779
8137
|
* * it is not empty
|
|
7780
8138
|
* * it does not contain the char '\0'
|
|
7781
8139
|
* * it does not begin with "$TS_"
|
|
8140
|
+
*
|
|
8141
|
+
* @throws {Error} when called in a React Native environment.
|
|
7782
8142
|
*/
|
|
7783
8143
|
collection(name) {
|
|
7784
8144
|
return new Collection(name, this);
|
|
@@ -7789,6 +8149,8 @@ class Store {
|
|
|
7789
8149
|
*
|
|
7790
8150
|
* @return A {@link PendingCollectionsOperation} object that you can use to
|
|
7791
8151
|
* fetch or observe the collections in the store
|
|
8152
|
+
*
|
|
8153
|
+
* @throws {Error} when called in a React Native environment.
|
|
7792
8154
|
*/
|
|
7793
8155
|
collections() {
|
|
7794
8156
|
return new PendingCollectionsOperation(this);
|
|
@@ -7796,6 +8158,8 @@ class Store {
|
|
|
7796
8158
|
/**
|
|
7797
8159
|
* Returns the names of all available collections in the store of the
|
|
7798
8160
|
* related {@link Ditto} instance.
|
|
8161
|
+
*
|
|
8162
|
+
* @throws {Error} when called in a React Native environment.
|
|
7799
8163
|
*/
|
|
7800
8164
|
collectionNames() {
|
|
7801
8165
|
return this.ditto.deferClose((dittoHandle) => {
|
|
@@ -7845,7 +8209,6 @@ class Store {
|
|
|
7845
8209
|
throw new DittoError('query/arguments-invalid', `Unable to encode query arguments: ${error.message}`);
|
|
7846
8210
|
}
|
|
7847
8211
|
}
|
|
7848
|
-
// prettier-ignore
|
|
7849
8212
|
const queryResultPointer = await mapFFIErrorsAsync(async () => await performAsyncToWorkaroundNonAsyncFFIAPI(() => tryExecStatement(dittoHandle.deref(), writeTransaction, query, queryArgumentsCBOR)));
|
|
7850
8213
|
return Bridge.queryResult.bridge(queryResultPointer, () => new QueryResult(queryResultPointer));
|
|
7851
8214
|
});
|
|
@@ -7856,6 +8219,7 @@ class Store {
|
|
|
7856
8219
|
* Allows you to group multiple operations together that affect multiple documents, potentially across multiple collections.
|
|
7857
8220
|
*
|
|
7858
8221
|
* @param callback is given access to a {@link WriteTransaction | write transaction object} that can be used to perform operations on the store.
|
|
8222
|
+
* @throws {Error} when called in a React Native environment.
|
|
7859
8223
|
* @returns a list of `WriteTransactionResult`s. There is a result for each operation performed as part of the write transaction.
|
|
7860
8224
|
*/
|
|
7861
8225
|
async write(callback) {
|
|
@@ -7936,9 +8300,8 @@ class Store {
|
|
|
7936
8300
|
* attachment from a file path in a web browser.
|
|
7937
8301
|
*/
|
|
7938
8302
|
async newAttachment(pathOrData, metadata) {
|
|
7939
|
-
if (metadata != null)
|
|
8303
|
+
if (metadata != null)
|
|
7940
8304
|
validateAttachmentMetadata(metadata);
|
|
7941
|
-
}
|
|
7942
8305
|
return this.ditto.deferCloseAsync(async (dittoHandle) => {
|
|
7943
8306
|
//
|
|
7944
8307
|
// Create inputs for the attachment token from either a file path or raw data.
|
|
@@ -7960,7 +8323,12 @@ class Store {
|
|
|
7960
8323
|
}
|
|
7961
8324
|
throw new Error(`Can't create new attachment, only file path as string or raw data as Uint8Array are supported, but got: ${typeof pathOrData}, ${pathOrData}`);
|
|
7962
8325
|
})();
|
|
7963
|
-
const attachmentTokenJSON = {
|
|
8326
|
+
const attachmentTokenJSON = {
|
|
8327
|
+
_id: id,
|
|
8328
|
+
_len: len,
|
|
8329
|
+
_meta: { ...metadata },
|
|
8330
|
+
[DittoCRDTTypeKey]: DittoCRDTType.attachment,
|
|
8331
|
+
};
|
|
7964
8332
|
const attachmentToken = new AttachmentToken(attachmentTokenJSON);
|
|
7965
8333
|
// this.ditto is safe to use because we are inside a deferCloseAsync block
|
|
7966
8334
|
const attachment = new Attachment(this.ditto, attachmentToken);
|
|
@@ -8026,21 +8394,21 @@ class Store {
|
|
|
8026
8394
|
* the attachment fails for other reasons.
|
|
8027
8395
|
*/
|
|
8028
8396
|
fetchAttachment(token, eventHandler) {
|
|
8029
|
-
if (token == null)
|
|
8397
|
+
if (token == null)
|
|
8030
8398
|
throw new Error("Missing required parameter 'token'");
|
|
8031
|
-
}
|
|
8032
8399
|
let attachmentToken;
|
|
8033
|
-
if (token instanceof AttachmentToken)
|
|
8400
|
+
if (token instanceof AttachmentToken)
|
|
8034
8401
|
attachmentToken = token;
|
|
8035
|
-
|
|
8036
|
-
else {
|
|
8402
|
+
else
|
|
8037
8403
|
attachmentToken = new AttachmentToken(token);
|
|
8038
|
-
}
|
|
8039
8404
|
return this.ditto.deferClose(() => {
|
|
8040
8405
|
const attachmentFetcher = new AttachmentFetcher(this.ditto, attachmentToken, null, eventHandler);
|
|
8041
8406
|
// @ts-expect-error modifying readonly property
|
|
8042
8407
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
8043
|
-
this.attachmentFetchers = Object.freeze([
|
|
8408
|
+
this.attachmentFetchers = Object.freeze([
|
|
8409
|
+
...this.attachmentFetchers,
|
|
8410
|
+
attachmentFetcher,
|
|
8411
|
+
]);
|
|
8044
8412
|
return attachmentFetcher;
|
|
8045
8413
|
});
|
|
8046
8414
|
}
|
|
@@ -8095,7 +8463,6 @@ class Store {
|
|
|
8095
8463
|
throw new DittoError('query/arguments-invalid', `Invalid query arguments: ${error.message}`);
|
|
8096
8464
|
}
|
|
8097
8465
|
}
|
|
8098
|
-
// prettier-ignore
|
|
8099
8466
|
return this.ditto.deferCloseAsync(async (dittoHandle) => {
|
|
8100
8467
|
const webhookIDCBOR = await mapFFIErrorsAsync(async () => await tryRegisterStoreObserverWebhook(dittoHandle.deref(), query, queryArgumentsCBOR, url));
|
|
8101
8468
|
return new DocumentID(webhookIDCBOR, true);
|
|
@@ -8126,16 +8493,14 @@ class Store {
|
|
|
8126
8493
|
}
|
|
8127
8494
|
// Return early if the store observer has already been removed.
|
|
8128
8495
|
const indexToDelete = this.observers.findIndex((observer) => observer === storeObserver);
|
|
8129
|
-
if (indexToDelete === -1)
|
|
8496
|
+
if (indexToDelete === -1)
|
|
8130
8497
|
return false;
|
|
8131
|
-
}
|
|
8132
8498
|
const newObservers = [...this.observers];
|
|
8133
8499
|
newObservers.splice(indexToDelete, 1);
|
|
8134
8500
|
// @ts-expect-error modifying readonly property
|
|
8135
8501
|
this.observers = Object.freeze(newObservers);
|
|
8136
8502
|
const dittoHandle = Bridge.ditto.handleFor(this.ditto);
|
|
8137
8503
|
this.ditto.deferClose(() => {
|
|
8138
|
-
// prettier-ignore
|
|
8139
8504
|
mapFFIErrors(() => liveQueryStop(dittoHandle.deref(), storeObserver.liveQueryID));
|
|
8140
8505
|
});
|
|
8141
8506
|
return true;
|
|
@@ -8162,9 +8527,8 @@ class Store {
|
|
|
8162
8527
|
throw new DittoError('internal', "Internal inconsistency, can't remove attachment fetcher that has not stopped");
|
|
8163
8528
|
}
|
|
8164
8529
|
const indexToDelete = this.attachmentFetchers.findIndex((fetcher) => fetcher === attachmentFetcher);
|
|
8165
|
-
if (indexToDelete === -1)
|
|
8530
|
+
if (indexToDelete === -1)
|
|
8166
8531
|
return false;
|
|
8167
|
-
}
|
|
8168
8532
|
const newAttachmentFetchers = [...this.attachmentFetchers];
|
|
8169
8533
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
8170
8534
|
newAttachmentFetchers.splice(indexToDelete, 1);
|
|
@@ -8175,12 +8539,10 @@ class Store {
|
|
|
8175
8539
|
}
|
|
8176
8540
|
/** @internal */
|
|
8177
8541
|
close() {
|
|
8178
|
-
for (const observer of this.observers)
|
|
8542
|
+
for (const observer of this.observers)
|
|
8179
8543
|
observer.cancel();
|
|
8180
|
-
|
|
8181
|
-
for (const fetcher of this.attachmentFetchers) {
|
|
8544
|
+
for (const fetcher of this.attachmentFetchers)
|
|
8182
8545
|
fetcher.stop();
|
|
8183
|
-
}
|
|
8184
8546
|
// NOTE: live query webhook is taken care of by the FFI's
|
|
8185
8547
|
// `ditto_shutdown()`, no need to unregister it here.
|
|
8186
8548
|
}
|
|
@@ -8372,7 +8734,9 @@ class Presence {
|
|
|
8372
8734
|
*/
|
|
8373
8735
|
observe(didChangeHandler) {
|
|
8374
8736
|
const observerToken = this.observerManager.addObserver(didChangeHandler);
|
|
8375
|
-
const observer = new Observer(this.observerManager, observerToken, {
|
|
8737
|
+
const observer = new Observer(this.observerManager, observerToken, {
|
|
8738
|
+
stopsWhenFinalized: true,
|
|
8739
|
+
});
|
|
8376
8740
|
// REFACTOR: make the initial callback call async, too (othewise we'd be
|
|
8377
8741
|
// mixing sync with async, which is a bit problematic in general but might
|
|
8378
8742
|
// be OK with single-threaded JS). This is a bit tricky, simply
|
|
@@ -8472,9 +8836,8 @@ class LiveQueryManager {
|
|
|
8472
8836
|
for (const liveQueryID in this.liveQueriesByID) {
|
|
8473
8837
|
const weakLiveQuery = this.liveQueriesByID[liveQueryID];
|
|
8474
8838
|
const liveQuery = weakLiveQuery.deref();
|
|
8475
|
-
if (liveQuery)
|
|
8839
|
+
if (liveQuery)
|
|
8476
8840
|
this.stopLiveQuery(liveQuery);
|
|
8477
|
-
}
|
|
8478
8841
|
}
|
|
8479
8842
|
}
|
|
8480
8843
|
stopLiveQueryWithID(liveQueryID) {
|
|
@@ -8746,7 +9109,7 @@ class SyncSubscription {
|
|
|
8746
9109
|
constructor(ditto, query, queryArguments, queryArgumentsCBOR) {
|
|
8747
9110
|
// --------------------------- Private --------------------------------------
|
|
8748
9111
|
/**
|
|
8749
|
-
* `true` when the
|
|
9112
|
+
* `true` when the sync subscription has been cancelled.
|
|
8750
9113
|
*
|
|
8751
9114
|
* We mark the sync subscription as cancelled here as an optimization to avoid
|
|
8752
9115
|
* a scan of all subscriptions in the store whenever the `isCancelled`
|
|
@@ -8758,7 +9121,9 @@ class SyncSubscription {
|
|
|
8758
9121
|
}
|
|
8759
9122
|
this.ditto = ditto;
|
|
8760
9123
|
this.queryString = query;
|
|
8761
|
-
this.queryArguments = queryArguments
|
|
9124
|
+
this.queryArguments = queryArguments
|
|
9125
|
+
? Object.freeze({ ...queryArguments })
|
|
9126
|
+
: undefined;
|
|
8762
9127
|
this.queryArgumentsCBOR = queryArgumentsCBOR;
|
|
8763
9128
|
}
|
|
8764
9129
|
}
|
|
@@ -8823,7 +9188,7 @@ class Sync {
|
|
|
8823
9188
|
/** @internal */
|
|
8824
9189
|
constructor(ditto) {
|
|
8825
9190
|
/**
|
|
8826
|
-
* All currently active sync subscriptions.
|
|
9191
|
+
* All currently active {@link SyncSubscription | sync subscriptions}.
|
|
8827
9192
|
*
|
|
8828
9193
|
* **Note:** Manage sync subscriptions using
|
|
8829
9194
|
* {@link registerSubscription | registerSubscription()} to register a new
|
|
@@ -8839,7 +9204,14 @@ class Sync {
|
|
|
8839
9204
|
this.mdnsServerAdvertiserPointer = null;
|
|
8840
9205
|
const identity = ditto.identity;
|
|
8841
9206
|
const transportConfig = new TransportConfig();
|
|
8842
|
-
const parameters = {
|
|
9207
|
+
const parameters = {
|
|
9208
|
+
identity,
|
|
9209
|
+
transportConfig,
|
|
9210
|
+
ditto,
|
|
9211
|
+
isWebValid: false,
|
|
9212
|
+
isX509Valid: false,
|
|
9213
|
+
isSyncActive: false,
|
|
9214
|
+
};
|
|
8843
9215
|
this.ditto = ditto;
|
|
8844
9216
|
this.state = stateFrom(parameters);
|
|
8845
9217
|
transportsInit();
|
|
@@ -8868,15 +9240,13 @@ class Sync {
|
|
|
8868
9240
|
throw new DittoError('internal', "Internal inconsistency, can't remove replication subscription that has not been cancelled");
|
|
8869
9241
|
}
|
|
8870
9242
|
const indexToDelete = this.subscriptions.findIndex((s) => s === syncSubscription);
|
|
8871
|
-
if (indexToDelete === -1)
|
|
9243
|
+
if (indexToDelete === -1)
|
|
8872
9244
|
return false;
|
|
8873
|
-
}
|
|
8874
9245
|
const newSyncSubscriptions = [...this.subscriptions];
|
|
8875
9246
|
newSyncSubscriptions.splice(indexToDelete, 1);
|
|
8876
9247
|
// @ts-expect-error modifying readonly property
|
|
8877
9248
|
this.subscriptions = Object.freeze(newSyncSubscriptions);
|
|
8878
9249
|
this.ditto.deferClose((dittoHandle) => {
|
|
8879
|
-
// prettier-ignore
|
|
8880
9250
|
mapFFIErrors(() => tryRemoveSyncSubscription(dittoHandle.deref(), syncSubscription.queryString, syncSubscription.queryArgumentsCBOR));
|
|
8881
9251
|
});
|
|
8882
9252
|
return true;
|
|
@@ -8907,9 +9277,8 @@ class Sync {
|
|
|
8907
9277
|
if (this.parameters.isSyncActive) {
|
|
8908
9278
|
throw new Error(`Internal inconsistency, can't close sync object while sync is active, please 'stopSync()' first.`);
|
|
8909
9279
|
}
|
|
8910
|
-
for (const subscription of this.subscriptions)
|
|
9280
|
+
for (const subscription of this.subscriptions)
|
|
8911
9281
|
subscription.cancel();
|
|
8912
|
-
}
|
|
8913
9282
|
// Nothing to do, when sync is stopped, this object should be
|
|
8914
9283
|
// already be cleaned up properly.
|
|
8915
9284
|
}
|
|
@@ -8919,10 +9288,12 @@ class Sync {
|
|
|
8919
9288
|
const bluetoothLENew = stateNew.effectiveTransportConfig.peerToPeer.bluetoothLE;
|
|
8920
9289
|
const shouldStart = !bluetoothLEOld.isEnabled && bluetoothLENew.isEnabled;
|
|
8921
9290
|
const shouldStop = bluetoothLEOld.isEnabled && !bluetoothLENew.isEnabled;
|
|
8922
|
-
if (shouldStart && this.bluetoothLETransportPointer)
|
|
9291
|
+
if (shouldStart && this.bluetoothLETransportPointer) {
|
|
8923
9292
|
throw new Error(`Internal inconsistency, when starting BLE transport, no BLE transport pointer should exist.`);
|
|
8924
|
-
|
|
9293
|
+
}
|
|
9294
|
+
if (shouldStop && !this.bluetoothLETransportPointer) {
|
|
8925
9295
|
throw new Error(`Internal inconsistency, when stopping BLE transport, a BLE transport pointer should exist.`);
|
|
9296
|
+
}
|
|
8926
9297
|
// HACK: quick & dirty Linux & Windows hack. A proper implementation
|
|
8927
9298
|
// should encapsulate everything behind the transports module. To undo,
|
|
8928
9299
|
// remove the whole if block.
|
|
@@ -8947,7 +9318,8 @@ class Sync {
|
|
|
8947
9318
|
}
|
|
8948
9319
|
if (shouldStop) {
|
|
8949
9320
|
// null check is performed above
|
|
8950
|
-
transportsBLEDestroy(this
|
|
9321
|
+
transportsBLEDestroy(this
|
|
9322
|
+
.bluetoothLETransportPointer);
|
|
8951
9323
|
this.bluetoothLETransportPointer = null;
|
|
8952
9324
|
}
|
|
8953
9325
|
});
|
|
@@ -8958,10 +9330,12 @@ class Sync {
|
|
|
8958
9330
|
const awdlNew = stateNew.effectiveTransportConfig.peerToPeer.awdl;
|
|
8959
9331
|
const shouldStart = !awdlOld.isEnabled && awdlNew.isEnabled;
|
|
8960
9332
|
const shouldStop = awdlOld.isEnabled && !awdlNew.isEnabled;
|
|
8961
|
-
if (shouldStart && this.awdlTransportPointer)
|
|
9333
|
+
if (shouldStart && this.awdlTransportPointer) {
|
|
8962
9334
|
throw new Error(`Internal inconsistency, when starting AWDL transport, no AWDL transport pointer should exist.`);
|
|
8963
|
-
|
|
9335
|
+
}
|
|
9336
|
+
if (shouldStop && !this.awdlTransportPointer) {
|
|
8964
9337
|
throw new Error(`Internal inconsistency, when stopping AWDL transport, an AWDL transport pointer should exist.`);
|
|
9338
|
+
}
|
|
8965
9339
|
if (shouldStart) {
|
|
8966
9340
|
this.awdlTransportPointer = transportsAWDLCreate(dittoHandle.deref());
|
|
8967
9341
|
}
|
|
@@ -8982,27 +9356,28 @@ class Sync {
|
|
|
8982
9356
|
if (process.platform === 'win32' || process.platform === 'linux') {
|
|
8983
9357
|
if (lanOld.isEnabled) {
|
|
8984
9358
|
if (lanOld.isMdnsEnabled) {
|
|
8985
|
-
if (this.mdnsClientTransportPointer == null)
|
|
9359
|
+
if (this.mdnsClientTransportPointer == null) {
|
|
8986
9360
|
throw new Error(`Internal inconsistency, when stopping LAN transport, a LAN transport pointer should exist.`);
|
|
9361
|
+
}
|
|
8987
9362
|
mdnsClientFreeHandle(this.mdnsClientTransportPointer);
|
|
8988
9363
|
this.mdnsClientTransportPointer = null;
|
|
8989
|
-
if (this.mdnsServerAdvertiserPointer == null)
|
|
9364
|
+
if (this.mdnsServerAdvertiserPointer == null) {
|
|
8990
9365
|
throw new Error(`Internal inconsistency, when stopping LAN transport, a LAN transport pointer should exist.`);
|
|
9366
|
+
}
|
|
8991
9367
|
mdnsServerFreeHandle(this.mdnsServerAdvertiserPointer);
|
|
8992
9368
|
this.mdnsServerAdvertiserPointer = null;
|
|
8993
9369
|
}
|
|
8994
|
-
if (lanOld.isMulticastEnabled)
|
|
9370
|
+
if (lanOld.isMulticastEnabled)
|
|
8995
9371
|
dittoRemoveMulticastTransport(dittoHandle.deref());
|
|
8996
|
-
}
|
|
8997
9372
|
}
|
|
8998
9373
|
if (lanNew.isEnabled) {
|
|
8999
9374
|
if (lanNew.isMdnsEnabled) {
|
|
9000
9375
|
this.mdnsClientTransportPointer = dittoAddInternalMdnsTransport(dittoHandle.deref());
|
|
9001
|
-
this.mdnsServerAdvertiserPointer =
|
|
9376
|
+
this.mdnsServerAdvertiserPointer =
|
|
9377
|
+
dittoAddInternalMdnsAdvertiser(dittoHandle.deref());
|
|
9002
9378
|
}
|
|
9003
|
-
if (lanNew.isMulticastEnabled)
|
|
9379
|
+
if (lanNew.isMulticastEnabled)
|
|
9004
9380
|
dittoAddMulticastTransport(dittoHandle.deref());
|
|
9005
|
-
}
|
|
9006
9381
|
}
|
|
9007
9382
|
return;
|
|
9008
9383
|
}
|
|
@@ -9010,24 +9385,24 @@ class Sync {
|
|
|
9010
9385
|
// via some additional state signaling the actions here.
|
|
9011
9386
|
if (lanOld.isEnabled) {
|
|
9012
9387
|
if (lanOld.isMdnsEnabled) {
|
|
9013
|
-
if (this.lanTransportPointer == null)
|
|
9388
|
+
if (this.lanTransportPointer == null) {
|
|
9014
9389
|
throw new Error(`Internal inconsistency, when stopping LAN transport, a LAN transport pointer should exist.`);
|
|
9390
|
+
}
|
|
9015
9391
|
transportsLANDestroy(this.lanTransportPointer);
|
|
9016
9392
|
this.lanTransportPointer = null;
|
|
9017
9393
|
}
|
|
9018
|
-
if (lanOld.isMulticastEnabled)
|
|
9394
|
+
if (lanOld.isMulticastEnabled)
|
|
9019
9395
|
dittoRemoveMulticastTransport(dittoHandle.deref());
|
|
9020
|
-
}
|
|
9021
9396
|
}
|
|
9022
9397
|
if (lanNew.isEnabled) {
|
|
9023
9398
|
if (lanNew.isMdnsEnabled) {
|
|
9024
|
-
if (this.lanTransportPointer != null)
|
|
9399
|
+
if (this.lanTransportPointer != null) {
|
|
9025
9400
|
throw new Error(`Internal inconsistency, when starting LAN transport, no LAN transport pointer should exist.`);
|
|
9401
|
+
}
|
|
9026
9402
|
this.lanTransportPointer = transportsLANCreate(dittoHandle.deref());
|
|
9027
9403
|
}
|
|
9028
|
-
if (lanNew.isMulticastEnabled)
|
|
9404
|
+
if (lanNew.isMulticastEnabled)
|
|
9029
9405
|
dittoAddMulticastTransport(dittoHandle.deref());
|
|
9030
|
-
}
|
|
9031
9406
|
}
|
|
9032
9407
|
});
|
|
9033
9408
|
}
|
|
@@ -9039,8 +9414,9 @@ class Sync {
|
|
|
9039
9414
|
return;
|
|
9040
9415
|
if (tcpOld.isEnabled)
|
|
9041
9416
|
dittoStopTCPServer(dittoHandle.deref());
|
|
9042
|
-
if (tcpNew.isEnabled)
|
|
9417
|
+
if (tcpNew.isEnabled) {
|
|
9043
9418
|
dittoStartTCPServer(dittoHandle.deref(), `${tcpNew.interfaceIP}:${tcpNew.port}`);
|
|
9419
|
+
}
|
|
9044
9420
|
});
|
|
9045
9421
|
}
|
|
9046
9422
|
updateListenHTTP(stateOld, stateNew) {
|
|
@@ -9073,7 +9449,8 @@ class Sync {
|
|
|
9073
9449
|
}
|
|
9074
9450
|
updateGlobal(stateOld, stateNew) {
|
|
9075
9451
|
this.ditto.deferClose((dittoHandle) => {
|
|
9076
|
-
if (stateOld.effectiveTransportConfig.global.syncGroup !==
|
|
9452
|
+
if (stateOld.effectiveTransportConfig.global.syncGroup !==
|
|
9453
|
+
stateNew.effectiveTransportConfig.global.syncGroup) {
|
|
9077
9454
|
dittoSetSyncGroup(dittoHandle.deref(), stateNew.effectiveTransportConfig.global.syncGroup);
|
|
9078
9455
|
}
|
|
9079
9456
|
});
|
|
@@ -9110,7 +9487,8 @@ function stateFrom(parameters) {
|
|
|
9110
9487
|
let appID;
|
|
9111
9488
|
let customDittoCloudURL;
|
|
9112
9489
|
let isDittoCloudSyncEnabled = false;
|
|
9113
|
-
if (identity.type === 'onlinePlayground' ||
|
|
9490
|
+
if (identity.type === 'onlinePlayground' ||
|
|
9491
|
+
identity.type === 'onlineWithAuthentication') {
|
|
9114
9492
|
// NOTE: enableDittoCloudSync is true by default for any online identity.
|
|
9115
9493
|
appID = identity.appID;
|
|
9116
9494
|
customDittoCloudURL = (_a = identity.customDittoCloudURL) !== null && _a !== void 0 ? _a : null;
|
|
@@ -9125,12 +9503,10 @@ function stateFrom(parameters) {
|
|
|
9125
9503
|
transportConfig.listen.tcp.isEnabled = false;
|
|
9126
9504
|
transportConfig.connect.tcpServers = [];
|
|
9127
9505
|
}
|
|
9128
|
-
if (!isSyncActive || !isWebValid)
|
|
9506
|
+
if (!isSyncActive || !isWebValid)
|
|
9129
9507
|
transportConfig.connect.websocketURLs = [];
|
|
9130
|
-
|
|
9131
|
-
if (!isSyncActive) {
|
|
9508
|
+
if (!isSyncActive)
|
|
9132
9509
|
transportConfig.listen.http.isEnabled = false;
|
|
9133
|
-
}
|
|
9134
9510
|
// NOTE: we have to smuggle in an additional Ditto Cloud websocket URL to
|
|
9135
9511
|
// connect to if cloud sync is enabled.
|
|
9136
9512
|
if (isSyncActive && isWebValid && isDittoCloudSyncEnabled) {
|
|
@@ -9160,21 +9536,19 @@ function validateEnabledTransportsAvailable(ditto, transportConfig) {
|
|
|
9160
9536
|
const isBLEAvailable = transportsBLEIsAvailable(dittoHandle.deref());
|
|
9161
9537
|
const isAWDLAvailable = transportsAWDLIsAvailable(dittoHandle.deref());
|
|
9162
9538
|
const isLANAvailable = transportsLANIsAvailable(dittoHandle.deref());
|
|
9163
|
-
if (transportConfig.peerToPeer.bluetoothLE.isEnabled && !isBLEAvailable)
|
|
9539
|
+
if (transportConfig.peerToPeer.bluetoothLE.isEnabled && !isBLEAvailable)
|
|
9164
9540
|
unavailableButEnabledP2PTransports.push('BluetoothLE');
|
|
9165
|
-
|
|
9166
|
-
if (transportConfig.peerToPeer.awdl.isEnabled && !isAWDLAvailable) {
|
|
9541
|
+
if (transportConfig.peerToPeer.awdl.isEnabled && !isAWDLAvailable)
|
|
9167
9542
|
unavailableButEnabledP2PTransports.push('AWDL');
|
|
9168
|
-
|
|
9169
|
-
if (transportConfig.peerToPeer.lan.isEnabled && !isLANAvailable) {
|
|
9543
|
+
if (transportConfig.peerToPeer.lan.isEnabled && !isLANAvailable)
|
|
9170
9544
|
unavailableButEnabledP2PTransports.push('LAN');
|
|
9171
|
-
}
|
|
9172
9545
|
if (unavailableButEnabledP2PTransports.length > 0) {
|
|
9173
9546
|
throw new Error(`The following P2P transports are enabled in the transport config but are not supported in the current environment: ${unavailableButEnabledP2PTransports.join(', ')}`);
|
|
9174
9547
|
}
|
|
9175
9548
|
// - Websocket / HTTP
|
|
9176
9549
|
// - TCP
|
|
9177
|
-
if (transportConfig.connect.tcpServers.length > 0 &&
|
|
9550
|
+
if (transportConfig.connect.tcpServers.length > 0 &&
|
|
9551
|
+
isWebBuild) {
|
|
9178
9552
|
throw new Error(`The transport config contains TCP servers, but this transport is not supported in web environments`);
|
|
9179
9553
|
}
|
|
9180
9554
|
if (transportConfig.listen.http.isEnabled && isWebBuild) {
|
|
@@ -9397,9 +9771,8 @@ class SmallPeerInfo {
|
|
|
9397
9771
|
* @throws when set to a non-boolean value.
|
|
9398
9772
|
*/
|
|
9399
9773
|
set isEnabled(newValue) {
|
|
9400
|
-
if (typeof newValue !== 'boolean')
|
|
9774
|
+
if (typeof newValue !== 'boolean')
|
|
9401
9775
|
throw new TypeError(`Expected boolean, got ${typeof newValue}`);
|
|
9402
|
-
}
|
|
9403
9776
|
void this.ditto.deferCloseAsync(async (dittoHandle) => {
|
|
9404
9777
|
return dittoSmallPeerInfoSetEnabled(dittoHandle.deref(), newValue);
|
|
9405
9778
|
});
|
|
@@ -9455,9 +9828,8 @@ class SmallPeerInfo {
|
|
|
9455
9828
|
* {@link SmallPeerInfo.metadata | `metadata`}.
|
|
9456
9829
|
*/
|
|
9457
9830
|
set metadataJSONString(metadata) {
|
|
9458
|
-
if (typeof metadata !== 'string')
|
|
9831
|
+
if (typeof metadata !== 'string')
|
|
9459
9832
|
throw new TypeError(`Expected string, got ${typeof metadata}`);
|
|
9460
|
-
}
|
|
9461
9833
|
this.ditto.deferClose((dittoHandle) => {
|
|
9462
9834
|
// throws if any validation errors occur
|
|
9463
9835
|
dittoSmallPeerInfoSetMetadata(dittoHandle.deref(), metadata);
|
|
@@ -9560,7 +9932,7 @@ class Ditto {
|
|
|
9560
9932
|
return (_a = this._isActivated) !== null && _a !== void 0 ? _a : false;
|
|
9561
9933
|
}
|
|
9562
9934
|
/**
|
|
9563
|
-
* `true` once {@link close | Ditto.close()} has been called, otherwise
|
|
9935
|
+
* `true` once {@link Ditto.close | Ditto.close()} has been called, otherwise
|
|
9564
9936
|
* `false`.
|
|
9565
9937
|
*/
|
|
9566
9938
|
get isClosed() {
|
|
@@ -9609,8 +9981,12 @@ class Ditto {
|
|
|
9609
9981
|
throw new Error('Ditto does not support this JavaScript environment. Please consult the Ditto JavaScript documentation for a list of supported environments and browsers. You can use `Ditto.isEnvironmentSupported()` to run this check anytime.');
|
|
9610
9982
|
}
|
|
9611
9983
|
loggerInit();
|
|
9612
|
-
this.persistenceDirectory =
|
|
9613
|
-
|
|
9984
|
+
this.persistenceDirectory =
|
|
9985
|
+
Ditto.initPersistenceDirectory(persistenceDirectory);
|
|
9986
|
+
const identityOrDefault = identity !== null && identity !== void 0 ? identity : {
|
|
9987
|
+
type: 'offlinePlayground',
|
|
9988
|
+
appID: '',
|
|
9989
|
+
};
|
|
9614
9990
|
const validIdentity = Object.freeze(this.validateIdentity(identityOrDefault));
|
|
9615
9991
|
this.identity = Object.freeze(validIdentity);
|
|
9616
9992
|
// Check if device name stays the same on sdk and core levels (#10729).
|
|
@@ -9653,12 +10029,10 @@ class Ditto {
|
|
|
9653
10029
|
const dittoPointer = dittoMake(this.persistenceDirectory, identityConfig, historyTracking);
|
|
9654
10030
|
dittoAuthClientSetValidityListener(dittoPointer, function (...args) {
|
|
9655
10031
|
const ditto = weakThis.deref();
|
|
9656
|
-
if (ditto == null || ditto.isClosed)
|
|
10032
|
+
if (ditto == null || ditto.isClosed)
|
|
9657
10033
|
Logger.info('Ditto is closed, ignoring auth client validity change');
|
|
9658
|
-
|
|
9659
|
-
else {
|
|
10034
|
+
else
|
|
9660
10035
|
ditto.authClientValidityChanged(...args);
|
|
9661
|
-
}
|
|
9662
10036
|
});
|
|
9663
10037
|
const isWebValid = dittoAuthClientIsWebValid(dittoPointer);
|
|
9664
10038
|
const isX509Valid = dittoAuthClientIsX509Valid(dittoPointer);
|
|
@@ -9715,10 +10089,18 @@ class Ditto {
|
|
|
9715
10089
|
this.isX509Valid = isX509Valid;
|
|
9716
10090
|
this.isWebValid = isWebValid;
|
|
9717
10091
|
this.sync = new Sync(this);
|
|
9718
|
-
this.sync.update({
|
|
10092
|
+
this.sync.update({
|
|
10093
|
+
isSyncActive: false,
|
|
10094
|
+
isX509Valid,
|
|
10095
|
+
isWebValid,
|
|
10096
|
+
identity: validIdentity,
|
|
10097
|
+
ditto: this,
|
|
10098
|
+
transportConfig,
|
|
10099
|
+
});
|
|
9719
10100
|
// We don't need a license when running in the browser, so we
|
|
9720
10101
|
// mark the instance as activated from the get-go in that case.
|
|
9721
|
-
this._isActivated =
|
|
10102
|
+
this._isActivated =
|
|
10103
|
+
!IdentityTypesRequiringOfflineLicenseToken.includes(validIdentity.type);
|
|
9722
10104
|
this.store = new Store(this);
|
|
9723
10105
|
this.smallPeerInfo = new SmallPeerInfo(this);
|
|
9724
10106
|
this.presence = new Presence(this);
|
|
@@ -9748,7 +10130,7 @@ class Ditto {
|
|
|
9748
10130
|
* disables this behavior, thus allowing the use of an interactive debugger
|
|
9749
10131
|
* without triggering the deadlock detection.
|
|
9750
10132
|
*
|
|
9751
|
-
* This feature is
|
|
10133
|
+
* This feature is only available in Node.js environments.
|
|
9752
10134
|
*/
|
|
9753
10135
|
static disableDeadlockDetection() {
|
|
9754
10136
|
{
|
|
@@ -9770,15 +10152,13 @@ class Ditto {
|
|
|
9770
10152
|
* {@link Ditto.disableDeadlockDetection | Ditto.disableDeadlockDetection()}
|
|
9771
10153
|
* for more information.
|
|
9772
10154
|
*
|
|
9773
|
-
*
|
|
9774
|
-
* is
|
|
10155
|
+
* When called outside of a Node.js environment, this method always returns
|
|
10156
|
+
* `false` as deadlock detection is only available in Node.js.
|
|
9775
10157
|
*
|
|
9776
10158
|
* @returns `true` if deadlock detection is enabled
|
|
9777
10159
|
*/
|
|
9778
10160
|
static hasDeadlockDetection() {
|
|
9779
|
-
|
|
9780
|
-
return getDeadlockTimeout() !== 0;
|
|
9781
|
-
}
|
|
10161
|
+
return getDeadlockTimeout() !== 0;
|
|
9782
10162
|
}
|
|
9783
10163
|
/**
|
|
9784
10164
|
* Check if the current environment supports running Ditto.
|
|
@@ -9796,8 +10176,13 @@ class Ditto {
|
|
|
9796
10176
|
static isEnvironmentSupported() {
|
|
9797
10177
|
// From https://stackoverflow.com/questions/21825157/internet-explorer-11-detection
|
|
9798
10178
|
let isIE = false;
|
|
9799
|
-
if (typeof window !== 'undefined' &&
|
|
9800
|
-
|
|
10179
|
+
if (typeof window !== 'undefined' &&
|
|
10180
|
+
window.navigator &&
|
|
10181
|
+
window.navigator.userAgent &&
|
|
10182
|
+
window.navigator.appVersion) {
|
|
10183
|
+
isIE =
|
|
10184
|
+
window.navigator.userAgent.indexOf('MSIE') !== -1 ||
|
|
10185
|
+
window.navigator.appVersion.indexOf('Trident/') > -1;
|
|
9801
10186
|
}
|
|
9802
10187
|
let hasRequiredAPIs;
|
|
9803
10188
|
try {
|
|
@@ -9821,15 +10206,12 @@ class Ditto {
|
|
|
9821
10206
|
*/
|
|
9822
10207
|
static initPersistenceDirectory(path) {
|
|
9823
10208
|
let validatedPath;
|
|
9824
|
-
if (path == null)
|
|
10209
|
+
if (path == null)
|
|
9825
10210
|
validatedPath = DEFAULT_PERSISTENCE_DIRECTORY;
|
|
9826
|
-
|
|
9827
|
-
else if (path.trim().length === 0) {
|
|
10211
|
+
else if (path.trim().length === 0)
|
|
9828
10212
|
throw new Error(`Invalid argument for path parameter: '${path}'`);
|
|
9829
|
-
|
|
9830
|
-
else {
|
|
10213
|
+
else
|
|
9831
10214
|
validatedPath = path;
|
|
9832
|
-
}
|
|
9833
10215
|
{
|
|
9834
10216
|
const fs = require('fs');
|
|
9835
10217
|
const path = require('path');
|
|
@@ -9919,7 +10301,14 @@ class Ditto {
|
|
|
9919
10301
|
const identity = this.identity;
|
|
9920
10302
|
const isWebValid = this.isWebValid;
|
|
9921
10303
|
const isX509Valid = this.isX509Valid;
|
|
9922
|
-
this.sync.update({
|
|
10304
|
+
this.sync.update({
|
|
10305
|
+
transportConfig: transportConfigNew,
|
|
10306
|
+
identity,
|
|
10307
|
+
isWebValid,
|
|
10308
|
+
isX509Valid,
|
|
10309
|
+
isSyncActive: this.isSyncActive,
|
|
10310
|
+
ditto: this,
|
|
10311
|
+
});
|
|
9923
10312
|
const configSerializeReady = transportConfigToSerializable(transportConfigNew);
|
|
9924
10313
|
const configCBOR = CBOR.encode(configSerializeReady);
|
|
9925
10314
|
this.deferClose((dittoHandle) => {
|
|
@@ -9942,9 +10331,9 @@ class Ditto {
|
|
|
9942
10331
|
*
|
|
9943
10332
|
* By default Ditto will enable all peer-to-peer transport types. On **Node**,
|
|
9944
10333
|
* this means BluetoothLE, WiFi/LAN, and AWDL. On the **Web**, only connecting
|
|
9945
|
-
* via Websockets is supported. The network configuration can be
|
|
9946
|
-
*
|
|
9947
|
-
* or replaced
|
|
10334
|
+
* via Websockets is supported. The default network configuration can be
|
|
10335
|
+
* modified with {@link updateTransportConfig | updateTransportConfig()}
|
|
10336
|
+
* or replaced with {@link setTransportConfig | setTransportConfig()}.
|
|
9948
10337
|
*
|
|
9949
10338
|
* Performance of initial sync when bootstrapping new peers can be improved by
|
|
9950
10339
|
* calling {@link disableSyncWithV3 | disableSyncWithV3()} before
|
|
@@ -9968,7 +10357,7 @@ class Ditto {
|
|
|
9968
10357
|
/**
|
|
9969
10358
|
* Stops all network transports.
|
|
9970
10359
|
*
|
|
9971
|
-
* You may continue to use the
|
|
10360
|
+
* You may continue to use the Ditto store locally but no data will sync to or
|
|
9972
10361
|
* from other devices.
|
|
9973
10362
|
*
|
|
9974
10363
|
* @see {@link isSyncActive}
|
|
@@ -9993,7 +10382,9 @@ class Ditto {
|
|
|
9993
10382
|
observePeers(callback) {
|
|
9994
10383
|
Logger.warning('`ditto.observePeers()` is deprecated, please use `ditto.presence.observe()` instead.');
|
|
9995
10384
|
const token = this.presenceManager.addObserver(callback);
|
|
9996
|
-
return new Observer(this.presenceManager, token, {
|
|
10385
|
+
return new Observer(this.presenceManager, token, {
|
|
10386
|
+
stopsWhenFinalized: true,
|
|
10387
|
+
});
|
|
9997
10388
|
}
|
|
9998
10389
|
/**
|
|
9999
10390
|
* Register observer for changes of underlying transport conditions.
|
|
@@ -10006,7 +10397,9 @@ class Ditto {
|
|
|
10006
10397
|
*/
|
|
10007
10398
|
observeTransportConditions(callback) {
|
|
10008
10399
|
const token = this.transportConditionsManager.addObserver(callback);
|
|
10009
|
-
return new Observer(this.transportConditionsManager, token, {
|
|
10400
|
+
return new Observer(this.transportConditionsManager, token, {
|
|
10401
|
+
stopsWhenFinalized: true,
|
|
10402
|
+
});
|
|
10010
10403
|
}
|
|
10011
10404
|
/**
|
|
10012
10405
|
* Removes all sync metadata for remote peers that aren't currently connected.
|
|
@@ -10042,7 +10435,8 @@ class Ditto {
|
|
|
10042
10435
|
* This improves performance of initial sync when this peer has never before
|
|
10043
10436
|
* connected to a Ditto mesh for which sync with v3 peers is disabled.
|
|
10044
10437
|
*
|
|
10045
|
-
* @throws {Error}
|
|
10438
|
+
* @throws {Error} when called in a React Native environment where sync with v3
|
|
10439
|
+
* is always disabled.
|
|
10046
10440
|
*/
|
|
10047
10441
|
async disableSyncWithV3() {
|
|
10048
10442
|
return this.deferCloseAsync(async (dittoHandle) => {
|
|
@@ -10056,9 +10450,8 @@ class Ditto {
|
|
|
10056
10450
|
* persistence directory.
|
|
10057
10451
|
*/
|
|
10058
10452
|
async close() {
|
|
10059
|
-
if (this.isClosed)
|
|
10453
|
+
if (this.isClosed)
|
|
10060
10454
|
return;
|
|
10061
|
-
}
|
|
10062
10455
|
this._isClosed = true;
|
|
10063
10456
|
this.stopSync();
|
|
10064
10457
|
this.store.close();
|
|
@@ -10075,14 +10468,15 @@ class Ditto {
|
|
|
10075
10468
|
}
|
|
10076
10469
|
// Await all pending operations before closing. Rejected promises are
|
|
10077
10470
|
// ignored because they are handled at the original call site.
|
|
10078
|
-
do
|
|
10471
|
+
do
|
|
10079
10472
|
await Promise.allSettled(this.pendingOperations);
|
|
10080
|
-
|
|
10081
|
-
|
|
10082
|
-
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
|
|
10473
|
+
while (
|
|
10474
|
+
// REFACTOR: in theory, we could end up in an endless loop here if for
|
|
10475
|
+
// some reason a resolved or rejected promise isn't removed from
|
|
10476
|
+
// `pendingOperations`. AFAICS, this is not possible atm due to the
|
|
10477
|
+
// way `deferClose` and `deferCloseAsync` is implemented. Would be
|
|
10478
|
+
// great to rework this and make it more solid if possible.
|
|
10479
|
+
this.pendingOperations.size > 0);
|
|
10086
10480
|
this.deferCloseAllowed = false;
|
|
10087
10481
|
await Bridge.ditto.close(this);
|
|
10088
10482
|
}
|
|
@@ -10155,26 +10549,45 @@ class Ditto {
|
|
|
10155
10549
|
this.isX509Valid = isX509Valid;
|
|
10156
10550
|
this.isWebValid = isWebValid;
|
|
10157
10551
|
this.auth['@ditto.authClientValidityChanged'](isWebValid, isX509Valid);
|
|
10158
|
-
this.sync.update({
|
|
10552
|
+
this.sync.update({
|
|
10553
|
+
transportConfig,
|
|
10554
|
+
identity,
|
|
10555
|
+
isWebValid,
|
|
10556
|
+
isX509Valid,
|
|
10557
|
+
isSyncActive,
|
|
10558
|
+
ditto: this,
|
|
10559
|
+
});
|
|
10159
10560
|
}
|
|
10160
10561
|
validateIdentity(identity) {
|
|
10161
10562
|
const validIdentity = { ...identity };
|
|
10162
10563
|
// @ts-expect-error we validate the existence of the value below.
|
|
10163
10564
|
const appID = identity.appID;
|
|
10164
|
-
if (![
|
|
10565
|
+
if (![
|
|
10566
|
+
'offlinePlayground',
|
|
10567
|
+
'sharedKey',
|
|
10568
|
+
'manual',
|
|
10569
|
+
'onlinePlayground',
|
|
10570
|
+
'onlineWithAuthentication',
|
|
10571
|
+
].includes(identity.type)) {
|
|
10165
10572
|
throw new Error(`Can't create Ditto instance, unknown identity type: ${identity.type}`);
|
|
10166
10573
|
}
|
|
10167
|
-
if ((identity.type === 'offlinePlayground' ||
|
|
10574
|
+
if ((identity.type === 'offlinePlayground' ||
|
|
10575
|
+
identity.type === 'sharedKey' ||
|
|
10576
|
+
identity.type === 'onlinePlayground' ||
|
|
10577
|
+
identity.type === 'onlineWithAuthentication') &&
|
|
10578
|
+
typeof appID === 'undefined')
|
|
10168
10579
|
throw new Error(`Property .appID must be given for identity, but isn't.`);
|
|
10169
|
-
}
|
|
10170
10580
|
if (typeof appID !== 'undefined' && typeof appID !== 'string') {
|
|
10171
10581
|
throw new Error(`Property .appID must be be of type string, but is of type '${typeof appID}': ${appID}`);
|
|
10172
10582
|
}
|
|
10173
|
-
if ((identity.type === 'offlinePlayground' ||
|
|
10583
|
+
if ((identity.type === 'offlinePlayground' ||
|
|
10584
|
+
identity.type === 'sharedKey') &&
|
|
10585
|
+
typeof identity.siteID !== 'undefined') {
|
|
10174
10586
|
const siteID = identity.siteID;
|
|
10175
10587
|
const isSiteIDNumberOrBigInt = typeof siteID === 'number' || typeof siteID === 'bigint';
|
|
10176
|
-
if (!isSiteIDNumberOrBigInt)
|
|
10588
|
+
if (!isSiteIDNumberOrBigInt) {
|
|
10177
10589
|
throw new Error("Can't create Ditto instance, siteID must be a number or BigInt");
|
|
10590
|
+
}
|
|
10178
10591
|
if (siteID < 0)
|
|
10179
10592
|
throw new Error("Can't create Ditto instance, siteID must be >= 0");
|
|
10180
10593
|
if (siteID > BigInt('0xffffffffffffffff'))
|
|
@@ -10196,36 +10609,40 @@ class Ditto {
|
|
|
10196
10609
|
}
|
|
10197
10610
|
setSyncActive(flag) {
|
|
10198
10611
|
this.deferClose((dittoHandle) => {
|
|
10199
|
-
if (flag &&
|
|
10612
|
+
if (flag &&
|
|
10613
|
+
IdentityTypesRequiringOfflineLicenseToken.includes(this.identity.type) &&
|
|
10614
|
+
!this.isActivated) {
|
|
10200
10615
|
throw new Error('Sync could not be started because Ditto has not yet been activated. This can be achieved with a successful call to `setOfflineOnlyLicenseToken`. If you need to obtain a license token then please visit https://portal.ditto.live.');
|
|
10201
10616
|
}
|
|
10202
|
-
if (!this.isSyncActive && flag)
|
|
10617
|
+
if (!this.isSyncActive && flag)
|
|
10203
10618
|
this.keepAlive.retain('sync');
|
|
10204
|
-
|
|
10205
|
-
if (this.isSyncActive && !flag) {
|
|
10619
|
+
if (this.isSyncActive && !flag)
|
|
10206
10620
|
this.keepAlive.release('sync');
|
|
10207
|
-
}
|
|
10208
10621
|
this._isSyncActive = flag;
|
|
10209
10622
|
const isWebValid = this.isWebValid;
|
|
10210
10623
|
const isX509Valid = this.isX509Valid;
|
|
10211
10624
|
const identity = this.identity;
|
|
10212
10625
|
const transportConfig = this.transportConfig;
|
|
10213
10626
|
this._deviceName = dittoSetDeviceName(dittoHandle.deref(), this.deviceName);
|
|
10214
|
-
this.sync.update({
|
|
10627
|
+
this.sync.update({
|
|
10628
|
+
identity,
|
|
10629
|
+
transportConfig,
|
|
10630
|
+
isWebValid,
|
|
10631
|
+
isX509Valid,
|
|
10632
|
+
isSyncActive: !!flag,
|
|
10633
|
+
ditto: this,
|
|
10634
|
+
});
|
|
10215
10635
|
});
|
|
10216
10636
|
}
|
|
10217
10637
|
makeDefaultTransportConfig() {
|
|
10218
10638
|
return this.deferClose((dittoHandle) => {
|
|
10219
10639
|
const transportConfig = new TransportConfig();
|
|
10220
|
-
if (transportsBLEIsAvailable(dittoHandle.deref()))
|
|
10640
|
+
if (transportsBLEIsAvailable(dittoHandle.deref()))
|
|
10221
10641
|
transportConfig.peerToPeer.bluetoothLE.isEnabled = true;
|
|
10222
|
-
|
|
10223
|
-
if (transportsAWDLIsAvailable(dittoHandle.deref())) {
|
|
10642
|
+
if (transportsAWDLIsAvailable(dittoHandle.deref()))
|
|
10224
10643
|
transportConfig.peerToPeer.awdl.isEnabled = true;
|
|
10225
|
-
|
|
10226
|
-
if (transportsLANIsAvailable(dittoHandle.deref())) {
|
|
10644
|
+
if (transportsLANIsAvailable(dittoHandle.deref()))
|
|
10227
10645
|
transportConfig.peerToPeer.lan.isEnabled = true;
|
|
10228
|
-
}
|
|
10229
10646
|
return transportConfig.freeze();
|
|
10230
10647
|
});
|
|
10231
10648
|
}
|
|
@@ -10239,7 +10656,11 @@ class Ditto {
|
|
|
10239
10656
|
* @internal
|
|
10240
10657
|
*/
|
|
10241
10658
|
const checkAPIs = (_globalObject) => {
|
|
10242
|
-
const requiredBrowserAPIs = [
|
|
10659
|
+
const requiredBrowserAPIs = [
|
|
10660
|
+
'BigInt',
|
|
10661
|
+
'WeakRef',
|
|
10662
|
+
'FinalizationRegistry',
|
|
10663
|
+
];
|
|
10243
10664
|
const globalObject = _globalObject || globalThis || global || window;
|
|
10244
10665
|
return requiredBrowserAPIs.every((apiName) => !!globalObject[apiName]);
|
|
10245
10666
|
};
|
|
@@ -10254,9 +10675,11 @@ const checkAPIs = (_globalObject) => {
|
|
|
10254
10675
|
const disableDeadlockTimeoutWhenDebugging = () => {
|
|
10255
10676
|
var _a, _b;
|
|
10256
10677
|
{
|
|
10257
|
-
const hasInspector = process &&
|
|
10258
|
-
|
|
10259
|
-
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')))) ||
|
|
10678
|
+
const hasInspector = process &&
|
|
10679
|
+
((_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')));
|
|
10680
|
+
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')))) ||
|
|
10681
|
+
// @ts-ignore - v8debug may be undefined
|
|
10682
|
+
typeof v8debug === 'object';
|
|
10260
10683
|
if (hasInspector || hasLegacyDebugMode) {
|
|
10261
10684
|
try {
|
|
10262
10685
|
setDeadlockTimeout(0);
|
|
@@ -10353,9 +10776,8 @@ class QueryResultItem {
|
|
|
10353
10776
|
*/
|
|
10354
10777
|
get value() {
|
|
10355
10778
|
this.materialize();
|
|
10356
|
-
if (this.materializedValue === undefined)
|
|
10779
|
+
if (this.materializedValue === undefined)
|
|
10357
10780
|
throw new Error('Internal Error: Materialized value is undefined');
|
|
10358
|
-
}
|
|
10359
10781
|
return this.materializedValue;
|
|
10360
10782
|
}
|
|
10361
10783
|
/**
|
|
@@ -10444,12 +10866,10 @@ function getBridgeLoad() {
|
|
|
10444
10866
|
const countsByType = {};
|
|
10445
10867
|
Bridge.all.map((bridgeWeakRef) => {
|
|
10446
10868
|
const bridge = bridgeWeakRef.deref();
|
|
10447
|
-
if (!bridge)
|
|
10869
|
+
if (!bridge)
|
|
10448
10870
|
return null;
|
|
10449
|
-
|
|
10450
|
-
if (bridge.count === 0) {
|
|
10871
|
+
if (bridge.count === 0)
|
|
10451
10872
|
return null;
|
|
10452
|
-
}
|
|
10453
10873
|
countsByType[bridge.type.name] = bridge.count;
|
|
10454
10874
|
});
|
|
10455
10875
|
return countsByType;
|