@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,2012 @@
1
+ //
2
+ // Copyright © 2023 DittoLive Incorporated. All rights reserved.
3
+ //
4
+
5
+ // NOTE: This is a temporary *hand-written* shim file for glue code for the
6
+ // Ditto native Node module. Mid to long term, we'll either move all of this
7
+ // code to the native side or generate this file via a companion CLI tool or
8
+ // something equivalent.
9
+ //
10
+ // IMPORTANT: please leave this file self-contained and do not import any
11
+ // sources from the JS SDK.
12
+
13
+ import * as dittoCore from './@ditto.core'
14
+ import * as Environment from './@environment'
15
+ import { throwOnErrorResult } from './ffi-error'
16
+
17
+ // -------------------------------------------------------------- Prelude ------
18
+
19
+ /** @internal */
20
+ export type Pointer<Type> = { type: Type; addr: string }
21
+
22
+ /** @internal */
23
+ export var isTracingEnabled = false
24
+
25
+ /** @internal */
26
+ export const DittoCRDTTypeKey = '_ditto_internal_type_jkb12973t4b'
27
+
28
+ /** @internal */
29
+ export const DittoCRDTValueKey = '_value'
30
+
31
+ // ---------------------------------------------------------------- Types ------
32
+
33
+ /** @internal */
34
+ export type FFIError = 'dittoffi_error_t'
35
+
36
+ /** @internal */
37
+ export type FFIWriteStrategy = 'merge' | 'insertIfAbsent' | 'insertDefaultIfAbsent'
38
+
39
+ /** @internal */
40
+ export type FFIDocument = 'CDocument_t'
41
+
42
+ /** @internal */
43
+ export type FFIDqlResult = 'CDqlResult_t'
44
+
45
+ /** @internal */
46
+ export type FFIDqlResponse = 'CDqlResponse_t'
47
+
48
+ /** @internal */
49
+ export type FFIReadTransaction = 'CReadTransaction_t'
50
+
51
+ /** @internal */
52
+ export type FFIWriteTransaction = 'CWriteTransaction_t'
53
+
54
+ /** @internal */
55
+ export type FFIUninitializedDitto = 'CUninitializedDitto_t'
56
+
57
+ /** @internal */
58
+ export type FFIDitto = 'CDitto_t'
59
+
60
+ /** @internal */
61
+ export type FFIIdentityConfig = 'CIdentityConfig_t'
62
+
63
+ /** @internal */
64
+ export type FFILoginProvider = 'CLoginProvider_t'
65
+
66
+ /** @internal */
67
+ export type FFIAuthServerAuthRequest = 'CAuthServerAuthRequest_t'
68
+
69
+ /** @internal */
70
+ export type FFIAuthServerRefreshRequest = 'CAuthServerRefreshRequest_t'
71
+
72
+ /** @internal */
73
+ export type FFIStaticTCPClient = 'TransportHandle_StaticTCPClientPlatformEvent_t'
74
+
75
+ /** @internal */
76
+ export type FFIWebsocketClient = 'TransportHandle_WebsocketClientPlatformEvent_t'
77
+
78
+ /** @internal */
79
+ export type FFISmallPeerInfoSyncScope = 'LocalPeerOnly' | 'BigPeerOnly'
80
+
81
+ /** @internal */
82
+ export type LiveQueryAvailability = 'Always' | 'WhenSignalled'
83
+
84
+ /** @internal */
85
+ export type StringPrimitiveFormat = 'WithQuotes' | 'WithoutQuotes'
86
+
87
+ /** @internal */
88
+ export type LogLevel = 'Error' | 'Warning' | 'Info' | 'Debug' | 'Verbose'
89
+
90
+ /** @internal */
91
+ export type LicenseVerificationResult = 'LicenseOk' | 'VerificationFailed' | 'LicenseExpired' | 'UnsupportedFutureVersion'
92
+
93
+ /** @internal */
94
+ export type WebsocketMode = 'Enabled' | 'Disabled'
95
+
96
+ /** @internal */
97
+ export type HistoryTracking = 'Enabled' | 'Disabled'
98
+
99
+ /** @internal */
100
+ export type ConditionSource = 'Bluetooth' | 'Tcp' | 'Awdl' | 'Mdns'
101
+
102
+ /** @internal */
103
+ export type TransportCondition = 'Unknown' | 'Ok' | 'GenericFailure' | 'AppInBackground' | 'MdnsFailure' | 'TcpListenFailure' | 'NoBleCentralPermission' | 'NoBlePeripheralPermission' | 'CannotEstablishConnection' | 'BleDisabled' | 'NoBleHardware' | 'WifiDisabled' | 'TemporarilyUnavailable'
104
+
105
+ /** @internal */
106
+ export type TransportsError = 'None' | 'Generic' | 'Unavailable'
107
+
108
+ /** @internal */
109
+ export type TransportBluetooth = 'DittoTransportsBLE_t'
110
+
111
+ /** @internal */
112
+ export type TransportAWDL = 'DittoTransportsAWDL_t'
113
+
114
+ /** @internal */
115
+ export type TransportLAN = 'DittoTransportsLAN_t'
116
+
117
+ /** @internal */
118
+ export type TransportMdnsClient = 'DittoTransportsMdnsClient_t'
119
+
120
+ /** @internal */
121
+ export type TransportMdnsServer = 'DittoTransportsMdnsServer_t'
122
+
123
+ /** @internal */
124
+ export type Platform = 'Windows' | 'Mac' | 'Ios' | 'Android' | 'Linux' | 'Web' | 'Unknown'
125
+
126
+ /** @internal */
127
+ export type Language = 'Swift' | 'ObjectiveC' | 'CPlusPlus' | 'CSharp' | 'JavaScript' | 'Unknown' | 'Rust'
128
+
129
+ /** @internal */
130
+ export type OrderBy = { query: string; direction: 'Ascending' | 'Descending' }
131
+
132
+ /** @internal */
133
+ export type PathAccessorType = 'String' | 'Number' | 'Int' | 'UInt' | 'Float' | 'Double' | 'Bool' | 'Null' | 'Object' | 'Array' | 'Any' | 'Counter' | 'Register' | 'Attachment' | 'Rga' | 'RWMap'
134
+
135
+ /** @internal */
136
+ export enum DittoCRDTType {
137
+ counter = 0,
138
+ register = 1,
139
+ attachment = 2,
140
+ rga = 3,
141
+ rwMap = 4,
142
+ }
143
+
144
+ /** @internal */
145
+ export type CBParamsDocs = {
146
+ documents: Pointer<FFIDocument>[]
147
+ is_initial: boolean
148
+ old_documents: Pointer<FFIDocument>[]
149
+ insertions: number[]
150
+ deletions: number[]
151
+ updates: number[]
152
+ moves: [number, number][]
153
+ }
154
+
155
+ /** @internal */
156
+ export type CBParamsQueryResult = {
157
+ query_result: Pointer<FFIDqlResponse>
158
+ }
159
+
160
+ /** @internal */
161
+ export type CBORPathResult = {
162
+ statusCode: number
163
+ cbor: Uint8Array
164
+ }
165
+
166
+ /** Various options to pass the web assembly module to Ditto. */
167
+ export type WebAssemblyModule = RequestInfo | URL | Response | BufferSource | WebAssembly.Module | string | null
168
+
169
+ /** @internal */
170
+ export type AttachmentFileOperation = 'Copy' | 'Move'
171
+
172
+ /** @internal */
173
+ // IDEA: rename to AttachmentHandleType for consistency.
174
+ export type AttachmentHandle = 'AttachmentHandle_t'
175
+
176
+ /** @internal */
177
+ export type RawAttachment = {
178
+ id: Uint8Array
179
+ len: number | BigInt
180
+ handle: Pointer<AttachmentHandle>
181
+ }
182
+
183
+ // -------------------------------------- Linux & Windows Transports ------
184
+
185
+ export function dittoAddInternalBLEClientTransport(ditto: Pointer<FFIDitto>): any {
186
+ if (Environment.isNodeBuild) {
187
+ return dittoCore.ditto_add_internal_ble_client_transport(ditto)
188
+ }
189
+
190
+ if (Environment.isWebBuild) {
191
+ throw new Error(`Internal inconsistency, can't add internal BLE client, only available for non-apple Ditto Node builds.`)
192
+ }
193
+ }
194
+
195
+ export function dittoAddInternalBLEServerTransport(ditto: Pointer<FFIDitto>): any {
196
+ if (Environment.isNodeBuild) {
197
+ return dittoCore.ditto_add_internal_ble_server_transport(ditto)
198
+ }
199
+
200
+ if (Environment.isWebBuild) {
201
+ throw new Error(`Internal inconsistency, can't add internal BLE server, only available for non-apple Ditto Node builds.`)
202
+ }
203
+ }
204
+
205
+ export function bleClientFreeHandle(handle: any) {
206
+ if (Environment.isNodeBuild) {
207
+ return dittoCore.ble_client_free_handle(handle)
208
+ }
209
+
210
+ if (Environment.isWebBuild) {
211
+ throw new Error(`Internal inconsistency, can't free BLE client handle, only available for non-apple Ditto Node builds.`)
212
+ }
213
+ }
214
+
215
+ export function bleServerFreeHandle(handle: any) {
216
+ if (Environment.isNodeBuild) {
217
+ return dittoCore.ble_server_free_handle(handle)
218
+ }
219
+
220
+ if (Environment.isWebBuild) {
221
+ throw new Error(`Internal inconsistency, can't free BLE server handle, only available for non-apple Ditto Node builds.`)
222
+ }
223
+ }
224
+
225
+ export function dittoAddInternalMdnsTransport(ditto: Pointer<FFIDitto>): any {
226
+ if (Environment.isNodeBuild) {
227
+ return dittoCore.ditto_add_internal_mdns_client_transport(ditto)
228
+ }
229
+
230
+ if (Environment.isWebBuild) {
231
+ throw new Error(`Internal inconsistency, can't add internal MDNS transport/client, only available for non-apple Ditto Node builds.`)
232
+ }
233
+ }
234
+
235
+ export function mdnsClientFreeHandle(handle: any) {
236
+ if (Environment.isNodeBuild) {
237
+ return dittoCore.mdns_client_free_handle(handle)
238
+ }
239
+
240
+ if (Environment.isWebBuild) {
241
+ throw new Error(`Internal inconsistency, can't free MDNS transport/client handle, only available for non-apple Ditto Node builds.`)
242
+ }
243
+ }
244
+
245
+ export function dittoAddInternalMdnsAdvertiser(ditto: Pointer<FFIDitto>): any {
246
+ if (Environment.isNodeBuild) {
247
+ return dittoCore.ditto_add_internal_mdns_server_transport(ditto)
248
+ }
249
+
250
+ if (Environment.isWebBuild) {
251
+ throw new Error(`Internal inconsistency, can't add internal MDNS advertiser/server, only available for non-apple Ditto Node builds.`)
252
+ }
253
+ }
254
+
255
+ export function mdnsServerFreeHandle(handle: any) {
256
+ if (Environment.isNodeBuild) {
257
+ return dittoCore.mdns_server_free_handle(handle)
258
+ }
259
+
260
+ if (Environment.isWebBuild) {
261
+ throw new Error(`Internal inconsistency, can't free MDNS advertiser/server handle, only available for non-apple Ditto Node builds.`)
262
+ }
263
+ }
264
+
265
+ // ------------------------------------------------------------- Document ------
266
+
267
+ /** @internal */
268
+ export function documentSetCBORWithTimestamp(document: Pointer<FFIDocument>, path: string, cbor: Uint8Array, timestamp: number) {
269
+ trace()
270
+ ensureInitialized()
271
+
272
+ const pathX = bytesFromString(path)
273
+ const errorCode = dittoCore.ditto_document_set_cbor_with_timestamp(document, pathX, cbor, timestamp)
274
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_document_set_cbor_with_timestamp() failed with error code: ${errorCode}`)
275
+ }
276
+
277
+ /** @internal */
278
+ export function documentSetCBOR(document: Pointer<FFIDocument>, path: string, cbor: Uint8Array) {
279
+ trace()
280
+ ensureInitialized()
281
+
282
+ // NOTE: not sure if this should be async or not.
283
+ const pathX = bytesFromString(path)
284
+ const errorCode = dittoCore.ditto_document_set_cbor(document, pathX, cbor)
285
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_document_set_cbor() failed with error code: ${errorCode}`)
286
+ }
287
+
288
+ /** @internal */
289
+ export function documentID(self: Pointer<FFIDocument>): Uint8Array {
290
+ trace()
291
+ ensureInitialized()
292
+
293
+ const documentIDX = dittoCore.ditto_document_id(self)
294
+ return dittoCore.boxCBytesIntoBuffer(documentIDX)
295
+ }
296
+
297
+ /** @internal */
298
+ export function documentCBOR(self: Pointer<FFIDocument>): Uint8Array {
299
+ trace()
300
+ ensureInitialized()
301
+
302
+ const cborBytes = dittoCore.ditto_document_cbor(self)
303
+ return dittoCore.boxCBytesIntoBuffer(cborBytes)
304
+ }
305
+
306
+ /** @internal */
307
+ export function documentGetCBORWithPathType(document: Pointer<FFIDocument>, path: string, pathType: PathAccessorType): CBORPathResult {
308
+ trace()
309
+ ensureInitialized()
310
+
311
+ const pathBytes = bytesFromString(path)
312
+ const cborPathResultRaw = dittoCore.ditto_document_get_cbor_with_path_type(document, pathBytes, pathType)
313
+
314
+ const cborPathResult: CBORPathResult = {
315
+ statusCode: cborPathResultRaw.status_code,
316
+ cbor: dittoCore.boxCBytesIntoBuffer(cborPathResultRaw.cbor),
317
+ }
318
+
319
+ return cborPathResult
320
+ }
321
+
322
+ /** @internal */
323
+ export function documentRemove(document: Pointer<FFIDocument>, path: string) {
324
+ trace()
325
+ ensureInitialized()
326
+
327
+ const pathBytes = bytesFromString(path)
328
+ const errorCode = dittoCore.ditto_document_remove(document, pathBytes)
329
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_document_remove() failed with error code: ${errorCode}`)
330
+ }
331
+
332
+ /** @internal */
333
+ export function documentIncrementCounter(document: Pointer<FFIDocument>, path: string, amount: number) {
334
+ trace()
335
+ ensureInitialized()
336
+
337
+ const pathBytes = bytesFromString(path)
338
+ const errorCode = dittoCore.ditto_document_increment_counter(document, pathBytes, amount)
339
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_document_increment_counter() failed with error code: ${errorCode}`)
340
+ }
341
+
342
+ /** @internal */
343
+ export function documentFree(self: Pointer<FFIDocument>) {
344
+ trace()
345
+ ensureInitialized()
346
+
347
+ dittoCore.ditto_document_free(self)
348
+ }
349
+
350
+ // ----------------------------------------------------------- DocumentID ------
351
+
352
+ /** @internal */
353
+ export function documentIDQueryCompatible(docID: Uint8Array, stringPrimitiveFormat: StringPrimitiveFormat): string {
354
+ trace()
355
+ ensureInitialized()
356
+
357
+ const docIDString = dittoCore.ditto_document_id_query_compatible(docID, stringPrimitiveFormat)
358
+ return dittoCore.boxCStringIntoString(docIDString)
359
+ }
360
+
361
+ /** @internal */
362
+ export function validateDocumentID(docID: Uint8Array): Uint8Array {
363
+ trace()
364
+ ensureInitialized()
365
+
366
+ const cborCBytes = dittoCore.withOutBoxCBytes((outCBOR) => {
367
+ const errorCode = dittoCore.ditto_validate_document_id(docID, outCBOR)
368
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_validate_document_id() failed with error code: ${errorCode}`)
369
+ return outCBOR
370
+ })
371
+
372
+ return dittoCore.boxCBytesIntoBuffer(cborCBytes)
373
+ }
374
+
375
+ // ----------------------------------------------------------- Collection ------
376
+
377
+ /** @internal */
378
+ export async function collectionGet(ditto: Pointer<FFIDitto>, collectionName: string, documentID: Uint8Array, readTransaction: Pointer<FFIReadTransaction>): Promise<Pointer<FFIDocument> | null> {
379
+ trace()
380
+ ensureInitialized()
381
+
382
+ const collectionNameX = bytesFromString(collectionName)
383
+
384
+ const { status_code: errorCode, document } = await dittoCore.ditto_collection_get(ditto, collectionNameX, documentID, readTransaction)
385
+ if (errorCode === NOT_FOUND_ERROR_CODE) return null
386
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_collection_get() failed with error code: ${errorCode}`)
387
+ return document
388
+ }
389
+
390
+ /** @internal */
391
+ export async function collectionInsertValue(ditto: Pointer<FFIDitto>, collectionName: string, doc_cbor: Uint8Array, writeStrategy: FFIWriteStrategy, writeTransaction?: Pointer<FFIWriteTransaction>): Promise<Uint8Array> {
392
+ trace()
393
+ ensureInitialized()
394
+
395
+ const collectionNameX = bytesFromString(collectionName)
396
+
397
+ let strategy
398
+ switch (writeStrategy) {
399
+ case 'merge':
400
+ strategy = 'Merge'
401
+ break
402
+ case 'insertIfAbsent':
403
+ strategy = 'InsertIfAbsent'
404
+ break
405
+ case 'insertDefaultIfAbsent':
406
+ strategy = 'InsertDefaultIfAbsent'
407
+ break
408
+ default:
409
+ throw new Error('Invalid write strategy provided')
410
+ }
411
+
412
+ const { status_code: errorCode, id } = await dittoCore.ditto_collection_insert_value(ditto, collectionNameX, doc_cbor, strategy, null, writeTransaction ?? null)
413
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_collection_insert_value() failed with error code: ${errorCode}`)
414
+ return dittoCore.boxCBytesIntoBuffer(id)
415
+ }
416
+
417
+ /** @internal */
418
+ export async function collectionRemove(ditto: Pointer<FFIDitto>, collectionName: string, writeTransaction: Pointer<FFIWriteTransaction>, documentID: Uint8Array): Promise<boolean> {
419
+ trace()
420
+ ensureInitialized()
421
+
422
+ const collectionNameX = bytesFromString(collectionName)
423
+
424
+ const { status_code: errorCode, bool_value: didRemove } = await dittoCore.ditto_collection_remove(ditto, collectionNameX, writeTransaction, documentID)
425
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_collection_remove() failed with error code: ${errorCode}`)
426
+ return didRemove
427
+ }
428
+
429
+ /** @internal */
430
+ export async function collectionEvict(ditto: Pointer<FFIDitto>, collectionName: string, writeTransaction: Pointer<FFIWriteTransaction>, documentID: Uint8Array): Promise<boolean> {
431
+ trace()
432
+ ensureInitialized()
433
+
434
+ const collectionNameX = bytesFromString(collectionName)
435
+
436
+ const { status_code: errorCode, bool_value: didEvict } = await dittoCore.ditto_collection_evict(ditto, collectionNameX, writeTransaction, documentID)
437
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_collection_evict() failed with error code: ${errorCode}`)
438
+ return didEvict
439
+ }
440
+
441
+ /** @internal */
442
+ export async function collectionUpdate(ditto: Pointer<FFIDitto>, collectionName: string, writeTransaction: Pointer<FFIWriteTransaction>, document: Pointer<FFIDocument>): Promise<void> {
443
+ trace()
444
+ ensureInitialized()
445
+
446
+ const collectionNameX = bytesFromString(collectionName)
447
+ const errorCode = await dittoCore.ditto_collection_update(ditto, collectionNameX, writeTransaction, document)
448
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_collection_update() failed with error code: ${errorCode}`)
449
+ }
450
+
451
+ /** @internal */
452
+ export async function collectionUpdateMultiple(ditto: Pointer<FFIDitto>, collectionName: string, writeTransaction: Pointer<FFIWriteTransaction>, documents: Pointer<FFIDocument>[]): Promise<void> {
453
+ trace()
454
+ ensureInitialized()
455
+
456
+ const collectionNameX = bytesFromString(collectionName)
457
+ const cDocuments = dittoCore.jsDocsToCDocs(documents)
458
+ const errorCode = await dittoCore.ditto_collection_update_multiple(ditto, collectionNameX, writeTransaction, cDocuments)
459
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_collection_update_multiple() failed with error code: ${errorCode}`)
460
+ }
461
+
462
+ /** @internal */
463
+ export async function collectionExecQueryStr(ditto: Pointer<FFIDitto>, collectionName: string, writeTransaction: Pointer<FFIWriteTransaction>, query: string, queryArgsCBOR: Uint8Array | null, orderBy: OrderBy[], limit: number, offset: number): Promise<Pointer<FFIDocument>[]> {
464
+ trace()
465
+ ensureInitialized()
466
+
467
+ const collectionNameX = bytesFromString(collectionName)
468
+ const queryX = bytesFromString(query)
469
+ return await dittoCore.ditto_collection_exec_query_str(ditto, collectionNameX, writeTransaction, queryX, queryArgsCBOR, orderBy, limit, offset)
470
+ }
471
+
472
+ /** @internal */
473
+ export async function collectionRemoveQueryStr(ditto: Pointer<FFIDitto>, collectionName: string, writeTransaction: Pointer<FFIWriteTransaction>, query: string, queryArgsCBOR: Uint8Array | null, orderBy: OrderBy[], limit: number, offset: number): Promise<Uint8Array[]> {
474
+ trace()
475
+ ensureInitialized()
476
+
477
+ const collectionNameX = bytesFromString(collectionName)
478
+ const queryX = bytesFromString(query)
479
+ return await dittoCore.ditto_collection_remove_query_str(ditto, collectionNameX, writeTransaction, queryX, queryArgsCBOR, orderBy, limit, offset)
480
+ }
481
+
482
+ /** @internal */
483
+ export async function collectionEvictQueryStr(ditto: Pointer<FFIDitto>, collectionName: string, writeTransaction: Pointer<FFIWriteTransaction>, query: string, queryArgsCBOR: Uint8Array | null, orderBy: OrderBy[], limit: number, offset: number): Promise<Uint8Array[]> {
484
+ trace()
485
+ ensureInitialized()
486
+
487
+ const collectionNameX = bytesFromString(collectionName)
488
+ const queryX = bytesFromString(query)
489
+ return await dittoCore.ditto_collection_evict_query_str(ditto, collectionNameX, writeTransaction, queryX, queryArgsCBOR, orderBy, limit, offset)
490
+ }
491
+
492
+ /**
493
+ * This FFI can error:
494
+ * - DQL parser error
495
+ * - Incorrect arguments to query parameters
496
+ * - Collection is not found.
497
+ *
498
+ * @internal
499
+ */
500
+ export async function tryExperimentalExecQueryStr(ditto: Pointer<FFIDitto>, writeTransaction: Pointer<FFIWriteTransaction> | null, query: string, queryArgsCBOR: Uint8Array | null): Promise<Pointer<FFIDqlResponse>> {
501
+ trace()
502
+ ensureInitialized()
503
+
504
+ const queryBytesPointer = bytesFromString(query)
505
+ const result = await dittoCore.dittoffi_try_experimental_exec_statement_str(ditto, writeTransaction, queryBytesPointer, queryArgsCBOR)
506
+ throwOnErrorResult(result.error, 'dittoffi_try_experimental_exec_statement_str')
507
+ return result.success
508
+ }
509
+
510
+ /** @internal */
511
+ export function addSubscription(ditto: Pointer<FFIDitto>, collectionName: string, query: string, queryArgsCBOR: Uint8Array | null, orderBy: OrderBy[], limit: number, offset: number) {
512
+ trace()
513
+ ensureInitialized()
514
+
515
+ const collectionNameX = bytesFromString(collectionName)
516
+ const queryX = bytesFromString(query)
517
+ return dittoCore.ditto_add_subscription(ditto, collectionNameX, queryX, queryArgsCBOR, orderBy, limit, offset)
518
+ }
519
+
520
+ /** @internal */
521
+ export function removeSubscription(ditto: Pointer<FFIDitto>, collectionName: string, query: string, queryArgsCBOR: Uint8Array | null, orderBy: OrderBy[], limit: number, offset: number) {
522
+ trace()
523
+ ensureInitialized()
524
+
525
+ const collectionNameX = bytesFromString(collectionName)
526
+ const queryX = bytesFromString(query)
527
+ return dittoCore.ditto_remove_subscription(ditto, collectionNameX, queryX, queryArgsCBOR, orderBy, limit, offset)
528
+ }
529
+
530
+ /** @internal */
531
+ export function tryExperimentalAddDQLSubscription(dittoPointer: Pointer<FFIDitto>, query: string, queryArgsCBOR: Uint8Array | null): void {
532
+ trace()
533
+ ensureInitialized()
534
+
535
+ const queryBuffer = bytesFromString(query)
536
+ const result = dittoCore.dittoffi_try_experimental_add_dql_subscription(dittoPointer, queryBuffer, queryArgsCBOR)
537
+ throwOnErrorResult(result.error, 'dittoffi_try_experimental_add_dql_subscription')
538
+ return
539
+ }
540
+
541
+ /** @internal */
542
+ export function tryExperimentalRemoveDQLSubscription(dittoPointer: Pointer<FFIDitto>, query: string, queryArgsCBOR: Uint8Array | null): void {
543
+ trace()
544
+ ensureInitialized()
545
+
546
+ const queryBuffer = bytesFromString(query)
547
+ const result = dittoCore.dittoffi_try_experimental_remove_dql_subscription(dittoPointer, queryBuffer, queryArgsCBOR)
548
+ throwOnErrorResult(result.error, 'dittoffi_try_experimental_remove_dql_subscription')
549
+ return
550
+ }
551
+
552
+ // ----------------------------------------------------------- DqlResponse ------
553
+
554
+ /**
555
+ * Doesn't error
556
+ *
557
+ * @internal
558
+ */
559
+ export function dqlResponseFree(responsePointer: Pointer<FFIDqlResponse>) {
560
+ trace()
561
+ ensureInitialized()
562
+
563
+ dittoCore.ditto_dql_response_free(responsePointer)
564
+ }
565
+
566
+ /**
567
+ * Doesn't error
568
+ *
569
+ * @internal
570
+ */
571
+ export function dqlResultFree(resultPointer: Pointer<FFIDqlResult>) {
572
+ trace()
573
+ ensureInitialized()
574
+
575
+ dittoCore.ditto_dql_result_free(resultPointer)
576
+ }
577
+
578
+ /**
579
+ * Can error only on internal bug.
580
+ *
581
+ * @internal */
582
+ export function dqlResponseResults(responsePointer: Pointer<FFIDqlResponse>): Pointer<FFIDqlResult>[] {
583
+ trace()
584
+ ensureInitialized()
585
+
586
+ const rv = []
587
+ const resultCount = dittoCore.ditto_dql_response_result_count(responsePointer)
588
+ for (let i = 0; i < resultCount; i++) {
589
+ rv.push(dittoCore.ditto_dql_response_result_at(responsePointer, i))
590
+ }
591
+ return rv
592
+ }
593
+
594
+ /**
595
+ * Doesn't error
596
+ *
597
+ * @internal
598
+ */
599
+ export function dqlMutatedDocumentIDs(responsePointer: Pointer<FFIDqlResponse>): Uint8Array[] {
600
+ trace()
601
+ ensureInitialized()
602
+
603
+ const rv = []
604
+ const resultCount = dittoCore.ditto_dql_response_affected_document_id_count(responsePointer)
605
+ for (let i = 0; i < resultCount; i++) {
606
+ const cborBytes = dittoCore.ditto_dql_response_affected_document_id_at(responsePointer, i)
607
+ rv.push(dittoCore.boxCBytesIntoBuffer(cborBytes))
608
+ }
609
+ return rv
610
+ }
611
+
612
+ /**
613
+ * The result CBOR contains a map/object with fields and values.
614
+ * No CRDTs are present there as they are not needed. Currently
615
+ * only values from registers are returned and non-register fields are
616
+ * ignored. Reading values from other CRDTs will be supported with
617
+ * definition support.
618
+ *
619
+ * Doesn't error
620
+ *
621
+ * @internal
622
+ */
623
+ export function dqlResultCBOR(resultPointer: Pointer<FFIDqlResult>): Uint8Array {
624
+ trace()
625
+ ensureInitialized()
626
+
627
+ const cborBytes = dittoCore.ditto_result_cbor(resultPointer)
628
+ return dittoCore.boxCBytesIntoBuffer(cborBytes)
629
+ }
630
+
631
+ /**
632
+ * Returns JSON-encoded results given a DQL result pointer.
633
+ *
634
+ * Compare for {@link dqlResultCBOR} above.
635
+ *
636
+ * Doesn't error
637
+ *
638
+ * @internal
639
+ */
640
+ export function dqlResultJSON(resultPointer: Pointer<FFIDqlResult>): string {
641
+ trace()
642
+ ensureInitialized()
643
+
644
+ const jsonBytes = dittoCore.ditto_result_json(resultPointer)
645
+ return dittoCore.boxCStringIntoString(jsonBytes)
646
+ }
647
+
648
+ // ------------------------------------------------------------ LiveQuery ------
649
+
650
+ /** @internal */
651
+ export function liveQueryRegister(
652
+ ditto: Pointer<FFIDitto>,
653
+ collectionName: string,
654
+ query: string,
655
+ queryArgsCBOR: Uint8Array | null,
656
+ orderBy: OrderBy[],
657
+ limit: number,
658
+ offset: number,
659
+ eventHandler: (cbParams: CBParamsDocs) => any,
660
+ // Cb may be called in parallel at any point, so let's use
661
+ // an optional error handler (which defaults to the ditto logger at 'Error' level).
662
+ onError?: (error: any) => void,
663
+ ): number {
664
+ trace()
665
+ ensureInitialized()
666
+
667
+ const collectionNameBuffer = bytesFromString(collectionName)
668
+ const queryBuffer = bytesFromString(query)
669
+ // Note(Daniel): the callback is now registered to be called in a detached
670
+ // manner: if the FFI / Rust does `cb()`, then when that call returns, the js
671
+ // callback itself may not have completed. This is fine, since `signalNext()`
672
+ // shall be the proper way to let the Rust core know the FFI call completed.
673
+ const { status_code: errorCode, i64: id } = dittoCore.ditto_live_query_register_str_detached(ditto, collectionNameBuffer, queryBuffer, queryArgsCBOR, orderBy, limit, offset, wrapBackgroundCbForFFI(onError, eventHandler))
674
+ if (errorCode !== 0) throw new Error(errorMessage() || `\`ditto_live_query_register_str()\` failed with error code: ${errorCode}`)
675
+ return id
676
+ }
677
+
678
+ /** @internal */
679
+ export function tryExperimentalRegisterChangeObserver(ditto: Pointer<FFIDitto>, query: string, queryArgsCBOR: Uint8Array | null, changeHandler: (cbParams: CBParamsQueryResult) => any): number {
680
+ trace()
681
+ ensureInitialized()
682
+
683
+ const errorHandler = (err: any) => log('Error', `The registered store observer callback failed with ${err}`)
684
+ const wrappedCallback = wrapBackgroundCbForFFI(errorHandler, changeHandler)
685
+ const queryBuffer = bytesFromString(query)
686
+ // Note(Daniel): the callback is registered to be called in a detached manner:
687
+ // if the FFI / Rust does `cb()`, then when that call returns, the js callback
688
+ // itself may not have completed. This is fine, since `signalNext()` shall be
689
+ // the proper way to let the Rust core know the FFI call completed.
690
+ const result = dittoCore.dittoffi_try_experimental_register_change_observer_str_detached(ditto, queryBuffer, queryArgsCBOR, wrappedCallback)
691
+ throwOnErrorResult(result.error, 'dittoffi_try_experimental_register_change_observer_str_detached')
692
+ return result.success
693
+ }
694
+
695
+ /** @internal */
696
+ export async function liveQueryStart(ditto: Pointer<FFIDitto>, liveQueryID: number): Promise<void> {
697
+ trace()
698
+ ensureInitialized()
699
+
700
+ const errorCode = await dittoCore.ditto_live_query_start(ditto, liveQueryID)
701
+ if (errorCode !== 0) throw new Error(errorMessage() || `\`ditto_live_query_start()\` failed with error code: ${errorCode}`)
702
+ }
703
+
704
+ /** @internal */
705
+ export function liveQueryStop(ditto: Pointer<FFIDitto>, liveQueryID: number) {
706
+ trace()
707
+ ensureInitialized()
708
+
709
+ dittoCore.ditto_live_query_stop(ditto, liveQueryID)
710
+ }
711
+
712
+ /** @internal */
713
+ export async function liveQuerySignalAvailableNext(ditto: Pointer<FFIDitto>, liveQueryID: number): Promise<void> {
714
+ trace()
715
+ ensureInitialized()
716
+
717
+ await dittoCore.ditto_live_query_signal_available_next(ditto, liveQueryID)
718
+ }
719
+
720
+ // ------------------------------------------------------------ Webhook ------
721
+
722
+ /** @internal */
723
+ export async function liveQueryWebhookRegister(ditto: Pointer<FFIDitto>, collectionName: string, query: string, orderBy: OrderBy[], limit: number, offset: number, url: string): Promise<Uint8Array> {
724
+ trace()
725
+ ensureInitialized()
726
+ const collectionNameBuffer = bytesFromString(collectionName)
727
+ const queryBuffer = bytesFromString(query)
728
+ const urlBuffer = bytesFromString(url)
729
+
730
+ const { status_code: errorCode, id } = await dittoCore.ditto_live_query_webhook_register_str(ditto, collectionNameBuffer, queryBuffer, orderBy, limit, offset, urlBuffer)
731
+ if (errorCode !== 0) throw new Error(errorMessage() || `\`ditto_live_query_webhook_register_str()\` failed with error code: ${errorCode}`)
732
+
733
+ return dittoCore.boxCBytesIntoBuffer(id)
734
+ }
735
+
736
+ /** @internal */
737
+ export async function tryExperimentalWebhookRegisterDqlLiveQuery(ditto: Pointer<FFIDitto>, query: string, queryArgsCBOR: Uint8Array | null, url: string): Promise<Uint8Array> {
738
+ trace()
739
+ ensureInitialized()
740
+
741
+ const queryBuffer = bytesFromString(query)
742
+ const urlBuffer = bytesFromString(url)
743
+
744
+ const result = await dittoCore.dittoffi_try_experimental_webhook_register_dql_live_query_str(ditto, queryBuffer, queryArgsCBOR, urlBuffer)
745
+ throwOnErrorResult(result.error, 'dittoffi_try_experimental_webhook_register_dql_live_query_str')
746
+ const documentIdCBOR = result.success
747
+
748
+ return dittoCore.boxCBytesIntoBuffer(documentIdCBOR)
749
+ }
750
+
751
+ // ------------------------------------------------------ ReadTransaction ------
752
+
753
+ /** @internal */
754
+ export async function readTransaction(ditto: Pointer<FFIDitto>): Promise<Pointer<FFIReadTransaction>> {
755
+ trace()
756
+ ensureInitialized()
757
+
758
+ const { status_code: errorCode, txn: readTransaction } = await dittoCore.ditto_read_transaction(ditto)
759
+ if (errorCode !== 0) throw new Error(errorMessage() || `\`ditto_read_transaction()\` failed with error code: ${errorCode}`)
760
+ return readTransaction
761
+ }
762
+
763
+ /** @internal */
764
+ export function readTransactionFree(self: Pointer<FFIReadTransaction>) {
765
+ trace()
766
+ ensureInitialized()
767
+
768
+ return dittoCore.ditto_read_transaction_free(self)
769
+ }
770
+
771
+ // ----------------------------------------------------- WriteTransaction ------
772
+
773
+ /** @internal */
774
+ export async function writeTransaction(ditto: Pointer<FFIDitto>): Promise<Pointer<FFIWriteTransaction>> {
775
+ trace()
776
+ ensureInitialized()
777
+
778
+ const { status_code: errorCode, txn: writeTransaction } = await dittoCore.ditto_write_transaction(ditto, null)
779
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_write_transaction() failed with error code: ${errorCode}`)
780
+ return writeTransaction
781
+ }
782
+
783
+ /** @internal */
784
+ export async function writeTransactionCommit(ditto: Pointer<FFIDitto>, self: Pointer<FFIWriteTransaction>): Promise<void> {
785
+ trace()
786
+ ensureInitialized()
787
+
788
+ const errorCode = await dittoCore.ditto_write_transaction_commit(ditto, self)
789
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_write_transaction_commit() failed with error code: ${errorCode}`)
790
+ }
791
+
792
+ /** @internal */
793
+ export async function writeTransactionRollback(ditto: Pointer<FFIDitto>, transaction: Pointer<FFIWriteTransaction>): Promise<void> {
794
+ trace()
795
+ ensureInitialized()
796
+
797
+ const errorCode = await dittoCore.ditto_write_transaction_rollback(ditto, transaction)
798
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_write_transaction_rollback() failed with error code: ${errorCode}`)
799
+ }
800
+
801
+ // ------------------------------------------------------ StaticTCPClient ------
802
+
803
+ /** @internal */
804
+ export function addStaticTCPClient(ditto: Pointer<FFIDitto>, address: string): Pointer<FFIStaticTCPClient> {
805
+ trace()
806
+ ensureInitialized()
807
+
808
+ const addressBuffer = bytesFromString(address)
809
+ return dittoCore.ditto_add_static_tcp_client(ditto, addressBuffer)
810
+ }
811
+
812
+ /** @internal */
813
+ export function staticTCPClientFreeHandle(self: Pointer<FFIStaticTCPClient>) {
814
+ trace()
815
+ dittoCore.static_tcp_client_free_handle(self)
816
+ }
817
+
818
+ // ------------------------------------------------------ WebsocketClient ------
819
+
820
+ /** @internal */
821
+ export function addWebsocketClient(ditto: Pointer<FFIDitto>, address: string, routingHint: number): Pointer<FFIWebsocketClient> {
822
+ trace()
823
+ ensureInitialized()
824
+
825
+ const addressBuffer = bytesFromString(address)
826
+ return dittoCore.ditto_add_websocket_client(ditto, addressBuffer, routingHint)
827
+ }
828
+
829
+ /** @internal */
830
+ export function websocketClientFreeHandle(self: Pointer<FFIWebsocketClient>) {
831
+ trace()
832
+ ensureInitialized()
833
+ dittoCore.websocket_client_free_handle(self)
834
+ }
835
+
836
+ // --------------------------------------------------------------- Logger ------
837
+
838
+ /** @internal */
839
+ export function loggerInit() {
840
+ trace()
841
+ ensureInitialized()
842
+ dittoCore.ditto_logger_init()
843
+ }
844
+
845
+ /** @internal */
846
+ export async function loggerSetCustomLogCb(cb: null | ((lvl: LogLevel, msg: string) => void)): Promise<void> {
847
+ trace()
848
+ ensureInitialized()
849
+
850
+ if (null === cb) {
851
+ await dittoCore.ditto_logger_set_custom_log_cb(null)
852
+ } else {
853
+ // IDEA: pass custom error handler here instead of null?
854
+ const wrappedCallback = wrapBackgroundCbForFFI(null, (loglevel: LogLevel, cMsg: Pointer<'char'>) => {
855
+ try {
856
+ const msg: string = dittoCore.boxCStringIntoString(cMsg)
857
+ cb(loglevel, msg)
858
+ } catch (e) {
859
+ log('Error', `The registered cb in \`ditto_logger_set_custom_log_cb()\` failed with: ${e}`)
860
+ }
861
+ })
862
+ await dittoCore.ditto_logger_set_custom_log_cb(wrappedCallback)
863
+ }
864
+ }
865
+
866
+ /** @internal */
867
+ export function loggerEnabled(enabled: boolean) {
868
+ trace()
869
+ ensureInitialized()
870
+ dittoCore.ditto_logger_enabled(!!enabled)
871
+ }
872
+
873
+ /** @internal */
874
+ export function loggerEnabledGet(): boolean {
875
+ trace()
876
+ ensureInitialized()
877
+ return !!dittoCore.ditto_logger_enabled_get()
878
+ }
879
+
880
+ /** @internal */
881
+ export function loggerEmojiHeadingsEnabled(loggerEmojiHeadingsEnabled: boolean) {
882
+ trace()
883
+ ensureInitialized()
884
+ dittoCore.ditto_logger_emoji_headings_enabled(loggerEmojiHeadingsEnabled)
885
+ }
886
+
887
+ /** @internal */
888
+ export function loggerEmojiHeadingsEnabledGet() {
889
+ trace()
890
+ ensureInitialized()
891
+ return dittoCore.ditto_logger_emoji_headings_enabled_get()
892
+ }
893
+
894
+ /** @internal */
895
+ export function loggerMinimumLogLevel(logLevel: LogLevel) {
896
+ trace()
897
+ ensureInitialized()
898
+ dittoCore.ditto_logger_minimum_log_level(logLevel)
899
+ }
900
+
901
+ /** @internal */
902
+ export function loggerMinimumLogLevelGet() {
903
+ trace()
904
+ ensureInitialized()
905
+ return dittoCore.ditto_logger_minimum_log_level_get()
906
+ }
907
+
908
+ /** @internal */
909
+ export function loggerSetLogFile(path?: string) {
910
+ trace()
911
+ ensureInitialized()
912
+ const pathBytesOrNull = path ? bytesFromString(path) : null
913
+ const errorCode = dittoCore.ditto_logger_set_log_file(pathBytesOrNull)
914
+ if (errorCode !== 0) {
915
+ const message = errorMessage()
916
+ throw new Error(`Can't set log file, due to error: ${errorMessage}`)
917
+ }
918
+ }
919
+
920
+ /** @internal */
921
+ export function log(level: LogLevel, message: string) {
922
+ trace()
923
+ ensureInitialized()
924
+ const messageBuffer = bytesFromString(message)
925
+ dittoCore.ditto_log(level, messageBuffer)
926
+ }
927
+
928
+ // ----------------------------------------------------------- AuthClient ------
929
+
930
+ /** @internal */
931
+ export function dittoIdentityConfigMakeOnlinePlayground(appID: string, sharedToken: string, baseURL: string): Pointer<FFIIdentityConfig> {
932
+ trace()
933
+ ensureInitialized()
934
+
935
+ const appIDX = bytesFromString(appID)
936
+ const sharedTokenX = bytesFromString(sharedToken)
937
+ const baseURLX = bytesFromString(baseURL)
938
+
939
+ const { status_code: errorCode, identity_config: identityConfig } = dittoCore.ditto_identity_config_make_online_playground(appIDX, sharedTokenX, baseURLX)
940
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_identity_config_make_online_playground() failed with error code: ${errorCode}`)
941
+ return identityConfig
942
+ }
943
+
944
+ /** @internal */
945
+ export function dittoIdentityConfigMakeOnlineWithAuthentication(appID: string, baseURL: string): Pointer<FFIIdentityConfig> {
946
+ trace()
947
+ ensureInitialized()
948
+
949
+ const appIDX = bytesFromString(appID)
950
+ const baseURLX = bytesFromString(baseURL)
951
+
952
+ const { status_code: errorCode, identity_config: identityConfig } = dittoCore.ditto_identity_config_make_online_with_authentication(appIDX, baseURLX)
953
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_identity_config_make_online_with_authentication() failed with error code: ${errorCode}`)
954
+ return identityConfig
955
+ }
956
+
957
+ /** @internal */
958
+ export function dittoIdentityConfigMakeOfflinePlayground(appId: string, siteID: number | BigInt): Pointer<FFIIdentityConfig> {
959
+ trace()
960
+ ensureInitialized()
961
+
962
+ const appIdX = bytesFromString(appId)
963
+ const siteIDX = Number(siteID)
964
+ const { status_code: errorCode, identity_config: identityConfig } = dittoCore.ditto_identity_config_make_offline_playground(appIdX, siteIDX)
965
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_identity_config_make_offline_playground() failed with error code: ${errorCode}`)
966
+ return identityConfig
967
+ }
968
+
969
+ /** @internal */
970
+ export function dittoIdentityConfigMakeSharedKey(appId: string, sharedKey: string, siteID: number | BigInt): Pointer<FFIIdentityConfig> {
971
+ trace()
972
+ ensureInitialized()
973
+
974
+ const appIdX = bytesFromString(appId)
975
+ const sharedKeyX = bytesFromString(sharedKey)
976
+ const siteIDX = Number(siteID)
977
+ const { status_code: errorCode, identity_config: identityConfig } = dittoCore.ditto_identity_config_make_shared_key(appIdX, sharedKeyX, siteIDX)
978
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_identity_config_make_shared_key() failed with error code: ${errorCode}`)
979
+ return identityConfig
980
+ }
981
+
982
+ /** @internal */
983
+ export function dittoIdentityConfigMakeManual(configCBORBase64: string): Pointer<FFIIdentityConfig> {
984
+ trace()
985
+ ensureInitialized()
986
+
987
+ const configCBORBase64X = bytesFromString(configCBORBase64)
988
+
989
+ const { status_code: errorCode, identity_config: identityConfig } = dittoCore.ditto_identity_config_make_manual_v0(configCBORBase64X)
990
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_identity_config_make_manual_v0() failed with error code: ${errorCode}`)
991
+ return identityConfig
992
+ }
993
+
994
+ /** @internal */
995
+ export function dittoAuthClientGetSiteID(ditto: Pointer<FFIDitto>): number | BigInt {
996
+ trace()
997
+ ensureInitialized()
998
+ return dittoCore.ditto_auth_client_get_site_id(ditto)
999
+ }
1000
+
1001
+ /** @internal */
1002
+ export function dittoAuthClientGetAppID(ditto: Pointer<FFIDitto>): string {
1003
+ trace()
1004
+ ensureInitialized()
1005
+ const cString = dittoCore.ditto_auth_client_get_app_id(ditto)
1006
+ return dittoCore.boxCStringIntoString(cString)
1007
+ }
1008
+
1009
+ export function dittoAuthClientUserID(ditto: Pointer<FFIDitto>): string {
1010
+ trace()
1011
+ ensureInitialized()
1012
+ const cStr = dittoCore.ditto_auth_client_user_id(ditto)
1013
+ return dittoCore.boxCStringIntoString(cStr)
1014
+ }
1015
+
1016
+ /** @internal */
1017
+ export function dittoAuthClientIsWebValid(ditto: Pointer<FFIDitto>): boolean {
1018
+ trace()
1019
+ ensureInitialized()
1020
+ return dittoCore.ditto_auth_client_is_web_valid(ditto) !== 0
1021
+ }
1022
+
1023
+ export function dittoAuthClientIsX509Valid(ditto: Pointer<FFIDitto>): boolean {
1024
+ trace()
1025
+ ensureInitialized()
1026
+ return dittoCore.ditto_auth_client_is_x509_valid(ditto) !== 0
1027
+ }
1028
+
1029
+ export async function dittoAuthClientLoginWithToken(ditto: Pointer<FFIDitto>, token: string, provider: string) {
1030
+ trace()
1031
+ ensureInitialized()
1032
+
1033
+ const tokenBytes = bytesFromString(token)
1034
+ const providerBytes = bytesFromString(provider)
1035
+
1036
+ const errorCode = await dittoCore.ditto_auth_client_login_with_token(ditto, tokenBytes, providerBytes)
1037
+ if (errorCode !== 0) throw new Error(errorMessage() || `Ditto failed to authenticate (error code: ${errorCode}).`)
1038
+ }
1039
+
1040
+ export async function dittoAuthClientLoginWithUsernameAndPassword(ditto: Pointer<FFIDitto>, username: string, password: string, provider: string) {
1041
+ trace()
1042
+ ensureInitialized()
1043
+
1044
+ const usernameBytes = bytesFromString(username)
1045
+ const passwordBytes = bytesFromString(password)
1046
+ const providerBytes = bytesFromString(provider)
1047
+
1048
+ const errorCode = await dittoCore.ditto_auth_client_login_with_credentials(ditto, usernameBytes, passwordBytes, providerBytes)
1049
+ if (errorCode !== 0) throw new Error(errorMessage() || `Ditto failed to authenticate (error code: ${errorCode}).`)
1050
+ }
1051
+
1052
+ export async function dittoAuthClientLogout(ditto: Pointer<FFIDitto>) {
1053
+ trace()
1054
+ ensureInitialized()
1055
+
1056
+ const errorCode = await dittoCore.ditto_auth_client_logout(ditto)
1057
+ if (errorCode !== 0) throw new Error(errorMessage() || `Ditto failed to logout (error code: ${errorCode}).`)
1058
+ }
1059
+
1060
+ // ---------------------------------------------------------------- Ditto ------
1061
+
1062
+ /** @internal */
1063
+ export function uninitializedDittoMake(path: string): Pointer<FFIUninitializedDitto> {
1064
+ trace()
1065
+ ensureInitialized()
1066
+ const pathX = bytesFromString(path)
1067
+ return dittoCore.ditto_uninitialized_ditto_make(pathX)
1068
+ }
1069
+
1070
+ /** @internal */
1071
+ export function dittoMake(uninitializedDitto: Pointer<FFIUninitializedDitto>, identityConfig: Pointer<FFIIdentityConfig>): Pointer<FFIDitto> {
1072
+ trace()
1073
+ ensureInitialized()
1074
+ return dittoCore.ditto_make(uninitializedDitto, identityConfig, 'Disabled')
1075
+ }
1076
+
1077
+ /** @internal */
1078
+ export async function dittoGetCollectionNames(self: Pointer<FFIDitto>): Promise<string[]> {
1079
+ trace()
1080
+ ensureInitialized()
1081
+
1082
+ const result = await dittoCore.ditto_get_collection_names(self)
1083
+ const errorCode = result.status_code
1084
+ const cStringVec: { ptr: Pointer<'char *'>; len: number; cap: number } = result.names
1085
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_get_collection_names() failed with error code: ${errorCode}`)
1086
+
1087
+ const strings: string[] = dittoCore.cStringVecToStringArray(cStringVec) as string[]
1088
+ return strings
1089
+ }
1090
+
1091
+ /** @internal */
1092
+ export function dittoFree(self: Pointer<FFIDitto>) {
1093
+ trace()
1094
+ ensureInitialized()
1095
+
1096
+ return dittoCore.ditto_free(self)
1097
+ }
1098
+
1099
+ /** @internal */
1100
+ export function getDeadlockTimeout(): number {
1101
+ trace()
1102
+ ensureInitialized()
1103
+
1104
+ return dittoCore.getDeadlockTimeout()
1105
+ }
1106
+
1107
+ /** @internal */
1108
+ export function setDeadlockTimeout(duration: number): void {
1109
+ trace()
1110
+ ensureInitialized()
1111
+
1112
+ dittoCore.setDeadlockTimeout(duration)
1113
+ }
1114
+
1115
+ /** @internal */
1116
+ export async function dittoRegisterPresenceV1Callback(self: Pointer<FFIDitto>, cb: (string) => void): Promise<void> {
1117
+ trace()
1118
+ ensureInitialized()
1119
+
1120
+ dittoCore.ditto_register_presence_v1_callback(
1121
+ self,
1122
+ wrapBackgroundCbForFFI(
1123
+ (err) => log('Error', `The registered presence callback v1 errored with ${err}`),
1124
+ (cJsonStr: Pointer<'char'>) => {
1125
+ const jsonStr: string = dittoCore.refCStringToString(cJsonStr)
1126
+ cb(jsonStr)
1127
+ },
1128
+ ),
1129
+ )
1130
+ }
1131
+
1132
+ /** @internal */
1133
+ export async function dittoClearPresenceCallback(self: Pointer<FFIDitto>): Promise<void> {
1134
+ trace()
1135
+ ensureInitialized()
1136
+ await dittoCore.ditto_clear_presence_callback(self)
1137
+ }
1138
+
1139
+ /** @internal */
1140
+ export async function dittoRegisterPresenceV3Callback(self: Pointer<FFIDitto>, cb: (string) => void) {
1141
+ trace()
1142
+ ensureInitialized()
1143
+
1144
+ dittoCore.ditto_register_presence_v3_callback(
1145
+ self,
1146
+ wrapBackgroundCbForFFI(
1147
+ (err) => log('Error', `The registered presence callback v3 errored with ${err}`),
1148
+ (cJsonStr: Pointer<'char'>) => {
1149
+ const jsonStr: string = dittoCore.refCStringToString(cJsonStr)
1150
+ cb(jsonStr)
1151
+ },
1152
+ ),
1153
+ )
1154
+ }
1155
+
1156
+ /** @internal */
1157
+ export async function dittoClearPresenceV3Callback(self: Pointer<FFIDitto>) {
1158
+ trace()
1159
+ ensureInitialized()
1160
+ dittoCore.ditto_clear_presence_v3_callback(self)
1161
+ }
1162
+
1163
+ /** @internal */
1164
+ export function dittoSmallPeerInfoGetIsEnabled(dittoPointer: Pointer<FFIDitto>): boolean {
1165
+ trace()
1166
+ ensureInitialized()
1167
+
1168
+ return dittoCore.ditto_small_peer_info_get_is_enabled(dittoPointer)
1169
+ }
1170
+
1171
+ /** @internal */
1172
+ export async function dittoSmallPeerInfoSetEnabled(dittoPointer: Pointer<FFIDitto>, isEnabled: boolean): Promise<void> {
1173
+ trace()
1174
+ ensureInitialized()
1175
+
1176
+ dittoCore.ditto_small_peer_info_set_enabled(dittoPointer, isEnabled)
1177
+ }
1178
+
1179
+ /** @internal */
1180
+ export async function dittoSmallPeerInfoGetSyncScope(dittoPointer: Pointer<FFIDitto>): Promise<FFISmallPeerInfoSyncScope> {
1181
+ trace()
1182
+ ensureInitialized()
1183
+
1184
+ return dittoCore.ditto_small_peer_info_get_sync_scope(dittoPointer)
1185
+ }
1186
+
1187
+ /** @internal */
1188
+ export async function dittoSmallPeerInfoSetSyncScope(dittoPointer: Pointer<FFIDitto>, syncScope: FFISmallPeerInfoSyncScope): Promise<void> {
1189
+ trace()
1190
+ ensureInitialized()
1191
+
1192
+ return dittoCore.ditto_small_peer_info_set_sync_scope(dittoPointer, syncScope)
1193
+ }
1194
+
1195
+ /** @internal */
1196
+ export function dittoSmallPeerInfoGetMetadata(dittoPointer: Pointer<FFIDitto>): string {
1197
+ trace()
1198
+ ensureInitialized()
1199
+
1200
+ const cString = dittoCore.ditto_small_peer_info_get_metadata(dittoPointer)
1201
+ return dittoCore.boxCStringIntoString(cString)
1202
+ }
1203
+
1204
+ /** @internal */
1205
+ export function dittoSmallPeerInfoSetMetadata(dittoPointer: Pointer<FFIDitto>, metadata: string): void {
1206
+ trace()
1207
+ ensureInitialized()
1208
+
1209
+ const metadataCString = bytesFromString(metadata)
1210
+ const statusCode = dittoCore.ditto_small_peer_info_set_metadata(dittoPointer, metadataCString)
1211
+ switch (statusCode) {
1212
+ case 0:
1213
+ return
1214
+ case -1:
1215
+ throw new Error('Internal inconsistency, the observability subsystem is unavailable.')
1216
+ case 1:
1217
+ throw new Error(`Validation error, size limit exceeded: ${errorMessage() || 'metadata is too big'}`)
1218
+ case 2:
1219
+ throw new Error(`Validation error, ${errorMessage() || 'depth limit for metadata object exceeded'}`)
1220
+ case 3:
1221
+ throw new Error(`Validation error, ${errorMessage() || `'${metadata}' is not a valid JSON object`}`)
1222
+ default:
1223
+ throw new Error(errorMessage() || `Internal inconsistency, ditto_small_peer_info_set_metadata() returned an unknown error code: ${statusCode}`)
1224
+ }
1225
+ }
1226
+
1227
+ /** @internal */
1228
+ export async function dittoSmallPeerInfoCollectionSetTransportConfigData(self: Pointer<FFIDitto>, transportConfigData: Uint8Array) {
1229
+ trace()
1230
+ ensureInitialized()
1231
+ dittoCore.ditto_small_peer_info_set_transport_config_data(self, transportConfigData)
1232
+ }
1233
+
1234
+ /** @internal */
1235
+ export function dittoRegisterTransportConditionChangedCallback(self: Pointer<FFIDitto>, cb: null | ((ConditionSource, TransportCondition) => void)) {
1236
+ trace()
1237
+ ensureInitialized()
1238
+
1239
+ if (!cb) {
1240
+ dittoCore.ditto_register_transport_condition_changed_callback(self, null)
1241
+ } else {
1242
+ dittoCore.ditto_register_transport_condition_changed_callback(
1243
+ self,
1244
+ wrapBackgroundCbForFFI((err) => log('Error', `The registered "transport condition changed" callback errored with ${err}`), cb),
1245
+ )
1246
+ }
1247
+ }
1248
+
1249
+ /** @internal */
1250
+ export function dittoSetDeviceName(dittoPointer: Pointer<FFIDitto>, deviceName: string) {
1251
+ trace()
1252
+ ensureInitialized()
1253
+
1254
+ let deviceNameCString = bytesFromString(deviceName)
1255
+
1256
+ if (Environment.isWebBuild) {
1257
+ if (deviceNameCString.length > 64) {
1258
+ const sanitizedDeviceName = deviceName.replace(/[^ -~]+/g, '')
1259
+ deviceNameCString = bytesFromString(sanitizedDeviceName.substr(0, 63))
1260
+
1261
+ // NOTE: to reproduce the issue, comment the following two in. You'll see
1262
+ // that an ASCII string of 64 characters and more will make prevent sync
1263
+ // from working.
1264
+ //
1265
+ // const demoDeviceName = "123456789-10-456789-20-456789-30-456789-40-456789-50-456789-60-4" //56789-70-456789-80-456789-90-456789" // 100
1266
+ // deviceNameCString = bytesFromString(demoDeviceName)
1267
+ }
1268
+ }
1269
+
1270
+ return dittoCore.ditto_set_device_name(dittoPointer, deviceNameCString)
1271
+ }
1272
+
1273
+ /** @internal */
1274
+ export function dittoSetConnectRetryInterval(dittoPointer: Pointer<FFIDitto>, retryInterval: number) {
1275
+ trace()
1276
+ ensureInitialized()
1277
+
1278
+ // Unfortunately JS doesn't have a u32::MAX const...
1279
+ const UINT32_MAX = 4_294_967_295
1280
+ // Clamp the provided interval to u32 range (0 .. 2^32-1).
1281
+ const clampedInterval = Math.min(Math.max(0, retryInterval), UINT32_MAX)
1282
+
1283
+ return dittoCore.ditto_set_connect_retry_interval(dittoPointer, clampedInterval)
1284
+ }
1285
+
1286
+ /** @internal */
1287
+ export function dittoSetSyncGroup(dittoPointer: Pointer<FFIDitto>, syncGroup: number) {
1288
+ trace()
1289
+ ensureInitialized()
1290
+
1291
+ return dittoCore.ditto_set_sync_group(dittoPointer, syncGroup)
1292
+ }
1293
+
1294
+ // Not supported on Wasm.
1295
+ /** @internal */
1296
+ export function dittoNewAttachmentFromFile(ditto: Pointer<FFIDitto>, sourcePath: string, fileOperation: AttachmentFileOperation): RawAttachment {
1297
+ trace()
1298
+ ensureInitialized()
1299
+
1300
+ const sourcePathCString = bytesFromString(sourcePath)
1301
+ const outAttachment: any = {}
1302
+ const errorCode = dittoCore.ditto_new_attachment_from_file(ditto, sourcePathCString, fileOperation, outAttachment)
1303
+ if (errorCode !== 0) {
1304
+ throw new Error(errorMessage() || `ditto_new_attachment_from_file() failed with error code: ${errorCode}`)
1305
+ }
1306
+ return outAttachment
1307
+ }
1308
+
1309
+ /** @internal */
1310
+ export async function dittoNewAttachmentFromBytes(ditto: Pointer<FFIDitto>, bytes: Uint8Array): Promise<RawAttachment> {
1311
+ trace()
1312
+ ensureInitialized()
1313
+
1314
+ const outAttachment: any = {}
1315
+ const errorCode = await dittoCore.ditto_new_attachment_from_bytes(ditto, bytes, outAttachment)
1316
+ if (errorCode !== 0) {
1317
+ throw new Error(errorMessage() || `ditto_new_attachment_from_bytes() failed with error code: ${errorCode}`)
1318
+ }
1319
+ return outAttachment
1320
+ }
1321
+
1322
+ /** @internal */
1323
+ export async function dittoResolveAttachment(
1324
+ ditto: Pointer<FFIDitto>,
1325
+ id: Uint8Array,
1326
+ namedCallbacks: {
1327
+ onComplete: (attachmentHandlePointer: Pointer<AttachmentHandle>) => void
1328
+ onProgress: (downloaded: number | BigInt, toDownload: number | BigInt) => void
1329
+ onDelete: () => void
1330
+ },
1331
+ // Cb may be called in parallel at any point, so let's use
1332
+ // an optional error handler (which defaults to the ditto logger at 'Error' level).
1333
+ onError?: (error: any) => void,
1334
+ ): Promise<number | BigInt> {
1335
+ trace()
1336
+ ensureInitialized()
1337
+
1338
+ const { onComplete, onProgress, onDelete } = namedCallbacks
1339
+ const { status_code: errorCode, cancel_token: cancelToken } = await dittoCore.ditto_resolve_attachment(ditto, id, wrapBackgroundCbForFFI(onError, onComplete), wrapBackgroundCbForFFI(onError, onProgress), wrapBackgroundCbForFFI(onError, onDelete))
1340
+ if (errorCode !== 0) {
1341
+ throw new Error(errorMessage() || `ditto_resolve_attachment() failed with error code: ${errorCode}`)
1342
+ }
1343
+ return cancelToken
1344
+ }
1345
+
1346
+ /** @internal */
1347
+ export function dittoCancelResolveAttachment(dittoPointer: Pointer<FFIDitto>, id: Uint8Array, cancelToken: any) {
1348
+ trace()
1349
+ ensureInitialized()
1350
+
1351
+ const errorCode = dittoCore.ditto_cancel_resolve_attachment(dittoPointer, id, cancelToken)
1352
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_cancel_resolve_attachment() failed with error code: ${errorCode}`)
1353
+ }
1354
+
1355
+ /** @internal */
1356
+ export function freeAttachmentHandle(attachmentHandlePointer: Pointer<AttachmentHandle>) {
1357
+ trace()
1358
+ ensureInitialized()
1359
+ dittoCore.ditto_free_attachment_handle(attachmentHandlePointer)
1360
+ }
1361
+
1362
+ /** @internal */
1363
+ export async function dittoGetAttachmentStatus(dittoPointer: Pointer<FFIDitto>, id: Uint8Array): Promise<Pointer<AttachmentHandle>> {
1364
+ trace()
1365
+ ensureInitialized()
1366
+
1367
+ const { status_code: errorCode, handle: attachmentHandle } = await dittoCore.ditto_get_attachment_status(dittoPointer, id)
1368
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_get_attachment_status() failed with error code: ${errorCode}`)
1369
+ return attachmentHandle
1370
+ }
1371
+
1372
+ /** @internal */
1373
+ export async function dittoGetCompleteAttachmentData(dittoPointer: Pointer<FFIDitto>, attachmentHandlePointer: Pointer<AttachmentHandle>): Promise<Uint8Array> {
1374
+ trace()
1375
+ ensureInitialized()
1376
+
1377
+ const { status: errorCode, data } = await dittoCore.ditto_get_complete_attachment_data(dittoPointer, attachmentHandlePointer)
1378
+ if (errorCode !== 0) throw new Error(errorMessage() || `\`ditto_get_complete_attachment_data()\` failed with error code: ${errorCode}`)
1379
+ return dittoCore.boxCBytesIntoBuffer(data)
1380
+ }
1381
+
1382
+ /** @internal */
1383
+ export function dittoGetCompleteAttachmentPath(dittoPointer: Pointer<FFIDitto>, attachmentHandlePointer: Pointer<AttachmentHandle>): string {
1384
+ trace()
1385
+ ensureInitialized()
1386
+
1387
+ const pathCString = dittoCore.ditto_get_complete_attachment_path(dittoPointer, attachmentHandlePointer)
1388
+ return dittoCore.refCStringToString(pathCString)
1389
+ }
1390
+
1391
+ /** @internal */
1392
+ export function dittoGetSDKVersion(ditto: Pointer<FFIDitto>): string {
1393
+ trace()
1394
+ ensureInitialized()
1395
+
1396
+ const cString = dittoCore.ditto_get_sdk_version(ditto)
1397
+ return dittoCore.boxCStringIntoString(cString)
1398
+ }
1399
+
1400
+ /** @internal */
1401
+ export function dittoGetSDKSemver(): string {
1402
+ trace()
1403
+ ensureInitialized()
1404
+
1405
+ const cString = dittoCore.dittoffi_get_sdk_semver()
1406
+ return dittoCore.boxCStringIntoString(cString)
1407
+ }
1408
+
1409
+ /** @internal */
1410
+ export function dittoPresenceV1(self: Pointer<FFIDitto>): string {
1411
+ trace()
1412
+ ensureInitialized()
1413
+
1414
+ const cString = dittoCore.ditto_presence_v1(self)
1415
+ return dittoCore.boxCStringIntoString(cString)
1416
+ }
1417
+
1418
+ /** @internal */
1419
+ export function dittoPresenceV2(self: Pointer<FFIDitto>): string {
1420
+ trace()
1421
+ ensureInitialized()
1422
+
1423
+ const cString = dittoCore.ditto_presence_v2(self)
1424
+ return dittoCore.boxCStringIntoString(cString)
1425
+ }
1426
+
1427
+ /** @internal */
1428
+ export function dittoPresenceV3(self: Pointer<FFIDitto>): string {
1429
+ trace()
1430
+ ensureInitialized()
1431
+
1432
+ const cString = dittoCore.ditto_presence_v3(self)
1433
+ return dittoCore.boxCStringIntoString(cString)
1434
+ }
1435
+
1436
+ /** @internal */
1437
+ export function dittoStartTCPServer(dittoPointer: Pointer<FFIDitto>, bind: string) {
1438
+ trace()
1439
+ ensureInitialized()
1440
+
1441
+ const bindBuffer = bytesFromString(bind)
1442
+ return dittoCore.ditto_start_tcp_server(dittoPointer, bindBuffer)
1443
+ }
1444
+
1445
+ /** @internal */
1446
+ export function dittoStopTCPServer(dittoPointer: Pointer<FFIDitto>) {
1447
+ trace()
1448
+ ensureInitialized()
1449
+
1450
+ return dittoCore.ditto_stop_tcp_server(dittoPointer)
1451
+ }
1452
+
1453
+ /** @internal */
1454
+ export async function dittoShutdown(dittoPointer: Pointer<FFIDitto>): Promise<void> {
1455
+ trace()
1456
+ ensureInitialized()
1457
+
1458
+ return await dittoCore.ditto_shutdown(dittoPointer)
1459
+ }
1460
+
1461
+ /** @internal */
1462
+ export function dittoAddMulticastTransport(dittoPointer: Pointer<FFIDitto>) {
1463
+ trace()
1464
+ ensureInitialized()
1465
+
1466
+ return dittoCore.ditto_add_multicast_transport(dittoPointer)
1467
+ }
1468
+
1469
+ /** @internal */
1470
+ export function dittoRemoveMulticastTransport(dittoPointer: Pointer<FFIDitto>) {
1471
+ trace()
1472
+ ensureInitialized()
1473
+
1474
+ return dittoCore.ditto_remove_multicast_transport(dittoPointer)
1475
+ }
1476
+
1477
+ /** @internal */
1478
+ export function dittoStartHTTPServer(dittoPointer: Pointer<FFIDitto>, bind: string, staticPath: string | null, websocketMode: WebsocketMode, tlsCertPath: string | null, tlsKeyPath: string | null) {
1479
+ trace()
1480
+ ensureInitialized()
1481
+
1482
+ const bindBuffer = bytesFromString(bind)
1483
+ const staticPathBuffer = bytesFromString(staticPath)
1484
+ const tlsCertPathBuffer = bytesFromString(tlsCertPath)
1485
+ const tlsKeyPathBuffer = bytesFromString(tlsKeyPath)
1486
+ return dittoCore.ditto_start_http_server(dittoPointer, bindBuffer, staticPathBuffer, websocketMode, tlsCertPathBuffer, tlsKeyPathBuffer)
1487
+ }
1488
+
1489
+ /** @internal */
1490
+ export function dittoStopHTTPServer(dittoPointer: Pointer<FFIDitto>) {
1491
+ trace()
1492
+ ensureInitialized()
1493
+ return dittoCore.ditto_stop_http_server(dittoPointer)
1494
+ }
1495
+
1496
+ /** @internal */
1497
+ export function dittoRunGarbageCollection(dittoPointer: Pointer<FFIDitto>) {
1498
+ trace()
1499
+ ensureInitialized()
1500
+ return dittoCore.ditto_run_garbage_collection(dittoPointer)
1501
+ }
1502
+
1503
+ /** @internal */
1504
+ export async function dittoDisableSyncWithV3(dittoPointer: Pointer<FFIDitto>): Promise<void> {
1505
+ trace()
1506
+ ensureInitialized()
1507
+ const errorCode = await dittoCore.ditto_disable_sync_with_v3(dittoPointer)
1508
+ if (errorCode !== 0) throw new Error(errorMessage() || `ditto_disable_sync_with_v3() failed with error code: ${errorCode}`)
1509
+ }
1510
+
1511
+ export function dittoSetStaticTCPClients(ditto: Pointer<FFIDitto>, listOfServers: string[]) {
1512
+ trace()
1513
+ ensureInitialized()
1514
+
1515
+ if (Environment.isNodeBuild) {
1516
+ const listOfServersBytes = listOfServers.map((server) => bytesFromString(server))
1517
+ dittoCore.ditto_set_static_tcp_clients(ditto, listOfServersBytes)
1518
+ }
1519
+
1520
+ if (Environment.isWebBuild) {
1521
+ if (listOfServers.length > 0) {
1522
+ throw new Error(`Web variant of Ditto does not support connecting to TCP servers: ${listOfServers.join(', ')}`)
1523
+ }
1524
+ }
1525
+ }
1526
+
1527
+ export function dittoSetStaticWebsocketClients(ditto: Pointer<FFIDitto>, listOfServers: string[], routingHint: number) {
1528
+ trace()
1529
+ ensureInitialized()
1530
+ const listOfServersBytes = listOfServers.map((server) => bytesFromString(server))
1531
+ dittoCore.ditto_set_static_websocket_clients(ditto, listOfServersBytes, routingHint)
1532
+ }
1533
+
1534
+ // ------------------------------------------------------ Hash & Mnemonic ------
1535
+
1536
+ /** @internal */
1537
+ export function documentsHash(documents: Pointer<FFIDocument>[]): BigInt {
1538
+ trace()
1539
+ ensureInitialized()
1540
+ const { status_code: errorCode, u64: hash } = dittoCore.ditto_documents_hash(documents)
1541
+ if (errorCode !== 0) throw new Error(errorMessage() || `\`ditto_documents_hash()\` failed with error code: ${errorCode}`)
1542
+ // `hash` is of type `number | BigInt`, let's unify it to `BigInt` to keep it simple.
1543
+ return BigInt(hash)
1544
+ }
1545
+
1546
+ /** @internal */
1547
+ export function documentsHashMnemonic(documents: Pointer<FFIDocument>[]): string {
1548
+ trace()
1549
+ ensureInitialized()
1550
+ const { status_code: errorCode, c_string } = dittoCore.ditto_documents_hash_mnemonic(documents)
1551
+ if (errorCode !== 0) throw new Error(errorMessage() || `\`ditto_documents_hash_mnemonic()\` failed with error code: ${errorCode}`)
1552
+ return dittoCore.boxCStringIntoString(c_string)
1553
+ }
1554
+
1555
+ // ------------------------------------------------------------- Auth ----------
1556
+
1557
+ /** @internal */
1558
+ export function authServerAuthSubmitWithSuccess(req: Pointer<FFIAuthServerAuthRequest>, successCbor: Uint8Array): void {
1559
+ trace()
1560
+ ensureInitialized()
1561
+ return dittoCore.auth_server_auth_submit_with_success(req, successCbor)
1562
+ }
1563
+
1564
+ /** @internal */
1565
+ export function authServerAuthSubmitWithError(req: Pointer<FFIAuthServerAuthRequest>, errorCode: number): void {
1566
+ trace()
1567
+ ensureInitialized()
1568
+ return dittoCore.auth_server_auth_submit_with_error(req, errorCode)
1569
+ }
1570
+
1571
+ /** @internal */
1572
+ export function authServerRefreshSubmitWithSuccess(req: Pointer<FFIAuthServerRefreshRequest>, successCbor: Uint8Array): void {
1573
+ trace()
1574
+ ensureInitialized()
1575
+ return dittoCore.auth_server_refresh_submit_with_success(req, successCbor)
1576
+ }
1577
+
1578
+ /** @internal */
1579
+ export function authServerRefreshSubmitWithError(req: Pointer<FFIAuthServerRefreshRequest>, errorCode: number): void {
1580
+ trace()
1581
+ ensureInitialized()
1582
+ return dittoCore.auth_server_refresh_submit_with_error(req, errorCode)
1583
+ }
1584
+
1585
+ /** @internal */
1586
+ export function dittoUnregisterLocalAuthServer(ditto: Pointer<FFIDitto>): void {
1587
+ trace()
1588
+ ensureInitialized()
1589
+ return dittoCore.ditto_unregister_local_auth_server(ditto)
1590
+ }
1591
+
1592
+ /** @internal */
1593
+ export function dittoRegisterLocalAuthServer(namedArgs: {
1594
+ ditto: Pointer<FFIDitto>
1595
+ signingKeyPem: string
1596
+ verifyingKeysPem: string[]
1597
+ caKeyPem?: string
1598
+ authCb: (authRequest: Pointer<FFIAuthServerAuthRequest>, cbor: Uint8Array) => void
1599
+ refreshCb: (authRequest: Pointer<FFIAuthServerRefreshRequest>, cbor: Uint8Array) => void
1600
+ // Cb may be called in parallel at any point, so let's use
1601
+ // an optional error handler
1602
+ onError?: (error: any) => void
1603
+ }): void {
1604
+ const { ditto, signingKeyPem, verifyingKeysPem, caKeyPem, authCb, refreshCb, onError } = namedArgs
1605
+ trace()
1606
+ ensureInitialized()
1607
+
1608
+ const signingKeyPemFfi = bytesFromString(signingKeyPem)
1609
+ const verifyingKeysPemFfi = verifyingKeysPem.map(bytesFromString)
1610
+ const caKeyPemFfi = typeof caKeyPem !== 'undefined' ? bytesFromString(caKeyPem) : null
1611
+ const authCbFfi = wrapBackgroundCbForFFI(onError, authCb)
1612
+ const refreshCbFfi = wrapBackgroundCbForFFI(onError, refreshCb)
1613
+ return dittoCore.ditto_register_local_auth_server(ditto, signingKeyPemFfi, verifyingKeysPemFfi, caKeyPemFfi, authCbFfi, refreshCbFfi)
1614
+ }
1615
+
1616
+ /** @internal */
1617
+ export async function dittoAuthSetLoginProvider(ditto: Pointer<FFIDitto>, loginProvider: Pointer<FFILoginProvider>): Promise<void> {
1618
+ trace()
1619
+ ensureInitialized()
1620
+
1621
+ return await dittoCore.ditto_auth_set_login_provider(ditto, loginProvider)
1622
+ }
1623
+
1624
+ /** @internal */
1625
+ export function dittoAuthClientMakeLoginProvider(
1626
+ expiringCb: (secs_until_expiry: number) => void,
1627
+ // Cb may be called in parallel at any point, so let's use an optional error handler
1628
+ onError?: (error: any) => void,
1629
+ ): Pointer<FFILoginProvider> {
1630
+ trace()
1631
+ ensureInitialized()
1632
+
1633
+ return dittoCore.ditto_auth_client_make_login_provider(wrapBackgroundCbForFFI(onError, expiringCb))
1634
+ }
1635
+
1636
+ /** @internal */
1637
+ export function dittoAuthClientSetValidityListener(
1638
+ ditto: Pointer<FFIDitto>,
1639
+ validityUpdateCb: (isWebValid: boolean, isX509Valid: boolean) => void,
1640
+ // Cb may be called in parallel at any point, so let's use an optional error handler
1641
+ onError?: (error: any) => void,
1642
+ ): void {
1643
+ trace()
1644
+ ensureInitialized()
1645
+
1646
+ const validityUpdateRawCb = wrapBackgroundCbForFFI(onError, function (isWebValidInt: number, isX509ValidInt: number) {
1647
+ return validityUpdateCb(isWebValidInt === 1, isX509ValidInt === 1)
1648
+ })
1649
+
1650
+ return dittoCore.ditto_auth_client_set_validity_listener(ditto, validityUpdateRawCb)
1651
+ }
1652
+
1653
+ // ----------------------------------------------------------- Transports ------
1654
+
1655
+ /**
1656
+ * We currently don't initialize transports through this function but rather in
1657
+ * the NAPI module registration in `transports.c` due to an issue with importing
1658
+ * the functions required by this function.
1659
+ * See https://github.com/getditto/ditto/issues/10723
1660
+ *
1661
+ * @internal
1662
+ */
1663
+ export function transportsInit(): void {
1664
+ trace()
1665
+ ensureInitialized()
1666
+
1667
+ const transportsErrorPointer = dittoCore.ditto_sdk_transports_error_new()
1668
+ const wasInitialized = dittoCore.ditto_sdk_transports_init(transportsErrorPointer)
1669
+
1670
+ const errorType: TransportsError = dittoCore.ditto_sdk_transports_error_value(transportsErrorPointer)
1671
+ dittoCore.ditto_sdk_transports_error_free(transportsErrorPointer)
1672
+
1673
+ if (wasInitialized === false) {
1674
+ throw new Error(`Failed to initialize transports (${errorType} error)`)
1675
+ }
1676
+ }
1677
+
1678
+ /** @internal */
1679
+ export function transportsBLEIsAvailable(ditto: Pointer<FFIDitto>): boolean {
1680
+ trace()
1681
+ ensureInitialized()
1682
+
1683
+ return dittoCore.ditto_sdk_transports_ble_is_available(ditto)
1684
+ }
1685
+
1686
+ /** @internal */
1687
+ export function transportsBLECreate(ditto: Pointer<FFIDitto>): Pointer<TransportBluetooth> | null | void {
1688
+ trace()
1689
+ ensureInitialized()
1690
+
1691
+ const transportsErrorPointer = dittoCore.ditto_sdk_transports_error_new()
1692
+ const blePointer: Pointer<TransportBluetooth> = dittoCore.ditto_sdk_transports_ble_create(ditto, transportsErrorPointer)
1693
+
1694
+ const errorType: TransportsError = dittoCore.ditto_sdk_transports_error_value(transportsErrorPointer)
1695
+ dittoCore.ditto_sdk_transports_error_free(transportsErrorPointer)
1696
+
1697
+ if (blePointer != null) {
1698
+ log('Info', `Bluetooth transport created.`)
1699
+ } else {
1700
+ log('Error', `Can't create bluetooth transport (${errorType} error).`)
1701
+ }
1702
+
1703
+ return blePointer
1704
+ }
1705
+
1706
+ /** @internal */
1707
+ export function transportsBLEDestroy(ble: Pointer<TransportBluetooth>): boolean | void {
1708
+ trace()
1709
+ ensureInitialized()
1710
+
1711
+ const ffiError = dittoCore.ditto_sdk_transports_error_new()
1712
+ const wasDestroyed = dittoCore.ditto_sdk_transports_ble_destroy(ble, ffiError)
1713
+
1714
+ const errorType: TransportsError = dittoCore.ditto_sdk_transports_error_value(ffiError)
1715
+ dittoCore.ditto_sdk_transports_error_free(ffiError)
1716
+
1717
+ if (wasDestroyed === true) {
1718
+ log('Info', 'Bluetooth transport disabled.')
1719
+ } else {
1720
+ log('Error', `Bluetooth transport could not be disabled (${errorType} error).`)
1721
+ }
1722
+ return wasDestroyed
1723
+ }
1724
+
1725
+ /** @internal */
1726
+ export function transportsLANIsAvailable(ditto: Pointer<FFIDitto>): boolean {
1727
+ trace()
1728
+ ensureInitialized()
1729
+
1730
+ return dittoCore.ditto_sdk_transports_lan_is_available(ditto)
1731
+ }
1732
+
1733
+ /** @internal */
1734
+ export function transportsLANCreate(ditto: Pointer<FFIDitto>): Pointer<TransportLAN> | void {
1735
+ trace()
1736
+ ensureInitialized()
1737
+
1738
+ const transportsErrorPointer = dittoCore.ditto_sdk_transports_error_new()
1739
+ const lanPointer: Pointer<TransportLAN> = dittoCore.ditto_sdk_transports_lan_create(ditto, transportsErrorPointer)
1740
+
1741
+ const errorType: TransportsError = dittoCore.ditto_sdk_transports_error_value(transportsErrorPointer)
1742
+ dittoCore.ditto_sdk_transports_error_free(transportsErrorPointer)
1743
+
1744
+ if (lanPointer != null) {
1745
+ log('Info', `LAN transport created.`)
1746
+ } else {
1747
+ log('Error', `Can't create LAN transport (${errorType} error).`)
1748
+ }
1749
+
1750
+ return lanPointer
1751
+ }
1752
+
1753
+ /** @internal */
1754
+ export function transportsLANDestroy(lan: Pointer<TransportLAN>): boolean | void {
1755
+ trace()
1756
+ ensureInitialized()
1757
+
1758
+ const ffiError = dittoCore.ditto_sdk_transports_error_new()
1759
+ const wasDestroyed = dittoCore.ditto_sdk_transports_lan_destroy(lan, ffiError)
1760
+
1761
+ const error: TransportsError = dittoCore.ditto_sdk_transports_error_value(ffiError)
1762
+ dittoCore.ditto_sdk_transports_error_free(ffiError)
1763
+
1764
+ if (wasDestroyed === true) {
1765
+ log('Info', 'LAN transport disabled.')
1766
+ } else {
1767
+ log('Error', `LAN transport could not be disabled (${error} error).`)
1768
+ }
1769
+ return wasDestroyed
1770
+ }
1771
+
1772
+ /** @internal */
1773
+ export function transportsAWDLIsAvailable(ditto: Pointer<FFIDitto>): boolean {
1774
+ trace()
1775
+ ensureInitialized()
1776
+
1777
+ return dittoCore.ditto_sdk_transports_awdl_is_available(ditto)
1778
+ }
1779
+
1780
+ /** @internal */
1781
+ export function transportsAWDLCreate(ditto: Pointer<FFIDitto>): Pointer<TransportAWDL> | null | void {
1782
+ trace()
1783
+ ensureInitialized()
1784
+
1785
+ const transportsErrorPointer = dittoCore.ditto_sdk_transports_error_new()
1786
+ const awdlPointer: Pointer<TransportAWDL> = dittoCore.ditto_sdk_transports_awdl_create(ditto, transportsErrorPointer)
1787
+
1788
+ const errorType: TransportsError = dittoCore.ditto_sdk_transports_error_value(transportsErrorPointer)
1789
+ dittoCore.ditto_sdk_transports_error_free(transportsErrorPointer)
1790
+
1791
+ if (awdlPointer != null) {
1792
+ log('Info', `AWDL transport created.`)
1793
+ } else {
1794
+ log('Error', `Can't create AWDL transport (${errorType} error).`)
1795
+ }
1796
+
1797
+ return awdlPointer
1798
+ }
1799
+
1800
+ /** @internal */
1801
+ export function transportsAWDLDestroy(awdl: Pointer<TransportAWDL>): boolean | void {
1802
+ trace()
1803
+ ensureInitialized()
1804
+
1805
+ const ffiError = dittoCore.ditto_sdk_transports_error_new()
1806
+ const wasDestroyed = dittoCore.ditto_sdk_transports_awdl_destroy(awdl, ffiError)
1807
+
1808
+ const errorType: TransportsError = dittoCore.ditto_sdk_transports_error_value(ffiError)
1809
+ dittoCore.ditto_sdk_transports_error_free(ffiError)
1810
+
1811
+ if (wasDestroyed === true) {
1812
+ log('Info', 'AWDL transport disabled.')
1813
+ } else {
1814
+ log('Error', `AWDL transport could not be disabled (${errorType} error).`)
1815
+ }
1816
+ return wasDestroyed
1817
+ }
1818
+
1819
+ // ---------------------------------------------------------------- Other ------
1820
+
1821
+ /** @internal */
1822
+ let isInitialized = false
1823
+
1824
+ /** @internal */
1825
+ let withOutPtr: (ptr_type: string, callback: any) => any
1826
+
1827
+ if (Environment.isNodeBuild) {
1828
+ isInitialized = true
1829
+ withOutPtr = wrapFFIOutFunction(dittoCore.withOutPtr)
1830
+ }
1831
+
1832
+ if (Environment.isReactNativeBuild) {
1833
+ isInitialized = true
1834
+ }
1835
+
1836
+ /** @internal */
1837
+ export async function init(webAssemblyModule?: WebAssemblyModule): Promise<void> {
1838
+ if (Environment.isWebBuild) {
1839
+ if (webAssemblyModule) {
1840
+ await dittoCore.init(webAssemblyModule as any)
1841
+ } else {
1842
+ await dittoCore.init()
1843
+ }
1844
+
1845
+ isInitialized = true
1846
+ withOutPtr = wrapFFIOutFunction(dittoCore.withOutPtr)
1847
+ }
1848
+ }
1849
+
1850
+ /** @internal */
1851
+ export function initSDKVersion(platform: Platform, language: Language, semVer: string) {
1852
+ trace()
1853
+ ensureInitialized()
1854
+
1855
+ const platformCString = bytesFromString(platform)
1856
+ const languageCString = bytesFromString(language)
1857
+ const semVerCString = bytesFromString(semVer)
1858
+
1859
+ const errorCode = dittoCore.ditto_init_sdk_version(platform, language, semVerCString)
1860
+ if (typeof errorCode !== 'undefined' && errorCode !== 0) throw new Error(errorMessage() || `ditto_init_sdk_version() failed with error code: ${errorCode}`)
1861
+ }
1862
+
1863
+ /** @internal */
1864
+ export function verifyLicense(license: string): { result: LicenseVerificationResult; errorMessage?: string } {
1865
+ trace()
1866
+ ensureInitialized()
1867
+
1868
+ const licenseBuffer = bytesFromString(license)
1869
+ let result: LicenseVerificationResult
1870
+ const errorMessageCString = withOutPtr('char *', (outErrorMessage) => {
1871
+ result = dittoCore.ditto_verify_license(licenseBuffer, outErrorMessage)
1872
+ return outErrorMessage
1873
+ })
1874
+
1875
+ const errorMessage = dittoCore.boxCStringIntoString(errorMessageCString)
1876
+ return { result, errorMessage }
1877
+ }
1878
+
1879
+ // ---------------------------------------------------- React Native only ------
1880
+
1881
+ /** @internal */
1882
+ export function getDeviceName(): string | void {
1883
+ if (Environment.isReactNativeBuild) {
1884
+ // @ts-expect-error Throws method not-found on non-RN envs.
1885
+ return dittoCore.getDeviceName()
1886
+ }
1887
+ }
1888
+
1889
+ /** @internal */
1890
+ export function getRandomValues<T extends ArrayBufferView>(array: T): T | void {
1891
+ if (Environment.isReactNativeBuild) {
1892
+ // @ts-expect-error Throws method not-found on non-RN envs.
1893
+ return dittoCore.getRandomValues(array)
1894
+ }
1895
+ }
1896
+
1897
+ /** @internal */
1898
+ export function createDirectory(path: string): string | void {
1899
+ if (Environment.isReactNativeBuild) {
1900
+ // @ts-expect-error Throws method not-found on non-RN envs.
1901
+ return dittoCore.createDirectory(path)
1902
+ }
1903
+ }
1904
+
1905
+ // -------------------------------------------------------------- Private ------
1906
+
1907
+ /** @internal */
1908
+ const NOT_FOUND_ERROR_CODE = -30798
1909
+
1910
+ /** @internal */
1911
+ function wrapBackgroundCbForFFI(onError: null | ((err: any) => void), cb: any): any {
1912
+ // There's no need to wrap callbacks through JSI while they are not returning
1913
+ // anything.
1914
+ if (Environment.isReactNativeBuild) {
1915
+ return cb
1916
+ }
1917
+ // Basic fallback based off: https://stackoverflow.com/a/3390635
1918
+ if (onError === undefined) {
1919
+ onError = (err: any) => log('Error', `The registered callback failed with ${err}`)
1920
+ }
1921
+ return (ret_sender, ...args) => {
1922
+ let ret
1923
+ try {
1924
+ ret = cb(...args)
1925
+ } catch (err) {
1926
+ try {
1927
+ onError(err)
1928
+ } catch (nested_error) {
1929
+ log('Error', `Internal error: \`onError()\` handler oughtn't throw, but it did throw ${nested_error}`)
1930
+ }
1931
+ }
1932
+ return ret_sender(ret)
1933
+ }
1934
+ }
1935
+
1936
+ /** @internal */
1937
+ function wrapFFIOutFunction(ffiOutFunction: any): any {
1938
+ return function (...args) {
1939
+ let occurredError = undefined
1940
+ let callbackResult = undefined
1941
+ let isCallbackResultOutParameter = false
1942
+
1943
+ const callback = args[args.length - 1]
1944
+ const previous_args = args.splice(0, args.length - 1)
1945
+
1946
+ const dittoCoreResult = ffiOutFunction(...previous_args, (outParameter) => {
1947
+ try {
1948
+ callbackResult = callback(outParameter)
1949
+ isCallbackResultOutParameter = callbackResult === outParameter
1950
+ } catch (error) {
1951
+ occurredError = error
1952
+ }
1953
+ })
1954
+
1955
+ if (occurredError) {
1956
+ throw occurredError
1957
+ }
1958
+
1959
+ return isCallbackResultOutParameter ? dittoCoreResult : callbackResult
1960
+ }
1961
+ }
1962
+
1963
+ /** @internal */
1964
+ function bytesFromString(jsString: string | null | undefined): Uint8Array | null | undefined {
1965
+ if (typeof jsString === 'undefined') return undefined
1966
+ if (jsString === null) return null
1967
+ if (typeof jsString !== 'string') throw new Error(`Can't convert string to Uint8Array, not a string: ${jsString}`)
1968
+ const textEncoder = new TextEncoder()
1969
+ return textEncoder.encode(`${jsString}\0`)
1970
+ }
1971
+
1972
+ /**
1973
+ * This is the legacy error-message retrieving function, using thread-local
1974
+ * storage.
1975
+ *
1976
+ * @internal
1977
+ */
1978
+ function errorMessage(): string {
1979
+ trace()
1980
+ ensureInitialized()
1981
+
1982
+ // eslint-disable-next-line
1983
+ const errorMessageCString = dittoCore.ditto_error_message()
1984
+ return dittoCore.boxCStringIntoString(errorMessageCString)
1985
+ }
1986
+
1987
+ /** @internal */
1988
+ function trace() {
1989
+ if (isTracingEnabled) {
1990
+ // Copied and adapted from a Stack Overflow comment:
1991
+ // https://stackoverflow.com/a/38435618
1992
+ const error = new Error()
1993
+ const stack = error.stack
1994
+ let caller = '<unknown>'
1995
+ try {
1996
+ caller = stack.split('\n')[2].trim().split(/\s+/)[1].replace('Module.', '')
1997
+ } catch (error) {
1998
+ // Nothing to do, caller remains unknown.
1999
+ }
2000
+
2001
+ // We use use `console.log()` instead of `log()` here to avoid infinite recursion.
2002
+ // eslint-disable-next-line no-console
2003
+ console.log(`💗 [TRACE] ${caller}()`)
2004
+ }
2005
+ }
2006
+
2007
+ /** @internal */
2008
+ function ensureInitialized() {
2009
+ if (!isInitialized) {
2010
+ throw new Error('Ditto needs to be initialized before using any of its API, please make sure to call `await init()` first.')
2011
+ }
2012
+ }