@dittolive/ditto 4.7.4-rc.2 → 4.7.4
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 +2 -2
- package/node/ditto.cjs.js +1 -1
- 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/node/transports.darwin-arm64.node +0 -0
- package/node/transports.darwin-x64.node +0 -0
- package/package.json +5 -2
- package/react-native/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/react-native/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/react-native/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/react-native/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/react-native/android/.gradle/8.9/gc.properties +0 -0
- package/react-native/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/react-native/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/react-native/android/.gradle/vcs-1/gc.properties +0 -0
- package/react-native/android/CMakeLists.txt +36 -0
- package/react-native/android/build.gradle +190 -0
- package/react-native/android/cpp-adapter.cpp +259 -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 +120 -0
- package/react-native/android/src/main/java/com/dittolive/rnsdk/DittoRNSDKPackage.java +28 -0
- package/react-native/cpp/include/Arc.hpp +159 -0
- package/react-native/cpp/include/Attachment.h +20 -0
- package/react-native/cpp/include/Authentication.h +23 -0
- package/react-native/cpp/include/Collection.h +13 -0
- package/react-native/cpp/include/ConnectionRequest.h +18 -0
- package/react-native/cpp/include/DQL.h +21 -0
- package/react-native/cpp/include/Document.h +17 -0
- package/react-native/cpp/include/FFIUtils.h +16 -0
- package/react-native/cpp/include/IO.h +13 -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 +30 -0
- package/react-native/cpp/include/Presence.h +18 -0
- package/react-native/cpp/include/SmallPeerInfo.h +19 -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 +70 -0
- package/react-native/cpp/include/main.h +10 -0
- package/react-native/cpp/src/Attachment.cpp +272 -0
- package/react-native/cpp/src/Authentication.cpp +227 -0
- package/react-native/cpp/src/Collection.cpp +56 -0
- package/react-native/cpp/src/ConnectionRequest.cpp +123 -0
- package/react-native/cpp/src/DQL.cpp +256 -0
- package/react-native/cpp/src/Document.cpp +146 -0
- package/react-native/cpp/src/FFIUtils.cpp +122 -0
- package/react-native/cpp/src/IO.cpp +35 -0
- package/react-native/cpp/src/Identity.cpp +122 -0
- package/react-native/cpp/src/Lifecycle.cpp +93 -0
- package/react-native/cpp/src/LiveQuery.cpp +63 -0
- package/react-native/cpp/src/Logger.cpp +199 -0
- package/react-native/cpp/src/Misc.cpp +322 -0
- package/react-native/cpp/src/Presence.cpp +166 -0
- package/react-native/cpp/src/SmallPeerInfo.cpp +142 -0
- package/react-native/cpp/src/Transports.cpp +275 -0
- package/react-native/cpp/src/TypedArray.cpp +303 -0
- package/react-native/cpp/src/Utils.cpp +139 -0
- package/react-native/cpp/src/main.cpp +178 -0
- package/react-native/dittoffi/dittoffi.h +4873 -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 +159 -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 +61 -0
- package/react-native/lib/commonjs/index.js.map +1 -0
- package/react-native/lib/module/ditto.rn.js +89 -0
- package/react-native/lib/module/ditto.rn.js.map +1 -0
- package/react-native/lib/module/index.js +27 -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/src/ditto.rn.ts +123 -0
- package/react-native/src/environment/environment.fallback.ts +4 -0
- package/react-native/src/index.ts +29 -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 +1 -0
- package/react-native/src/sources/attachment-fetch-event.ts +54 -0
- package/react-native/src/sources/attachment-fetcher-manager.ts +145 -0
- package/react-native/src/sources/attachment-fetcher.ts +265 -0
- package/react-native/src/sources/attachment-token.ts +129 -0
- package/react-native/src/sources/attachment.ts +121 -0
- package/react-native/src/sources/augment.ts +108 -0
- package/react-native/src/sources/authenticator.ts +314 -0
- package/react-native/src/sources/base-pending-cursor-operation.ts +255 -0
- package/react-native/src/sources/base-pending-id-specific-operation.ts +112 -0
- package/react-native/src/sources/bridge.ts +557 -0
- package/react-native/src/sources/build-time-constants.ts +8 -0
- package/react-native/src/sources/cbor.ts +20 -0
- package/react-native/src/sources/collection-interface.ts +73 -0
- package/react-native/src/sources/collection.ts +219 -0
- package/react-native/src/sources/collections-event.ts +99 -0
- package/react-native/src/sources/connection-request.ts +142 -0
- package/react-native/src/sources/counter.ts +82 -0
- package/react-native/src/sources/ditto.ts +991 -0
- package/react-native/src/sources/document-id.ts +163 -0
- package/react-native/src/sources/document-path.ts +308 -0
- package/react-native/src/sources/document.ts +237 -0
- package/react-native/src/sources/epilogue.ts +32 -0
- package/react-native/src/sources/error-codes.ts +114 -0
- package/react-native/src/sources/error.ts +256 -0
- package/react-native/src/sources/essentials.ts +81 -0
- package/react-native/src/sources/ffi-error.ts +134 -0
- package/react-native/src/sources/ffi.ts +2190 -0
- package/react-native/src/sources/identity.ts +163 -0
- package/react-native/src/sources/init.ts +71 -0
- package/react-native/src/sources/internal.ts +143 -0
- package/react-native/src/sources/keep-alive.ts +73 -0
- package/react-native/src/sources/key-path.ts +198 -0
- package/react-native/src/sources/live-query-event.ts +208 -0
- package/react-native/src/sources/live-query-manager.ts +110 -0
- package/react-native/src/sources/live-query.ts +167 -0
- package/react-native/src/sources/logger.ts +196 -0
- package/react-native/src/sources/main.ts +61 -0
- package/react-native/src/sources/observer-manager.ts +185 -0
- package/react-native/src/sources/observer.ts +79 -0
- package/react-native/src/sources/pending-collections-operation.ts +241 -0
- package/react-native/src/sources/pending-cursor-operation.ts +218 -0
- package/react-native/src/sources/pending-id-specific-operation.ts +218 -0
- package/react-native/src/sources/presence-manager.ts +170 -0
- package/react-native/src/sources/presence.ts +427 -0
- package/react-native/src/sources/query-result-item.ts +131 -0
- package/react-native/src/sources/query-result.ts +55 -0
- package/react-native/src/sources/register.ts +95 -0
- package/react-native/src/sources/small-peer-info.ts +166 -0
- package/react-native/src/sources/static-tcp-client.ts +8 -0
- package/react-native/src/sources/store-observer.ts +170 -0
- package/react-native/src/sources/store.ts +630 -0
- package/react-native/src/sources/subscription-manager.ts +99 -0
- package/react-native/src/sources/subscription.ts +89 -0
- package/react-native/src/sources/sync-subscription.ts +90 -0
- package/react-native/src/sources/sync.ts +561 -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 +430 -0
- package/react-native/src/sources/update-result.ts +66 -0
- package/react-native/src/sources/update-results-map.ts +65 -0
- package/react-native/src/sources/websocket-client.ts +7 -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 +74 -0
- package/react-native/src/sources/write-transaction.ts +121 -0
- package/react-native.config.js +9 -0
- package/web/ditto.es6.js +1 -1
- package/web/ditto.umd.js +1 -1
- package/web/ditto.wasm +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright © 2021 DittoLive Incorporated. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// NOTE: this is the last file to be rolled up. Typically, you'll use this to
|
|
6
|
+
// tie up certain associations that would otherwise lead to import cycles. The
|
|
7
|
+
// bridges are a good example of this: the type constructors for the bridged
|
|
8
|
+
// types need to be registered with it, but can't do it at creation time of the
|
|
9
|
+
// bridges, because anything that would require a certain bridge would
|
|
10
|
+
// immediately require the bridged type, oftentimes leading to an import cycle.
|
|
11
|
+
|
|
12
|
+
import { Attachment } from './attachment'
|
|
13
|
+
import { Document, MutableDocument } from './document'
|
|
14
|
+
|
|
15
|
+
import { StaticTCPClient } from './static-tcp-client'
|
|
16
|
+
import { WebsocketClient } from './websocket-client'
|
|
17
|
+
|
|
18
|
+
import { Ditto } from './ditto'
|
|
19
|
+
import { Bridge } from './bridge'
|
|
20
|
+
import { ConnectionRequest } from './connection-request'
|
|
21
|
+
import { QueryResult } from './query-result'
|
|
22
|
+
import { QueryResultItem } from './query-result-item'
|
|
23
|
+
|
|
24
|
+
Bridge.attachment.registerType(Attachment)
|
|
25
|
+
Bridge.connectionRequest.registerType(ConnectionRequest)
|
|
26
|
+
Bridge.document.registerType(Document)
|
|
27
|
+
Bridge.queryResultItem.registerType(QueryResultItem)
|
|
28
|
+
Bridge.queryResult.registerType(QueryResult)
|
|
29
|
+
Bridge.mutableDocument.registerType(MutableDocument)
|
|
30
|
+
Bridge.staticTCPClient.registerType(StaticTCPClient)
|
|
31
|
+
Bridge.websocketClient.registerType(WebsocketClient)
|
|
32
|
+
Bridge.ditto.registerType(Ditto)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright © 2023 DittoLive Incorporated. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
export type ErrorCode = keyof typeof ERROR_CODES
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Error code and default error message for all Ditto error messages.
|
|
9
|
+
*
|
|
10
|
+
* Keys of this object define _error codes_ with each value containing the
|
|
11
|
+
* accompanying error description that is set as the default error message for
|
|
12
|
+
* errors instantiated with this code.
|
|
13
|
+
*
|
|
14
|
+
* Error codes may start with an error domain and a slash to group categories of
|
|
15
|
+
* errors together.
|
|
16
|
+
*/
|
|
17
|
+
export const ERROR_CODES = {
|
|
18
|
+
/** Internal error for unexpected system states */
|
|
19
|
+
internal: 'An unexpected internal error occurred. Please get in touch with Ditto customer service to report this incident.',
|
|
20
|
+
|
|
21
|
+
/** Internal error with an unknown error cause */
|
|
22
|
+
'internal/unknown-error': 'An unexpected internal error occurred. Please get in touch with Ditto customer service to report this incident.',
|
|
23
|
+
|
|
24
|
+
//
|
|
25
|
+
// Errors originating in the SDK
|
|
26
|
+
//
|
|
27
|
+
|
|
28
|
+
/** Error when using a feature not supported by the current environment */
|
|
29
|
+
'sdk/unsupported': 'The feature is not supported by the current environment.',
|
|
30
|
+
|
|
31
|
+
//
|
|
32
|
+
// Query errors
|
|
33
|
+
//
|
|
34
|
+
|
|
35
|
+
/** Error for invalid DQL query arguments. */
|
|
36
|
+
'query/arguments-invalid': 'The query arguments were invalid.',
|
|
37
|
+
|
|
38
|
+
/** Errors that occur during execution of a query */
|
|
39
|
+
'query/execution': 'The query could not be executed.',
|
|
40
|
+
|
|
41
|
+
/** Error for an invalid DQL query. */
|
|
42
|
+
'query/invalid': 'The query was invalid.',
|
|
43
|
+
|
|
44
|
+
/** Error for a query that uses DQL features not supported in this version or not supported by the currently used API. */
|
|
45
|
+
'query/unsupported': 'The query contains unsupported features.',
|
|
46
|
+
|
|
47
|
+
/** Error for a failed query updating system parameters */
|
|
48
|
+
'query/parameter': 'The query to update system parameters failed.',
|
|
49
|
+
|
|
50
|
+
//
|
|
51
|
+
// Store errors
|
|
52
|
+
//
|
|
53
|
+
|
|
54
|
+
/** Error in the storage backend. */
|
|
55
|
+
'store/backend': 'An error occurred with the storage backend.',
|
|
56
|
+
|
|
57
|
+
/** Error for an invalid CRDT. */
|
|
58
|
+
'store/crdt': 'An error occurred processing a CRDT.',
|
|
59
|
+
|
|
60
|
+
/** Error for a document not found. */
|
|
61
|
+
'store/document-not-found': 'The document with the provided ID could not be found.',
|
|
62
|
+
|
|
63
|
+
/** Permission has been denied for a file operation when working with attachments. */
|
|
64
|
+
'store/attachment-file-permission-denied': 'Permission has been denied for a file operation when working with attachments.',
|
|
65
|
+
|
|
66
|
+
/** The source file for an attachment does not exist. */
|
|
67
|
+
'store/attachment-file-not-found': 'The source file for the attachment does not exist.',
|
|
68
|
+
|
|
69
|
+
/** Attachment could not be found. */
|
|
70
|
+
'store/attachment-not-found': 'The attachment could not be found.',
|
|
71
|
+
|
|
72
|
+
/** Attachment token is invalid. */
|
|
73
|
+
'store/attachment-token-invalid': 'The attachment token is invalid.',
|
|
74
|
+
|
|
75
|
+
/** An unclassified error while creating an attachment. */
|
|
76
|
+
'store/failed-to-create-attachment': 'The attachment could not be created.',
|
|
77
|
+
|
|
78
|
+
/** An unclassified error while fetching an attachment. */
|
|
79
|
+
'store/failed-to-fetch-attachment': 'The attachment could not be fetched.',
|
|
80
|
+
|
|
81
|
+
//
|
|
82
|
+
// Activation errors
|
|
83
|
+
//
|
|
84
|
+
|
|
85
|
+
/** An error representing an invalid license token. */
|
|
86
|
+
'activation/license-token-verification-failed': 'Please provide a valid license token.',
|
|
87
|
+
|
|
88
|
+
/** An error representing an expired license token. */
|
|
89
|
+
'activation/license-token-expired': 'The license token expired. Please renew it.',
|
|
90
|
+
|
|
91
|
+
/** An error representing a token is in an unsupported future format. */
|
|
92
|
+
'activation/license-token-unsupported-future-version': 'The provided license token is in an unsupported future format.',
|
|
93
|
+
|
|
94
|
+
/** The operation failed because it requires an activated Ditto instance. */
|
|
95
|
+
'activation/not-activated': 'The operation failed because the Ditto instance has not yet been activated.',
|
|
96
|
+
|
|
97
|
+
/** A validation error where the maximum depth limit was exceeded. */
|
|
98
|
+
'validation/depth-limit-exceeded': 'The maximum depth limit has been exceeded.',
|
|
99
|
+
|
|
100
|
+
/** A validation error where the value is not valid CBOR. */
|
|
101
|
+
'validation/invalid-cbor': 'The value provided is not valid CBOR.',
|
|
102
|
+
|
|
103
|
+
/** A validation error where the value is not valid JSON. */
|
|
104
|
+
'validation/invalid-json': 'The value provided is not valid JSON.',
|
|
105
|
+
|
|
106
|
+
/** A validation error where a value is required to be a JavaScript object */
|
|
107
|
+
'validation/not-an-object': 'The value provided is not of type object.',
|
|
108
|
+
|
|
109
|
+
/** The value provided can not be serialized as JSON. */
|
|
110
|
+
'validation/not-json-compatible': 'Value is not serializable as JSON.',
|
|
111
|
+
|
|
112
|
+
/** A validation error where a size limit was exceeded. */
|
|
113
|
+
'validation/size-limit-exceeded': 'The size limit has been exceeded.',
|
|
114
|
+
} as const
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright © 2023 DittoLive Incorporated. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { ErrorCode, ERROR_CODES } from './error-codes'
|
|
6
|
+
import { DittoFFIError } from './ffi-error'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Additional data, which provides context to the error and may help with
|
|
10
|
+
* debugging.
|
|
11
|
+
*
|
|
12
|
+
* **Warning:** The contents of this object may change in future versions of
|
|
13
|
+
* the SDK.
|
|
14
|
+
*/
|
|
15
|
+
export type ErrorContext = Record<string, any>
|
|
16
|
+
|
|
17
|
+
/** @see {@link FFIErrorMapping} */
|
|
18
|
+
const DEFAULT_STATUS_CODE_MAPPING: FFIErrorMapping = {
|
|
19
|
+
//
|
|
20
|
+
// Activation errors
|
|
21
|
+
//
|
|
22
|
+
ActivationLicenseTokenExpired: ['activation/license-token-expired'],
|
|
23
|
+
ActivationLicenseTokenInvalid: ['activation/license-token-verification-failed'],
|
|
24
|
+
ActivationLicenseUnsupportedFutureVersion: ['activation/license-token-unsupported-future-version'],
|
|
25
|
+
ActivationNotActivated: ['activation/not-activated'],
|
|
26
|
+
|
|
27
|
+
//
|
|
28
|
+
// SDK-specific errors
|
|
29
|
+
//
|
|
30
|
+
JsFloatingStoreOperation: ['internal', 'Internal inconsistency, an outstanding store operation was not awaited.'],
|
|
31
|
+
|
|
32
|
+
//
|
|
33
|
+
// DQL errors
|
|
34
|
+
//
|
|
35
|
+
DqlQueryCompilation: ['query/invalid'],
|
|
36
|
+
DqlInvalidQueryArgs: ['query/arguments-invalid'],
|
|
37
|
+
DqlUnsupported: ['query/unsupported'],
|
|
38
|
+
StoreQuery: ['query/execution'],
|
|
39
|
+
ParameterQuery: ['query/parameter'],
|
|
40
|
+
|
|
41
|
+
//
|
|
42
|
+
// Store errors
|
|
43
|
+
//
|
|
44
|
+
StoreDocumentNotFound: ['store/document-not-found'],
|
|
45
|
+
StoreDatabase: ['store/backend'],
|
|
46
|
+
Crdt: ['store/crdt'],
|
|
47
|
+
|
|
48
|
+
//
|
|
49
|
+
// Validation errors
|
|
50
|
+
//
|
|
51
|
+
// This gets mapped to `internal` by default but depending on the context,
|
|
52
|
+
// it should be mapped to a more specific error code.
|
|
53
|
+
Base64Invalid: ['internal', 'Invalid base64 encoding.'],
|
|
54
|
+
ValidationDepthLimitExceeded: ['validation/depth-limit-exceeded'],
|
|
55
|
+
ValidationInvalidCbor: ['validation/invalid-cbor'],
|
|
56
|
+
ValidationInvalidJson: ['validation/invalid-json'],
|
|
57
|
+
ValidationNotAMap: ['validation/not-an-object'],
|
|
58
|
+
ValidationSizeLimitExceeded: ['validation/size-limit-exceeded'],
|
|
59
|
+
|
|
60
|
+
default: ['internal/unknown-error'],
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* `DittoError` is a subclass of the default Javascript `Error`. You can
|
|
65
|
+
* identify specific errors programatically using the
|
|
66
|
+
* {@link DittoError.code | code} property.
|
|
67
|
+
*
|
|
68
|
+
* Please reference {@link ERROR_CODES} for a comprehensive list of
|
|
69
|
+
* possible error codes in this SDK version.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* Handling a specific error code:
|
|
73
|
+
* ```typescript
|
|
74
|
+
* import { Attachment, DittoError } from '@dittolive/ditto'
|
|
75
|
+
*
|
|
76
|
+
* let attachment: Attachment
|
|
77
|
+
* try {
|
|
78
|
+
* attachment = await ditto.store.newAttachment(filePath)
|
|
79
|
+
* } catch (error) {
|
|
80
|
+
* if (error instanceof DittoError && error.code === 'store/attachment-file-not-found') {
|
|
81
|
+
* // Handle a non-existing file
|
|
82
|
+
* }
|
|
83
|
+
* throw error // Rethrow any other error
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export class DittoError extends Error {
|
|
88
|
+
/**
|
|
89
|
+
* Use the error code to identify a specific error programatically.
|
|
90
|
+
*
|
|
91
|
+
* @see {@link ERROR_CODES} for a comprehensive list of possible
|
|
92
|
+
* error codes in this SDK version.
|
|
93
|
+
*/
|
|
94
|
+
readonly code: ErrorCode = 'internal'
|
|
95
|
+
|
|
96
|
+
/** Some environments provide a stack trace that is attached to any error. */
|
|
97
|
+
readonly stack?: string
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Additional data, which provides context to the error and may help with
|
|
101
|
+
* debugging.
|
|
102
|
+
*
|
|
103
|
+
* **Warning:** The contents of this object may change in future versions of
|
|
104
|
+
* the SDK.
|
|
105
|
+
*/
|
|
106
|
+
readonly context: Readonly<ErrorContext>
|
|
107
|
+
|
|
108
|
+
// --------------------------------------- Internal ------------------------
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @internal
|
|
112
|
+
* @param code string error code, see {@link ERROR_CODES}
|
|
113
|
+
* @param message optional error message that replace's the message for the given error code
|
|
114
|
+
* @param context optional object containing data that can help debug this error
|
|
115
|
+
* @throws {@link DittoError} `internal`: when supplied with an invalid error code
|
|
116
|
+
*/
|
|
117
|
+
constructor(code: ErrorCode, message?: string | null, context: ErrorContext = {}) {
|
|
118
|
+
if (ERROR_CODES[code] == null) {
|
|
119
|
+
throw new DittoError('internal', `Invalid error code: ${code}`)
|
|
120
|
+
}
|
|
121
|
+
super(message || ERROR_CODES[code])
|
|
122
|
+
|
|
123
|
+
this.code = code
|
|
124
|
+
this.context = Object.freeze({ ...context })
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Create a {@link DittoError} from a {@link DittoFFIError}.
|
|
129
|
+
*
|
|
130
|
+
* The error message will be set to the optional `messageOverride` parameter
|
|
131
|
+
* if supplied, otherwise it will be taken from the `message` property of the
|
|
132
|
+
* given {@link DittoFFIError}. In any case, the details of the underlying FFI
|
|
133
|
+
* error are made available in the error context of the returned {@link DittoError}.
|
|
134
|
+
*
|
|
135
|
+
* @internal
|
|
136
|
+
* @param code string error code from {@link ERROR_CODES}
|
|
137
|
+
* @param messageOverride optional string that will be used as the error
|
|
138
|
+
* message even if a thread-local error message has been retrieved from the
|
|
139
|
+
* FFI
|
|
140
|
+
* @param context optional object containing data that can help debug this error
|
|
141
|
+
* @returns {@link DittoError}
|
|
142
|
+
*/
|
|
143
|
+
static fromFFIError(ffiError: DittoFFIError, code: ErrorCode, messageOverride?: string, context?: ErrorContext): DittoError {
|
|
144
|
+
const message = messageOverride || ffiError.message
|
|
145
|
+
const errorContext = {
|
|
146
|
+
coreError: ffiError.code,
|
|
147
|
+
coreErrorMessage: ffiError.message,
|
|
148
|
+
...context,
|
|
149
|
+
}
|
|
150
|
+
const err = new DittoError(code, message, errorContext)
|
|
151
|
+
if (ffiError.stack != null) {
|
|
152
|
+
// @ts-ignore
|
|
153
|
+
err.stack = ffiError.stack
|
|
154
|
+
}
|
|
155
|
+
return err
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Wraps the given function to catch any {@link DittoFFIError} and rethrow it as
|
|
161
|
+
* a {@link DittoError}, mapping status codes of the {@link DittoFFIError} to
|
|
162
|
+
* error codes of the {@link DittoError} using the given `statusCodeMapping`.
|
|
163
|
+
*
|
|
164
|
+
* Use either this function or {@link mapFFIErrorsAsync} to wrap all calls into
|
|
165
|
+
* the FFI that can fail.
|
|
166
|
+
*
|
|
167
|
+
* If the given status code mapping contains error messages, they will replace
|
|
168
|
+
* any error message that is returned by the FFI.
|
|
169
|
+
*
|
|
170
|
+
* @internal
|
|
171
|
+
* @param closure function that can throw a {@link DittoFFIError}
|
|
172
|
+
* @param statusCodeMapping optional mapping of status codes of the
|
|
173
|
+
* {@link DittoFFIError} to error codes of the {@link DittoError}
|
|
174
|
+
* @param context optional object containing data that can help debug this error
|
|
175
|
+
* @throws {@link DittoError} when the wrapped function throws a {@link DittoFFIError}
|
|
176
|
+
*/
|
|
177
|
+
export function mapFFIErrors<T>(closure: () => T, statusCodeMapping?: FFIErrorMapping, context?: ErrorContext): T {
|
|
178
|
+
try {
|
|
179
|
+
return closure()
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (error instanceof DittoFFIError) {
|
|
182
|
+
throw translateFFIError(error, statusCodeMapping, context)
|
|
183
|
+
}
|
|
184
|
+
throw error
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Wraps the given async function to catch any {@link DittoFFIError} and rethrow
|
|
190
|
+
* it as a {@link DittoError}, mapping status codes of the {@link DittoFFIError}
|
|
191
|
+
* to error codes of the {@link DittoError} using the given `statusCodeMapping`.
|
|
192
|
+
*
|
|
193
|
+
* Use either this function or {@link mapFFIErrors} to wrap any calls into the
|
|
194
|
+
* FFI that can fail.
|
|
195
|
+
*
|
|
196
|
+
* If the given status code mapping contains error messages, they will replace
|
|
197
|
+
* any error message that is returned by the FFI.
|
|
198
|
+
*
|
|
199
|
+
* @internal
|
|
200
|
+
* @param closure async function that can throw a {@link DittoFFIError}
|
|
201
|
+
* @param statusCodeMapping optional mapping of status codes of the
|
|
202
|
+
* {@link DittoFFIError} to error codes of the {@link DittoError}.
|
|
203
|
+
* @param context optional object containing data that can help debug this error
|
|
204
|
+
* @throws {@link DittoError} when the wrapped function throws a {@link DittoFFIError}
|
|
205
|
+
*/
|
|
206
|
+
export async function mapFFIErrorsAsync<T>(closure: () => Promise<T>, statusCodeMapping?: FFIErrorMapping, context?: ErrorContext): Promise<T> {
|
|
207
|
+
try {
|
|
208
|
+
return await closure()
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (error instanceof DittoFFIError) {
|
|
211
|
+
throw translateFFIError(error, statusCodeMapping, context)
|
|
212
|
+
}
|
|
213
|
+
throw error
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Defines which status code of an FFI response should be mapped to which error
|
|
219
|
+
* code of a {@link DittoError}.
|
|
220
|
+
*
|
|
221
|
+
* The second element of the tuple is an optional string that will be used as
|
|
222
|
+
* the error message of the {@link DittoError} instead of the FFI error message
|
|
223
|
+
* if defined.
|
|
224
|
+
*
|
|
225
|
+
* If a key `default` is present, it will be used as the error code if no
|
|
226
|
+
* mapping for a given status code is found.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```typescript
|
|
230
|
+
* const statusCodeMapping: FFIErrorMapping = {
|
|
231
|
+
* '1': ['activation/failed', 'It just didn\'t work out'],
|
|
232
|
+
* 'default': ['sdk/environment-incompatible']
|
|
233
|
+
* }
|
|
234
|
+
* ```
|
|
235
|
+
*
|
|
236
|
+
* @internal
|
|
237
|
+
*/
|
|
238
|
+
type FFIErrorMapping = {
|
|
239
|
+
// statusCode needs to be `string` because it may be a negative number, which
|
|
240
|
+
// can't be used as an object key.
|
|
241
|
+
[statusCode: string]: [ErrorCode, string?]
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const translateFFIError = (ffiError: DittoFFIError, customStatusCodeMapping?: FFIErrorMapping, context?: ErrorContext): DittoError => {
|
|
245
|
+
// Convert the status code to a string because it may be a negative number,
|
|
246
|
+
// which can't be used as an index into an object.
|
|
247
|
+
const statusCode = ffiError.code.toString()
|
|
248
|
+
|
|
249
|
+
let code: ErrorCode, message: string | undefined
|
|
250
|
+
if (customStatusCodeMapping != null && customStatusCodeMapping[statusCode] != null) {
|
|
251
|
+
;[code, message] = customStatusCodeMapping[statusCode]
|
|
252
|
+
} else {
|
|
253
|
+
;[code, message] = DEFAULT_STATUS_CODE_MAPPING[statusCode] ?? DEFAULT_STATUS_CODE_MAPPING.default
|
|
254
|
+
}
|
|
255
|
+
return DittoError.fromFFIError(ffiError, code, message, context)
|
|
256
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright © 2021 DittoLive Incorporated. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { DocumentID } from './document-id'
|
|
6
|
+
|
|
7
|
+
import type { Attachment } from './attachment'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Describes the direction when sorting a query.
|
|
11
|
+
*/
|
|
12
|
+
export type SortDirection = 'ascending' | 'descending'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Defines the various strategies available when inserting a document into a
|
|
16
|
+
* collection.
|
|
17
|
+
*
|
|
18
|
+
* - `merge`: the existing document will be merged with the document being
|
|
19
|
+
* inserted, if there is a pre-existing document.
|
|
20
|
+
*
|
|
21
|
+
* - `insertIfAbsent`: insert the document only if there is not already a
|
|
22
|
+
* document with the same ID in the store. If there is already a document in
|
|
23
|
+
* the store with the same ID then this will be a no-op.
|
|
24
|
+
*
|
|
25
|
+
* - `insertDefaultIfAbsent`: insert the document, with its contents treated as
|
|
26
|
+
* default data, only if there is not already a document with the same ID in
|
|
27
|
+
* the store. If there is already a document in the store with the same ID
|
|
28
|
+
* then this will be a no-op. Use this strategy if you want to insert default
|
|
29
|
+
* data for a given document ID, which you want to treat as common initial
|
|
30
|
+
* data amongst all peers and that you expect to be mutated or overwritten in
|
|
31
|
+
* due course.
|
|
32
|
+
*/
|
|
33
|
+
// We support an additional write strategy, `updateDifferentValues`, but don't
|
|
34
|
+
// expose it in the public API of the JS SDK. This strategy was added to the
|
|
35
|
+
// Cocoa, Android, and .NET SDKs but is not necessarily something we want users to use.
|
|
36
|
+
// If we need to expose this strategy for JS in the future, it is already
|
|
37
|
+
// implemented and can be added to the union type and documentation to make it
|
|
38
|
+
// publicly available. c.f. #8638
|
|
39
|
+
export type WriteStrategy = 'merge' | 'insertIfAbsent' | 'insertDefaultIfAbsent'
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Represents a dictionary of values to be incorporated into a query keyed by
|
|
43
|
+
* the placeholder used within that query. See method
|
|
44
|
+
* {@link Collection.find | find()} of {@link Collection} for more info.
|
|
45
|
+
*
|
|
46
|
+
* This value must not contain any non-finite numbers (`NaN`, `Infinity`,
|
|
47
|
+
* `-Infinity`).
|
|
48
|
+
*/
|
|
49
|
+
// When CBOR-encoding a `QueryArguments` object, consider using
|
|
50
|
+
// `desugarJSObject()` to convert data to a JSON-compatible representation.
|
|
51
|
+
//
|
|
52
|
+
// NOTE: We decided not to upgrade this type to a more specific type alias
|
|
53
|
+
// because this type is only used in the query builder API which is in
|
|
54
|
+
// maintenance only mode.
|
|
55
|
+
export type QueryArguments = {
|
|
56
|
+
[key: string]: any
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Values to be incorporated into a query, keyed by the placeholder used within
|
|
61
|
+
* that query.
|
|
62
|
+
*
|
|
63
|
+
* This value must not contain any non-finite numbers (`NaN`, `Infinity`,
|
|
64
|
+
* `-Infinity`).
|
|
65
|
+
*/
|
|
66
|
+
// When CBOR-encoding a `DQLQueryArguments` object, consider using
|
|
67
|
+
// `desugarJSObject()` to convert data to a JSON-compatible representation.
|
|
68
|
+
//
|
|
69
|
+
// Internally, this supports all types supported by `ditto_types::Value`.
|
|
70
|
+
// https://github.com/getditto/ditto/blob/releases/stable/sdk-4.4/utils/types/src/value.rs#L267
|
|
71
|
+
//
|
|
72
|
+
// NOTE: This type contains `DocumentID` as the only exception to this rule,
|
|
73
|
+
// that should be removed once we remove the query builder API.
|
|
74
|
+
export type DQLQueryArguments = {
|
|
75
|
+
[key: string]: DQLQueryArgumentValue | undefined
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Individual value in a {@link DQLQueryArguments} object.
|
|
80
|
+
*/
|
|
81
|
+
export type DQLQueryArgumentValue = string | number | boolean | Uint8Array | null | Attachment | DocumentID | DQLQueryArgumentValue[] | { [key: string]: DQLQueryArgumentValue }
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright © 2023 DittoLive Incorporated. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// This internal module contains the Ditto FFI error type and helper functions
|
|
5
|
+
// for working with it. Except for the feature flag helper, this module should
|
|
6
|
+
// not depend on any other parts of the Ditto Javascript SDK.
|
|
7
|
+
|
|
8
|
+
import * as dittoCore from './@ditto.core'
|
|
9
|
+
|
|
10
|
+
import type { Pointer, FFIError } from './ffi'
|
|
11
|
+
|
|
12
|
+
/** Matches a `<...>` prefix in an FFI result error message, e.g. `<dql> ...`. **/
|
|
13
|
+
const PREFIX_REGEX = new RegExp(/^<.*?>\s*/)
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Error codes for FFI result.
|
|
17
|
+
*
|
|
18
|
+
* This should include all variants of `FfiError` in `ffi/src/result/error.rs`.
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
// prettier-ignore
|
|
23
|
+
export type FFIResultErrorCode =
|
|
24
|
+
'ActivationLicenseTokenExpired' |
|
|
25
|
+
'ActivationLicenseTokenInvalid' |
|
|
26
|
+
'ActivationLicenseUnsupportedFutureVersion' |
|
|
27
|
+
'ActivationNotActivated' |
|
|
28
|
+
'Base64Invalid' |
|
|
29
|
+
'Crdt' |
|
|
30
|
+
'DqlInvalidQueryArgs' |
|
|
31
|
+
'DqlQueryCompilation' |
|
|
32
|
+
'DqlUnsupported' |
|
|
33
|
+
'JsFloatingStoreOperation' |
|
|
34
|
+
'StoreDatabase' |
|
|
35
|
+
'StoreDocumentNotFound' |
|
|
36
|
+
'StoreQuery' |
|
|
37
|
+
'ValidationDepthLimitExceeded' |
|
|
38
|
+
'ValidationInvalidCbor' |
|
|
39
|
+
'ValidationInvalidJson' |
|
|
40
|
+
'ValidationNotAMap' |
|
|
41
|
+
'ValidationSizeLimitExceeded'
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Represents an exception that occurred during a call into the Ditto FFI.
|
|
45
|
+
*
|
|
46
|
+
* Use the {@link throwOnErrorStatus | throwOnErrorStatus()} helper to
|
|
47
|
+
* automatically throw this error when an FFI call returns with a non-zero
|
|
48
|
+
* return value.
|
|
49
|
+
*
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
export class DittoFFIError extends Error {
|
|
53
|
+
/**
|
|
54
|
+
* The code identifying this error.
|
|
55
|
+
*
|
|
56
|
+
* May be a numerical status code returned by an FFI call or an
|
|
57
|
+
* {@link FFIResultErrorCode} for errors returned on FFI result objects.
|
|
58
|
+
*
|
|
59
|
+
* @see {@link throwOnErrorStatus | throwOnErrorStatus()}
|
|
60
|
+
*/
|
|
61
|
+
readonly code: number | FFIResultErrorCode
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Only call this constructor after having called `FFI.ensureInitialized()`
|
|
65
|
+
* and `FFI.trace()`.
|
|
66
|
+
*
|
|
67
|
+
* @param code numerical status code returned by an FFI call or an
|
|
68
|
+
* {@link FFIResultErrorCode} for errors returned on FFI result objects
|
|
69
|
+
* @param messageOverride overrides the thread-local error message set in
|
|
70
|
+
* Ditto core
|
|
71
|
+
* @param messageFallback fallback message to use if the thread-local error
|
|
72
|
+
* message is empty
|
|
73
|
+
*/
|
|
74
|
+
constructor(code: number | FFIResultErrorCode, messageOverride?: string, messageFallback?: string) {
|
|
75
|
+
// Call `ffiErrorMessage()` even when an override is provided to ensure that
|
|
76
|
+
// the thread-local error message is cleared.
|
|
77
|
+
const threadLocalErrorMessage = ffiErrorMessage()
|
|
78
|
+
super(messageOverride || threadLocalErrorMessage || messageFallback)
|
|
79
|
+
this.code = code
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Throws a {@link DittoFFIError} if the given `ffiError` is not `null`.
|
|
85
|
+
*
|
|
86
|
+
* Removes the thread-local error message from Ditto core. If an error description
|
|
87
|
+
* is available on the given `ffiError`, it is used as the error message of the
|
|
88
|
+
* thrown {@link DittoFFIError}. A prefix in the error message is removed, e.g.
|
|
89
|
+
* `<dql> ...`.
|
|
90
|
+
*
|
|
91
|
+
* If no error description is available, the given `ffiFunctionName` is used to
|
|
92
|
+
* produce a fallback error message.
|
|
93
|
+
*
|
|
94
|
+
* @internal
|
|
95
|
+
*/
|
|
96
|
+
export function throwOnErrorResult(ffiError: Pointer<FFIError> | null, ffiFunctionName: string): void {
|
|
97
|
+
if (ffiError !== null) {
|
|
98
|
+
let errorCode: FFIResultErrorCode
|
|
99
|
+
let errorMsg: string | null
|
|
100
|
+
try {
|
|
101
|
+
errorCode = dittoCore.dittoffi_error_code(ffiError)
|
|
102
|
+
errorMsg = dittoCore.boxCStringIntoString(dittoCore.dittoffi_error_description(ffiError))
|
|
103
|
+
dittoCore.dittoffi_error_free(ffiError)
|
|
104
|
+
} catch (err: any) {
|
|
105
|
+
throw new DittoFFIError(-1, `Failed to retrieve Ditto core error message: ${err.message}`)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (errorMsg == null) {
|
|
109
|
+
errorMsg = `${ffiFunctionName}() failed with error code: ${errorCode}`
|
|
110
|
+
} else {
|
|
111
|
+
// Remove prefix from error message, e.g. `<dql> ...`.
|
|
112
|
+
errorMsg = errorMsg.replace(PREFIX_REGEX, '')
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
throw new DittoFFIError(errorCode, errorMsg)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Retrieves last thread-local error message and removes it.
|
|
121
|
+
*
|
|
122
|
+
* Subsequent call to this function (if no new error message has been set) will
|
|
123
|
+
* always return `null`.
|
|
124
|
+
*
|
|
125
|
+
* This function is not calling `FFI.ensureInitialized()` to avoid a circular
|
|
126
|
+
* dependency. This is okay as long as this function is only called from a
|
|
127
|
+
* context where `FFI.ensureInitialized()` has already been called.
|
|
128
|
+
*
|
|
129
|
+
* @internal
|
|
130
|
+
*/
|
|
131
|
+
function ffiErrorMessage(): string | null {
|
|
132
|
+
const errorMessageCString = dittoCore.ditto_error_message()
|
|
133
|
+
return dittoCore.boxCStringIntoString(errorMessageCString)
|
|
134
|
+
}
|