@dittolive/ditto 4.4.5 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DittoReactNative.podspec +27 -0
- package/README.md +68 -51
- package/node/ditto.cjs.js +1639 -851
- package/node/ditto.darwin-arm64.node +0 -0
- package/node/ditto.darwin-x64.node +0 -0
- package/node/ditto.linux-arm.node +0 -0
- package/node/ditto.linux-x64.node +0 -0
- package/node/ditto.win32-x64.node +0 -0
- package/node/transports.darwin-arm64.node +0 -0
- package/node/transports.darwin-x64.node +0 -0
- package/package.json +34 -38
- package/react-native/android/CMakeLists.txt +37 -0
- package/react-native/android/build.gradle +203 -0
- package/react-native/android/cpp-adapter.cpp +254 -0
- package/react-native/android/gradle.properties +5 -0
- package/react-native/android/src/main/AndroidManifest.xml +4 -0
- package/react-native/android/src/main/java/com/dittolive/rnsdk/DittoRNSDKModule.java +85 -0
- package/react-native/android/src/main/java/com/dittolive/rnsdk/DittoRNSDKPackage.java +28 -0
- package/react-native/boost/boost/assert.hpp +85 -0
- package/react-native/boost/boost/config/abi/borland_prefix.hpp +27 -0
- package/react-native/boost/boost/config/abi/borland_suffix.hpp +12 -0
- package/react-native/boost/boost/config/abi/msvc_prefix.hpp +22 -0
- package/react-native/boost/boost/config/abi/msvc_suffix.hpp +8 -0
- package/react-native/boost/boost/config/abi_prefix.hpp +25 -0
- package/react-native/boost/boost/config/abi_suffix.hpp +25 -0
- package/react-native/boost/boost/config/assert_cxx03.hpp +211 -0
- package/react-native/boost/boost/config/assert_cxx11.hpp +209 -0
- package/react-native/boost/boost/config/assert_cxx14.hpp +47 -0
- package/react-native/boost/boost/config/assert_cxx17.hpp +62 -0
- package/react-native/boost/boost/config/assert_cxx20.hpp +59 -0
- package/react-native/boost/boost/config/assert_cxx98.hpp +23 -0
- package/react-native/boost/boost/config/auto_link.hpp +525 -0
- package/react-native/boost/boost/config/compiler/borland.hpp +339 -0
- package/react-native/boost/boost/config/compiler/clang.hpp +366 -0
- package/react-native/boost/boost/config/compiler/clang_version.hpp +83 -0
- package/react-native/boost/boost/config/compiler/codegear.hpp +385 -0
- package/react-native/boost/boost/config/compiler/comeau.hpp +59 -0
- package/react-native/boost/boost/config/compiler/common_edg.hpp +183 -0
- package/react-native/boost/boost/config/compiler/compaq_cxx.hpp +19 -0
- package/react-native/boost/boost/config/compiler/cray.hpp +446 -0
- package/react-native/boost/boost/config/compiler/diab.hpp +26 -0
- package/react-native/boost/boost/config/compiler/digitalmars.hpp +143 -0
- package/react-native/boost/boost/config/compiler/gcc.hpp +383 -0
- package/react-native/boost/boost/config/compiler/gcc_xml.hpp +114 -0
- package/react-native/boost/boost/config/compiler/greenhills.hpp +28 -0
- package/react-native/boost/boost/config/compiler/hp_acc.hpp +149 -0
- package/react-native/boost/boost/config/compiler/intel.hpp +577 -0
- package/react-native/boost/boost/config/compiler/kai.hpp +33 -0
- package/react-native/boost/boost/config/compiler/metrowerks.hpp +198 -0
- package/react-native/boost/boost/config/compiler/mpw.hpp +140 -0
- package/react-native/boost/boost/config/compiler/nvcc.hpp +61 -0
- package/react-native/boost/boost/config/compiler/pathscale.hpp +138 -0
- package/react-native/boost/boost/config/compiler/pgi.hpp +23 -0
- package/react-native/boost/boost/config/compiler/sgi_mipspro.hpp +29 -0
- package/react-native/boost/boost/config/compiler/sunpro_cc.hpp +222 -0
- package/react-native/boost/boost/config/compiler/vacpp.hpp +186 -0
- package/react-native/boost/boost/config/compiler/visualc.hpp +391 -0
- package/react-native/boost/boost/config/compiler/xlcpp.hpp +299 -0
- package/react-native/boost/boost/config/compiler/xlcpp_zos.hpp +173 -0
- package/react-native/boost/boost/config/detail/cxx_composite.hpp +203 -0
- package/react-native/boost/boost/config/detail/posix_features.hpp +95 -0
- package/react-native/boost/boost/config/detail/select_compiler_config.hpp +157 -0
- package/react-native/boost/boost/config/detail/select_platform_config.hpp +147 -0
- package/react-native/boost/boost/config/detail/select_stdlib_config.hpp +121 -0
- package/react-native/boost/boost/config/detail/suffix.hpp +1294 -0
- package/react-native/boost/boost/config/header_deprecated.hpp +26 -0
- package/react-native/boost/boost/config/helper_macros.hpp +37 -0
- package/react-native/boost/boost/config/no_tr1/cmath.hpp +28 -0
- package/react-native/boost/boost/config/no_tr1/complex.hpp +28 -0
- package/react-native/boost/boost/config/no_tr1/functional.hpp +28 -0
- package/react-native/boost/boost/config/no_tr1/memory.hpp +28 -0
- package/react-native/boost/boost/config/no_tr1/utility.hpp +28 -0
- package/react-native/boost/boost/config/platform/aix.hpp +33 -0
- package/react-native/boost/boost/config/platform/amigaos.hpp +15 -0
- package/react-native/boost/boost/config/platform/beos.hpp +26 -0
- package/react-native/boost/boost/config/platform/bsd.hpp +83 -0
- package/react-native/boost/boost/config/platform/cloudabi.hpp +18 -0
- package/react-native/boost/boost/config/platform/cray.hpp +18 -0
- package/react-native/boost/boost/config/platform/cygwin.hpp +71 -0
- package/react-native/boost/boost/config/platform/haiku.hpp +31 -0
- package/react-native/boost/boost/config/platform/hpux.hpp +87 -0
- package/react-native/boost/boost/config/platform/irix.hpp +31 -0
- package/react-native/boost/boost/config/platform/linux.hpp +106 -0
- package/react-native/boost/boost/config/platform/macos.hpp +87 -0
- package/react-native/boost/boost/config/platform/qnxnto.hpp +31 -0
- package/react-native/boost/boost/config/platform/solaris.hpp +31 -0
- package/react-native/boost/boost/config/platform/symbian.hpp +97 -0
- package/react-native/boost/boost/config/platform/vms.hpp +25 -0
- package/react-native/boost/boost/config/platform/vxworks.hpp +422 -0
- package/react-native/boost/boost/config/platform/wasm.hpp +23 -0
- package/react-native/boost/boost/config/platform/win32.hpp +90 -0
- package/react-native/boost/boost/config/platform/zos.hpp +32 -0
- package/react-native/boost/boost/config/pragma_message.hpp +31 -0
- package/react-native/boost/boost/config/requires_threads.hpp +92 -0
- package/react-native/boost/boost/config/stdlib/dinkumware.hpp +324 -0
- package/react-native/boost/boost/config/stdlib/libcomo.hpp +93 -0
- package/react-native/boost/boost/config/stdlib/libcpp.hpp +180 -0
- package/react-native/boost/boost/config/stdlib/libstdcpp3.hpp +482 -0
- package/react-native/boost/boost/config/stdlib/modena.hpp +79 -0
- package/react-native/boost/boost/config/stdlib/msl.hpp +98 -0
- package/react-native/boost/boost/config/stdlib/roguewave.hpp +208 -0
- package/react-native/boost/boost/config/stdlib/sgi.hpp +168 -0
- package/react-native/boost/boost/config/stdlib/stlport.hpp +258 -0
- package/react-native/boost/boost/config/stdlib/vacpp.hpp +74 -0
- package/react-native/boost/boost/config/stdlib/xlcpp_zos.hpp +61 -0
- package/react-native/boost/boost/config/user.hpp +133 -0
- package/react-native/boost/boost/config/warning_disable.hpp +47 -0
- package/react-native/boost/boost/config/workaround.hpp +305 -0
- package/react-native/boost/boost/config.hpp +67 -0
- package/react-native/boost/boost/cstdint.hpp +556 -0
- package/react-native/boost/boost/intrusive_ptr.hpp +18 -0
- package/react-native/boost/boost/smart_ptr/detail/atomic_count.hpp +103 -0
- package/react-native/boost/boost/smart_ptr/detail/atomic_count_gcc.hpp +79 -0
- package/react-native/boost/boost/smart_ptr/detail/atomic_count_gcc_atomic.hpp +63 -0
- package/react-native/boost/boost/smart_ptr/detail/atomic_count_gcc_x86.hpp +88 -0
- package/react-native/boost/boost/smart_ptr/detail/atomic_count_nt.hpp +66 -0
- package/react-native/boost/boost/smart_ptr/detail/atomic_count_pt.hpp +104 -0
- package/react-native/boost/boost/smart_ptr/detail/atomic_count_spin.hpp +69 -0
- package/react-native/boost/boost/smart_ptr/detail/atomic_count_std_atomic.hpp +67 -0
- package/react-native/boost/boost/smart_ptr/detail/atomic_count_sync.hpp +72 -0
- package/react-native/boost/boost/smart_ptr/detail/atomic_count_win32.hpp +70 -0
- package/react-native/boost/boost/smart_ptr/detail/local_counted_base.hpp +148 -0
- package/react-native/boost/boost/smart_ptr/detail/local_sp_deleter.hpp +91 -0
- package/react-native/boost/boost/smart_ptr/detail/operator_bool.hpp +64 -0
- package/react-native/boost/boost/smart_ptr/detail/requires_cxx11.hpp +23 -0
- package/react-native/boost/boost/smart_ptr/detail/shared_count.hpp +707 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_convertible.hpp +92 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base.hpp +92 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_acc_ia64.hpp +163 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_aix.hpp +152 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_cw_ppc.hpp +185 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_gcc_atomic.hpp +148 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_gcc_ia64.hpp +170 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_gcc_mips.hpp +200 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_gcc_ppc.hpp +194 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_gcc_sparc.hpp +179 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp +186 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_nt.hpp +119 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_pt.hpp +147 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_snc_ps3.hpp +174 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_spin.hpp +141 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_std_atomic.hpp +147 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_sync.hpp +165 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_vacpp_ppc.hpp +163 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_base_w32.hpp +140 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_counted_impl.hpp +309 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_disable_deprecated.hpp +40 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_forward.hpp +52 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_has_gcc_intrinsics.hpp +27 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_has_sync_intrinsics.hpp +69 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_interlocked.hpp +173 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_noexcept.hpp +48 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_nullptr_t.hpp +45 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_obsolete.hpp +32 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_thread_pause.hpp +51 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_thread_sleep.hpp +104 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_thread_yield.hpp +100 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_typeinfo_.hpp +58 -0
- package/react-native/boost/boost/smart_ptr/detail/sp_win32_sleep.hpp +49 -0
- package/react-native/boost/boost/smart_ptr/intrusive_ptr.hpp +409 -0
- package/react-native/boost/boost/smart_ptr/intrusive_ref_counter.hpp +188 -0
- package/react-native/cpp/include/Attachment.h +16 -0
- package/react-native/cpp/include/Authentication.h +23 -0
- package/react-native/cpp/include/Collection.h +13 -0
- package/react-native/cpp/include/DQL.h +21 -0
- package/react-native/cpp/include/Document.h +17 -0
- package/react-native/cpp/include/Identity.h +17 -0
- package/react-native/cpp/include/Lifecycle.h +16 -0
- package/react-native/cpp/include/LiveQuery.h +17 -0
- package/react-native/cpp/include/Logger.h +22 -0
- package/react-native/cpp/include/Misc.h +26 -0
- package/react-native/cpp/include/Presence.h +14 -0
- package/react-native/cpp/include/RetainableState.h +24 -0
- package/react-native/cpp/include/SmallPeerInfo.h +17 -0
- package/react-native/cpp/include/Transports.h +25 -0
- package/react-native/cpp/include/TypedArray.hpp +167 -0
- package/react-native/cpp/include/Utils.h +61 -0
- package/react-native/cpp/include/main.h +10 -0
- package/react-native/cpp/src/Attachment.cpp +86 -0
- package/react-native/cpp/src/Authentication.cpp +224 -0
- package/react-native/cpp/src/Collection.cpp +54 -0
- package/react-native/cpp/src/DQL.cpp +254 -0
- package/react-native/cpp/src/Document.cpp +146 -0
- package/react-native/cpp/src/Identity.cpp +123 -0
- package/react-native/cpp/src/Lifecycle.cpp +75 -0
- package/react-native/cpp/src/LiveQuery.cpp +64 -0
- package/react-native/cpp/src/Logger.cpp +200 -0
- package/react-native/cpp/src/Misc.cpp +271 -0
- package/react-native/cpp/src/Presence.cpp +77 -0
- package/react-native/cpp/src/RetainableState.cpp +15 -0
- package/react-native/cpp/src/SmallPeerInfo.cpp +108 -0
- package/react-native/cpp/src/Transports.cpp +270 -0
- package/react-native/cpp/src/TypedArray.cpp +303 -0
- package/react-native/cpp/src/Utils.cpp +138 -0
- package/react-native/cpp/src/main.cpp +149 -0
- package/react-native/dittoffi/dittoffi.h +4698 -0
- package/react-native/dittoffi/ifaddrs.cpp +385 -0
- package/react-native/dittoffi/ifaddrs.h +206 -0
- package/react-native/ios/DittoRNSDK.h +7 -0
- package/react-native/ios/DittoRNSDK.mm +107 -0
- package/react-native/ios/YeetJSIUtils.h +60 -0
- package/react-native/ios/YeetJSIUtils.mm +196 -0
- package/react-native/lib/commonjs/ditto.rn.js +93 -0
- package/react-native/lib/commonjs/ditto.rn.js.map +1 -0
- package/react-native/lib/commonjs/index.js +14 -0
- package/react-native/lib/commonjs/index.js.map +1 -0
- package/react-native/lib/module/ditto.rn.js +83 -0
- package/react-native/lib/module/ditto.rn.js.map +1 -0
- package/react-native/lib/module/index.js +13 -0
- package/react-native/lib/module/index.js.map +1 -0
- package/react-native/lib/typescript/ditto.rn.d.ts +15 -0
- package/react-native/lib/typescript/ditto.rn.d.ts.map +1 -0
- package/react-native/lib/typescript/index.d.ts +1 -0
- package/react-native/lib/typescript/index.d.ts.map +1 -0
- package/react-native/scripts/ruby/include_local_boost.rb +78 -0
- package/react-native/src/ditto.rn.ts +91 -0
- package/react-native/src/environment/environment.fallback.ts +4 -0
- package/react-native/src/index.ts +26 -0
- package/react-native/src/sources/@cbor-redux.ts +2 -0
- package/react-native/src/sources/@ditto.core.ts +1 -0
- package/react-native/src/sources/@environment.ts +2 -0
- package/react-native/src/sources/attachment-fetch-event.ts +54 -0
- package/react-native/src/sources/attachment-fetcher-manager.ts +129 -0
- package/react-native/src/sources/attachment-fetcher.ts +124 -0
- package/react-native/src/sources/attachment-token.ts +48 -0
- package/react-native/src/sources/attachment.ts +59 -0
- package/react-native/src/sources/augment.ts +89 -0
- package/react-native/src/sources/authenticator.ts +314 -0
- package/react-native/src/sources/base-pending-cursor-operation.ts +237 -0
- package/react-native/src/sources/base-pending-id-specific-operation.ts +109 -0
- package/react-native/src/sources/bridge.ts +549 -0
- package/react-native/src/sources/build-time-constants.ts +4 -0
- package/react-native/src/sources/cbor.ts +35 -0
- package/react-native/src/sources/collection-interface.ts +67 -0
- package/react-native/src/sources/collection.ts +212 -0
- package/react-native/src/sources/collections-event.ts +99 -0
- package/react-native/src/sources/counter.ts +77 -0
- package/react-native/src/sources/ditto.ts +945 -0
- package/react-native/src/sources/document-id.ts +159 -0
- package/react-native/src/sources/document-path.ts +303 -0
- package/react-native/src/sources/document.ts +192 -0
- package/react-native/src/sources/epilogue.ts +24 -0
- package/react-native/src/sources/error-codes.ts +52 -0
- package/react-native/src/sources/error.ts +203 -0
- package/react-native/src/sources/essentials.ts +53 -0
- package/react-native/src/sources/ffi-error.ts +117 -0
- package/react-native/src/sources/ffi.ts +1972 -0
- package/react-native/src/sources/identity.ts +163 -0
- package/react-native/src/sources/init.ts +70 -0
- package/react-native/src/sources/internal.ts +113 -0
- package/react-native/src/sources/keep-alive.ts +69 -0
- package/react-native/src/sources/key-path.ts +195 -0
- package/react-native/src/sources/live-query-event.ts +208 -0
- package/react-native/src/sources/live-query-manager.ts +101 -0
- package/react-native/src/sources/live-query.ts +164 -0
- package/react-native/src/sources/logger.ts +196 -0
- package/react-native/src/sources/observer-manager.ts +175 -0
- package/react-native/src/sources/observer.ts +77 -0
- package/react-native/src/sources/pending-collections-operation.ts +232 -0
- package/react-native/src/sources/pending-cursor-operation.ts +218 -0
- package/react-native/src/sources/pending-id-specific-operation.ts +216 -0
- package/react-native/src/sources/presence-manager.ts +160 -0
- package/react-native/src/sources/presence.ts +238 -0
- package/react-native/src/sources/query-result-item.ts +116 -0
- package/react-native/src/sources/query-result.ts +55 -0
- package/react-native/src/sources/register.ts +92 -0
- package/react-native/src/sources/small-peer-info.ts +176 -0
- package/react-native/src/sources/static-tcp-client.ts +6 -0
- package/react-native/src/sources/store-observer.ts +176 -0
- package/react-native/src/sources/store.ts +365 -0
- package/react-native/src/sources/subscription-manager.ts +98 -0
- package/react-native/src/sources/subscription.ts +88 -0
- package/react-native/src/sources/sync-subscription.ts +90 -0
- package/react-native/src/sources/sync.ts +495 -0
- package/react-native/src/sources/test-helpers.ts +24 -0
- package/react-native/src/sources/transport-conditions-manager.ts +104 -0
- package/react-native/src/sources/transport-config.ts +428 -0
- package/react-native/src/sources/update-result.ts +66 -0
- package/react-native/src/sources/update-results-map.ts +57 -0
- package/react-native/src/sources/websocket-client.ts +6 -0
- package/react-native/src/sources/write-transaction-collection.ts +122 -0
- package/react-native/src/sources/write-transaction-pending-cursor-operation.ts +101 -0
- package/react-native/src/sources/write-transaction-pending-id-specific-operation.ts +72 -0
- package/react-native/src/sources/write-transaction.ts +119 -0
- package/react-native.config.js +9 -0
- package/types/ditto.d.ts +1230 -798
- package/web/ditto.es6.js +1 -1
- package/web/ditto.umd.js +1 -1
- package/web/ditto.wasm +0 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright © 2021 DittoLive Incorporated. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as FFI from './ffi'
|
|
6
|
+
import { Bridge } from './bridge'
|
|
7
|
+
|
|
8
|
+
import { CBOR, documentIDReplacer } from './cbor'
|
|
9
|
+
import { Collection } from './collection'
|
|
10
|
+
import { StoreObserver } from './store-observer'
|
|
11
|
+
import { DocumentID } from './document-id'
|
|
12
|
+
import { DittoError, mapFFIErrors, mapFFIErrorsAsync } from './error'
|
|
13
|
+
import { performAsyncToWorkaroundNonAsyncFFIAPI, step, validateQuery } from './internal'
|
|
14
|
+
import { Logger } from './logger'
|
|
15
|
+
import { PendingCollectionsOperation } from './pending-collections-operation'
|
|
16
|
+
import { QueryResult } from './query-result'
|
|
17
|
+
import { WriteTransaction } from './write-transaction'
|
|
18
|
+
|
|
19
|
+
import type { StoreObservationHandlerWithSignalNext, StoreObservationHandler } from './store-observer'
|
|
20
|
+
import type { Ditto } from './ditto'
|
|
21
|
+
import type { DQLQueryArguments } from './essentials'
|
|
22
|
+
import type { QueryResultItem } from './query-result-item'
|
|
23
|
+
import type { WriteTransactionResult } from './write-transaction'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The entrypoint for all actions that relate to data stored by Ditto. Provides
|
|
27
|
+
* access to collections, a write transaction API, and a query hash API.
|
|
28
|
+
*
|
|
29
|
+
* You don't create one directly but can access it from a particular
|
|
30
|
+
* {@link Ditto} instance via its {@link Ditto.store | store} property.
|
|
31
|
+
*/
|
|
32
|
+
export class Store {
|
|
33
|
+
/** The {@link Ditto} instance this store belongs to. */
|
|
34
|
+
readonly ditto: Ditto
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* All currently active store observers.
|
|
38
|
+
*
|
|
39
|
+
* **Note:** Manage store observers using
|
|
40
|
+
* {@link registerObserver | registerObserver()} to register a new store
|
|
41
|
+
* observer and {@link StoreObserver.cancel | StoreObserver.cancel()} to
|
|
42
|
+
* remove an existing store observer.
|
|
43
|
+
*/
|
|
44
|
+
readonly observers: Readonly<Array<StoreObserver>> = Object.freeze([])
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Register a handler to be called whenever a query's results change in the
|
|
48
|
+
* local store.
|
|
49
|
+
*
|
|
50
|
+
* Convenience method, same as
|
|
51
|
+
* {@link registerObserverWithSignalNext | registerObserverWithSignalNext()},
|
|
52
|
+
* except that here, the next invocation of the observation handler is
|
|
53
|
+
* triggered automatically instead of having to call the passed in
|
|
54
|
+
* `signalNext` function.
|
|
55
|
+
*
|
|
56
|
+
* @param query a string containing a valid query expressed in DQL.
|
|
57
|
+
* @param observationHandler a function that is called whenever the query's results
|
|
58
|
+
* change. The function is passed a {@link QueryResult} containing a
|
|
59
|
+
* {@link QueryResultItem} for each match.
|
|
60
|
+
* @param queryArguments an object of values keyed by the placeholder name
|
|
61
|
+
* without the leading `:`. Example: `{ "name": "Joanna" }` for a query like
|
|
62
|
+
* `SELECT * FROM people WHERE name = :name`.
|
|
63
|
+
* @returns a {@link StoreObserver} that can be used to cancel the
|
|
64
|
+
* observation.
|
|
65
|
+
* @throws {@link DittoError} `query/invalid`: if `query` argument is not a
|
|
66
|
+
* string or not valid DQL.
|
|
67
|
+
* @throws {@link DittoError} `query/arguments-invalid`: if `queryArguments`
|
|
68
|
+
* argument is invalid (e.g. contains unsupported types).
|
|
69
|
+
* @throws {@link DittoError} `query/unsupported`: if the query is not a
|
|
70
|
+
* `SELECT` query.
|
|
71
|
+
* @throws {@link DittoError} may throw other errors.
|
|
72
|
+
*/
|
|
73
|
+
registerObserver(query: string, observationHandler: StoreObservationHandler, queryArguments?: DQLQueryArguments): StoreObserver {
|
|
74
|
+
const changeHandlerWithSignalNext: StoreObservationHandlerWithSignalNext = (queryResult: QueryResult, signalNext: () => void) => {
|
|
75
|
+
observationHandler(queryResult)
|
|
76
|
+
signalNext()
|
|
77
|
+
}
|
|
78
|
+
return this.registerObserverWithSignalNext(query, changeHandlerWithSignalNext, queryArguments)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Registers and returns a store observer for a query, configuring Ditto to
|
|
83
|
+
* trigger the passed in observation handler whenever documents in the local
|
|
84
|
+
* store change such that the result of the matching query changes. The passed
|
|
85
|
+
* in query must be a `SELECT` query.
|
|
86
|
+
*
|
|
87
|
+
* Here, a function is passed as an additional argument to the observation
|
|
88
|
+
* handler. Call this function as soon as the observation handler is ready to
|
|
89
|
+
* process the the next change event. This allows the observation handler to
|
|
90
|
+
* control how frequently it is called. See
|
|
91
|
+
* {@link registerObserver | registerObserver()} for a convenience method that
|
|
92
|
+
* automatically signals the next invocation.
|
|
93
|
+
*
|
|
94
|
+
* The first invocation of `observationHandler` will always happen after this
|
|
95
|
+
* method has returned.
|
|
96
|
+
*
|
|
97
|
+
* @param query a string containing a valid query expressed in DQL.
|
|
98
|
+
* @param observationHandler an observation handler function that is called
|
|
99
|
+
* whenever the query's results change. The function is passed a
|
|
100
|
+
* {@link QueryResult} containing a {@link QueryResultItem} for each match.
|
|
101
|
+
* @param queryArguments an object of values keyed by the placeholder name
|
|
102
|
+
* without the leading `:`. Example: `{ "name": "Joanna" }` for a query like
|
|
103
|
+
* `SELECT * FROM people WHERE name = :name`.
|
|
104
|
+
* @returns a {@link StoreObserver} that can be used to cancel the
|
|
105
|
+
* observation.
|
|
106
|
+
* @throws {@link DittoError} `query/invalid`: if `query` argument is not a
|
|
107
|
+
* string or not valid DQL.
|
|
108
|
+
* @throws {@link DittoError} `query/arguments-invalid`: if `queryArguments`
|
|
109
|
+
* argument is invalid (e.g. contains unsupported types).
|
|
110
|
+
* @throws {@link DittoError} `query/unsupported`: if the query is not a
|
|
111
|
+
* `SELECT` query.
|
|
112
|
+
* @throws {@link DittoError} may throw other errors.
|
|
113
|
+
*/
|
|
114
|
+
registerObserverWithSignalNext(query: string, observationHandler: StoreObservationHandlerWithSignalNext, queryArguments?: DQLQueryArguments): StoreObserver {
|
|
115
|
+
if (typeof query !== 'string') {
|
|
116
|
+
throw new DittoError('query/invalid', `Expected parameter 'query' to be of type 'string', found: ${typeof query}`)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const storeObserver = new StoreObserver(this.ditto, query, queryArguments ?? null, observationHandler)
|
|
120
|
+
|
|
121
|
+
this.observers = Object.freeze([...this.observers, storeObserver])
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
const dittoHandle = Bridge.ditto.handleFor(this.ditto)
|
|
125
|
+
this.ditto.deferCloseAsync(async () => {
|
|
126
|
+
return new Promise<void>((resolve) => {
|
|
127
|
+
step(async () => {
|
|
128
|
+
try {
|
|
129
|
+
// prettier-ignore
|
|
130
|
+
await mapFFIErrorsAsync(
|
|
131
|
+
async () => await FFI.liveQueryStart(dittoHandle.deref(), storeObserver.liveQueryID)
|
|
132
|
+
)
|
|
133
|
+
} catch (error: any) {
|
|
134
|
+
|
|
135
|
+
Logger.error(`Failed to start live query: ${error.message}`)
|
|
136
|
+
}
|
|
137
|
+
resolve()
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
return storeObserver
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Returns the collection for the given name. If the collection doesn't
|
|
147
|
+
* exist yet, it will be created automatically as soon as the first
|
|
148
|
+
* entry is inserted.
|
|
149
|
+
* A collection name is valid if:
|
|
150
|
+
* * its length is less than 100
|
|
151
|
+
* * it is not empty
|
|
152
|
+
* * it does not contain the char '\0'
|
|
153
|
+
* * it does not begin with "$TS_"
|
|
154
|
+
*/
|
|
155
|
+
collection(name: string): Collection {
|
|
156
|
+
return new Collection(name, this)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Returns an object that lets you fetch or observe the collections in the
|
|
161
|
+
* store.
|
|
162
|
+
*
|
|
163
|
+
* @return A {@link PendingCollectionsOperation} object that you can use to
|
|
164
|
+
* fetch or observe the collections in the store
|
|
165
|
+
*/
|
|
166
|
+
collections(): PendingCollectionsOperation {
|
|
167
|
+
return new PendingCollectionsOperation(this)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Returns the names of all available collections in the store of the
|
|
172
|
+
* related {@link Ditto} instance.
|
|
173
|
+
*/
|
|
174
|
+
collectionNames(): Promise<string[]> {
|
|
175
|
+
const ditto = this.ditto
|
|
176
|
+
const dittoHandle = Bridge.ditto.handleFor(ditto)
|
|
177
|
+
return ditto.deferClose(() => {
|
|
178
|
+
return mapFFIErrors(() => FFI.dittoGetCollectionNames(dittoHandle.deref()))
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Executes a DQL query and returns matching items as a query result.
|
|
184
|
+
*
|
|
185
|
+
* @param query a string containing a valid query expressed in DQL.
|
|
186
|
+
* @param queryArguments an object of values keyed by the placeholder name
|
|
187
|
+
* without the leading `:`. Example: `{ "name": "John" }` for a query like
|
|
188
|
+
* `SELECT * FROM people WHERE name = :name`.
|
|
189
|
+
* @returns a promise for a {@link QueryResult} containing a
|
|
190
|
+
* {@link QueryResultItem} for each match.
|
|
191
|
+
* @throws {@link DittoError} `query/invalid`: if `query` argument is not a
|
|
192
|
+
* string or not valid DQL.
|
|
193
|
+
* @throws {@link DittoError} `query/arguments-invalid`: if `queryArguments`
|
|
194
|
+
* argument is invalid (e.g. contains unsupported types).
|
|
195
|
+
* @throws {@link DittoError} may throw other errors.
|
|
196
|
+
*/
|
|
197
|
+
async execute(query: string, queryArguments?: DQLQueryArguments): Promise<QueryResult> {
|
|
198
|
+
if (typeof query !== 'string') {
|
|
199
|
+
throw new DittoError('query/invalid', `Expected parameter 'query' to be of type 'string', found: ${typeof query}`)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const dittoHandle = Bridge.ditto.handleFor(this.ditto)
|
|
203
|
+
return this.ditto.deferCloseAsync(async () => {
|
|
204
|
+
const writeTransaction = null
|
|
205
|
+
|
|
206
|
+
let queryArgumentsCBOR: Uint8Array | null = null
|
|
207
|
+
if (queryArguments != null) {
|
|
208
|
+
try {
|
|
209
|
+
queryArgumentsCBOR = CBOR.encode(queryArguments, documentIDReplacer)
|
|
210
|
+
} catch (error: any) {
|
|
211
|
+
throw new DittoError('query/arguments-invalid', `Unable to encode query arguments: ${error.message}`)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const errorContext = { query, queryArguments }
|
|
216
|
+
|
|
217
|
+
const responsePointer: FFI.Pointer<FFI.FFIDqlResponse> = await mapFFIErrorsAsync(
|
|
218
|
+
async () => await performAsyncToWorkaroundNonAsyncFFIAPI(
|
|
219
|
+
() => FFI.tryExperimentalExecQueryStr(dittoHandle.deref(), writeTransaction, query, queryArgumentsCBOR)
|
|
220
|
+
),
|
|
221
|
+
undefined,
|
|
222
|
+
errorContext
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
return Bridge.dqlResponse.bridge(responsePointer, () => new QueryResult(responsePointer))
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Initiate a write transaction in a callback.
|
|
231
|
+
*
|
|
232
|
+
* Allows you to group multiple operations together that affect multiple documents, potentially across multiple collections.
|
|
233
|
+
*
|
|
234
|
+
* @param callback is given access to a {@link WriteTransaction | write transaction object} that can be used to perform operations on the store.
|
|
235
|
+
* @returns a list of `WriteTransactionResult`s. There is a result for each operation performed as part of the write transaction.
|
|
236
|
+
*/
|
|
237
|
+
async write(callback: (transaction: WriteTransaction) => Promise<void>): Promise<WriteTransactionResult[]> {
|
|
238
|
+
return this.ditto.deferCloseAsync(async () => {
|
|
239
|
+
const transaction = await WriteTransaction.init(this.ditto)
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
await callback(transaction)
|
|
243
|
+
} catch (error: any) {
|
|
244
|
+
await transaction.rollback()
|
|
245
|
+
Logger.warning(`Transaction rolled back due to an error: ${error?.message}`)
|
|
246
|
+
throw error
|
|
247
|
+
}
|
|
248
|
+
await transaction.commit()
|
|
249
|
+
|
|
250
|
+
return transaction.results
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ----------------------------------------------------------- Internal ------
|
|
255
|
+
|
|
256
|
+
/** @internal */
|
|
257
|
+
constructor(ditto: Ditto) {
|
|
258
|
+
this.ditto = ditto
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Registers a URL to be called whenever the given `SELECT` query observes
|
|
263
|
+
* changes.
|
|
264
|
+
*
|
|
265
|
+
* No validation is performed on the URL, so it is up to the caller to ensure
|
|
266
|
+
* that the URL is valid and can be reached.
|
|
267
|
+
*
|
|
268
|
+
* @internal
|
|
269
|
+
* @returns a promise for a document id that acts as a webhook id
|
|
270
|
+
* @throws {@link DittoError} `store/query-invalid`: if the query is invalid
|
|
271
|
+
* @throws {@link DittoError} `store/query-arguments-invalid`: if the query arguments
|
|
272
|
+
* are invalid
|
|
273
|
+
* @throws {@link DittoError} `store/query-unsupported`: if the query is not a
|
|
274
|
+
* `SELECT` query
|
|
275
|
+
* @throws {@link DittoError} for any other error that occurs during query execution
|
|
276
|
+
*/
|
|
277
|
+
async registerObserverWebhook(query: string, url: string, queryArguments?: DQLQueryArguments): Promise<DocumentID> {
|
|
278
|
+
let queryArgumentsCBOR: Uint8Array | null = null
|
|
279
|
+
if (queryArguments != null) {
|
|
280
|
+
try {
|
|
281
|
+
queryArgumentsCBOR = CBOR.encode(queryArguments, documentIDReplacer)
|
|
282
|
+
} catch (error: any) {
|
|
283
|
+
throw new DittoError('query/arguments-invalid', `Invalid query arguments: ${error.message}`)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const errorContext = { query, queryArguments }
|
|
288
|
+
|
|
289
|
+
const dittoHandle = Bridge.ditto.handleFor(this.ditto)
|
|
290
|
+
return this.ditto.deferCloseAsync(async () => {
|
|
291
|
+
const webhookIDCBOR = await mapFFIErrorsAsync(
|
|
292
|
+
async () => await FFI.tryExperimentalWebhookRegisterDqlLiveQuery(dittoHandle.deref(), query, queryArgumentsCBOR, url),
|
|
293
|
+
undefined,
|
|
294
|
+
errorContext
|
|
295
|
+
)
|
|
296
|
+
return new DocumentID(webhookIDCBOR, true)
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Unregister a store observer. No-op if the change observer has already
|
|
302
|
+
* been removed.
|
|
303
|
+
*
|
|
304
|
+
* This must only be called by the store observer itself.
|
|
305
|
+
*
|
|
306
|
+
* @param changeObserver the store observer to unregister
|
|
307
|
+
* @returns true if the store observer was found and removed, false otherwise
|
|
308
|
+
* @throws {@link DittoError} `internal`: if the store observer does not belong to
|
|
309
|
+
* this store
|
|
310
|
+
* @throws {@link DittoError} `internal`: if the store observer has not been
|
|
311
|
+
* cancelled yet
|
|
312
|
+
* @throws {@link DittoError} `internal`: for any other error that occurs while
|
|
313
|
+
* trying to unregister the store observer
|
|
314
|
+
* @internal
|
|
315
|
+
*/
|
|
316
|
+
unregisterObserver(storeObserver: StoreObserver): boolean {
|
|
317
|
+
if (storeObserver.ditto !== this.ditto) {
|
|
318
|
+
throw new DittoError('internal', `Internal inconsistency, can't remove store observer that does not belong to this store`)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (!storeObserver.isCancelled) {
|
|
322
|
+
throw new DittoError('internal', "Internal inconsistency, can't remove store observer that has not been cancelled")
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const indexToDelete = this.observers.findIndex((observer) => observer === storeObserver)
|
|
326
|
+
if (indexToDelete === -1) {
|
|
327
|
+
return false
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const newObservers = [...this.observers]
|
|
331
|
+
newObservers.splice(indexToDelete, 1)
|
|
332
|
+
this.observers = Object.freeze(newObservers)
|
|
333
|
+
|
|
334
|
+
const dittoHandle = Bridge.ditto.handleFor(this.ditto)
|
|
335
|
+
this.ditto.deferClose(() => {
|
|
336
|
+
mapFFIErrors(
|
|
337
|
+
() => FFI.liveQueryStop(dittoHandle.deref(), storeObserver.liveQueryID)
|
|
338
|
+
)
|
|
339
|
+
})
|
|
340
|
+
return true
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** @internal */
|
|
344
|
+
close() {
|
|
345
|
+
for (const observer of this.observers) {
|
|
346
|
+
observer.cancel()
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Private method, used only by the Portal https://github.com/getditto/ditto/pull/3652
|
|
354
|
+
* @internal
|
|
355
|
+
*/
|
|
356
|
+
async registerLiveQueryWebhook(collectionName: string, query: string, url: string): Promise<DocumentID> {
|
|
357
|
+
const ditto = this.ditto
|
|
358
|
+
const dittoHandle = Bridge.ditto.handleFor(ditto)
|
|
359
|
+
return ditto.deferCloseAsync(async () => {
|
|
360
|
+
const validatedQuery = validateQuery(query)
|
|
361
|
+
const idCBOR = await FFI.liveQueryWebhookRegister(dittoHandle.deref(), collectionName, validatedQuery, [], 0, 0, url)
|
|
362
|
+
return new DocumentID(idCBOR, true)
|
|
363
|
+
})
|
|
364
|
+
}
|
|
365
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright © 2023 DittoLive Incorporated. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as FFI from './ffi'
|
|
6
|
+
import { Bridge } from './bridge'
|
|
7
|
+
|
|
8
|
+
import type { Ditto } from './ditto'
|
|
9
|
+
import type { Subscription } from './subscription'
|
|
10
|
+
|
|
11
|
+
/** @internal */
|
|
12
|
+
export type SubscriptionContextInfo = {
|
|
13
|
+
id: string
|
|
14
|
+
collectionName: string
|
|
15
|
+
query: string
|
|
16
|
+
queryArgsCBOR: Uint8Array | null
|
|
17
|
+
orderBys: FFI.OrderBy[]
|
|
18
|
+
limit: number
|
|
19
|
+
offset: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Tracks `Subscription` instances in order to remove them when Ditto is
|
|
24
|
+
* closed.
|
|
25
|
+
*
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
export class SubscriptionManager {
|
|
29
|
+
/** @internal */
|
|
30
|
+
constructor(ditto: Ditto) {
|
|
31
|
+
this.ditto = ditto
|
|
32
|
+
this.subscriptions = {}
|
|
33
|
+
this.finalizationRegistry = new FinalizationRegistry(this.removeWithContextInfo.bind(this))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Begin tracking a subscription instance and start it.
|
|
38
|
+
*
|
|
39
|
+
* @internal */
|
|
40
|
+
add(subscription: Subscription) {
|
|
41
|
+
const ditto = this.ditto
|
|
42
|
+
const dittoHandle = Bridge.ditto.handleFor(ditto)
|
|
43
|
+
const contextInfo = subscription.contextInfo
|
|
44
|
+
ditto.deferClose(async () => {
|
|
45
|
+
this.subscriptions[contextInfo.id] = new WeakRef(subscription)
|
|
46
|
+
this.finalizationRegistry.register(subscription, subscription.contextInfo, subscription)
|
|
47
|
+
FFI.addSubscription(dittoHandle.deref(), contextInfo.collectionName, contextInfo.query, contextInfo.queryArgsCBOR, contextInfo.orderBys, contextInfo.limit, contextInfo.offset)
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Stop tracking a subscription instance and cancel it.
|
|
53
|
+
*
|
|
54
|
+
* @internal */
|
|
55
|
+
remove(subscription: Subscription) {
|
|
56
|
+
if (this.subscriptions[subscription.contextInfo.id] == null) {
|
|
57
|
+
throw new Error(`Internal inconsistency, tried to remove a subscription that is not tracked: ${subscription.contextInfo.id}`)
|
|
58
|
+
}
|
|
59
|
+
this.finalizationRegistry.unregister(subscription)
|
|
60
|
+
this.removeWithContextInfo(subscription.contextInfo)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Stop tracking all subscriptions and cancel them.
|
|
65
|
+
*
|
|
66
|
+
* @internal */
|
|
67
|
+
close() {
|
|
68
|
+
this.ditto.deferClose(async () => {
|
|
69
|
+
for (const subscriptionID in this.subscriptions) {
|
|
70
|
+
const subscription = this.subscriptions[subscriptionID].deref()
|
|
71
|
+
if (subscription != null) {
|
|
72
|
+
|
|
73
|
+
this.remove(subscription)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ----------------------------------------------------------- Internal ------
|
|
80
|
+
|
|
81
|
+
private ditto: Ditto
|
|
82
|
+
private subscriptions: { [subscriptionID: string]: WeakRef<Subscription> }
|
|
83
|
+
private finalizationRegistry: FinalizationRegistry<SubscriptionContextInfo>
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Remove tracked subscription without unregistering from finalization
|
|
87
|
+
* registry.
|
|
88
|
+
*
|
|
89
|
+
* @internal */
|
|
90
|
+
private removeWithContextInfo(contextInfo: SubscriptionContextInfo) {
|
|
91
|
+
const ditto = this.ditto
|
|
92
|
+
const dittoHandle = Bridge.ditto.handleFor(ditto)
|
|
93
|
+
ditto.deferClose(() => {
|
|
94
|
+
delete this.subscriptions[contextInfo.id]
|
|
95
|
+
FFI.removeSubscription(dittoHandle.deref(), contextInfo.collectionName, contextInfo.query, contextInfo.queryArgsCBOR, contextInfo.orderBys, contextInfo.limit, contextInfo.offset)
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright © 2021 DittoLive Incorporated. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as FFI from './ffi'
|
|
6
|
+
import { generateEphemeralToken } from './internal'
|
|
7
|
+
|
|
8
|
+
import type { Collection } from './collection'
|
|
9
|
+
import type { SubscriptionManager, SubscriptionContextInfo } from './subscription-manager'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Used to subscribe to receive updates from remote peers about matching
|
|
13
|
+
* documents.
|
|
14
|
+
*
|
|
15
|
+
* While {@link Subscription} objects remain in scope they ensure that
|
|
16
|
+
* documents in the collection specified and that match the query provided will
|
|
17
|
+
* try to be kept up-to-date with the latest changes from remote peers.
|
|
18
|
+
*/
|
|
19
|
+
export class Subscription {
|
|
20
|
+
/**
|
|
21
|
+
* The query that the subscription is based on.
|
|
22
|
+
*/
|
|
23
|
+
readonly query: string
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Returns `true` if subscription has been explicitly cancelled, `false`
|
|
27
|
+
* otherwise.
|
|
28
|
+
*/
|
|
29
|
+
get isCancelled(): boolean {
|
|
30
|
+
return this._isCancelled
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The name of the collection that the subscription is based on.
|
|
35
|
+
*/
|
|
36
|
+
get collectionName() {
|
|
37
|
+
return this.collection.name
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Cancels a subscription and releases all associated resources.
|
|
42
|
+
*/
|
|
43
|
+
cancel() {
|
|
44
|
+
if (!this.isCancelled) {
|
|
45
|
+
this._isCancelled = true
|
|
46
|
+
this.manager.remove(this)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ----------------------------------------------------------- Internal ------
|
|
51
|
+
|
|
52
|
+
/** @internal */
|
|
53
|
+
constructor(collection: Collection, query: string, queryArgsCBOR: Uint8Array | null, orderBys: FFI.OrderBy[], limit: number, offset: number) {
|
|
54
|
+
this.query = query
|
|
55
|
+
this.queryArgsCBOR = queryArgsCBOR
|
|
56
|
+
this.collection = collection
|
|
57
|
+
this.contextInfo = {
|
|
58
|
+
id: generateEphemeralToken(),
|
|
59
|
+
collectionName: collection.name,
|
|
60
|
+
query,
|
|
61
|
+
queryArgsCBOR,
|
|
62
|
+
orderBys,
|
|
63
|
+
limit,
|
|
64
|
+
offset,
|
|
65
|
+
}
|
|
66
|
+
this.manager = collection.store.ditto.subscriptionManager
|
|
67
|
+
this.manager.add(this)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The collection this subscription belongs to.
|
|
72
|
+
* @internal Because not exposed in any of the other SDKs (yet?).
|
|
73
|
+
*/
|
|
74
|
+
readonly collection: Collection
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The corresponding named arguments for {@link query}, if any.
|
|
78
|
+
* @internal Because not exposed in any of the other SDKs (yet?).
|
|
79
|
+
*/
|
|
80
|
+
readonly queryArgsCBOR: Uint8Array | null
|
|
81
|
+
|
|
82
|
+
/** @internal */
|
|
83
|
+
readonly contextInfo: SubscriptionContextInfo
|
|
84
|
+
|
|
85
|
+
private readonly manager: SubscriptionManager
|
|
86
|
+
|
|
87
|
+
private _isCancelled = false
|
|
88
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright © 2023 DittoLive Incorporated. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { DittoError } from './error'
|
|
6
|
+
|
|
7
|
+
import type { DQLQueryArguments } from './essentials'
|
|
8
|
+
import type { Ditto } from './ditto'
|
|
9
|
+
import type { Sync } from './sync'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A sync subscription configures Ditto to receive updates from remote peers
|
|
13
|
+
* about documents matching the subscription's query.
|
|
14
|
+
*
|
|
15
|
+
* The sync subscription will remain active until it is
|
|
16
|
+
* {@link SyncSubscription.cancel | cancelled}, or the Ditto instance managing
|
|
17
|
+
* the subscription has been {@link Ditto.close | closed}.
|
|
18
|
+
*
|
|
19
|
+
* Create a sync subscription by calling
|
|
20
|
+
* {@link Sync.registerSubscription | `ditto.sync.registerSubscription()`}.
|
|
21
|
+
*/
|
|
22
|
+
export class SyncSubscription {
|
|
23
|
+
/**
|
|
24
|
+
* Documents matching this query will be included in the sync subscription.
|
|
25
|
+
*/
|
|
26
|
+
readonly queryString: string
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The query arguments of the sync subscription (as passed when
|
|
30
|
+
* adding it to the store).
|
|
31
|
+
*/
|
|
32
|
+
readonly queryArguments?: Readonly<DQLQueryArguments>
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The {@link Ditto} instance this sync subscription belongs to.
|
|
36
|
+
*/
|
|
37
|
+
readonly ditto: Ditto
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* `true` when the sync subscription has been cancelled or the {@link Ditto}
|
|
41
|
+
* instance managing this subscription has been closed.
|
|
42
|
+
*/
|
|
43
|
+
get isCancelled() {
|
|
44
|
+
return this._isCancelled
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Cancels the sync subscription and unregisters it. No-op
|
|
49
|
+
* if the sync subscription has already been cancelled or the {@link Ditto}
|
|
50
|
+
* instance managing this subscription has been closed.
|
|
51
|
+
*/
|
|
52
|
+
cancel() {
|
|
53
|
+
if (this._isCancelled) return
|
|
54
|
+
this._isCancelled = true
|
|
55
|
+
this.ditto.sync.unregisterSubscription(this)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// --------------------------- Internal --------------------------------------
|
|
59
|
+
|
|
60
|
+
/** @internal */
|
|
61
|
+
constructor(ditto: Ditto, query: string, queryArguments: DQLQueryArguments | null, queryArgumentsCBOR: Uint8Array | null) {
|
|
62
|
+
if ((queryArguments == null) !== (queryArgumentsCBOR == null)) {
|
|
63
|
+
throw new DittoError('internal', 'Internal inconsistency, query arguments and query arguments CBOR must be both null or both non-null', { queryArguments, queryArgumentsCBOR })
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.ditto = ditto
|
|
67
|
+
this.queryString = query
|
|
68
|
+
this.queryArguments = queryArguments ? Object.freeze({ ...queryArguments }) : undefined
|
|
69
|
+
this.queryArgumentsCBOR = queryArgumentsCBOR
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The CBOR-encoded query arguments, or `null` if no query arguments were
|
|
74
|
+
* passed in.
|
|
75
|
+
*
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
78
|
+
readonly queryArgumentsCBOR: Uint8Array | null
|
|
79
|
+
|
|
80
|
+
// --------------------------- Private --------------------------------------
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* `true` when the ssync ubscription has been cancelled.
|
|
84
|
+
*
|
|
85
|
+
* We mark the sync subscription as cancelled here as an optimization to avoid
|
|
86
|
+
* a scan of all subscriptions in the store whenever the `isCancelled`
|
|
87
|
+
* property is checked.
|
|
88
|
+
*/
|
|
89
|
+
private _isCancelled = false
|
|
90
|
+
}
|