@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,584 @@
1
+ import { resolveSyncularClientConfig } from './client-config';
2
+ import { syncularDiagnosticAttemptFields } from './diagnostics';
3
+ import {
4
+ classifySyncularError,
5
+ SyncularClientError,
6
+ syncularErrorDetails,
7
+ syncularErrorMessage,
8
+ syncularErrorStatus,
9
+ } from './errors';
10
+ import {
11
+ dispatchGeneratedSyncularWorkerRequest,
12
+ generatedSyncularWorkerRequestDiagnosticSource,
13
+ isGeneratedSyncularAbortableWorkerRequestType,
14
+ isGeneratedSyncularDiagnosedSuccessWorkerRequestType,
15
+ } from './generated-bridge';
16
+ import { createSyncularRuntimeInfo } from './runtime-contract';
17
+ import { openSyncularRustClient, type SyncularRustClient } from './rust-client';
18
+ import type {
19
+ ResolvedSyncularClientConfig,
20
+ SyncularDiagnosticEvent,
21
+ } from './types';
22
+ import {
23
+ getSyncularRustRuntimeInfo,
24
+ getSyncularWasmGlueUrl,
25
+ getSyncularWasmUrl,
26
+ type SyncularWasmGlue,
27
+ } from './wasm-runtime';
28
+ import type {
29
+ SyncularWorkerErrorPayload,
30
+ SyncularWorkerOutboundMessage,
31
+ SyncularWorkerRequest,
32
+ SyncularWorkerRuntimeArtifact,
33
+ } from './worker-protocol';
34
+ import {
35
+ createSyncularWorkerErrorPayload,
36
+ SYNCULAR_WORKER_PROTOCOL_VERSION,
37
+ } from './worker-protocol';
38
+ import { SyncularWorkerRealtimeController } from './worker-realtime';
39
+
40
+ let client: SyncularRustClient | undefined;
41
+ let openedConfig: ResolvedSyncularClientConfig | undefined;
42
+ let openedRuntime: SyncularWorkerRuntimeArtifact | undefined;
43
+ let removeRowsChangedListener: (() => void) | undefined;
44
+ const canceledRequests = new Set<number>();
45
+ const abortControllers = new Map<number, AbortController>();
46
+ const realtime = new SyncularWorkerRealtimeController({
47
+ getClient: requireClient,
48
+ getConfig: () => openedConfig,
49
+ getLocationOrigin: () =>
50
+ typeof self.location?.origin === 'string'
51
+ ? self.location.origin
52
+ : 'http://localhost',
53
+ createWebSocket: (url) => new WebSocket(url),
54
+ postEvent: (event) => self.postMessage(event),
55
+ postDiagnostic,
56
+ });
57
+
58
+ self.onmessage = (event: MessageEvent<SyncularWorkerRequest>) => {
59
+ void handleRequest(event.data);
60
+ };
61
+
62
+ async function handleRequest(request: SyncularWorkerRequest): Promise<void> {
63
+ if (request.protocolVersion !== SYNCULAR_WORKER_PROTOCOL_VERSION) {
64
+ post({
65
+ id: request.id,
66
+ protocolVersion: SYNCULAR_WORKER_PROTOCOL_VERSION,
67
+ ok: false,
68
+ error: createSyncularWorkerErrorPayload(
69
+ 'worker.protocol_mismatch',
70
+ `Unsupported Syncular worker protocol ${request.protocolVersion}`,
71
+ { details: { supported: SYNCULAR_WORKER_PROTOCOL_VERSION } }
72
+ ),
73
+ });
74
+ return;
75
+ }
76
+
77
+ if (request.type === 'cancel') {
78
+ canceledRequests.add(request.requestId);
79
+ const controller = abortControllers.get(request.requestId);
80
+ controller?.abort();
81
+ if (controller) {
82
+ postDiagnostic({
83
+ level: 'warn',
84
+ source: 'worker',
85
+ code: 'worker.request_aborted',
86
+ message: 'Syncular worker request aborted',
87
+ details: { requestId: request.requestId },
88
+ });
89
+ }
90
+ post({
91
+ id: request.id,
92
+ protocolVersion: request.protocolVersion,
93
+ ok: true,
94
+ });
95
+ return;
96
+ }
97
+
98
+ const startedAt = Date.now();
99
+ const abortController = createAbortController(request.type);
100
+ if (abortController) {
101
+ abortControllers.set(request.id, abortController);
102
+ }
103
+ try {
104
+ if (abortController) client?.setAbortSignal(abortController.signal);
105
+ const value = await dispatch(request);
106
+ if (canceledRequests.delete(request.id)) return;
107
+ post({
108
+ id: request.id,
109
+ protocolVersion: request.protocolVersion,
110
+ ok: true,
111
+ value,
112
+ });
113
+ postRequestSuccessDiagnostic(request, value, Date.now() - startedAt);
114
+ } catch (err) {
115
+ if (canceledRequests.delete(request.id)) return;
116
+ const source = requestDiagnosticSource(request.type);
117
+ const resyncRequired = errorRequiresFullRefresh(err);
118
+ const encodedError = encodeWorkerError(err);
119
+ const diagnosticCode = resyncRequired
120
+ ? `${source}.resync_required`
121
+ : encodedError.code.startsWith(`${source}.`)
122
+ ? encodedError.code
123
+ : `${source}.${request.type}.failed`;
124
+ postDiagnostic({
125
+ level: requestDiagnosticLevel(request.type, err),
126
+ source,
127
+ code: diagnosticCode,
128
+ message: resyncRequired
129
+ ? `Syncular worker request ${request.type} requires full resync`
130
+ : `Syncular worker request ${request.type} failed`,
131
+ details: {
132
+ requestType: request.type,
133
+ durationMs: Date.now() - startedAt,
134
+ error: errorMessage(err),
135
+ errorCode: encodedError.code,
136
+ ...(encodedError.category ? { category: encodedError.category } : {}),
137
+ ...(encodedError.retryable != null
138
+ ? { retryable: encodedError.retryable }
139
+ : {}),
140
+ ...(encodedError.recommendedAction
141
+ ? { recommendedAction: encodedError.recommendedAction }
142
+ : {}),
143
+ ...(resyncRequired ? { resyncRequired: true } : {}),
144
+ ...diagnosticStatus(err),
145
+ },
146
+ ...requestSyncAttemptDiagnosticFields(request),
147
+ });
148
+ post({
149
+ id: request.id,
150
+ protocolVersion: request.protocolVersion,
151
+ ok: false,
152
+ error: encodedError,
153
+ });
154
+ } finally {
155
+ abortControllers.delete(request.id);
156
+ if (abortController) clearClientAbortSignal();
157
+ }
158
+ }
159
+
160
+ async function dispatch(request: SyncularWorkerRequest): Promise<unknown> {
161
+ return dispatchGeneratedSyncularWorkerRequest(
162
+ {
163
+ requireClient,
164
+ openClient,
165
+ startRealtime: (options) => {
166
+ realtime.start(options);
167
+ return true;
168
+ },
169
+ stopRealtime: () => {
170
+ realtime.stop();
171
+ return true;
172
+ },
173
+ sendPresence: (action, scopeKey, metadata) => {
174
+ realtime.sendPresence(action, scopeKey, metadata);
175
+ return true;
176
+ },
177
+ runtimeInfo,
178
+ closeClient,
179
+ },
180
+ request
181
+ );
182
+ }
183
+
184
+ async function openClient(
185
+ request: Extract<SyncularWorkerRequest, { type: 'open' }>
186
+ ): Promise<boolean> {
187
+ const config = resolveSyncularClientConfig(request.config);
188
+ realtime.stop();
189
+ detachRowsChangedListener();
190
+ client?.close();
191
+ client = undefined;
192
+ openedConfig = undefined;
193
+ openedRuntime = undefined;
194
+ const runtime = request.runtime;
195
+ client = await openSyncularRustClient({
196
+ config,
197
+ module: runtime?.wasmGlueUrl
198
+ ? loadWorkerWasmGlue(runtime.wasmGlueUrl)
199
+ : undefined,
200
+ wasmGlueUrl: runtime?.wasmGlueUrl,
201
+ wasmUrl: runtime?.wasmUrl,
202
+ });
203
+ attachRowsChangedListener(client);
204
+ openedConfig = config;
205
+ openedRuntime = runtime;
206
+ return true;
207
+ }
208
+
209
+ function closeClient(): boolean {
210
+ realtime.stop();
211
+ detachRowsChangedListener();
212
+ client?.close();
213
+ client = undefined;
214
+ openedConfig = undefined;
215
+ openedRuntime = undefined;
216
+ return true;
217
+ }
218
+
219
+ async function runtimeInfo(): Promise<
220
+ ReturnType<typeof createSyncularRuntimeInfo>
221
+ > {
222
+ const wasmUrl = getSyncularWasmUrl();
223
+ const selectedWasmUrl = openedRuntime?.wasmUrl ?? wasmUrl;
224
+ const selectedWasmGlueUrl =
225
+ openedRuntime?.wasmGlueUrl ?? getSyncularWasmGlueUrl();
226
+ return createSyncularRuntimeInfo({
227
+ storage: openedConfig?.storage,
228
+ workerUrl:
229
+ typeof self.location?.href === 'string' ? self.location.href : '',
230
+ wasmGlueUrl: selectedWasmGlueUrl,
231
+ wasmUrl: selectedWasmUrl,
232
+ rust: await getSyncularRustRuntimeInfo(
233
+ openedRuntime?.wasmGlueUrl
234
+ ? loadWorkerWasmGlue(openedRuntime.wasmGlueUrl)
235
+ : undefined,
236
+ selectedWasmUrl
237
+ ),
238
+ });
239
+ }
240
+
241
+ function loadWorkerWasmGlue(wasmGlueUrl: string): Promise<SyncularWasmGlue> {
242
+ return import(/* @vite-ignore */ wasmGlueUrl) as Promise<SyncularWasmGlue>;
243
+ }
244
+
245
+ function requireClient(): SyncularRustClient {
246
+ if (!client) {
247
+ throw createSyncularWorkerErrorPayload(
248
+ 'worker.not_open',
249
+ 'Syncular worker client is not open'
250
+ );
251
+ }
252
+ return client;
253
+ }
254
+
255
+ function attachRowsChangedListener(nextClient: SyncularRustClient): void {
256
+ removeRowsChangedListener = nextClient.addRowsChangedListener((event) => {
257
+ post({
258
+ protocolVersion: SYNCULAR_WORKER_PROTOCOL_VERSION,
259
+ type: 'rowsChanged',
260
+ source: event.source,
261
+ changedTables: event.changedTables,
262
+ changedRows: event.changedRows,
263
+ changedRowsTruncated: event.changedRowsTruncated,
264
+ });
265
+ });
266
+ }
267
+
268
+ function detachRowsChangedListener(): void {
269
+ removeRowsChangedListener?.();
270
+ removeRowsChangedListener = undefined;
271
+ }
272
+
273
+ function post(message: SyncularWorkerOutboundMessage): void {
274
+ self.postMessage(message);
275
+ }
276
+
277
+ function createAbortController(
278
+ type: SyncularWorkerRequest['type']
279
+ ): AbortController | undefined {
280
+ if (!isAbortableRequest(type) || typeof AbortController === 'undefined') {
281
+ return undefined;
282
+ }
283
+ return new AbortController();
284
+ }
285
+
286
+ function isAbortableRequest(type: SyncularWorkerRequest['type']): boolean {
287
+ return isGeneratedSyncularAbortableWorkerRequestType(type);
288
+ }
289
+
290
+ function clearClientAbortSignal(): void {
291
+ try {
292
+ client?.setAbortSignal(null);
293
+ } catch {
294
+ // Best effort after a request completed, failed, or closed the client.
295
+ }
296
+ }
297
+
298
+ function postDiagnostic(
299
+ event: Omit<SyncularDiagnosticEvent, 'at'> & { at?: number }
300
+ ): void {
301
+ self.postMessage({
302
+ protocolVersion: SYNCULAR_WORKER_PROTOCOL_VERSION,
303
+ type: 'diagnostic',
304
+ event: {
305
+ at: event.at ?? Date.now(),
306
+ level: event.level,
307
+ source: event.source,
308
+ code: event.code,
309
+ message: event.message,
310
+ ...(event.syncAttemptId ? { syncAttemptId: event.syncAttemptId } : {}),
311
+ ...(event.traceId ? { traceId: event.traceId } : {}),
312
+ ...(event.spanId ? { spanId: event.spanId } : {}),
313
+ ...(event.clientId ? { clientId: event.clientId } : {}),
314
+ ...(event.subscriptionId ? { subscriptionId: event.subscriptionId } : {}),
315
+ ...(event.table ? { table: event.table } : {}),
316
+ ...(event.rowId ? { rowId: event.rowId } : {}),
317
+ ...(event.cursor !== undefined ? { cursor: event.cursor } : {}),
318
+ ...(event.details ? { details: event.details } : {}),
319
+ },
320
+ });
321
+ }
322
+
323
+ function postRequestSuccessDiagnostic(
324
+ request: SyncularWorkerRequest,
325
+ value: unknown,
326
+ durationMs: number
327
+ ): void {
328
+ if (!isDiagnosedSuccessRequest(request.type)) return;
329
+ const syncRevocation = syncScopeRevocationDetails(value);
330
+ postDiagnostic({
331
+ level: 'info',
332
+ source: requestDiagnosticSource(request.type),
333
+ code: `${requestDiagnosticSource(request.type)}.${request.type}.completed`,
334
+ message: `Syncular worker request ${request.type} completed`,
335
+ ...requestSyncAttemptDiagnosticFields(request),
336
+ details: {
337
+ requestType: request.type,
338
+ durationMs,
339
+ ...requestSuccessDetails(request, value),
340
+ },
341
+ });
342
+ if (
343
+ syncRevocation &&
344
+ (request.type === 'syncPull' ||
345
+ request.type === 'syncPush' ||
346
+ request.type === 'syncOnce')
347
+ ) {
348
+ postDiagnostic({
349
+ level: 'warn',
350
+ source: 'sync',
351
+ code: 'sync.scope_revoked',
352
+ message: 'Syncular subscription scope revoked',
353
+ ...requestSyncAttemptDiagnosticFields(request),
354
+ details: {
355
+ requestType: request.type,
356
+ ...syncRevocation,
357
+ },
358
+ });
359
+ }
360
+ }
361
+
362
+ function requestSyncAttemptDiagnosticFields(
363
+ request: SyncularWorkerRequest
364
+ ): Pick<SyncularDiagnosticEvent, 'syncAttemptId' | 'traceId' | 'spanId'> {
365
+ if (
366
+ request.type !== 'syncPull' &&
367
+ request.type !== 'syncPush' &&
368
+ request.type !== 'syncOnce'
369
+ ) {
370
+ return {};
371
+ }
372
+ return syncularDiagnosticAttemptFields(request.syncAttempt);
373
+ }
374
+
375
+ function isDiagnosedSuccessRequest(
376
+ type: SyncularWorkerRequest['type']
377
+ ): boolean {
378
+ return isGeneratedSyncularDiagnosedSuccessWorkerRequestType(type);
379
+ }
380
+
381
+ function requestDiagnosticSource(
382
+ type: SyncularWorkerRequest['type']
383
+ ): SyncularDiagnosticEvent['source'] {
384
+ return generatedSyncularWorkerRequestDiagnosticSource(type);
385
+ }
386
+
387
+ function requestDiagnosticLevel(
388
+ type: SyncularWorkerRequest['type'],
389
+ error: unknown
390
+ ): SyncularDiagnosticEvent['level'] {
391
+ if (type === 'setAuthHeaders' || syncularErrorStatus(error)) return 'warn';
392
+ return 'error';
393
+ }
394
+
395
+ function requestSuccessDetails(
396
+ request: SyncularWorkerRequest,
397
+ value: unknown
398
+ ): Record<string, unknown> {
399
+ switch (request.type) {
400
+ case 'open':
401
+ return {
402
+ storage: openedConfig?.storage,
403
+ clientId: openedConfig?.clientId,
404
+ };
405
+ case 'setAuthHeaders':
406
+ return { headerCount: Object.keys(request.headers).length };
407
+ case 'setFieldEncryption':
408
+ return {
409
+ enabled: request.config != null,
410
+ ruleCount: request.config?.rules.length ?? 0,
411
+ };
412
+ case 'setEncryptedCrdt':
413
+ return {
414
+ enabled: request.config != null,
415
+ keyCount: request.config ? Object.keys(request.config.keys).length : 0,
416
+ };
417
+ case 'setBlobEncryption':
418
+ return {
419
+ enabled: request.config != null,
420
+ keyCount: request.config ? Object.keys(request.config.keys).length : 0,
421
+ };
422
+ case 'syncPull':
423
+ case 'syncPush':
424
+ case 'syncOnce':
425
+ return syncResultDetails(value);
426
+ case 'storeBlob':
427
+ return { immediate: request.options?.immediate === true };
428
+ case 'pruneBlobCache':
429
+ return { maxBytes: request.maxBytes ?? null, prunedBytes: value };
430
+ case 'compactStorage':
431
+ return objectRecord(value);
432
+ case 'localHealthCheck': {
433
+ const report = objectRecord(value);
434
+ const findings = Array.isArray(report.findings) ? report.findings : [];
435
+ return {
436
+ ok: report.ok === true,
437
+ findingCount: findings.length,
438
+ };
439
+ }
440
+ case 'exportLocalSupportBundle': {
441
+ const bundle = objectRecord(value);
442
+ const health = objectRecord(bundle.health);
443
+ const findings = Array.isArray(health.findings) ? health.findings : [];
444
+ return {
445
+ redacted: bundle.redacted === true,
446
+ source: bundle.source,
447
+ findingCount: findings.length,
448
+ };
449
+ }
450
+ case 'importLocalSupportBundle':
451
+ return objectRecord(value);
452
+ case 'repairLocalHealth':
453
+ return objectRecord(value);
454
+ case 'resetLocalSyncState':
455
+ return objectRecord(value);
456
+ case 'processBlobUploadQueue':
457
+ return objectRecord(value);
458
+ default:
459
+ return {};
460
+ }
461
+ }
462
+
463
+ function syncResultDetails(value: unknown): Record<string, unknown> {
464
+ const result = objectRecord(value);
465
+ const changedTables = Array.isArray(result.changedTables)
466
+ ? result.changedTables
467
+ : [];
468
+ const changedRows = Array.isArray(result.changedRows)
469
+ ? result.changedRows
470
+ : [];
471
+ return {
472
+ changedTables,
473
+ changedRows,
474
+ changedRowsTruncated: result.changedRowsTruncated === true,
475
+ changedTableCount: changedTables.length,
476
+ changedRowCount: changedRows.length,
477
+ bootstrap: objectRecord(result.bootstrap),
478
+ pushedCommits:
479
+ typeof result.pushedCommits === 'number' ? result.pushedCommits : 0,
480
+ };
481
+ }
482
+
483
+ function syncScopeRevocationDetails(value: unknown): {
484
+ revokedSubscriptionIds: string[];
485
+ revokedSubscriptionCount: number;
486
+ } | null {
487
+ const result = objectRecord(value);
488
+ const subscriptions = Array.isArray(result.subscriptions)
489
+ ? result.subscriptions
490
+ : [];
491
+ const revokedSubscriptionIds = subscriptions
492
+ .filter((subscription): subscription is { id: string; status: string } => {
493
+ return (
494
+ subscription !== null &&
495
+ typeof subscription === 'object' &&
496
+ (subscription as { status?: unknown }).status === 'revoked' &&
497
+ typeof (subscription as { id?: unknown }).id === 'string'
498
+ );
499
+ })
500
+ .map((subscription) => subscription.id);
501
+
502
+ return revokedSubscriptionIds.length > 0
503
+ ? {
504
+ revokedSubscriptionIds,
505
+ revokedSubscriptionCount: revokedSubscriptionIds.length,
506
+ }
507
+ : null;
508
+ }
509
+
510
+ function diagnosticStatus(error: unknown): Record<string, unknown> {
511
+ const status = syncularErrorStatus(error);
512
+ return status ? { status } : {};
513
+ }
514
+
515
+ function objectRecord(value: unknown): Record<string, unknown> {
516
+ return value && typeof value === 'object'
517
+ ? (value as Record<string, unknown>)
518
+ : {};
519
+ }
520
+
521
+ function errorMessage(error: unknown): string {
522
+ return syncularErrorMessage(error);
523
+ }
524
+
525
+ function errorRequiresFullRefresh(error: unknown): boolean {
526
+ return errorMessage(error).includes('full snapshot resync required');
527
+ }
528
+
529
+ function encodeWorkerError(error: unknown): SyncularWorkerErrorPayload {
530
+ if (isWorkerErrorPayload(error) && !(error instanceof Error)) return error;
531
+ if (error instanceof SyncularClientError) {
532
+ return {
533
+ code: error.code,
534
+ message: error.message,
535
+ category: error.category,
536
+ retryable: error.retryable,
537
+ recommendedAction: error.recommendedAction,
538
+ name: error.name,
539
+ stack: error.stack,
540
+ details: error.details,
541
+ };
542
+ }
543
+ if (error instanceof Error) {
544
+ const details = syncularErrorDetails(error);
545
+ const classification = classifySyncularError(error, error.message, details);
546
+ if (!classification) {
547
+ return createSyncularWorkerErrorPayload('worker.failed', error.message, {
548
+ name: error.name,
549
+ stack: error.stack,
550
+ details,
551
+ });
552
+ }
553
+ return {
554
+ code: classification.code,
555
+ message: error.message,
556
+ category: classification.category,
557
+ retryable: classification.retryable,
558
+ recommendedAction: classification.recommendedAction,
559
+ name: error.name,
560
+ stack: error.stack,
561
+ details,
562
+ };
563
+ }
564
+ const message = String(error);
565
+ const classification = classifySyncularError(error, message);
566
+ if (!classification) {
567
+ return createSyncularWorkerErrorPayload('worker.failed', message);
568
+ }
569
+ return {
570
+ code: classification.code,
571
+ message,
572
+ category: classification.category,
573
+ retryable: classification.retryable,
574
+ recommendedAction: classification.recommendedAction,
575
+ };
576
+ }
577
+
578
+ function isWorkerErrorPayload(
579
+ value: unknown
580
+ ): value is SyncularWorkerErrorPayload {
581
+ return Boolean(
582
+ value && typeof value === 'object' && 'code' in value && 'message' in value
583
+ );
584
+ }
@@ -0,0 +1,109 @@
1
+ import { SYNCULAR_ERROR_DEFINITIONS } from '@syncular/core';
2
+ import type { SyncularGeneratedWorkerRequest } from './generated-bridge';
3
+ import type {
4
+ SyncularBootstrapStatus,
5
+ SyncularDiagnosticEvent,
6
+ SyncularErrorCategory,
7
+ SyncularErrorCode,
8
+ SyncularErrorRecommendedAction,
9
+ SyncularLiveQueryEvent,
10
+ SyncularRealtimeConnectionState,
11
+ SyncularRowsChangedEvent,
12
+ } from './types';
13
+
14
+ export const SYNCULAR_WORKER_PROTOCOL_VERSION = 2;
15
+
16
+ export interface SyncularWorkerErrorPayload {
17
+ code: SyncularErrorCode;
18
+ message: string;
19
+ category?: SyncularErrorCategory;
20
+ retryable?: boolean;
21
+ recommendedAction?: SyncularErrorRecommendedAction;
22
+ name?: string;
23
+ stack?: string;
24
+ details?: unknown;
25
+ }
26
+
27
+ export function createSyncularWorkerErrorPayload(
28
+ code: SyncularErrorCode,
29
+ message?: string,
30
+ options: {
31
+ name?: string;
32
+ stack?: string;
33
+ details?: unknown;
34
+ } = {}
35
+ ): SyncularWorkerErrorPayload {
36
+ const definition = SYNCULAR_ERROR_DEFINITIONS[code];
37
+ return {
38
+ code,
39
+ message: message ?? definition.message,
40
+ category: definition.category,
41
+ retryable: definition.retryable,
42
+ recommendedAction: definition.recommendedAction,
43
+ ...(options.name ? { name: options.name } : {}),
44
+ ...(options.stack ? { stack: options.stack } : {}),
45
+ ...(options.details !== undefined ? { details: options.details } : {}),
46
+ };
47
+ }
48
+
49
+ export type {
50
+ SyncularWorkerRealtimeOptions,
51
+ SyncularWorkerRuntimeArtifact,
52
+ } from './generated-bridge';
53
+ export type SyncularWorkerRequest = SyncularGeneratedWorkerRequest;
54
+
55
+ interface SyncularWorkerResponseBase {
56
+ id: number;
57
+ protocolVersion: typeof SYNCULAR_WORKER_PROTOCOL_VERSION;
58
+ }
59
+
60
+ interface SyncularWorkerEventBase {
61
+ protocolVersion: typeof SYNCULAR_WORKER_PROTOCOL_VERSION;
62
+ }
63
+
64
+ export type SyncularWorkerResponse =
65
+ | (SyncularWorkerResponseBase & { ok: true; value?: unknown })
66
+ | (SyncularWorkerResponseBase & {
67
+ ok: false;
68
+ error: SyncularWorkerErrorPayload;
69
+ });
70
+
71
+ export type SyncularWorkerEvent =
72
+ | (SyncularWorkerEventBase & {
73
+ type: 'liveQueryEvents';
74
+ events: Array<SyncularLiveQueryEvent<Record<string, unknown>>>;
75
+ })
76
+ | (SyncularWorkerEventBase &
77
+ SyncularRowsChangedEvent & {
78
+ type: 'rowsChanged';
79
+ })
80
+ | (SyncularWorkerEventBase & {
81
+ type: 'bootstrapChanged';
82
+ bootstrap: SyncularBootstrapStatus;
83
+ })
84
+ | (SyncularWorkerEventBase & {
85
+ type: 'realtimeState';
86
+ state: SyncularRealtimeConnectionState;
87
+ })
88
+ | (SyncularWorkerEventBase & {
89
+ type: 'presenceEvent';
90
+ action: 'join' | 'leave' | 'update' | 'snapshot';
91
+ scopeKey: string;
92
+ clientId?: string;
93
+ actorId?: string;
94
+ metadata?: Record<string, unknown>;
95
+ entries?: Array<{
96
+ clientId: string;
97
+ actorId: string;
98
+ joinedAt: number;
99
+ metadata?: Record<string, unknown>;
100
+ }>;
101
+ })
102
+ | (SyncularWorkerEventBase & {
103
+ type: 'diagnostic';
104
+ event: SyncularDiagnosticEvent;
105
+ });
106
+
107
+ export type SyncularWorkerOutboundMessage =
108
+ | SyncularWorkerResponse
109
+ | SyncularWorkerEvent;