@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
@@ -1,330 +0,0 @@
1
- /**
2
- * @syncular/client - Declarative client handler helper
3
- */
4
-
5
- import type {
6
- ColumnCodecDialect,
7
- ColumnCodecSource,
8
- ScopeDefinition,
9
- ScopeKeysFromDefinitions,
10
- ScopeValuesFromPatterns,
11
- SyncChange,
12
- SyncSnapshot,
13
- } from '@syncular/core';
14
- import {
15
- applyCodecsToDbRow,
16
- isRecord,
17
- normalizeScopes,
18
- toTableColumnCodecs,
19
- } from '@syncular/core';
20
- import { sql } from 'kysely';
21
- import type { SyncClientDb } from '../schema';
22
- import type {
23
- ClientClearContext,
24
- ClientHandlerContext,
25
- ClientSnapshotHookContext,
26
- ClientTableHandler,
27
- } from './types';
28
-
29
- const MAX_INSERT_BIND_PARAMETERS = 900;
30
-
31
- /**
32
- * Options for creating a declarative client handler.
33
- */
34
- export interface CreateClientHandlerOptions<
35
- DB extends SyncClientDb,
36
- TableName extends keyof DB & string,
37
- ScopeDefs extends readonly ScopeDefinition[] = readonly ScopeDefinition[],
38
- > {
39
- /** Table name in the database */
40
- table: TableName;
41
-
42
- /**
43
- * Scope definitions for this table.
44
- * Can be simple strings (column auto-derived) or objects with explicit mapping.
45
- *
46
- * @example
47
- * ```typescript
48
- * // Simple: column auto-derived from placeholder
49
- * scopes: ['user:{user_id}', 'org:{org_id}']
50
- *
51
- * // Explicit: when column differs from pattern variable
52
- * scopes: [
53
- * { pattern: 'user:{user_id}', column: 'owner_id' }
54
- * ]
55
- * ```
56
- */
57
- scopes: ScopeDefs;
58
-
59
- /**
60
- * Subscription configuration for this table.
61
- * - `true` (default): Subscribe to this table with default scopes/params
62
- * - `false`: Don't subscribe (handler only for local mutations)
63
- * - Object: Subscribe with custom scopes and params
64
- */
65
- subscribe?:
66
- | boolean
67
- | {
68
- scopes?: ScopeValuesFromPatterns<ScopeDefs>;
69
- params?: Record<string, unknown>;
70
- };
71
-
72
- /** Primary key column name (default: 'id') */
73
- primaryKey?: keyof DB[TableName] & string;
74
-
75
- /**
76
- * Optional version column name (e.g. 'server_version') to store `change.row_version`.
77
- * If omitted, row_version is ignored by the default handler.
78
- */
79
- versionColumn?: keyof DB[TableName] & string;
80
-
81
- /**
82
- * Optional column codec resolver.
83
- * Receives `{ table, column, sqlType?, dialect? }` and returns a codec.
84
- */
85
- codecs?: ColumnCodecSource;
86
-
87
- /**
88
- * Dialect used for codec dialect overrides.
89
- * Default: 'sqlite'
90
- */
91
- codecDialect?: ColumnCodecDialect;
92
-
93
- /**
94
- * Override: Apply a snapshot.
95
- * Default: upsert all rows (no delete on isFirstPage).
96
- */
97
- applySnapshot?: (
98
- ctx: ClientHandlerContext<DB>,
99
- snapshot: SyncSnapshot
100
- ) => Promise<void>;
101
-
102
- /**
103
- * Override: Apply a single change.
104
- * Default: upsert on upsert, delete on delete.
105
- */
106
- applyChange?: (
107
- ctx: ClientHandlerContext<DB>,
108
- change: SyncChange
109
- ) => Promise<void>;
110
-
111
- /**
112
- * Override: Clear all data for this table.
113
- * Default: delete all rows from the table.
114
- */
115
- clearAll?: (ctx: ClientClearContext<DB>) => Promise<void>;
116
-
117
- /**
118
- * Hook: Called when a snapshot begins (isFirstPage = true).
119
- * Default: no-op.
120
- */
121
- onSnapshotStart?: (ctx: ClientSnapshotHookContext<DB>) => Promise<void>;
122
-
123
- /**
124
- * Hook: Called when a snapshot ends (isLastPage = true).
125
- * Default: no-op.
126
- */
127
- onSnapshotEnd?: (ctx: ClientSnapshotHookContext<DB>) => Promise<void>;
128
- }
129
-
130
- /**
131
- * Create a declarative client table handler with sensible defaults.
132
- *
133
- * @example
134
- * ```typescript
135
- * import { createClientHandler } from '@syncular/client';
136
- * import type { ClientDb } from './db.generated';
137
- *
138
- * export const tasksHandler = createClientHandler<ClientDb, 'tasks'>({
139
- * table: 'tasks',
140
- * scopes: ['user:{user_id}'], // column auto-derived from placeholder
141
- * });
142
- *
143
- * // With custom column mapping:
144
- * export const tasksHandler = createClientHandler<ClientDb, 'tasks'>({
145
- * table: 'tasks',
146
- * scopes: [{ pattern: 'user:{user_id}', column: 'owner_id' }],
147
- * });
148
- *
149
- * // With soft delete pattern:
150
- * export const tasksHandler = createClientHandler<ClientDb, 'tasks'>({
151
- * table: 'tasks',
152
- * scopes: ['user:{user_id}'],
153
- * onSnapshotStart: async (ctx) => {
154
- * await ctx.trx.updateTable('tasks')
155
- * .set({ _sync_stale: 1 })
156
- * .where('user_id', '=', ctx.scopeKey.split(':')[1])
157
- * .execute();
158
- * },
159
- * onSnapshotEnd: async (ctx) => {
160
- * await ctx.trx.deleteFrom('tasks')
161
- * .where('_sync_stale', '=', 1)
162
- * .execute();
163
- * },
164
- * });
165
- * ```
166
- */
167
- export function createClientHandler<
168
- DB extends SyncClientDb,
169
- TableName extends keyof DB & string,
170
- ScopeDefs extends readonly ScopeDefinition[] = readonly ScopeDefinition[],
171
- >(
172
- options: CreateClientHandlerOptions<DB, TableName, ScopeDefs>
173
- ): ClientTableHandler<DB, TableName, ScopeKeysFromDefinitions<ScopeDefs>> {
174
- const { table, scopes: scopeDefs } = options;
175
- const primaryKey =
176
- options.primaryKey ?? ('id' as keyof DB[TableName] & string);
177
- const versionColumn = options.versionColumn;
178
- const codecDialect = options.codecDialect ?? 'sqlite';
179
- const codecCache = new Map<string, ReturnType<typeof toTableColumnCodecs>>();
180
- const resolveTableCodecs = (row: Record<string, unknown>) => {
181
- const codecs = options.codecs;
182
- if (!codecs) return {};
183
- const columns = Object.keys(row);
184
- if (columns.length === 0) return {};
185
- const cacheKey = columns.slice().sort().join('\u0000');
186
- const cached = codecCache.get(cacheKey);
187
- if (cached) return cached;
188
- const resolved = toTableColumnCodecs(table, codecs, columns, {
189
- dialect: codecDialect,
190
- });
191
- codecCache.set(cacheKey, resolved);
192
- return resolved;
193
- };
194
-
195
- // Normalize scopes to pattern map (stored for metadata)
196
- const scopeColumnMap = normalizeScopes(scopeDefs);
197
- const scopePatterns = Object.keys(scopeColumnMap);
198
-
199
- // Default applySnapshot: upsert all rows
200
- const defaultApplySnapshot = async (
201
- ctx: ClientHandlerContext<DB>,
202
- snapshot: SyncSnapshot
203
- ): Promise<void> => {
204
- const rows: Array<Record<string, unknown>> = [];
205
- for (const row of snapshot.rows ?? []) {
206
- if (!isRecord(row)) continue;
207
- rows.push(applyCodecsToDbRow(row, resolveTableCodecs(row), codecDialect));
208
- }
209
-
210
- if (rows.length === 0) return;
211
-
212
- // Get column names from first row
213
- const columns = Object.keys(rows[0]!);
214
- if (columns.length === 0) return;
215
- const updateColumns = columns.filter((c) => c !== primaryKey);
216
-
217
- const onConflict =
218
- updateColumns.length === 0
219
- ? sql`do nothing`
220
- : sql`do update set ${sql.join(
221
- updateColumns.map(
222
- (col) => sql`${sql.ref(col)} = ${sql.ref(`excluded.${col}`)}`
223
- ),
224
- sql`, `
225
- )}`;
226
-
227
- const maxRowsPerInsert = Math.max(
228
- 1,
229
- Math.floor(MAX_INSERT_BIND_PARAMETERS / columns.length)
230
- );
231
-
232
- for (
233
- let startIndex = 0;
234
- startIndex < rows.length;
235
- startIndex += maxRowsPerInsert
236
- ) {
237
- const batchRows = rows.slice(startIndex, startIndex + maxRowsPerInsert);
238
-
239
- await sql`
240
- insert into ${sql.table(table)} (${sql.join(columns.map((c) => sql.ref(c)))})
241
- values ${sql.join(
242
- batchRows.map(
243
- (row) =>
244
- sql`(${sql.join(
245
- columns.map((col) => sql.val(row[col] ?? null)),
246
- sql`, `
247
- )})`
248
- ),
249
- sql`, `
250
- )}
251
- on conflict (${sql.ref(primaryKey)}) ${onConflict}
252
- `.execute(ctx.trx);
253
- }
254
- };
255
-
256
- // Default applyChange: upsert on upsert, delete on delete
257
- const defaultApplyChange = async (
258
- ctx: ClientHandlerContext<DB>,
259
- change: SyncChange
260
- ): Promise<void> => {
261
- if (change.op === 'delete') {
262
- await sql`
263
- delete from ${sql.table(table)}
264
- where ${sql.ref(primaryKey)} = ${sql.val(change.row_id)}
265
- `.execute(ctx.trx);
266
- return;
267
- }
268
-
269
- const row = isRecord(change.row_json)
270
- ? applyCodecsToDbRow(
271
- change.row_json,
272
- resolveTableCodecs(change.row_json),
273
- codecDialect
274
- )
275
- : {};
276
- const insertRow: Record<string, unknown> = {
277
- ...row,
278
- [primaryKey]: change.row_id,
279
- };
280
-
281
- if (
282
- versionColumn &&
283
- change.row_version !== null &&
284
- change.row_version !== undefined
285
- ) {
286
- insertRow[versionColumn] = change.row_version;
287
- }
288
-
289
- const columns = Object.keys(insertRow);
290
- const updateColumns = columns.filter((c) => c !== primaryKey);
291
- const onConflict =
292
- updateColumns.length === 0
293
- ? sql`do nothing`
294
- : sql`do update set ${sql.join(
295
- updateColumns.map(
296
- (col) => sql`${sql.ref(col)} = ${sql.ref(`excluded.${col}`)}`
297
- ),
298
- sql`, `
299
- )}`;
300
-
301
- await sql`
302
- insert into ${sql.table(table)} (${sql.join(columns.map((c) => sql.ref(c)))})
303
- values (${sql.join(
304
- columns.map((col) => sql.val(insertRow[col] ?? null)),
305
- sql`, `
306
- )})
307
- on conflict (${sql.ref(primaryKey)}) ${onConflict}
308
- `.execute(ctx.trx);
309
- };
310
-
311
- // Default clearAll: delete all rows from the table
312
- const defaultClearAll = async (
313
- ctx: ClientClearContext<DB>
314
- ): Promise<void> => {
315
- await sql`delete from ${sql.table(table)}`.execute(ctx.trx);
316
- };
317
-
318
- return {
319
- table,
320
- scopePatterns,
321
- subscribe: options.subscribe,
322
-
323
- applySnapshot: options.applySnapshot ?? defaultApplySnapshot,
324
- applyChange: options.applyChange ?? defaultApplyChange,
325
- clearAll: options.clearAll ?? defaultClearAll,
326
-
327
- onSnapshotStart: options.onSnapshotStart,
328
- onSnapshotEnd: options.onSnapshotEnd,
329
- };
330
- }
@@ -1,108 +0,0 @@
1
- /**
2
- * @syncular/client - Sync client table handler interface
3
- */
4
-
5
- import type {
6
- ScopeValues,
7
- ScopeValuesForKeys,
8
- SyncChange,
9
- SyncSnapshot,
10
- } from '@syncular/core';
11
- import type { Transaction } from 'kysely';
12
-
13
- /**
14
- * Context passed to client table handler methods.
15
- */
16
- export interface ClientHandlerContext<DB> {
17
- /** Database transaction */
18
- trx: Transaction<DB>;
19
- }
20
-
21
- /**
22
- * Extended context for snapshot lifecycle hooks.
23
- */
24
- export interface ClientSnapshotHookContext<DB>
25
- extends ClientHandlerContext<DB> {
26
- /** The table being snapshotted */
27
- table: string;
28
- /** The scope values for this subscription */
29
- scopes: ScopeValues;
30
- }
31
-
32
- /**
33
- * Context for clearAll/clearScope operations.
34
- */
35
- export interface ClientClearContext<DB> extends ClientHandlerContext<DB> {
36
- /** The scope values to clear (data matching these scopes should be removed) */
37
- scopes: ScopeValues;
38
- }
39
-
40
- /**
41
- * Subscription configuration for a handler.
42
- */
43
- export interface HandlerSubscriptionConfig<ScopeKeys extends string = string> {
44
- /** Scope values for this subscription */
45
- scopes?: ScopeValuesForKeys<ScopeKeys>;
46
- /** Params for this subscription */
47
- params?: Record<string, unknown>;
48
- }
49
-
50
- /**
51
- * Client-side table handler for applying sync snapshots and changes.
52
- */
53
- export interface ClientTableHandler<
54
- DB,
55
- TableName extends keyof DB & string = keyof DB & string,
56
- ScopeKeys extends string = string,
57
- > {
58
- /** Table name (used as identifier in sync operations) */
59
- table: TableName;
60
-
61
- /**
62
- * Scope patterns used by this table.
63
- * Used for deriving safe default subscriptions in `createClient`.
64
- */
65
- scopePatterns?: string[];
66
-
67
- /**
68
- * Subscription configuration.
69
- * - `true`: Subscribe to this table (default)
70
- * - `false`: Don't subscribe (local-only handler)
71
- * - Object: Subscribe with custom scopes/params
72
- */
73
- subscribe?: boolean | HandlerSubscriptionConfig<ScopeKeys>;
74
-
75
- /**
76
- * Apply a snapshot page for this table.
77
- * The handler is responsible for upserting the rows.
78
- */
79
- applySnapshot(
80
- ctx: ClientHandlerContext<DB>,
81
- snapshot: SyncSnapshot
82
- ): Promise<void>;
83
-
84
- /**
85
- * Clear local data for this table matching the given scopes.
86
- * Used when subscription is removed or revoked.
87
- * If scopes is empty, clear all data for this table.
88
- */
89
- clearAll(ctx: ClientClearContext<DB>): Promise<void>;
90
-
91
- /**
92
- * Apply a single change (upsert/delete).
93
- * Must be idempotent (retries may re-apply).
94
- */
95
- applyChange(ctx: ClientHandlerContext<DB>, change: SyncChange): Promise<void>;
96
-
97
- /**
98
- * Optional: Called when a snapshot begins (isFirstPage = true).
99
- * Use this for marking existing rows as stale before applying snapshot.
100
- */
101
- onSnapshotStart?(ctx: ClientSnapshotHookContext<DB>): Promise<void>;
102
-
103
- /**
104
- * Optional: Called when a snapshot ends (isLastPage = true).
105
- * Use this for cleaning up stale rows after snapshot is complete.
106
- */
107
- onSnapshotEnd?(ctx: ClientSnapshotHookContext<DB>): Promise<void>;
108
- }