@syncular/client 0.0.6-95 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/README.md +713 -11
  2. package/dist/auth-leases.d.ts +11 -0
  3. package/dist/auth-leases.d.ts.map +1 -0
  4. package/dist/auth-leases.js +140 -0
  5. package/dist/auth-leases.js.map +1 -0
  6. package/dist/blob-limits.d.ts +15 -0
  7. package/dist/blob-limits.d.ts.map +1 -0
  8. package/dist/blob-limits.js +66 -0
  9. package/dist/blob-limits.js.map +1 -0
  10. package/dist/bridge-client.d.ts +69 -0
  11. package/dist/bridge-client.d.ts.map +1 -0
  12. package/dist/bridge-client.js +282 -0
  13. package/dist/bridge-client.js.map +1 -0
  14. package/dist/client-config.d.ts +6 -0
  15. package/dist/client-config.d.ts.map +1 -0
  16. package/dist/client-config.js +18 -0
  17. package/dist/client-config.js.map +1 -0
  18. package/dist/client.d.ts +65 -397
  19. package/dist/client.d.ts.map +1 -1
  20. package/dist/client.js +150 -960
  21. package/dist/client.js.map +1 -1
  22. package/dist/command-history.d.ts +35 -0
  23. package/dist/command-history.d.ts.map +1 -0
  24. package/dist/command-history.js +378 -0
  25. package/dist/command-history.js.map +1 -0
  26. package/dist/console-diagnostics.d.ts +33 -0
  27. package/dist/console-diagnostics.d.ts.map +1 -0
  28. package/dist/console-diagnostics.js +496 -0
  29. package/dist/console-diagnostics.js.map +1 -0
  30. package/dist/crdt-yjs/index.d.ts +4 -0
  31. package/dist/crdt-yjs/index.d.ts.map +1 -0
  32. package/dist/crdt-yjs/index.js +4 -0
  33. package/dist/crdt-yjs/index.js.map +1 -0
  34. package/dist/crdt-yjs/webview-host-facade.d.ts +126 -0
  35. package/dist/crdt-yjs/webview-host-facade.d.ts.map +1 -0
  36. package/dist/crdt-yjs/webview-host-facade.js +284 -0
  37. package/dist/crdt-yjs/webview-host-facade.js.map +1 -0
  38. package/dist/crdt-yjs/yjs-document-field-adapter.d.ts +153 -0
  39. package/dist/crdt-yjs/yjs-document-field-adapter.d.ts.map +1 -0
  40. package/dist/crdt-yjs/yjs-document-field-adapter.js +406 -0
  41. package/dist/crdt-yjs/yjs-document-field-adapter.js.map +1 -0
  42. package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts +73 -0
  43. package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts.map +1 -0
  44. package/dist/crdt-yjs/yjs-prosemirror-bridge.js +169 -0
  45. package/dist/crdt-yjs/yjs-prosemirror-bridge.js.map +1 -0
  46. package/dist/database.d.ts +83 -0
  47. package/dist/database.d.ts.map +1 -0
  48. package/dist/database.js +916 -0
  49. package/dist/database.js.map +1 -0
  50. package/dist/diagnostics.d.ts +10 -0
  51. package/dist/diagnostics.d.ts.map +1 -0
  52. package/dist/diagnostics.js +83 -0
  53. package/dist/diagnostics.js.map +1 -0
  54. package/dist/errors.d.ts +24 -0
  55. package/dist/errors.d.ts.map +1 -0
  56. package/dist/errors.js +211 -0
  57. package/dist/errors.js.map +1 -0
  58. package/dist/generated-bridge.d.ts +365 -0
  59. package/dist/generated-bridge.d.ts.map +1 -0
  60. package/dist/generated-bridge.js +250 -0
  61. package/dist/generated-bridge.js.map +1 -0
  62. package/dist/index.d.ts +13 -23
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +14 -25
  65. package/dist/index.js.map +1 -1
  66. package/dist/mutations.d.ts +4 -72
  67. package/dist/mutations.d.ts.map +1 -1
  68. package/dist/mutations.js +2 -568
  69. package/dist/mutations.js.map +1 -1
  70. package/dist/network.d.ts +3 -0
  71. package/dist/network.d.ts.map +1 -0
  72. package/dist/network.js +17 -0
  73. package/dist/network.js.map +1 -0
  74. package/dist/react/index.d.ts +169 -0
  75. package/dist/react/index.d.ts.map +1 -0
  76. package/dist/react/index.js +628 -0
  77. package/dist/react/index.js.map +1 -0
  78. package/dist/react-native/index.d.ts +35 -0
  79. package/dist/react-native/index.d.ts.map +1 -0
  80. package/dist/react-native/index.js +49 -0
  81. package/dist/react-native/index.js.map +1 -0
  82. package/dist/runtime-contract.d.ts +13 -0
  83. package/dist/runtime-contract.d.ts.map +1 -0
  84. package/dist/runtime-contract.js +24 -0
  85. package/dist/runtime-contract.js.map +1 -0
  86. package/dist/rust-client.d.ts +106 -0
  87. package/dist/rust-client.d.ts.map +1 -0
  88. package/dist/rust-client.js +936 -0
  89. package/dist/rust-client.js.map +1 -0
  90. package/dist/sentry.d.ts +35 -0
  91. package/dist/sentry.d.ts.map +1 -0
  92. package/dist/sentry.js +155 -0
  93. package/dist/sentry.js.map +1 -0
  94. package/dist/sql-safety.d.ts +3 -0
  95. package/dist/sql-safety.d.ts.map +1 -0
  96. package/dist/sql-safety.js +62 -0
  97. package/dist/sql-safety.js.map +1 -0
  98. package/dist/syncular-runtime-artifacts.json +61 -0
  99. package/dist/tauri/index.d.ts +35 -0
  100. package/dist/tauri/index.d.ts.map +1 -0
  101. package/dist/tauri/index.js +114 -0
  102. package/dist/tauri/index.js.map +1 -0
  103. package/dist/types.d.ts +1059 -0
  104. package/dist/types.d.ts.map +1 -0
  105. package/dist/types.js +2 -0
  106. package/dist/types.js.map +1 -0
  107. package/dist/wasm/.syncular-wasm-profile +1 -0
  108. package/dist/wasm/syncular-runtime-artifact.json +21 -0
  109. package/dist/wasm/syncular.d.ts +207 -0
  110. package/dist/wasm/syncular.js +2341 -0
  111. package/dist/wasm/syncular_bg.wasm +0 -0
  112. package/dist/wasm/syncular_bg.wasm.d.ts +97 -0
  113. package/dist/wasm-bindings/runtime-contract.d.ts +22 -0
  114. package/dist/wasm-bindings/runtime-contract.d.ts.map +1 -0
  115. package/dist/wasm-bindings/runtime-contract.js +112 -0
  116. package/dist/wasm-bindings/runtime-contract.js.map +1 -0
  117. package/dist/wasm-core/.syncular-wasm-profile +1 -0
  118. package/dist/wasm-core/syncular-runtime-artifact.json +17 -0
  119. package/dist/wasm-core/syncular.d.ts +162 -0
  120. package/dist/wasm-core/syncular.js +1847 -0
  121. package/dist/wasm-core/syncular_bg.wasm +0 -0
  122. package/dist/wasm-core/syncular_bg.wasm.d.ts +77 -0
  123. package/dist/wasm-perf/.syncular-wasm-profile +1 -0
  124. package/dist/wasm-perf/syncular-runtime-artifact.json +21 -0
  125. package/dist/wasm-perf/syncular.d.ts +207 -0
  126. package/dist/wasm-perf/syncular.js +2341 -0
  127. package/dist/wasm-perf/syncular_bg.wasm +0 -0
  128. package/dist/wasm-perf/syncular_bg.wasm.d.ts +97 -0
  129. package/dist/wasm-runtime.d.ts +18 -0
  130. package/dist/wasm-runtime.d.ts.map +1 -0
  131. package/dist/wasm-runtime.js +28 -0
  132. package/dist/wasm-runtime.js.map +1 -0
  133. package/dist/worker-client.d.ts +123 -0
  134. package/dist/worker-client.d.ts.map +1 -0
  135. package/dist/worker-client.js +1686 -0
  136. package/dist/worker-client.js.map +1 -0
  137. package/dist/worker-entry.d.ts +2 -0
  138. package/dist/worker-entry.d.ts.map +1 -0
  139. package/dist/worker-entry.js +479 -0
  140. package/dist/worker-entry.js.map +1 -0
  141. package/dist/worker-protocol.d.ts +64 -0
  142. package/dist/worker-protocol.d.ts.map +1 -0
  143. package/dist/worker-protocol.js +16 -0
  144. package/dist/worker-protocol.js.map +1 -0
  145. package/dist/worker-realtime.d.ts +39 -0
  146. package/dist/worker-realtime.d.ts.map +1 -0
  147. package/dist/worker-realtime.js +677 -0
  148. package/dist/worker-realtime.js.map +1 -0
  149. package/package.json +124 -17
  150. package/src/auth-leases.ts +251 -0
  151. package/src/blob-limits.ts +98 -0
  152. package/src/bridge-client.ts +512 -0
  153. package/src/client-config.ts +29 -0
  154. package/src/client.ts +256 -1414
  155. package/src/command-history.ts +623 -0
  156. package/src/console-diagnostics.ts +617 -0
  157. package/src/crdt-yjs/index.ts +3 -0
  158. package/src/crdt-yjs/webview-host-facade.ts +477 -0
  159. package/src/crdt-yjs/yjs-document-field-adapter.ts +733 -0
  160. package/src/crdt-yjs/yjs-prosemirror-bridge.ts +272 -0
  161. package/src/database.ts +1342 -0
  162. package/src/diagnostics.ts +116 -0
  163. package/src/errors.ts +269 -0
  164. package/src/generated-bridge.ts +741 -0
  165. package/src/index.ts +21 -25
  166. package/src/mutations.ts +12 -821
  167. package/src/network.ts +32 -0
  168. package/src/react/index.ts +1036 -0
  169. package/src/react-native/index.ts +152 -0
  170. package/src/runtime-contract.ts +48 -0
  171. package/src/rust-client.ts +1488 -0
  172. package/src/sentry.ts +215 -0
  173. package/src/sql-safety.ts +61 -0
  174. package/src/tauri/index.ts +211 -0
  175. package/src/types.ts +1356 -0
  176. package/src/wasm-bindings/generated-wasm-bindings.d.ts +70 -0
  177. package/src/wasm-bindings/runtime-contract.ts +158 -0
  178. package/src/wasm-runtime.ts +76 -0
  179. package/src/worker-client.ts +2215 -0
  180. package/src/worker-entry.ts +584 -0
  181. package/src/worker-protocol.ts +109 -0
  182. package/src/worker-realtime.ts +843 -0
  183. package/dist/blobs/index.d.ts +0 -7
  184. package/dist/blobs/index.d.ts.map +0 -1
  185. package/dist/blobs/index.js +0 -7
  186. package/dist/blobs/index.js.map +0 -1
  187. package/dist/blobs/manager.d.ts +0 -345
  188. package/dist/blobs/manager.d.ts.map +0 -1
  189. package/dist/blobs/manager.js +0 -749
  190. package/dist/blobs/manager.js.map +0 -1
  191. package/dist/blobs/migrate.d.ts +0 -14
  192. package/dist/blobs/migrate.d.ts.map +0 -1
  193. package/dist/blobs/migrate.js +0 -59
  194. package/dist/blobs/migrate.js.map +0 -1
  195. package/dist/blobs/types.d.ts +0 -62
  196. package/dist/blobs/types.d.ts.map +0 -1
  197. package/dist/blobs/types.js +0 -5
  198. package/dist/blobs/types.js.map +0 -1
  199. package/dist/conflicts.d.ts +0 -31
  200. package/dist/conflicts.d.ts.map +0 -1
  201. package/dist/conflicts.js +0 -112
  202. package/dist/conflicts.js.map +0 -1
  203. package/dist/create-client.d.ts +0 -118
  204. package/dist/create-client.d.ts.map +0 -1
  205. package/dist/create-client.js +0 -178
  206. package/dist/create-client.js.map +0 -1
  207. package/dist/engine/SyncEngine.d.ts +0 -272
  208. package/dist/engine/SyncEngine.d.ts.map +0 -1
  209. package/dist/engine/SyncEngine.js +0 -1860
  210. package/dist/engine/SyncEngine.js.map +0 -1
  211. package/dist/engine/index.d.ts +0 -6
  212. package/dist/engine/index.d.ts.map +0 -1
  213. package/dist/engine/index.js +0 -6
  214. package/dist/engine/index.js.map +0 -1
  215. package/dist/engine/types.d.ts +0 -345
  216. package/dist/engine/types.d.ts.map +0 -1
  217. package/dist/engine/types.js +0 -7
  218. package/dist/engine/types.js.map +0 -1
  219. package/dist/handlers/collection.d.ts +0 -6
  220. package/dist/handlers/collection.d.ts.map +0 -1
  221. package/dist/handlers/collection.js +0 -21
  222. package/dist/handlers/collection.js.map +0 -1
  223. package/dist/handlers/create-handler.d.ts +0 -120
  224. package/dist/handlers/create-handler.d.ts.map +0 -1
  225. package/dist/handlers/create-handler.js +0 -145
  226. package/dist/handlers/create-handler.js.map +0 -1
  227. package/dist/handlers/types.d.ts +0 -83
  228. package/dist/handlers/types.d.ts.map +0 -1
  229. package/dist/handlers/types.js +0 -5
  230. package/dist/handlers/types.js.map +0 -1
  231. package/dist/migrate.d.ts +0 -19
  232. package/dist/migrate.d.ts.map +0 -1
  233. package/dist/migrate.js +0 -238
  234. package/dist/migrate.js.map +0 -1
  235. package/dist/outbox.d.ts +0 -112
  236. package/dist/outbox.d.ts.map +0 -1
  237. package/dist/outbox.js +0 -294
  238. package/dist/outbox.js.map +0 -1
  239. package/dist/plugins/incrementing-version.d.ts +0 -34
  240. package/dist/plugins/incrementing-version.d.ts.map +0 -1
  241. package/dist/plugins/incrementing-version.js +0 -83
  242. package/dist/plugins/incrementing-version.js.map +0 -1
  243. package/dist/plugins/index.d.ts +0 -3
  244. package/dist/plugins/index.d.ts.map +0 -1
  245. package/dist/plugins/index.js +0 -3
  246. package/dist/plugins/index.js.map +0 -1
  247. package/dist/plugins/types.d.ts +0 -49
  248. package/dist/plugins/types.d.ts.map +0 -1
  249. package/dist/plugins/types.js +0 -15
  250. package/dist/plugins/types.js.map +0 -1
  251. package/dist/proxy/connection.d.ts +0 -33
  252. package/dist/proxy/connection.d.ts.map +0 -1
  253. package/dist/proxy/connection.js +0 -153
  254. package/dist/proxy/connection.js.map +0 -1
  255. package/dist/proxy/dialect.d.ts +0 -46
  256. package/dist/proxy/dialect.d.ts.map +0 -1
  257. package/dist/proxy/dialect.js +0 -58
  258. package/dist/proxy/dialect.js.map +0 -1
  259. package/dist/proxy/driver.d.ts +0 -42
  260. package/dist/proxy/driver.d.ts.map +0 -1
  261. package/dist/proxy/driver.js +0 -78
  262. package/dist/proxy/driver.js.map +0 -1
  263. package/dist/proxy/index.d.ts +0 -10
  264. package/dist/proxy/index.d.ts.map +0 -1
  265. package/dist/proxy/index.js +0 -10
  266. package/dist/proxy/index.js.map +0 -1
  267. package/dist/proxy/mutations.d.ts +0 -9
  268. package/dist/proxy/mutations.d.ts.map +0 -1
  269. package/dist/proxy/mutations.js +0 -11
  270. package/dist/proxy/mutations.js.map +0 -1
  271. package/dist/pull-engine.d.ts +0 -48
  272. package/dist/pull-engine.d.ts.map +0 -1
  273. package/dist/pull-engine.js +0 -648
  274. package/dist/pull-engine.js.map +0 -1
  275. package/dist/push-engine.d.ts +0 -18
  276. package/dist/push-engine.d.ts.map +0 -1
  277. package/dist/push-engine.js +0 -167
  278. package/dist/push-engine.js.map +0 -1
  279. package/dist/query/FingerprintCollector.d.ts +0 -18
  280. package/dist/query/FingerprintCollector.d.ts.map +0 -1
  281. package/dist/query/FingerprintCollector.js +0 -28
  282. package/dist/query/FingerprintCollector.js.map +0 -1
  283. package/dist/query/QueryContext.d.ts +0 -33
  284. package/dist/query/QueryContext.d.ts.map +0 -1
  285. package/dist/query/QueryContext.js +0 -16
  286. package/dist/query/QueryContext.js.map +0 -1
  287. package/dist/query/fingerprint.d.ts +0 -61
  288. package/dist/query/fingerprint.d.ts.map +0 -1
  289. package/dist/query/fingerprint.js +0 -91
  290. package/dist/query/fingerprint.js.map +0 -1
  291. package/dist/query/index.d.ts +0 -7
  292. package/dist/query/index.d.ts.map +0 -1
  293. package/dist/query/index.js +0 -7
  294. package/dist/query/index.js.map +0 -1
  295. package/dist/query/tracked-select.d.ts +0 -18
  296. package/dist/query/tracked-select.d.ts.map +0 -1
  297. package/dist/query/tracked-select.js +0 -90
  298. package/dist/query/tracked-select.js.map +0 -1
  299. package/dist/schema.d.ts +0 -83
  300. package/dist/schema.d.ts.map +0 -1
  301. package/dist/schema.js +0 -7
  302. package/dist/schema.js.map +0 -1
  303. package/dist/subscription-state.d.ts +0 -46
  304. package/dist/subscription-state.d.ts.map +0 -1
  305. package/dist/subscription-state.js +0 -185
  306. package/dist/subscription-state.js.map +0 -1
  307. package/dist/sync-loop.d.ts +0 -34
  308. package/dist/sync-loop.d.ts.map +0 -1
  309. package/dist/sync-loop.js +0 -253
  310. package/dist/sync-loop.js.map +0 -1
  311. package/dist/sync.d.ts +0 -32
  312. package/dist/sync.d.ts.map +0 -1
  313. package/dist/sync.js +0 -55
  314. package/dist/sync.js.map +0 -1
  315. package/dist/utils/id.d.ts +0 -17
  316. package/dist/utils/id.d.ts.map +0 -1
  317. package/dist/utils/id.js +0 -46
  318. package/dist/utils/id.js.map +0 -1
  319. package/src/blobs/index.ts +0 -7
  320. package/src/blobs/manager.ts +0 -1027
  321. package/src/blobs/migrate.ts +0 -67
  322. package/src/blobs/types.ts +0 -84
  323. package/src/client.test.ts +0 -417
  324. package/src/conflicts.ts +0 -171
  325. package/src/create-client.test.ts +0 -83
  326. package/src/create-client.ts +0 -330
  327. package/src/engine/SyncEngine.test.ts +0 -254
  328. package/src/engine/SyncEngine.ts +0 -2375
  329. package/src/engine/index.ts +0 -6
  330. package/src/engine/types.ts +0 -419
  331. package/src/handlers/collection.ts +0 -36
  332. package/src/handlers/create-handler.ts +0 -330
  333. package/src/handlers/types.ts +0 -108
  334. package/src/migrate.ts +0 -318
  335. package/src/outbox.ts +0 -383
  336. package/src/plugins/incrementing-version.ts +0 -133
  337. package/src/plugins/index.ts +0 -2
  338. package/src/plugins/types.ts +0 -63
  339. package/src/proxy/connection.ts +0 -191
  340. package/src/proxy/dialect.ts +0 -76
  341. package/src/proxy/driver.ts +0 -126
  342. package/src/proxy/index.ts +0 -10
  343. package/src/proxy/mutations.ts +0 -18
  344. package/src/pull-engine.test.ts +0 -151
  345. package/src/pull-engine.ts +0 -855
  346. package/src/push-engine.ts +0 -216
  347. package/src/query/FingerprintCollector.ts +0 -29
  348. package/src/query/QueryContext.ts +0 -54
  349. package/src/query/fingerprint.ts +0 -109
  350. package/src/query/index.ts +0 -10
  351. package/src/query/tracked-select.ts +0 -139
  352. package/src/schema.ts +0 -94
  353. package/src/subscription-state.ts +0 -259
  354. package/src/sync-loop.ts +0 -379
  355. package/src/sync.ts +0 -170
  356. package/src/utils/id.ts +0 -55
