@syncular/client 0.0.6-96 → 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,116 @@
1
+ import type {
2
+ SyncularBootstrapStatus,
3
+ SyncularDiagnosticEvent,
4
+ SyncularDiagnosticSubscriptionSnapshot,
5
+ SyncularSubscriptionSpec,
6
+ SyncularSyncAttempt,
7
+ SyncularSyncTimings,
8
+ } from './types';
9
+
10
+ export const SYNCULAR_DIAGNOSTIC_RING_LIMIT = 100;
11
+ export const SYNCULAR_SYNC_TIMINGS_RING_LIMIT = 20;
12
+
13
+ export function appendSyncularDiagnosticEvent(
14
+ events: SyncularDiagnosticEvent[],
15
+ event: SyncularDiagnosticEvent
16
+ ): void {
17
+ events.push(event);
18
+ trimRing(events, SYNCULAR_DIAGNOSTIC_RING_LIMIT);
19
+ }
20
+
21
+ export function appendSyncularSyncTimings(
22
+ timings: SyncularSyncTimings[],
23
+ timing: SyncularSyncTimings
24
+ ): void {
25
+ timings.push(timing);
26
+ trimRing(timings, SYNCULAR_SYNC_TIMINGS_RING_LIMIT);
27
+ }
28
+
29
+ export function summarizeSyncularDiagnosticSubscriptions(
30
+ subscriptions: readonly SyncularSubscriptionSpec[],
31
+ bootstrap: SyncularBootstrapStatus | undefined
32
+ ): SyncularDiagnosticSubscriptionSnapshot[] {
33
+ const bootstrapById = new Map(
34
+ (bootstrap?.subscriptions ?? []).map((entry) => [entry.id, entry])
35
+ );
36
+ return subscriptions.map((subscription) => {
37
+ const bootstrapped = bootstrapById.get(subscription.id);
38
+ return {
39
+ id: subscription.id,
40
+ table: subscription.table,
41
+ scopeKeys: Object.keys(subscription.scopes).sort(),
42
+ scopeValueCount: countRedactedValues(subscription.scopes),
43
+ paramsKeys: Object.keys(subscription.params ?? {}).sort(),
44
+ paramsValueCount: countRedactedValues(subscription.params ?? {}),
45
+ status: bootstrapped?.status ?? null,
46
+ ready: bootstrapped?.ready ?? false,
47
+ phase: bootstrapped?.phase ?? 'pending',
48
+ progressPercent: bootstrapped?.progressPercent ?? 0,
49
+ cursor: bootstrapped?.cursor ?? null,
50
+ bootstrapPhase:
51
+ bootstrapped?.bootstrapPhase ?? subscription.bootstrapPhase ?? 0,
52
+ bootstrapState: bootstrapped?.bootstrapState ?? null,
53
+ };
54
+ });
55
+ }
56
+
57
+ export function createSyncularSyncAttempt(): SyncularSyncAttempt {
58
+ const traceId = randomHex(16);
59
+ const spanId = randomHex(8);
60
+ return {
61
+ syncAttemptId: traceId,
62
+ traceId,
63
+ spanId,
64
+ traceparent: `00-${traceId}-${spanId}-01`,
65
+ };
66
+ }
67
+
68
+ export function syncularSyncAttemptHeaders(
69
+ attempt: SyncularSyncAttempt
70
+ ): Record<string, string> {
71
+ return {
72
+ traceparent: attempt.traceparent,
73
+ 'sentry-trace': `${attempt.traceId}-${attempt.spanId}-1`,
74
+ 'x-syncular-sync-attempt-id': attempt.syncAttemptId,
75
+ };
76
+ }
77
+
78
+ export function syncularDiagnosticAttemptFields(
79
+ attempt: SyncularSyncAttempt | undefined
80
+ ): Pick<SyncularDiagnosticEvent, 'syncAttemptId' | 'traceId' | 'spanId'> {
81
+ return attempt
82
+ ? {
83
+ syncAttemptId: attempt.syncAttemptId,
84
+ traceId: attempt.traceId,
85
+ spanId: attempt.spanId,
86
+ }
87
+ : {};
88
+ }
89
+
90
+ function countRedactedValues(values: Record<string, unknown>): number {
91
+ let count = 0;
92
+ for (const value of Object.values(values)) {
93
+ count += Array.isArray(value) ? value.length : 1;
94
+ }
95
+ return count;
96
+ }
97
+
98
+ function trimRing<T>(items: T[], limit: number): void {
99
+ if (items.length <= limit) return;
100
+ items.splice(0, items.length - limit);
101
+ }
102
+
103
+ function randomHex(byteLength: number): string {
104
+ const bytes = new Uint8Array(byteLength);
105
+ const cryptoSource = globalThis.crypto;
106
+ if (cryptoSource?.getRandomValues) {
107
+ cryptoSource.getRandomValues(bytes);
108
+ } else {
109
+ for (let index = 0; index < bytes.length; index += 1) {
110
+ bytes[index] = Math.floor(Math.random() * 256);
111
+ }
112
+ }
113
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join(
114
+ ''
115
+ );
116
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,269 @@
1
+ import { SYNCULAR_ERROR_DEFINITIONS } from '@syncular/core';
2
+ import type {
3
+ SyncularErrorCategory,
4
+ SyncularErrorCode,
5
+ SyncularErrorRecommendedAction,
6
+ } from './types';
7
+
8
+ export interface SyncularErrorEnvelope {
9
+ code: SyncularErrorCode;
10
+ message: string;
11
+ category: SyncularErrorCategory;
12
+ retryable: boolean;
13
+ recommendedAction: SyncularErrorRecommendedAction;
14
+ details?: Record<string, unknown>;
15
+ }
16
+
17
+ export class SyncularClientError extends Error {
18
+ readonly code: SyncularErrorCode;
19
+ readonly category: SyncularErrorCategory;
20
+ readonly retryable: boolean;
21
+ readonly recommendedAction: SyncularErrorRecommendedAction;
22
+ readonly details: Record<string, unknown> | undefined;
23
+
24
+ constructor(envelope: SyncularErrorEnvelope, options?: ErrorOptions) {
25
+ super(envelope.message, options);
26
+ this.name = 'SyncularClientError';
27
+ this.code = envelope.code;
28
+ this.category = envelope.category;
29
+ this.retryable = envelope.retryable;
30
+ this.recommendedAction = envelope.recommendedAction;
31
+ this.details = envelope.details;
32
+ }
33
+ }
34
+
35
+ export function toSyncularClientError(error: unknown): Error {
36
+ if (error instanceof SyncularClientError) return error;
37
+ const message = syncularErrorMessage(error);
38
+ const details = syncularErrorDetails(error);
39
+ const classification = classifySyncularError(error, message, details);
40
+ if (!classification) {
41
+ return error instanceof Error ? error : new Error(message);
42
+ }
43
+ return new SyncularClientError(
44
+ {
45
+ ...classification,
46
+ message,
47
+ ...(details ? { details } : {}),
48
+ },
49
+ error instanceof Error ? { cause: error } : undefined
50
+ );
51
+ }
52
+
53
+ export function syncularErrorMessage(error: unknown): string {
54
+ return error instanceof Error ? error.message : String(error);
55
+ }
56
+
57
+ export function syncularErrorDetails(
58
+ error: unknown
59
+ ): Record<string, unknown> | undefined {
60
+ if (error instanceof SyncularClientError) return error.details;
61
+ if (!(error instanceof Error)) return undefined;
62
+ const serverError = syncularServerErrorFromMessage(error.message);
63
+ const details: Record<string, unknown> = {
64
+ ...(httpStatusFromMessage(error.message) ?? {}),
65
+ ...(serverError
66
+ ? {
67
+ serverErrorCode: serverError.code,
68
+ serverErrorCategory: serverError.category,
69
+ serverRetryable: serverError.retryable,
70
+ serverRecommendedAction: serverError.recommendedAction,
71
+ }
72
+ : {}),
73
+ ...(serverError?.details ? { serverDetails: serverError.details } : {}),
74
+ ...(error.message.includes('full snapshot resync required')
75
+ ? { resyncRequired: true }
76
+ : {}),
77
+ ...(syncularKindFromError(error)
78
+ ? { syncularKind: syncularKindFromError(error) }
79
+ : {}),
80
+ ...(syncularDebugFromError(error)
81
+ ? { syncularDebug: syncularDebugFromError(error) }
82
+ : {}),
83
+ };
84
+ return Object.keys(details).length > 0 ? details : undefined;
85
+ }
86
+
87
+ export function classifySyncularError(
88
+ error: unknown,
89
+ message = syncularErrorMessage(error),
90
+ details: Record<string, unknown> | undefined = syncularErrorDetails(error)
91
+ ): Omit<SyncularErrorEnvelope, 'message' | 'details'> | null {
92
+ const serverError = syncularServerErrorFromMessage(message);
93
+ if (serverError) {
94
+ return {
95
+ code: serverError.code,
96
+ category: serverError.category,
97
+ retryable: serverError.retryable,
98
+ recommendedAction: serverError.recommendedAction,
99
+ };
100
+ }
101
+
102
+ const status =
103
+ details &&
104
+ 'status' in details &&
105
+ (details.status === 401 || details.status === 403)
106
+ ? details.status
107
+ : httpStatusFromMessage(message)?.status;
108
+ if (status === 401) {
109
+ return {
110
+ code: 'sync.auth_required',
111
+ category: 'auth-required',
112
+ retryable: true,
113
+ recommendedAction: 'refreshAuth',
114
+ };
115
+ }
116
+ if (status === 403) {
117
+ return {
118
+ code: 'sync.forbidden',
119
+ category: 'forbidden',
120
+ retryable: false,
121
+ recommendedAction: 'checkPermissions',
122
+ };
123
+ }
124
+
125
+ const syncularKind =
126
+ details && typeof details.syncularKind === 'string'
127
+ ? details.syncularKind
128
+ : syncularKindFromError(error);
129
+ const debug =
130
+ details && typeof details.syncularDebug === 'string'
131
+ ? details.syncularDebug
132
+ : syncularDebugFromError(error);
133
+ const haystack = `${message}\n${debug ?? ''}`;
134
+
135
+ if (/\boffline\b|network is unreachable/i.test(haystack)) {
136
+ return {
137
+ code: 'sync.offline',
138
+ category: 'offline',
139
+ retryable: true,
140
+ recommendedAction: 'retryLater',
141
+ };
142
+ }
143
+
144
+ if (syncularKind === 'Schema' || /\bschema version\b/i.test(haystack)) {
145
+ return {
146
+ code: 'sync.schema_mismatch',
147
+ category: 'schema-mismatch',
148
+ retryable: false,
149
+ recommendedAction: 'regenerateClient',
150
+ };
151
+ }
152
+
153
+ if (
154
+ syncularKind === 'Protocol' &&
155
+ /(hash mismatch|sha256 mismatch|byte length mismatch|manifest .*mismatch|integrity|chain root|commit root|verified root)/i.test(
156
+ haystack
157
+ )
158
+ ) {
159
+ return {
160
+ code: 'sync.integrity_rejected',
161
+ category: 'integrity-rejected',
162
+ retryable: false,
163
+ recommendedAction: 'forceResync',
164
+ };
165
+ }
166
+
167
+ return null;
168
+ }
169
+
170
+ export function isSyncularOfflineError(error: unknown): boolean {
171
+ if (error instanceof SyncularClientError) {
172
+ return error.code === 'sync.offline' || error.category === 'offline';
173
+ }
174
+ if (error && typeof error === 'object') {
175
+ const record = error as Record<string, unknown>;
176
+ if (record.code === 'sync.offline' || record.category === 'offline') {
177
+ return true;
178
+ }
179
+ }
180
+ const classification = classifySyncularError(error);
181
+ return (
182
+ classification?.code === 'sync.offline' ||
183
+ classification?.category === 'offline'
184
+ );
185
+ }
186
+
187
+ export function syncularErrorStatus(error: unknown): 401 | 403 | undefined {
188
+ const details =
189
+ error instanceof SyncularClientError ? error.details : undefined;
190
+ if (
191
+ details &&
192
+ 'status' in details &&
193
+ (details.status === 401 || details.status === 403)
194
+ ) {
195
+ return details.status;
196
+ }
197
+ return httpStatusFromMessage(syncularErrorMessage(error))?.status;
198
+ }
199
+
200
+ function httpStatusFromMessage(
201
+ message: string
202
+ ): { status: 401 | 403 } | undefined {
203
+ const match = /\bHTTP (401|403)\b/.exec(message);
204
+ if (!match) return undefined;
205
+ return { status: match[1] === '401' ? 401 : 403 };
206
+ }
207
+
208
+ function syncularKindFromError(error: unknown): string | undefined {
209
+ return error instanceof Error &&
210
+ typeof (error as Error & { syncularKind?: unknown }).syncularKind ===
211
+ 'string'
212
+ ? (error as Error & { syncularKind: string }).syncularKind
213
+ : undefined;
214
+ }
215
+
216
+ function syncularDebugFromError(error: unknown): string | undefined {
217
+ return error instanceof Error &&
218
+ typeof (error as Error & { syncularDebug?: unknown }).syncularDebug ===
219
+ 'string'
220
+ ? (error as Error & { syncularDebug: string }).syncularDebug
221
+ : undefined;
222
+ }
223
+
224
+ function syncularServerErrorFromMessage(
225
+ message: string
226
+ ): Omit<SyncularErrorEnvelope, 'message'> | null {
227
+ const match = /\bHTTP \d{3}\b: (\{.*\})\s*$/s.exec(message);
228
+ if (!match) return null;
229
+
230
+ let parsed: unknown;
231
+ try {
232
+ parsed = JSON.parse(match[1] ?? '');
233
+ } catch {
234
+ return null;
235
+ }
236
+ if (!parsed || typeof parsed !== 'object') return null;
237
+
238
+ const record = parsed as Record<string, unknown>;
239
+ const rawCode =
240
+ typeof record.code === 'string'
241
+ ? record.code
242
+ : typeof record.error === 'string'
243
+ ? record.error
244
+ : null;
245
+ if (!rawCode || !(rawCode in SYNCULAR_ERROR_DEFINITIONS)) return null;
246
+
247
+ const definition =
248
+ SYNCULAR_ERROR_DEFINITIONS[
249
+ rawCode as keyof typeof SYNCULAR_ERROR_DEFINITIONS
250
+ ];
251
+ return {
252
+ code: rawCode as SyncularErrorCode,
253
+ category:
254
+ typeof record.category === 'string'
255
+ ? (record.category as SyncularErrorCategory)
256
+ : definition.category,
257
+ retryable:
258
+ typeof record.retryable === 'boolean'
259
+ ? record.retryable
260
+ : definition.retryable,
261
+ recommendedAction:
262
+ typeof record.recommendedAction === 'string'
263
+ ? (record.recommendedAction as SyncularErrorRecommendedAction)
264
+ : definition.recommendedAction,
265
+ ...(record.details && typeof record.details === 'object'
266
+ ? { details: record.details as Record<string, unknown> }
267
+ : {}),
268
+ };
269
+ }