@dittolive/ditto 4.5.1-experimental.aarch64-linux.1.aarch64 → 4.5.2-rc.2

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.
Files changed (152) hide show
  1. package/DittoReactNative.podspec +25 -0
  2. package/README.md +2 -2
  3. package/node/ditto.cjs.js +1 -1
  4. package/node/ditto.cjs.js.map +1 -0
  5. package/node/ditto.cjs.pretty.js +9655 -0
  6. package/node/ditto.cjs.pretty.js.map +1 -0
  7. package/node/ditto.darwin-arm64.node +0 -0
  8. package/node/ditto.darwin-x64.node +0 -0
  9. package/node/{ditto.linux-arm64.node → ditto.linux-x64.node} +0 -0
  10. package/node/transports.darwin-arm64.node +0 -0
  11. package/node/transports.darwin-x64.node +0 -0
  12. package/package.json +2 -1
  13. package/react-native/android/CMakeLists.txt +37 -0
  14. package/react-native/android/build.gradle +186 -0
  15. package/react-native/android/cpp-adapter.cpp +254 -0
  16. package/react-native/android/gradle.properties +5 -0
  17. package/react-native/android/src/main/AndroidManifest.xml +4 -0
  18. package/react-native/android/src/main/java/com/dittolive/rnsdk/DittoRNSDKModule.java +85 -0
  19. package/react-native/android/src/main/java/com/dittolive/rnsdk/DittoRNSDKPackage.java +28 -0
  20. package/react-native/cpp/include/Arc.hpp +141 -0
  21. package/react-native/cpp/include/Attachment.h +16 -0
  22. package/react-native/cpp/include/Authentication.h +23 -0
  23. package/react-native/cpp/include/Collection.h +13 -0
  24. package/react-native/cpp/include/DQL.h +21 -0
  25. package/react-native/cpp/include/Document.h +17 -0
  26. package/react-native/cpp/include/Identity.h +17 -0
  27. package/react-native/cpp/include/Lifecycle.h +17 -0
  28. package/react-native/cpp/include/LiveQuery.h +17 -0
  29. package/react-native/cpp/include/Logger.h +22 -0
  30. package/react-native/cpp/include/Misc.h +27 -0
  31. package/react-native/cpp/include/Presence.h +14 -0
  32. package/react-native/cpp/include/SmallPeerInfo.h +19 -0
  33. package/react-native/cpp/include/Transports.h +25 -0
  34. package/react-native/cpp/include/TypedArray.hpp +167 -0
  35. package/react-native/cpp/include/Utils.h +61 -0
  36. package/react-native/cpp/include/main.h +10 -0
  37. package/react-native/cpp/src/Attachment.cpp +86 -0
  38. package/react-native/cpp/src/Authentication.cpp +227 -0
  39. package/react-native/cpp/src/Collection.cpp +54 -0
  40. package/react-native/cpp/src/DQL.cpp +256 -0
  41. package/react-native/cpp/src/Document.cpp +146 -0
  42. package/react-native/cpp/src/Identity.cpp +123 -0
  43. package/react-native/cpp/src/Lifecycle.cpp +110 -0
  44. package/react-native/cpp/src/LiveQuery.cpp +63 -0
  45. package/react-native/cpp/src/Logger.cpp +200 -0
  46. package/react-native/cpp/src/Misc.cpp +283 -0
  47. package/react-native/cpp/src/Presence.cpp +79 -0
  48. package/react-native/cpp/src/SmallPeerInfo.cpp +142 -0
  49. package/react-native/cpp/src/Transports.cpp +270 -0
  50. package/react-native/cpp/src/TypedArray.cpp +303 -0
  51. package/react-native/cpp/src/Utils.cpp +138 -0
  52. package/react-native/cpp/src/main.cpp +152 -0
  53. package/react-native/dittoffi/dittoffi.h +4700 -0
  54. package/react-native/dittoffi/ifaddrs.cpp +385 -0
  55. package/react-native/dittoffi/ifaddrs.h +206 -0
  56. package/react-native/ios/DittoRNSDK.h +7 -0
  57. package/react-native/ios/DittoRNSDK.mm +107 -0
  58. package/react-native/ios/YeetJSIUtils.h +60 -0
  59. package/react-native/ios/YeetJSIUtils.mm +196 -0
  60. package/react-native/lib/commonjs/ditto.rn.js +92 -0
  61. package/react-native/lib/commonjs/ditto.rn.js.map +1 -0
  62. package/react-native/lib/commonjs/index.js +61 -0
  63. package/react-native/lib/commonjs/index.js.map +1 -0
  64. package/react-native/lib/module/ditto.rn.js +88 -0
  65. package/react-native/lib/module/ditto.rn.js.map +1 -0
  66. package/react-native/lib/module/index.js +27 -0
  67. package/react-native/lib/module/index.js.map +1 -0
  68. package/react-native/lib/typescript/ditto.rn.d.ts +15 -0
  69. package/react-native/lib/typescript/ditto.rn.d.ts.map +1 -0
  70. package/react-native/lib/typescript/index.d.ts +1 -0
  71. package/react-native/lib/typescript/index.d.ts.map +1 -0
  72. package/react-native/src/ditto.rn.ts +91 -0
  73. package/react-native/src/environment/environment.fallback.ts +4 -0
  74. package/react-native/src/index.ts +26 -0
  75. package/react-native/src/sources/@cbor-redux.ts +2 -0
  76. package/react-native/src/sources/@ditto.core.ts +1 -0
  77. package/react-native/src/sources/@environment.ts +1 -0
  78. package/react-native/src/sources/attachment-fetch-event.ts +54 -0
  79. package/react-native/src/sources/attachment-fetcher-manager.ts +144 -0
  80. package/react-native/src/sources/attachment-fetcher.ts +134 -0
  81. package/react-native/src/sources/attachment-token.ts +48 -0
  82. package/react-native/src/sources/attachment.ts +74 -0
  83. package/react-native/src/sources/augment.ts +101 -0
  84. package/react-native/src/sources/authenticator.ts +314 -0
  85. package/react-native/src/sources/base-pending-cursor-operation.ts +239 -0
  86. package/react-native/src/sources/base-pending-id-specific-operation.ts +109 -0
  87. package/react-native/src/sources/bridge.ts +553 -0
  88. package/react-native/src/sources/build-time-constants.ts +8 -0
  89. package/react-native/src/sources/cbor.ts +35 -0
  90. package/react-native/src/sources/collection-interface.ts +67 -0
  91. package/react-native/src/sources/collection.ts +212 -0
  92. package/react-native/src/sources/collections-event.ts +99 -0
  93. package/react-native/src/sources/counter.ts +82 -0
  94. package/react-native/src/sources/ditto.ts +979 -0
  95. package/react-native/src/sources/document-id.ts +159 -0
  96. package/react-native/src/sources/document-path.ts +306 -0
  97. package/react-native/src/sources/document.ts +193 -0
  98. package/react-native/src/sources/epilogue.ts +30 -0
  99. package/react-native/src/sources/error-codes.ts +52 -0
  100. package/react-native/src/sources/error.ts +208 -0
  101. package/react-native/src/sources/essentials.ts +53 -0
  102. package/react-native/src/sources/ffi-error.ts +122 -0
  103. package/react-native/src/sources/ffi.ts +2012 -0
  104. package/react-native/src/sources/identity.ts +163 -0
  105. package/react-native/src/sources/init.ts +71 -0
  106. package/react-native/src/sources/internal.ts +109 -0
  107. package/react-native/src/sources/keep-alive.ts +73 -0
  108. package/react-native/src/sources/key-path.ts +198 -0
  109. package/react-native/src/sources/live-query-event.ts +208 -0
  110. package/react-native/src/sources/live-query-manager.ts +102 -0
  111. package/react-native/src/sources/live-query.ts +166 -0
  112. package/react-native/src/sources/logger.ts +196 -0
  113. package/react-native/src/sources/main.ts +60 -0
  114. package/react-native/src/sources/observer-manager.ts +178 -0
  115. package/react-native/src/sources/observer.ts +79 -0
  116. package/react-native/src/sources/pending-collections-operation.ts +232 -0
  117. package/react-native/src/sources/pending-cursor-operation.ts +218 -0
  118. package/react-native/src/sources/pending-id-specific-operation.ts +218 -0
  119. package/react-native/src/sources/presence-manager.ts +161 -0
  120. package/react-native/src/sources/presence.ts +233 -0
  121. package/react-native/src/sources/query-result-item.ts +116 -0
  122. package/react-native/src/sources/query-result.ts +55 -0
  123. package/react-native/src/sources/register.ts +95 -0
  124. package/react-native/src/sources/small-peer-info.ts +177 -0
  125. package/react-native/src/sources/static-tcp-client.ts +6 -0
  126. package/react-native/src/sources/store-observer.ts +177 -0
  127. package/react-native/src/sources/store.ts +385 -0
  128. package/react-native/src/sources/subscription-manager.ts +99 -0
  129. package/react-native/src/sources/subscription.ts +89 -0
  130. package/react-native/src/sources/sync-subscription.ts +90 -0
  131. package/react-native/src/sources/sync.ts +559 -0
  132. package/react-native/src/sources/test-helpers.ts +24 -0
  133. package/react-native/src/sources/transport-conditions-manager.ts +104 -0
  134. package/react-native/src/sources/transport-config.ts +430 -0
  135. package/react-native/src/sources/update-result.ts +66 -0
  136. package/react-native/src/sources/update-results-map.ts +57 -0
  137. package/react-native/src/sources/websocket-client.ts +7 -0
  138. package/react-native/src/sources/write-transaction-collection.ts +122 -0
  139. package/react-native/src/sources/write-transaction-pending-cursor-operation.ts +101 -0
  140. package/react-native/src/sources/write-transaction-pending-id-specific-operation.ts +74 -0
  141. package/react-native/src/sources/write-transaction.ts +121 -0
  142. package/react-native.config.js +9 -0
  143. package/types/ditto.d.ts.map +1 -0
  144. package/web/ditto.es6.js +1 -1
  145. package/web/ditto.es6.js.map +1 -0
  146. package/web/ditto.es6.pretty.js +12600 -0
  147. package/web/ditto.es6.pretty.js.map +1 -0
  148. package/web/ditto.umd.js +1 -1
  149. package/web/ditto.umd.js.map +1 -0
  150. package/web/ditto.umd.pretty.js +12669 -0
  151. package/web/ditto.umd.pretty.js.map +1 -0
  152. package/web/ditto.wasm +0 -0