@@ -0,0 +1,936 @@
1
+ import { issueSyncularAuthLease } from './auth-leases';
2
+ import { assertSyncularBlobPayloadLimit } from './blob-limits';
3
+ import { resolveSyncularClientConfig } from './client-config';
4
+ import { appendSyncularDiagnosticEvent, appendSyncularSyncTimings, createSyncularSyncAttempt, summarizeSyncularDiagnosticSubscriptions, syncularDiagnosticAttemptFields, syncularSyncAttemptHeaders, } from './diagnostics';
5
+ import { SyncularClientError, toSyncularClientError } from './errors';
6
+ import { createSyncularRuntimeInfo } from './runtime-contract';
7
+ import { assertSyncularReadonlySql } from './sql-safety';
8
+ import { getSyncularWasmGlueUrl, getSyncularWasmUrl, loadSyncularWasmGlue, readSyncularRustRuntimeInfo, } from './wasm-runtime';
9
+ export async function openSyncularRustClient(options) {
10
+ const wasmGlueUrl = options.runtime?.wasmGlueUrl ??
11
+ options.wasmGlueUrl ??
12
+ getSyncularWasmGlueUrl();
13
+ const mod = await (options.module ??
14
+ (options.runtime?.wasmGlueUrl || options.wasmGlueUrl
15
+ ? loadSyncularWasmGlueFromUrl(wasmGlueUrl)
16
+ : loadSyncularWasmGlue()));
17
+ const wasmUrl = options.runtime?.wasmUrl ?? options.wasmUrl ?? getSyncularWasmUrl();
18
+ const config = resolveSyncularClientConfig(options.config);
19
+ await mod.default(wasmUrl);
20
+ const rustRuntimeInfo = readSyncularRustRuntimeInfo(mod);
21
+ return new SyncularRustClient(await mod.openSyncularRustOwnedSqliteClient(config), createSyncularRuntimeInfo({
22
+ storage: config.storage,
23
+ wasmGlueUrl,
24
+ wasmUrl,
25
+ rust: rustRuntimeInfo,
26
+ }), config, config.pull, options.blobLimits);
27
+ }
28
+ function loadSyncularWasmGlueFromUrl(wasmGlueUrl) {
29
+ const href = wasmGlueUrl instanceof URL ? wasmGlueUrl.href : wasmGlueUrl;
30
+ return import(/* @vite-ignore */ href);
31
+ }
32
+ export class SyncularRustClient {
33
+ raw;
34
+ runtime;
35
+ config;
36
+ pullOptions;
37
+ blobLimits;
38
+ #rowsChangedListeners = new Set();
39
+ #diagnosticListeners = new Set();
40
+ #recentDiagnostics = [];
41
+ #recentSyncTimings = [];
42
+ #subscriptions = [];
43
+ #authHeaders = {};
44
+ #bootstrapById = new Map();
45
+ #closed = false;
46
+ constructor(raw, runtime, config, pullOptions, blobLimits) {
47
+ this.raw = raw;
48
+ this.runtime = runtime;
49
+ this.config = config;
50
+ this.pullOptions = pullOptions;
51
+ this.blobLimits = blobLimits;
52
+ }
53
+ #assertRuntimeFeature(feature, apiName) {
54
+ if (this.runtime.rust?.features.includes(feature))
55
+ return;
56
+ throw new Error(`Syncular runtime feature "${feature}" is required for ${apiName}. ` +
57
+ 'Select the full browser WASM runtime artifact.');
58
+ }
59
+ setSubscriptions(subscriptions) {
60
+ this.#subscriptions = [...subscriptions];
61
+ this.#bootstrapById.clear();
62
+ this.raw.setSubscriptionsJson(JSON.stringify(subscriptions));
63
+ this.#emitDiagnostic({
64
+ at: Date.now(),
65
+ level: 'info',
66
+ source: 'client',
67
+ code: 'client.subscriptions.updated',
68
+ message: 'Syncular subscriptions updated',
69
+ details: { subscriptionCount: subscriptions.length },
70
+ });
71
+ }
72
+ async forceSubscriptionsBootstrap(subscriptionIds = []) {
73
+ const count = parseJson(await this.raw.forceSubscriptionsBootstrapJson(JSON.stringify(subscriptionIds)));
74
+ for (const id of subscriptionIds.length > 0
75
+ ? subscriptionIds
76
+ : this.#subscriptions.map((subscription) => subscription.id)) {
77
+ this.#bootstrapById.delete(id);
78
+ }
79
+ return count;
80
+ }
81
+ setAuthHeaders(headers) {
82
+ this.#authHeaders = { ...headers };
83
+ this.raw.setAuthHeadersJson(JSON.stringify(this.#authHeaders));
84
+ }
85
+ async issueAuthLease(request) {
86
+ const lease = await issueSyncularAuthLease({
87
+ baseUrl: this.config.baseUrl,
88
+ headers: this.#authHeaders,
89
+ request,
90
+ appSchema: this.config.appSchema,
91
+ });
92
+ await this.upsertAuthLease(lease);
93
+ this.#emitDiagnostic({
94
+ at: Date.now(),
95
+ level: 'info',
96
+ source: 'auth',
97
+ code: 'auth_lease.issued',
98
+ message: 'Syncular auth lease issued and stored',
99
+ details: {
100
+ leaseId: lease.leaseId,
101
+ expiresAtMs: lease.expiresAtMs,
102
+ schemaVersion: lease.schemaVersion,
103
+ },
104
+ });
105
+ return lease;
106
+ }
107
+ async upsertAuthLease(lease) {
108
+ this.raw.upsertAuthLeaseJson(JSON.stringify(lease));
109
+ }
110
+ async authLease(leaseId) {
111
+ return parseJson(this.raw.authLeaseJson(leaseId));
112
+ }
113
+ async activeAuthLeases(actorId, nowMs = Date.now()) {
114
+ return parseJson(this.raw.activeAuthLeasesJson(actorId ?? null, BigInt(Math.trunc(nowMs))));
115
+ }
116
+ setFieldEncryption(config) {
117
+ if (config == null && !this.runtime.rust?.features.includes('e2ee'))
118
+ return;
119
+ this.#assertRuntimeFeature('e2ee', 'setFieldEncryption');
120
+ this.raw.setFieldEncryptionJson(config == null
121
+ ? 'null'
122
+ : JSON.stringify(normalizeFieldEncryptionConfig(config)));
123
+ }
124
+ setEncryptedCrdt(config) {
125
+ if (config == null && !this.runtime.rust?.features.includes('e2ee'))
126
+ return;
127
+ this.#assertRuntimeFeature('e2ee', 'setEncryptedCrdt');
128
+ this.raw.setEncryptedCrdtJson(config == null
129
+ ? 'null'
130
+ : JSON.stringify(normalizeEncryptedCrdtConfig(config)));
131
+ }
132
+ setBlobEncryption(config) {
133
+ if (config == null && !this.runtime.rust?.features.includes('e2ee'))
134
+ return;
135
+ this.#assertRuntimeFeature('e2ee', 'setBlobEncryption');
136
+ this.raw.setBlobEncryptionJson(config == null
137
+ ? 'null'
138
+ : JSON.stringify(normalizeBlobEncryptionConfig(config)));
139
+ }
140
+ setAbortSignal(signal) {
141
+ this.raw.setAbortSignal(signal ?? null);
142
+ }
143
+ async applyMutation(operation, localRow) {
144
+ const commitId = await this.raw.applyMutationJson(JSON.stringify(operation), localRow == null ? null : JSON.stringify(localRow));
145
+ this.#drainAndEmitRowsChanged();
146
+ return commitId;
147
+ }
148
+ async applyLeasedMutation(operation, localRow) {
149
+ const commitId = await this.raw.applyLeasedMutationJson(JSON.stringify(operation), localRow == null ? null : JSON.stringify(localRow));
150
+ this.#drainAndEmitRowsChanged();
151
+ return commitId;
152
+ }
153
+ async applyMutationsBatch(operations) {
154
+ const commitIds = parseJson(await this.raw.applyMutationsBatchJson(JSON.stringify(operations)));
155
+ this.#drainAndEmitRowsChanged();
156
+ return commitIds;
157
+ }
158
+ async applyMutationsCommit(operations) {
159
+ const commitId = parseJson(await this.raw.applyMutationsCommitJson(JSON.stringify(operations)));
160
+ this.#drainAndEmitRowsChanged();
161
+ return commitId;
162
+ }
163
+ async applyLeasedMutationsCommit(operations) {
164
+ const commitId = parseJson(await this.raw.applyLeasedMutationsCommitJson(JSON.stringify(operations)));
165
+ this.#drainAndEmitRowsChanged();
166
+ return commitId;
167
+ }
168
+ async syncPull(options = {}) {
169
+ const result = await this.#runTracedSync('syncPull', options.syncAttempt, async () => this.#decorateSyncResult(parseSyncResult(await this.raw.syncPullJson())));
170
+ this.#captureSyncTimings(result);
171
+ this.#emitRowsChanged('remotePull', result);
172
+ return result;
173
+ }
174
+ async applyRealtimeSyncPack(bytes) {
175
+ const result = this.#decorateSyncResult(parseSyncResult(await this.raw.applyRealtimeSyncPackBytes(bytes)));
176
+ this.#captureSyncTimings(result);
177
+ this.#emitRowsChanged('remotePull', result);
178
+ return result;
179
+ }
180
+ async syncPush(options = {}) {
181
+ try {
182
+ const result = await this.#runTracedSync('syncPush', options.syncAttempt, async () => this.#decorateSyncResult(parseSyncResult(await this.raw.syncPushJson())));
183
+ this.#captureSyncTimings(result);
184
+ this.#emitRowsChanged('localWrite', result);
185
+ return result;
186
+ }
187
+ catch (error) {
188
+ this.raw.recoverSyncPushErrorJson(String(error));
189
+ throw error;
190
+ }
191
+ }
192
+ async syncOnce(options = {}) {
193
+ try {
194
+ const result = await this.#runTracedSync('syncOnce', options.syncAttempt, async () => this.#decorateSyncResult(parseSyncResult(await this.raw.syncOnceJson())));
195
+ this.#captureSyncTimings(result);
196
+ this.#emitRowsChanged('remotePull', result);
197
+ return result;
198
+ }
199
+ catch (error) {
200
+ this.raw.recoverSyncPushErrorJson(String(error));
201
+ throw error;
202
+ }
203
+ }
204
+ resumeFromBackground(options = {}) {
205
+ return this.syncOnce(options);
206
+ }
207
+ transportStats() {
208
+ return parseJson(this.raw.transportStatsJson());
209
+ }
210
+ resetTransportStats() {
211
+ this.raw.resetTransportStats();
212
+ }
213
+ async conflictSummaries() {
214
+ return parseConflictSummaries(await this.raw.conflictSummariesJson());
215
+ }
216
+ retryConflictKeepLocal(id) {
217
+ return this.raw.retryConflictKeepLocal(id);
218
+ }
219
+ resolveConflict(id, resolution) {
220
+ return this.raw.resolveConflict(id, resolution);
221
+ }
222
+ executeSql(sql, params = []) {
223
+ assertSyncularReadonlySql(sql);
224
+ return this.raw.executeSqlValue(sql, params);
225
+ }
226
+ executeUnsafeSql(sql, params = []) {
227
+ return this.raw.executeUnsafeSqlValue(sql, params);
228
+ }
229
+ subscribeQuery(sql, params, tables, hints = []) {
230
+ return parseJson(this.raw.subscribeQueryJson(sql, stringifyParams(params), JSON.stringify(tables), JSON.stringify(hints)));
231
+ }
232
+ unsubscribeQuery(id) {
233
+ this.raw.unsubscribeQuery(id);
234
+ }
235
+ drainLiveQueryEvents() {
236
+ return parseJson(this.raw.drainLiveQueryEventsJson());
237
+ }
238
+ liveQueryDiagnostics() {
239
+ return parseJson(this.raw.liveQueryDiagnosticsJson());
240
+ }
241
+ addRowsChangedListener(listener) {
242
+ this.#rowsChangedListeners.add(listener);
243
+ return () => {
244
+ this.#rowsChangedListeners.delete(listener);
245
+ };
246
+ }
247
+ async listTable(table) {
248
+ return parseJson(await this.raw.listTableJson(table));
249
+ }
250
+ async storeBlob(data, options = {}) {
251
+ assertSyncularBlobPayloadLimit({
252
+ operation: 'store',
253
+ size: data.byteLength,
254
+ limits: this.blobLimits,
255
+ options,
256
+ diagnostics: (event) => this.#emitDiagnostic(event),
257
+ });
258
+ return parseJson(await this.raw.storeBlobJson(data, JSON.stringify(options)));
259
+ }
260
+ async retrieveBlob(ref) {
261
+ assertSyncularBlobPayloadLimit({
262
+ operation: 'retrieve',
263
+ size: ref.size,
264
+ limits: this.blobLimits,
265
+ refHash: ref.hash,
266
+ diagnostics: (event) => this.#emitDiagnostic(event),
267
+ });
268
+ const wasLocal = this.#hasDiagnosticListeners()
269
+ ? this.raw.isBlobLocal(ref.hash)
270
+ : undefined;
271
+ try {
272
+ const bytes = await this.raw.retrieveBlob(JSON.stringify(ref));
273
+ if (wasLocal !== undefined) {
274
+ this.#emitDiagnostic({
275
+ at: Date.now(),
276
+ level: 'info',
277
+ source: 'blob',
278
+ code: wasLocal ? 'blob.cache_hit' : 'blob.cache_miss',
279
+ message: wasLocal
280
+ ? 'Syncular blob served from local cache'
281
+ : 'Syncular blob fetched after local cache miss',
282
+ details: {
283
+ hash: ref.hash,
284
+ size: ref.size,
285
+ mimeType: ref.mimeType,
286
+ encrypted: ref.encrypted === true,
287
+ ...(ref.keyId ? { keyId: ref.keyId } : {}),
288
+ },
289
+ });
290
+ }
291
+ return bytes;
292
+ }
293
+ catch (error) {
294
+ if (this.#hasDiagnosticListeners()) {
295
+ this.#emitDiagnostic({
296
+ at: Date.now(),
297
+ level: 'warn',
298
+ source: 'blob',
299
+ code: 'blob.download_failed',
300
+ message: `Syncular blob download failed: ${String(error)}`,
301
+ details: {
302
+ hash: ref.hash,
303
+ size: ref.size,
304
+ mimeType: ref.mimeType,
305
+ encrypted: ref.encrypted === true,
306
+ ...(ref.keyId ? { keyId: ref.keyId } : {}),
307
+ },
308
+ });
309
+ }
310
+ throw error;
311
+ }
312
+ }
313
+ isBlobLocal(hash) {
314
+ const local = this.raw.isBlobLocal(hash);
315
+ if (this.#hasDiagnosticListeners()) {
316
+ this.#emitDiagnostic({
317
+ at: Date.now(),
318
+ level: 'debug',
319
+ source: 'blob',
320
+ code: local ? 'blob.cache_hit' : 'blob.cache_miss',
321
+ message: local
322
+ ? 'Syncular blob is available in local cache'
323
+ : 'Syncular blob is not available in local cache',
324
+ details: { hash },
325
+ });
326
+ }
327
+ return local;
328
+ }
329
+ async processBlobUploadQueue(options) {
330
+ const result = parseJson(await this.raw.processBlobUploadQueueJson(options?.retryNow === true));
331
+ if (this.#hasDiagnosticListeners()) {
332
+ this.#emitDiagnostic({
333
+ at: Date.now(),
334
+ level: result.failed > 0 ? 'warn' : 'info',
335
+ source: 'blob',
336
+ code: 'blob.upload_queue_processed',
337
+ message: 'Syncular blob upload queue processed',
338
+ details: result,
339
+ });
340
+ }
341
+ return result;
342
+ }
343
+ blobUploadQueueStats() {
344
+ return parseJson(this.raw.blobUploadQueueStatsJson());
345
+ }
346
+ blobCacheStats() {
347
+ return parseJson(this.raw.blobCacheStatsJson());
348
+ }
349
+ pruneBlobCache(maxBytes = 0) {
350
+ const prunedBytes = Number(this.raw.pruneBlobCache(BigInt(maxBytes)));
351
+ if (this.#hasDiagnosticListeners()) {
352
+ this.#emitDiagnostic({
353
+ at: Date.now(),
354
+ level: 'info',
355
+ source: 'blob',
356
+ code: 'blob.cache_pruned',
357
+ message: 'Syncular blob cache pruned',
358
+ details: { prunedBytes, maxBytes },
359
+ });
360
+ }
361
+ return prunedBytes;
362
+ }
363
+ clearBlobCache() {
364
+ this.raw.clearBlobCache();
365
+ if (this.#hasDiagnosticListeners()) {
366
+ this.#emitDiagnostic({
367
+ at: Date.now(),
368
+ level: 'info',
369
+ source: 'blob',
370
+ code: 'blob.cache_cleared',
371
+ message: 'Syncular blob cache cleared',
372
+ });
373
+ }
374
+ }
375
+ compactStorage(options = {}) {
376
+ return parseJson(this.raw.compactStorageJson(JSON.stringify(options)));
377
+ }
378
+ generatedSchemaState() {
379
+ return parseJson(this.raw.generatedSchemaStateJson());
380
+ }
381
+ async localHealthCheck() {
382
+ return parseJson(await this.raw.localHealthCheckJson());
383
+ }
384
+ async exportLocalSupportBundle() {
385
+ return parseJson(await this.raw.exportLocalSupportBundleJson());
386
+ }
387
+ async importLocalSupportBundle(bundle) {
388
+ const bundleJson = typeof bundle === 'string' ? bundle : JSON.stringify(bundle);
389
+ return parseJson(await this.raw.importLocalSupportBundleJson(bundleJson));
390
+ }
391
+ async repairLocalHealth(request) {
392
+ const normalized = {
393
+ action: request.action,
394
+ subscriptionIds: [...(request.subscriptionIds ?? [])],
395
+ tables: [...(request.tables ?? [])],
396
+ };
397
+ return parseJson(await this.raw.repairLocalHealthJson(JSON.stringify(normalized)));
398
+ }
399
+ async resetLocalSyncState(request = {}) {
400
+ const normalized = {
401
+ subscriptionIds: [...(request.subscriptionIds ?? [])],
402
+ clearSyncedRows: request.clearSyncedRows === true,
403
+ };
404
+ const result = parseJson(await this.raw.resetLocalSyncStateJson(JSON.stringify(normalized)));
405
+ for (const id of normalized.subscriptionIds.length > 0
406
+ ? normalized.subscriptionIds
407
+ : this.#subscriptions.map((subscription) => subscription.id)) {
408
+ this.#bootstrapById.delete(id);
409
+ }
410
+ return result;
411
+ }
412
+ buildYjsTextUpdate(args) {
413
+ this.#assertRuntimeFeature('crdt-yjs', 'buildYjsTextUpdate');
414
+ return parseJson(this.raw.buildYjsTextUpdateJson(JSON.stringify(args)));
415
+ }
416
+ applyYjsTextUpdates(args) {
417
+ this.#assertRuntimeFeature('crdt-yjs', 'applyYjsTextUpdates');
418
+ return parseJson(this.raw.applyYjsTextUpdatesJson(JSON.stringify(args)));
419
+ }
420
+ applyYjsEnvelopeToPayload(args) {
421
+ this.#assertRuntimeFeature('crdt-yjs', 'applyYjsEnvelopeToPayload');
422
+ const result = parseJson(this.raw.applyYjsEnvelopeToPayloadJson(JSON.stringify(args)));
423
+ return result.payload;
424
+ }
425
+ async openCrdtField(request) {
426
+ this.#assertRuntimeFeature('crdt-yjs', 'openCrdtField');
427
+ return parseJson(this.raw.openCrdtFieldJson(JSON.stringify(request)));
428
+ }
429
+ async applyCrdtFieldText(request) {
430
+ this.#assertRuntimeFeature('crdt-yjs', 'applyCrdtFieldText');
431
+ const receipt = parseJson(this.raw.applyCrdtFieldTextJson(JSON.stringify(request)));
432
+ this.#drainAndEmitRowsChanged();
433
+ return receipt;
434
+ }
435
+ async applyCrdtFieldYjsUpdate(request) {
436
+ this.#assertRuntimeFeature('crdt-yjs', 'applyCrdtFieldYjsUpdate');
437
+ const receipt = parseJson(this.raw.applyCrdtFieldYjsUpdateJson(JSON.stringify(request)));
438
+ this.#drainAndEmitRowsChanged();
439
+ return receipt;
440
+ }
441
+ async materializeCrdtField(request) {
442
+ this.#assertRuntimeFeature('crdt-yjs', 'materializeCrdtField');
443
+ return parseJson(this.raw.materializeCrdtFieldJson(JSON.stringify(request)));
444
+ }
445
+ async crdtDocumentSnapshot(request) {
446
+ this.#assertRuntimeFeature('crdt-yjs', 'crdtDocumentSnapshot');
447
+ return parseJson(this.raw.crdtDocumentSnapshotJson(JSON.stringify(request)));
448
+ }
449
+ async crdtUpdateLog(request) {
450
+ this.#assertRuntimeFeature('crdt-yjs', 'crdtUpdateLog');
451
+ return parseJson(this.raw.crdtUpdateLogJson(JSON.stringify(request)));
452
+ }
453
+ async snapshotCrdtFieldStateVector(request) {
454
+ this.#assertRuntimeFeature('crdt-yjs', 'snapshotCrdtFieldStateVector');
455
+ return parseJson(this.raw.snapshotCrdtFieldStateVectorJson(JSON.stringify(request)));
456
+ }
457
+ async compactCrdtField(request) {
458
+ this.#assertRuntimeFeature('crdt-yjs', 'compactCrdtField');
459
+ const receipt = parseJson(this.raw.compactCrdtFieldJson(JSON.stringify(request)));
460
+ this.#drainAndEmitRowsChanged();
461
+ return receipt;
462
+ }
463
+ encryptionHelper(method, args = {}) {
464
+ this.#assertRuntimeFeature('e2ee', 'encryptionHelper');
465
+ return parseJson(this.raw.encryptionHelperJson(method, JSON.stringify(args)));
466
+ }
467
+ async runtimeInfo() {
468
+ return this.runtime;
469
+ }
470
+ connectionState() {
471
+ const lastDiagnostic = this.#recentDiagnostics[this.#recentDiagnostics.length - 1];
472
+ return {
473
+ closed: this.#closed,
474
+ pendingRequests: 0,
475
+ realtime: 'disconnected',
476
+ ...(lastDiagnostic ? { lastDiagnostic } : {}),
477
+ };
478
+ }
479
+ lifecycleState() {
480
+ const bootstrap = buildBootstrapStatus(this.#subscriptions, this.#bootstrapById, this.pullOptions);
481
+ const lastDiagnostic = this.#recentDiagnostics[this.#recentDiagnostics.length - 1];
482
+ return {
483
+ phase: this.#closed
484
+ ? 'closed'
485
+ : bootstrap.complete
486
+ ? 'complete'
487
+ : 'offline',
488
+ realtime: 'disconnected',
489
+ online: false,
490
+ requiresAction: false,
491
+ pendingRequests: 0,
492
+ bootstrap: {
493
+ complete: bootstrap.complete,
494
+ criticalReady: bootstrap.criticalReady,
495
+ interactiveReady: bootstrap.interactiveReady,
496
+ isBootstrapping: bootstrap.isBootstrapping,
497
+ progressPercent: bootstrap.progressPercent,
498
+ },
499
+ ...(lastDiagnostic ? { lastDiagnostic } : {}),
500
+ };
501
+ }
502
+ async diagnosticSnapshot() {
503
+ const bootstrap = buildBootstrapStatus(this.#subscriptions, this.#bootstrapById, this.pullOptions);
504
+ return {
505
+ generatedAt: Date.now(),
506
+ runtime: this.runtime,
507
+ connection: this.connectionState(),
508
+ subscriptions: summarizeSyncularDiagnosticSubscriptions(this.#subscriptions, bootstrap),
509
+ recentDiagnostics: [...this.#recentDiagnostics],
510
+ recentSyncTimings: [...this.#recentSyncTimings],
511
+ bootstrap,
512
+ transportStats: this.transportStats(),
513
+ };
514
+ }
515
+ addDiagnosticListener(listener) {
516
+ this.#diagnosticListeners.add(listener);
517
+ return () => {
518
+ this.#diagnosticListeners.delete(listener);
519
+ };
520
+ }
521
+ #decorateSyncResult(result) {
522
+ for (const subscription of result.subscriptions) {
523
+ this.#bootstrapById.set(subscription.id, {
524
+ id: subscription.id,
525
+ table: subscription.table,
526
+ status: subscription.status,
527
+ ready: subscription.ready,
528
+ phase: subscription.phase,
529
+ progressPercent: subscription.progressPercent,
530
+ cursor: subscription.nextCursor,
531
+ bootstrapState: subscription.bootstrapState,
532
+ bootstrapPhase: subscription.bootstrapPhase,
533
+ });
534
+ }
535
+ return {
536
+ ...result,
537
+ bootstrap: buildBootstrapStatus(this.#subscriptions, this.#bootstrapById, this.pullOptions),
538
+ };
539
+ }
540
+ #emitRowsChanged(source, result) {
541
+ if (this.#rowsChangedListeners.size === 0 ||
542
+ (result.changedTables.length === 0 && result.changedRows.length === 0)) {
543
+ return;
544
+ }
545
+ for (const listener of this.#rowsChangedListeners) {
546
+ try {
547
+ listener({
548
+ source,
549
+ changedTables: result.changedTables,
550
+ changedRows: result.changedRows,
551
+ changedRowsTruncated: result.changedRowsTruncated,
552
+ });
553
+ }
554
+ catch {
555
+ // Row-change listeners must never break client control flow.
556
+ }
557
+ }
558
+ }
559
+ #captureSyncTimings(result) {
560
+ appendSyncularSyncTimings(this.#recentSyncTimings, result.timings);
561
+ }
562
+ async #runTracedSync(requestType, providedAttempt, run) {
563
+ const syncAttempt = providedAttempt ?? createSyncularSyncAttempt();
564
+ const startedAt = Date.now();
565
+ this.#emitDiagnostic({
566
+ at: startedAt,
567
+ level: 'debug',
568
+ source: 'sync',
569
+ code: `sync.${requestType}.started`,
570
+ message: `Syncular ${requestType} started`,
571
+ ...syncularDiagnosticAttemptFields(syncAttempt),
572
+ });
573
+ this.raw.setAuthHeadersJson(JSON.stringify({
574
+ ...this.#authHeaders,
575
+ ...syncularSyncAttemptHeaders(syncAttempt),
576
+ }));
577
+ try {
578
+ const result = await run();
579
+ this.#emitDiagnostic({
580
+ at: Date.now(),
581
+ level: 'info',
582
+ source: 'sync',
583
+ code: `sync.${requestType}.completed`,
584
+ message: `Syncular ${requestType} completed`,
585
+ ...syncularDiagnosticAttemptFields(syncAttempt),
586
+ details: { durationMs: Date.now() - startedAt },
587
+ });
588
+ if (isSyncularSyncResult(result)) {
589
+ this.#emitScopeRevokedDiagnostic(requestType, result, syncAttempt);
590
+ }
591
+ return result;
592
+ }
593
+ catch (error) {
594
+ const classifiedError = toSyncularClientError(error);
595
+ const classified = classifiedError instanceof SyncularClientError ? classifiedError : null;
596
+ this.#emitDiagnostic({
597
+ at: Date.now(),
598
+ level: 'warn',
599
+ source: 'sync',
600
+ code: classified?.code ?? `sync.${requestType}.failed`,
601
+ message: `Syncular ${requestType} failed`,
602
+ ...syncularDiagnosticAttemptFields(syncAttempt),
603
+ details: {
604
+ durationMs: Date.now() - startedAt,
605
+ error: classifiedError instanceof Error
606
+ ? classifiedError.message
607
+ : String(classifiedError),
608
+ ...(classified
609
+ ? {
610
+ errorCode: classified.code,
611
+ category: classified.category,
612
+ retryable: classified.retryable,
613
+ recommendedAction: classified.recommendedAction,
614
+ ...(classified.details ?? {}),
615
+ }
616
+ : {}),
617
+ },
618
+ });
619
+ throw classifiedError;
620
+ }
621
+ finally {
622
+ this.raw.setAuthHeadersJson(JSON.stringify(this.#authHeaders));
623
+ }
624
+ }
625
+ #emitDiagnostic(event) {
626
+ appendSyncularDiagnosticEvent(this.#recentDiagnostics, event);
627
+ if (this.#diagnosticListeners.size === 0)
628
+ return;
629
+ for (const listener of this.#diagnosticListeners) {
630
+ try {
631
+ listener(event);
632
+ }
633
+ catch {
634
+ // Diagnostics must never break client control flow.
635
+ }
636
+ }
637
+ }
638
+ #hasDiagnosticListeners() {
639
+ return this.#diagnosticListeners.size > 0;
640
+ }
641
+ #drainAndEmitRowsChanged() {
642
+ let events;
643
+ try {
644
+ events = parseJson(this.raw.drainRowsChangedEventsJson());
645
+ }
646
+ catch {
647
+ return;
648
+ }
649
+ if (this.#rowsChangedListeners.size === 0)
650
+ return;
651
+ for (const event of events) {
652
+ if (event.changedTables.length === 0 && event.changedRows.length === 0) {
653
+ continue;
654
+ }
655
+ for (const listener of this.#rowsChangedListeners) {
656
+ try {
657
+ listener(event);
658
+ }
659
+ catch {
660
+ // Row-change listeners must never break client control flow.
661
+ }
662
+ }
663
+ }
664
+ }
665
+ close() {
666
+ this.#closed = true;
667
+ this.raw.close();
668
+ }
669
+ #emitScopeRevokedDiagnostic(requestType, result, syncAttempt) {
670
+ const revokedSubscriptionIds = result.subscriptions
671
+ .filter((subscription) => subscription.status === 'revoked')
672
+ .map((subscription) => subscription.id);
673
+ if (revokedSubscriptionIds.length === 0)
674
+ return;
675
+ this.#emitDiagnostic({
676
+ at: Date.now(),
677
+ level: 'warn',
678
+ source: 'sync',
679
+ code: 'sync.scope_revoked',
680
+ message: 'Syncular subscription scope revoked',
681
+ ...syncularDiagnosticAttemptFields(syncAttempt),
682
+ details: {
683
+ requestType,
684
+ revokedSubscriptionIds,
685
+ revokedSubscriptionCount: revokedSubscriptionIds.length,
686
+ },
687
+ });
688
+ }
689
+ }
690
+ function isSyncularSyncResult(value) {
691
+ return (value !== null &&
692
+ typeof value === 'object' &&
693
+ Array.isArray(value.subscriptions));
694
+ }
695
+ function parseSyncResult(value) {
696
+ const raw = parseJson(value);
697
+ return {
698
+ changedTables: raw.changed_tables ?? [],
699
+ changedRows: raw.changed_rows ?? [],
700
+ changedRowsTruncated: raw.changed_rows_truncated ?? false,
701
+ subscriptions: (raw.subscriptions ?? []).map((subscription) => ({
702
+ id: subscription.id,
703
+ table: subscription.table,
704
+ status: subscription.status,
705
+ scopes: subscription.scopes,
706
+ nextCursor: subscription.next_cursor,
707
+ bootstrapPhase: subscription.bootstrap_phase ?? 0,
708
+ bootstrapState: parseBootstrapState(subscription.bootstrap_state),
709
+ ready: subscription.ready === true,
710
+ phase: subscription.phase ?? 'pending',
711
+ progressPercent: subscription.progress_percent ?? 0,
712
+ snapshotRows: subscription.snapshot_rows ?? [],
713
+ commits: subscription.commits ?? [],
714
+ })),
715
+ bootstrap: parseBootstrapStatus(raw.bootstrap),
716
+ pushedCommits: raw.pushed_commits ?? 0,
717
+ timings: {
718
+ totalMs: raw.timings?.total_ms ?? 0,
719
+ pushMs: raw.timings?.push_ms ?? 0,
720
+ pullMs: raw.timings?.pull_ms ?? 0,
721
+ pullRequestMs: raw.timings?.pull_request_ms ?? 0,
722
+ syncPackDecodeMs: raw.timings?.sync_pack_decode_ms ?? 0,
723
+ pullTransformMs: raw.timings?.pull_transform_ms ?? 0,
724
+ integrityVerifyMs: raw.timings?.integrity_verify_ms ?? 0,
725
+ snapshotFetchMs: raw.timings?.snapshot_fetch_ms ?? 0,
726
+ pullApplyMs: raw.timings?.pull_apply_ms ?? 0,
727
+ scopeClearMs: raw.timings?.scope_clear_ms ?? 0,
728
+ snapshotRowApplyMs: raw.timings?.snapshot_row_apply_ms ?? 0,
729
+ snapshotArtifactApplyMs: raw.timings?.snapshot_artifact_apply_ms ?? 0,
730
+ snapshotArtifactCheckpointMs: raw.timings?.snapshot_artifact_checkpoint_ms ?? 0,
731
+ snapshotArtifactCheckpointCount: raw.timings?.snapshot_artifact_checkpoint_count ?? 0,
732
+ snapshotChunkApplyMs: raw.timings?.snapshot_chunk_apply_ms ?? 0,
733
+ snapshotChunkMaterializeMs: raw.timings?.snapshot_chunk_materialize_ms ?? 0,
734
+ snapshotChunkResetMs: raw.timings?.snapshot_chunk_reset_ms ?? 0,
735
+ snapshotChunkBindMs: raw.timings?.snapshot_chunk_bind_ms ?? 0,
736
+ snapshotChunkStepMs: raw.timings?.snapshot_chunk_step_ms ?? 0,
737
+ commitApplyMs: raw.timings?.commit_apply_ms ?? 0,
738
+ subscriptionStateMs: raw.timings?.subscription_state_ms ?? 0,
739
+ notifyMs: raw.timings?.notify_ms ?? 0,
740
+ },
741
+ };
742
+ }
743
+ function parseBootstrapState(state) {
744
+ if (!state)
745
+ return null;
746
+ return {
747
+ asOfCommitSeq: state.asOfCommitSeq ?? 0,
748
+ tables: state.tables ?? [],
749
+ tableIndex: state.tableIndex ?? 0,
750
+ rowCursor: state.rowCursor ?? null,
751
+ };
752
+ }
753
+ function parseBootstrapStatus(raw) {
754
+ return {
755
+ channelPhase: raw?.channel_phase ?? 'idle',
756
+ progressPercent: raw?.progress_percent ?? 100,
757
+ isBootstrapping: raw?.is_bootstrapping === true,
758
+ criticalReady: raw?.critical_ready ?? true,
759
+ interactiveReady: raw?.interactive_ready ?? true,
760
+ complete: raw?.complete ?? true,
761
+ activePhase: raw?.active_phase ?? null,
762
+ expectedSubscriptionIds: raw?.expected_subscription_ids ?? [],
763
+ readySubscriptionIds: raw?.ready_subscription_ids ?? [],
764
+ pendingSubscriptionIds: raw?.pending_subscription_ids ?? [],
765
+ subscriptions: (raw?.subscriptions ?? []).map((subscription) => ({
766
+ id: subscription.id,
767
+ table: subscription.table,
768
+ expected: subscription.expected ?? true,
769
+ ready: subscription.ready === true,
770
+ status: subscription.status ?? null,
771
+ phase: subscription.phase ?? 'pending',
772
+ progressPercent: subscription.progress_percent ?? 0,
773
+ cursor: subscription.cursor ?? null,
774
+ bootstrapState: parseBootstrapState(subscription.bootstrap_state),
775
+ bootstrapPhase: subscription.bootstrap_phase ?? 0,
776
+ })),
777
+ phases: (raw?.phases ?? []).map((phase) => ({
778
+ phase: phase.phase ?? 0,
779
+ expectedSubscriptionIds: phase.expected_subscription_ids ?? [],
780
+ readySubscriptionIds: phase.ready_subscription_ids ?? [],
781
+ pendingSubscriptionIds: phase.pending_subscription_ids ?? [],
782
+ isReady: phase.is_ready === true,
783
+ progressPercent: phase.progress_percent ?? 0,
784
+ })),
785
+ };
786
+ }
787
+ function buildBootstrapStatus(configuredSubscriptions, cachedById, pullOptions) {
788
+ const criticalPhase = normalizeBootstrapPhase(pullOptions?.criticalBootstrapPhase);
789
+ const interactivePhase = Math.max(criticalPhase, normalizeBootstrapPhase(pullOptions?.interactiveBootstrapPhase ?? 1));
790
+ const subscriptions = configuredSubscriptions.map((spec) => {
791
+ const cached = cachedById.get(spec.id);
792
+ const bootstrapPhase = normalizeBootstrapPhase(spec.bootstrapPhase);
793
+ return {
794
+ id: spec.id,
795
+ table: spec.table,
796
+ expected: true,
797
+ ready: cached?.ready ?? false,
798
+ status: cached?.status ?? null,
799
+ phase: cached?.phase ?? 'pending',
800
+ progressPercent: cached?.progressPercent ?? 0,
801
+ cursor: cached?.cursor ?? null,
802
+ bootstrapState: cached?.bootstrapState ?? null,
803
+ bootstrapPhase,
804
+ };
805
+ });
806
+ const expectedSubscriptionIds = subscriptions.map((subscription) => subscription.id);
807
+ const readySubscriptionIds = subscriptions
808
+ .filter((subscription) => subscription.ready)
809
+ .map((subscription) => subscription.id);
810
+ const pendingSubscriptionIds = subscriptions
811
+ .filter((subscription) => !subscription.ready)
812
+ .map((subscription) => subscription.id);
813
+ const complete = pendingSubscriptionIds.length === 0;
814
+ const criticalReady = subscriptions.every((subscription) => subscription.bootstrapPhase > criticalPhase || subscription.ready);
815
+ const interactiveReady = subscriptions.every((subscription) => subscription.bootstrapPhase > interactivePhase || subscription.ready);
816
+ const activePhase = subscriptions
817
+ .filter((subscription) => !subscription.ready)
818
+ .reduce((lowest, subscription) => lowest === null || subscription.bootstrapPhase < lowest
819
+ ? subscription.bootstrapPhase
820
+ : lowest, null) ?? null;
821
+ const hasError = subscriptions.some((subscription) => subscription.phase === 'error');
822
+ const isBootstrapping = subscriptions.some((subscription) => !subscription.ready && subscription.phase !== 'error');
823
+ const channelPhase = hasError
824
+ ? 'error'
825
+ : isBootstrapping
826
+ ? 'bootstrapping'
827
+ : complete && expectedSubscriptionIds.length > 0
828
+ ? 'live'
829
+ : 'idle';
830
+ const progressPercent = subscriptions.length === 0
831
+ ? 100
832
+ : Math.round(subscriptions.reduce((sum, subscription) => sum + subscription.progressPercent, 0) / subscriptions.length);
833
+ const phaseMap = new Map();
834
+ for (const subscription of subscriptions) {
835
+ const phase = phaseMap.get(subscription.bootstrapPhase) ?? {
836
+ expectedSubscriptionIds: [],
837
+ readySubscriptionIds: [],
838
+ pendingSubscriptionIds: [],
839
+ progressPercent: 0,
840
+ };
841
+ phase.expectedSubscriptionIds.push(subscription.id);
842
+ phase.progressPercent += subscription.progressPercent;
843
+ if (subscription.ready) {
844
+ phase.readySubscriptionIds.push(subscription.id);
845
+ }
846
+ else {
847
+ phase.pendingSubscriptionIds.push(subscription.id);
848
+ }
849
+ phaseMap.set(subscription.bootstrapPhase, phase);
850
+ }
851
+ const phases = [...phaseMap.entries()]
852
+ .sort(([left], [right]) => left - right)
853
+ .map(([phase, summary]) => ({
854
+ phase,
855
+ expectedSubscriptionIds: summary.expectedSubscriptionIds,
856
+ readySubscriptionIds: summary.readySubscriptionIds,
857
+ pendingSubscriptionIds: summary.pendingSubscriptionIds,
858
+ isReady: summary.pendingSubscriptionIds.length === 0,
859
+ progressPercent: summary.expectedSubscriptionIds.length === 0
860
+ ? 100
861
+ : Math.round(summary.progressPercent / summary.expectedSubscriptionIds.length),
862
+ }));
863
+ return {
864
+ channelPhase,
865
+ progressPercent,
866
+ isBootstrapping,
867
+ criticalReady,
868
+ interactiveReady,
869
+ complete,
870
+ activePhase,
871
+ expectedSubscriptionIds,
872
+ readySubscriptionIds,
873
+ pendingSubscriptionIds,
874
+ subscriptions,
875
+ phases,
876
+ };
877
+ }
878
+ function normalizeBootstrapPhase(value) {
879
+ return Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
880
+ }
881
+ function parseConflictSummaries(value) {
882
+ return parseJson(value).map((conflict) => ({
883
+ id: conflict.id,
884
+ clientCommitId: conflict.client_commit_id,
885
+ opIndex: conflict.op_index,
886
+ resultStatus: conflict.result_status,
887
+ message: conflict.message,
888
+ code: conflict.code,
889
+ serverVersion: conflict.server_version,
890
+ resolvedAt: conflict.resolved_at,
891
+ resolution: conflict.resolution,
892
+ }));
893
+ }
894
+ function parseJson(value) {
895
+ return JSON.parse(value);
896
+ }
897
+ function stringifyParams(params) {
898
+ return JSON.stringify(params, (_key, value) => {
899
+ if (typeof value === 'bigint')
900
+ return Number(value);
901
+ if (value instanceof Uint8Array)
902
+ return Array.from(value);
903
+ return value;
904
+ });
905
+ }
906
+ function normalizeFieldEncryptionConfig(config) {
907
+ const keys = {};
908
+ for (const [kid, value] of Object.entries(config.keys)) {
909
+ keys[kid] = value instanceof Uint8Array ? bytesToBase64Url(value) : value;
910
+ }
911
+ return { ...config, keys };
912
+ }
913
+ function normalizeEncryptedCrdtConfig(config) {
914
+ const keys = {};
915
+ for (const [kid, value] of Object.entries(config.keys)) {
916
+ keys[kid] = value instanceof Uint8Array ? bytesToBase64Url(value) : value;
917
+ }
918
+ return { ...config, keys };
919
+ }
920
+ function normalizeBlobEncryptionConfig(config) {
921
+ const keys = {};
922
+ for (const [kid, value] of Object.entries(config.keys)) {
923
+ keys[kid] = value instanceof Uint8Array ? bytesToBase64Url(value) : value;
924
+ }
925
+ return { ...config, keys };
926
+ }
927
+ function bytesToBase64Url(bytes) {
928
+ let binary = '';
929
+ for (const byte of bytes)
930
+ binary += String.fromCharCode(byte);
931
+ return btoa(binary)
932
+ .replace(/\+/g, '-')
933
+ .replace(/\//g, '_')
934
+ .replace(/=+$/g, '');
935
+ }
936
+ //# sourceMappingURL=rust-client.js.map