@@ -0,0 +1,52 @@
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 occured. 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 occured. Please get in touch with Ditto customer service to report this incident.',
23
+
24
+ //
25
+ // Query errors
26
+ //
27
+
28
+ /** Error for invalid DQL query arguments. */
29
+ 'query/arguments-invalid': 'The query arguments were invalid.',
30
+
31
+ /** Errors that occur during execution of a query */
32
+ 'query/execution': 'The query could not be executed.',
33
+
34
+ /** Error for an invalid DQL query. */
35
+ 'query/invalid': 'The query was invalid.',
36
+
37
+ /** Error for a query that uses DQL features not supported in this version or not supported by the currently used API. */
38
+ 'query/unsupported': 'The query contains unsupported features.',
39
+
40
+ //
41
+ // Store errors
42
+ //
43
+
44
+ /** Error in the storage backend. */
45
+ 'store/backend': 'An error occurred with the storage backend.',
46
+
47
+ /** Error for an invalid CRDT. */
48
+ 'store/crdt': 'An error occurred processing a CRDT.',
49
+
50
+ /** Error for a document not found. */
51
+ 'store/document-not-found': 'The document with the provided ID could not be found.',
52
+ } as const
@@ -0,0 +1,208 @@
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
+ // SDK-specific errors
20
+ JsFloatingStoreOperation: ['internal', 'Internal inconsistency, an outstanding store operation was not awaited.'],
21
+
22
+ // DQL errors
23
+ DqlQueryCompilation: ['query/invalid'],
24
+ DqlInvalidQueryArgs: ['query/arguments-invalid'],
25
+ DqlUnsupported: ['query/unsupported'],
26
+ StoreQuery: ['query/execution'],
27
+
28
+ // Store errors
29
+ StoreDocumentNotFound: ['store/document-not-found'],
30
+ StoreDatabase: ['store/backend'],
31
+ Crdt: ['store/crdt'],
32
+
33
+ default: ['internal/unknown-error'],
34
+ }
35
+
36
+ /**
37
+ * `DittoError` is a subclass of the default Javascript `Error`. You can
38
+ * identify specific errors programatically using the
39
+ * {@link DittoError.code | code} property.
40
+ *
41
+ * Please reference {@link ERROR_CODES} for a comprehensive list of
42
+ * possible error codes in this SDK version.
43
+ */
44
+ export class DittoError extends Error {
45
+ /**
46
+ * Use the error code to identify a specific error programatically.
47
+ *
48
+ * @see {@link ERROR_CODES} for a comprehensive list of possible
49
+ * error codes in this SDK version.
50
+ */
51
+ readonly code: ErrorCode = 'internal'
52
+
53
+ /** Some environments provide a stack trace that is attached to any error. */
54
+ readonly stack?: string
55
+
56
+ /**
57
+ * Additional data, which provides context to the error and may help with
58
+ * debugging.
59
+ *
60
+ * **Warning:** The contents of this object may change in future versions of
61
+ * the SDK.
62
+ */
63
+ readonly context: Readonly<ErrorContext>
64
+
65
+ // --------------------------------------- Internal ------------------------
66
+
67
+ /**
68
+ * @internal
69
+ * @param code string error code, see {@link ERROR_CODES}
70
+ * @param message optional error message that replace's the message for the given error code
71
+ * @param context optional object containing data that can help debug this error
72
+ * @throws {@link DittoError} `internal`: when supplied with an invalid error code
73
+ */
74
+ constructor(code: ErrorCode, message?: string | null, context: ErrorContext = {}) {
75
+ if (ERROR_CODES[code] == null) {
76
+ throw new DittoError('internal', `Invalid error code: ${code}`)
77
+ }
78
+ super(`<${code}> ${message || ERROR_CODES[code]}`)
79
+
80
+ this.code = code
81
+ this.context = Object.freeze({ ...context })
82
+ }
83
+
84
+ /**
85
+ * Create a {@link DittoError} from a {@link DittoFFIError}.
86
+ *
87
+ * The error message will be set to the optional `messageOverride` parameter
88
+ * if supplied, otherwise it will be taken from the `message` property of the
89
+ * given {@link DittoFFIError}. In any case, the details of the underlying FFI
90
+ * error are made available in the error context of the returned {@link DittoError}.
91
+ *
92
+ * @internal
93
+ * @param code string error code from {@link ERROR_CODES}
94
+ * @param messageOverride optional string that will be used as the error
95
+ * message even if a thread-local error message has been retrieved from the
96
+ * FFI
97
+ * @param context optional object containing data that can help debug this error
98
+ * @returns {@link DittoError}
99
+ */
100
+ static fromFFIError(ffiError: DittoFFIError, code: ErrorCode, messageOverride?: string, context?: ErrorContext): DittoError {
101
+ const message = messageOverride || ffiError.message
102
+ const errorContext = {
103
+ coreError: ffiError.code,
104
+ coreErrorMessage: ffiError.message,
105
+ ...context,
106
+ }
107
+ return new DittoError(code, message, errorContext)
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Wraps the given function to catch any {@link DittoFFIError} and rethrow it as
113
+ * a {@link DittoError}, mapping status codes of the {@link DittoFFIError} to
114
+ * error codes of the {@link DittoError} using the given `statusCodeMapping`.
115
+ *
116
+ * Use either this function or {@link mapFFIErrorsAsync} to wrap all calls into
117
+ * the FFI that can fail.
118
+ *
119
+ * If the given status code mapping contains error messages, they will replace
120
+ * any error message that is returned by the FFI.
121
+ *
122
+ * @internal
123
+ * @param closure function that can throw a {@link DittoFFIError}
124
+ * @param statusCodeMapping optional mapping of status codes of the
125
+ * {@link DittoFFIError} to error codes of the {@link DittoError}
126
+ * @param context optional object containing data that can help debug this error
127
+ * @throws {@link DittoError} when the wrapped function throws a {@link DittoFFIError}
128
+ */
129
+ export function mapFFIErrors<T>(closure: () => T, statusCodeMapping?: FFIErrorMapping, context?: ErrorContext): T {
130
+ try {
131
+ return closure()
132
+ } catch (error) {
133
+ if (error instanceof DittoFFIError) {
134
+ throw translateFFIError(error, statusCodeMapping, context)
135
+ }
136
+ throw error
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Wraps the given async function to catch any {@link DittoFFIError} and rethrow
142
+ * it as a {@link DittoError}, mapping status codes of the {@link DittoFFIError}
143
+ * to error codes of the {@link DittoError} using the given `statusCodeMapping`.
144
+ *
145
+ * Use either this function or {@link mapFFIErrors} to wrap any calls into the
146
+ * FFI that can fail.
147
+ *
148
+ * If the given status code mapping contains error messages, they will replace
149
+ * any error message that is returned by the FFI.
150
+ *
151
+ * @internal
152
+ * @param closure async function that can throw a {@link DittoFFIError}
153
+ * @param statusCodeMapping optional mapping of status codes of the
154
+ * {@link DittoFFIError} to error codes of the {@link DittoError}.
155
+ * @param context optional object containing data that can help debug this error
156
+ * @throws {@link DittoError} when the wrapped function throws a {@link DittoFFIError}
157
+ */
158
+ export async function mapFFIErrorsAsync<T>(closure: () => Promise<T>, statusCodeMapping?: FFIErrorMapping, context?: ErrorContext): Promise<T> {
159
+ try {
160
+ return await closure()
161
+ } catch (error) {
162
+ if (error instanceof DittoFFIError) {
163
+ throw translateFFIError(error, statusCodeMapping, context)
164
+ }
165
+ throw error
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Defines which status code of an FFI response should be mapped to which error
171
+ * code of a {@link DittoError}.
172
+ *
173
+ * The second element of the tuple is an optional string that will be used as
174
+ * the error message of the {@link DittoError} instead of the FFI error message
175
+ * if defined.
176
+ *
177
+ * If a key `default` is present, it will be used as the error code if no
178
+ * mapping for a given status code is found.
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * const statusCodeMapping: FFIErrorMapping = {
183
+ * '1': ['activation/failed', 'It just didn\'t work out'],
184
+ * 'default': ['sdk/environment-incompatible']
185
+ * }
186
+ * ```
187
+ *
188
+ * @internal
189
+ */
190
+ type FFIErrorMapping = {
191
+ // statusCode needs to be `string` because it may be a negative number, which
192
+ // can't be used as an object key.
193
+ [statusCode: string]: [ErrorCode, string?]
194
+ }
195
+
196
+ const translateFFIError = (ffiError: DittoFFIError, customStatusCodeMapping?: FFIErrorMapping, context?: ErrorContext): DittoError => {
197
+ // Convert the status code to a string because it may be a negative number,
198
+ // which can't be used as an index into an object.
199
+ const statusCode = ffiError.code.toString()
200
+
201
+ let code: ErrorCode, message: string | undefined
202
+ if (customStatusCodeMapping != null && customStatusCodeMapping[statusCode] != null) {
203
+ ;[code, message] = customStatusCodeMapping[statusCode]
204
+ } else {
205
+ ;[code, message] = DEFAULT_STATUS_CODE_MAPPING[statusCode] ?? DEFAULT_STATUS_CODE_MAPPING.default
206
+ }
207
+ return DittoError.fromFFIError(ffiError, code, message, context)
208
+ }
@@ -0,0 +1,53 @@
1
+ //
2
+ // Copyright © 2021 DittoLive Incorporated. All rights reserved.
3
+ //
4
+
5
+ import { DocumentID } from './document-id'
6
+
7
+ /**
8
+ * Describes the direction when sorting a query.
9
+ */
10
+ export type SortDirection = 'ascending' | 'descending'
11
+
12
+ /**
13
+ * Defines the various strategies available when inserting a document into a
14
+ * collection.
15
+ *
16
+ * - `merge`: the existing document will be merged with the document being
17
+ * inserted, if there is a pre-existing document.
18
+ *
19
+ * - `insertIfAbsent`: insert the document only if there is not already a
20
+ * document with the same ID in the store. If there is already a document in
21
+ * the store with the same ID then this will be a no-op.
22
+ *
23
+ * - `insertDefaultIfAbsent`: insert the document, with its contents treated as
24
+ * default data, only if there is not already a document with the same ID in
25
+ * the store. If there is already a document in the store with the same ID
26
+ * then this will be a no-op. Use this strategy if you want to insert default
27
+ * data for a given document ID, which you want to treat as common initial
28
+ * data amongst all peers and that you expect to be mutated or overwritten in
29
+ * due course.
30
+ */
31
+ export type WriteStrategy = 'merge' | 'insertIfAbsent' | 'insertDefaultIfAbsent'
32
+
33
+ /**
34
+ * Represents a dictionary of values to be incorporated into a query keyed
35
+ * by the placeholder used within that query. See method
36
+ * {@link Collection.find | find()} of {@link Collection} for more info.
37
+ */
38
+ export type QueryArguments = {
39
+ [key: string]: any
40
+ }
41
+
42
+ /**
43
+ * Values to be incorporated into a query, keyed by the placeholder used within
44
+ * that query.
45
+ */
46
+ export type DQLQueryArguments = {
47
+ [key: string]: DQLQueryArgumentValue | undefined
48
+ }
49
+
50
+ /**
51
+ * Individual value in a {@link DQLQueryArguments} object.
52
+ */
53
+ export type DQLQueryArgumentValue = string | number | boolean | Uint8Array | null | DocumentID | DQLQueryArgumentValue[] | { [key: string]: DQLQueryArgumentValue }
@@ -0,0 +1,122 @@
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
+ * @internal
19
+ */
20
+ // prettier-ignore
21
+ export type FFIResultErrorCode =
22
+ 'StoreDocumentNotFound' |
23
+ 'StoreDatabase' |
24
+ 'StoreQuery' |
25
+ 'Crdt' |
26
+ 'JsFloatingStoreOperation' |
27
+ 'DqlQueryCompilation' |
28
+ 'DqlInvalidQueryArgs' |
29
+ 'DqlUnsupported'
30
+
31
+ /**
32
+ * Represents an exception that occurred during a call into the Ditto FFI.
33
+ *
34
+ * Use the {@link throwOnErrorStatus | throwOnErrorStatus()} helper to
35
+ * automatically throw this error when an FFI call returns with a non-zero
36
+ * return value.
37
+ *
38
+ * @internal
39
+ */
40
+ export class DittoFFIError extends Error {
41
+ /**
42
+ * The code identifying this error.
43
+ *
44
+ * May be a numerical status code returned by an FFI call or an
45
+ * {@link FFIResultErrorCode} for errors returned on FFI result objects.
46
+ *
47
+ * @see {@link throwOnErrorStatus | throwOnErrorStatus()}
48
+ */
49
+ readonly code: number | FFIResultErrorCode
50
+
51
+ /**
52
+ * Only call this constructor after having called `FFI.ensureInitialized()`
53
+ * and `FFI.trace()`.
54
+ *
55
+ * @param code numerical status code returned by an FFI call or an
56
+ * {@link FFIResultErrorCode} for errors returned on FFI result objects
57
+ * @param messageOverride overrides the thread-local error message set in
58
+ * Ditto core
59
+ * @param messageFallback fallback message to use if the thread-local error
60
+ * message is empty
61
+ */
62
+ constructor(code: number | FFIResultErrorCode, messageOverride?: string, messageFallback?: string) {
63
+ // Call `ffiErrorMessage()` even when an override is provided to ensure that
64
+ // the thread-local error message is cleared.
65
+ const threadLocalErrorMessage = ffiErrorMessage()
66
+ super(messageOverride || threadLocalErrorMessage || messageFallback)
67
+ this.code = code
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Throws a {@link DittoFFIError} if the given `ffiError` is not `null`.
73
+ *
74
+ * Removes the thread-local error message from Ditto core. If an error description
75
+ * is available on the given `ffiError`, it is used as the error message of the
76
+ * thrown {@link DittoFFIError}. A prefix in the error message is removed, e.g.
77
+ * `<dql> ...`.
78
+ *
79
+ * If no error description is available, the given `ffiFunctionName` is used to
80
+ * produce a fallback error message.
81
+ *
82
+ * @internal
83
+ */
84
+ export function throwOnErrorResult(ffiError: Pointer<FFIError> | null, ffiFunctionName: string): void {
85
+ if (ffiError !== null) {
86
+ let errorCode: FFIResultErrorCode
87
+ let errorMsg: string | null
88
+ try {
89
+ errorCode = dittoCore.dittoffi_error_code(ffiError)
90
+ errorMsg = dittoCore.boxCStringIntoString(dittoCore.dittoffi_error_description(ffiError))
91
+ dittoCore.dittoffi_error_free(ffiError)
92
+ } catch (err: any) {
93
+ throw new DittoFFIError(-1, `Failed to retrieve Ditto core error message: ${err.message}`)
94
+ }
95
+
96
+ if (errorMsg == null) {
97
+ errorMsg = `${ffiFunctionName}() failed with error code: ${errorCode}`
98
+ } else {
99
+ // Remove prefix from error message, e.g. `<dql> ...`.
100
+ errorMsg = errorMsg.replace(PREFIX_REGEX, '')
101
+ }
102
+
103
+ throw new DittoFFIError(errorCode, errorMsg)
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Retrieves last thread-local error message and removes it.
109
+ *
110
+ * Subsequent call to this function (if no new error message has been set) will
111
+ * always return `null`.
112
+ *
113
+ * This function is not calling `FFI.ensureInitialized()` to avoid a circular
114
+ * dependency. This is okay as long as this function is only called from a
115
+ * context where `FFI.ensureInitialized()` has already been called.
116
+ *
117
+ * @internal
118
+ */
119
+ function ffiErrorMessage(): string | null {
120
+ const errorMessageCString = dittoCore.ditto_error_message()
121
+ return dittoCore.boxCStringIntoString(errorMessageCString)
122
+ }