@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,133 +0,0 @@
1
- import type {
2
- SyncOperation,
3
- SyncPushRequest,
4
- SyncPushResponse,
5
- } from '@syncular/core';
6
- import type { SyncClientPlugin, SyncClientPluginContext } from './types';
7
-
8
- export interface IncrementingVersionPluginOptions {
9
- /**
10
- * Plugin name (used for debugging).
11
- * Defaults to "incrementing-version".
12
- */
13
- name?: string;
14
- /**
15
- * Maximum number of rows to track in memory.
16
- * Defaults to 10_000.
17
- */
18
- maxTrackedRows?: number;
19
- }
20
-
21
- function makeRowKey(op: Pick<SyncOperation, 'table' | 'row_id'>): string {
22
- return `${op.table}\u001f${op.row_id}`;
23
- }
24
-
25
- function touchLru(
26
- map: Map<string, number>,
27
- key: string,
28
- value: number,
29
- max: number
30
- ): void {
31
- // Maintain insertion order as an LRU.
32
- map.delete(key);
33
- map.set(key, value);
34
- if (map.size <= max) return;
35
- const firstKey = map.keys().next().value as string | undefined;
36
- if (firstKey) map.delete(firstKey);
37
- }
38
-
39
- /**
40
- * Automatically advances `base_version` for sequential operations on the same row.
41
- *
42
- * Why this exists:
43
- * - UI code often reads a row once and can enqueue multiple updates quickly.
44
- * - The server rejects stale `base_version` values (optimistic concurrency).
45
- * - When those updates all originate from the same client and are pushed in-order,
46
- * later ops are effectively based on the earlier ops.
47
- *
48
- * Assumptions:
49
- * - The server's row version is an integer that increments by 1 per applied upsert.
50
- * - Operations are pushed in commit order for a given client (outbox ordering).
51
- *
52
- * This plugin:
53
- * - Tracks the "next expected server version" per (table, row_id) based on
54
- * successfully applied pushes.
55
- * - Rewrites outgoing `base_version` to that expected version when it is higher
56
- * than the caller-provided value, preventing "self-conflicts" on hot rows.
57
- */
58
- export function createIncrementingVersionPlugin(
59
- options: IncrementingVersionPluginOptions = {}
60
- ): SyncClientPlugin {
61
- const name = options.name ?? 'incrementing-version';
62
- const maxTrackedRows = Math.max(
63
- 1,
64
- Math.min(1_000_000, options.maxTrackedRows ?? 10_000)
65
- );
66
- const nextExpectedBaseVersionByRow = new Map<string, number>();
67
-
68
- return {
69
- name,
70
-
71
- beforePush(
72
- _ctx: SyncClientPluginContext,
73
- request: SyncPushRequest
74
- ): SyncPushRequest {
75
- const operations = request.operations.map((op) => {
76
- if (typeof op.base_version !== 'number') return op;
77
-
78
- const key = makeRowKey(op);
79
- const nextExpected = nextExpectedBaseVersionByRow.get(key);
80
- if (typeof nextExpected !== 'number') return op;
81
-
82
- if (nextExpected > op.base_version) {
83
- return { ...op, base_version: nextExpected };
84
- }
85
-
86
- return op;
87
- });
88
-
89
- return { ...request, operations };
90
- },
91
-
92
- afterPush(
93
- _ctx: SyncClientPluginContext,
94
- args: { request: SyncPushRequest; response: SyncPushResponse }
95
- ): SyncPushResponse {
96
- // Rejected commits are rolled back by the server; nothing is applied.
97
- if (
98
- args.response.status !== 'applied' &&
99
- args.response.status !== 'cached'
100
- ) {
101
- return args.response;
102
- }
103
-
104
- for (const result of args.response.results ?? []) {
105
- if (result.status !== 'applied') continue;
106
-
107
- const op = args.request.operations[result.opIndex];
108
- if (!op) continue;
109
-
110
- const key = makeRowKey(op);
111
-
112
- if (op.op === 'delete') {
113
- nextExpectedBaseVersionByRow.delete(key);
114
- continue;
115
- }
116
-
117
- if (typeof op.base_version === 'number') {
118
- touchLru(
119
- nextExpectedBaseVersionByRow,
120
- key,
121
- op.base_version + 1,
122
- maxTrackedRows
123
- );
124
- } else {
125
- // Insert case: most tables start at version 1.
126
- touchLru(nextExpectedBaseVersionByRow, key, 1, maxTrackedRows);
127
- }
128
- }
129
-
130
- return args.response;
131
- },
132
- };
133
- }
@@ -1,2 +0,0 @@
1
- export * from './incrementing-version';
2
- export * from './types';
@@ -1,63 +0,0 @@
1
- import type {
2
- SyncPullRequest,
3
- SyncPullResponse,
4
- SyncPushRequest,
5
- SyncPushResponse,
6
- } from '@syncular/core';
7
-
8
- export interface SyncClientPluginContext {
9
- actorId: string;
10
- clientId: string;
11
- }
12
-
13
- /**
14
- * Plugin priority levels for ordering execution.
15
- * Lower numbers execute first.
16
- */
17
- export const PluginPriority = {
18
- /** Compression should run early (before encryption) */
19
- COMPRESSION: 10,
20
- /** Encryption should run after compression but before transport */
21
- ENCRYPTION: 20,
22
- /** Default priority for general plugins */
23
- DEFAULT: 50,
24
- /** Logging/telemetry should run last */
25
- TELEMETRY: 100,
26
- } as const;
27
-
28
- export interface SyncClientPlugin {
29
- name: string;
30
-
31
- /**
32
- * Plugin priority for ordering. Lower numbers execute first.
33
- * @default PluginPriority.DEFAULT (50)
34
- */
35
- priority?: number;
36
-
37
- /**
38
- * Called before sending a push request to the server.
39
- * Use this for client-side encryption, payload shaping, etc.
40
- */
41
- beforePush?(
42
- ctx: SyncClientPluginContext,
43
- request: SyncPushRequest
44
- ): Promise<SyncPushRequest> | SyncPushRequest;
45
-
46
- /**
47
- * Called after receiving a push response from the server.
48
- * Receives both the request and the response to allow opIndex correlation.
49
- */
50
- afterPush?(
51
- ctx: SyncClientPluginContext,
52
- args: { request: SyncPushRequest; response: SyncPushResponse }
53
- ): Promise<SyncPushResponse> | SyncPushResponse;
54
-
55
- /**
56
- * Called after receiving a pull response from the server (and after any
57
- * snapshot chunk materialization), but before applying it to the local DB.
58
- */
59
- afterPull?(
60
- ctx: SyncClientPluginContext,
61
- args: { request: SyncPullRequest; response: SyncPullResponse }
62
- ): Promise<SyncPullResponse> | SyncPullResponse;
63
- }
@@ -1,191 +0,0 @@
1
- /**
2
- * @syncular/client - Proxy Connection
3
- *
4
- * DatabaseConnection implementation that proxies queries over WebSocket.
5
- */
6
-
7
- import type {
8
- ProxyHandshake,
9
- ProxyHandshakeAck,
10
- ProxyMessage,
11
- ProxyResponse,
12
- } from '@syncular/core';
13
- import type { CompiledQuery, DatabaseConnection, QueryResult } from 'kysely';
14
-
15
- /**
16
- * WebSocket wrapper that handles reconnection and message correlation.
17
- */
18
- export class ProxyConnection implements DatabaseConnection {
19
- private ws: WebSocket;
20
- private messageId = 0;
21
- private pendingRequests = new Map<
22
- string,
23
- {
24
- resolve: (response: ProxyResponse) => void;
25
- reject: (error: Error) => void;
26
- }
27
- >();
28
- private handshakeComplete = false;
29
- private handshakePromise: Promise<void>;
30
- private handshakeResolve: (() => void) | null = null;
31
- private handshakeReject: ((error: Error) => void) | null = null;
32
- private closed = false;
33
-
34
- constructor(
35
- ws: WebSocket,
36
- private actorId: string,
37
- private clientId: string
38
- ) {
39
- this.ws = ws;
40
-
41
- this.handshakePromise = new Promise((resolve, reject) => {
42
- this.handshakeResolve = resolve;
43
- this.handshakeReject = reject;
44
- });
45
-
46
- this.ws.onmessage = (event) => {
47
- this.handleMessage(event.data as string);
48
- };
49
-
50
- this.ws.onerror = (_event) => {
51
- const error = new Error('WebSocket error');
52
- this.rejectAllPending(error);
53
- if (!this.handshakeComplete) {
54
- this.handshakeReject?.(error);
55
- }
56
- };
57
-
58
- this.ws.onclose = () => {
59
- this.closed = true;
60
- const error = new Error('WebSocket closed');
61
- this.rejectAllPending(error);
62
- if (!this.handshakeComplete) {
63
- this.handshakeReject?.(error);
64
- }
65
- };
66
-
67
- // Send handshake
68
- const handshake: ProxyHandshake = {
69
- type: 'handshake',
70
- actorId: this.actorId,
71
- clientId: this.clientId,
72
- };
73
- this.ws.send(JSON.stringify(handshake));
74
- }
75
-
76
- private handleMessage(data: string): void {
77
- try {
78
- const message = JSON.parse(data);
79
-
80
- // Handle handshake acknowledgement
81
- if (message.type === 'handshake_ack') {
82
- const ack = message as ProxyHandshakeAck;
83
- if (ack.ok) {
84
- this.handshakeComplete = true;
85
- this.handshakeResolve?.();
86
- } else {
87
- this.handshakeReject?.(new Error(ack.error ?? 'Handshake failed'));
88
- }
89
- return;
90
- }
91
-
92
- // Handle query responses
93
- const response = message as ProxyResponse;
94
- const pending = this.pendingRequests.get(response.id);
95
- if (pending) {
96
- this.pendingRequests.delete(response.id);
97
- pending.resolve(response);
98
- }
99
- } catch {
100
- // Ignore malformed messages
101
- }
102
- }
103
-
104
- private rejectAllPending(error: Error): void {
105
- for (const pending of this.pendingRequests.values()) {
106
- pending.reject(error);
107
- }
108
- this.pendingRequests.clear();
109
- }
110
-
111
- private async send(message: ProxyMessage): Promise<ProxyResponse> {
112
- // Wait for handshake to complete
113
- await this.handshakePromise;
114
-
115
- if (this.closed) {
116
- throw new Error('Connection is closed');
117
- }
118
-
119
- return new Promise((resolve, reject) => {
120
- this.pendingRequests.set(message.id, { resolve, reject });
121
- this.ws.send(JSON.stringify(message));
122
- });
123
- }
124
-
125
- private nextMessageId(): string {
126
- return `${++this.messageId}`;
127
- }
128
-
129
- async executeQuery<R>(query: CompiledQuery): Promise<QueryResult<R>> {
130
- const response = await this.send({
131
- id: this.nextMessageId(),
132
- type: 'query',
133
- sql: query.sql,
134
- parameters: query.parameters,
135
- });
136
-
137
- if (response.type === 'error') {
138
- throw new Error(response.error ?? 'Query failed');
139
- }
140
-
141
- return {
142
- rows: (response.rows ?? []) as R[],
143
- numAffectedRows:
144
- response.rowCount != null ? BigInt(response.rowCount) : undefined,
145
- };
146
- }
147
-
148
- async beginTransaction(): Promise<void> {
149
- const response = await this.send({
150
- id: this.nextMessageId(),
151
- type: 'begin',
152
- });
153
-
154
- if (response.type === 'error') {
155
- throw new Error(response.error ?? 'Failed to begin transaction');
156
- }
157
- }
158
-
159
- async commitTransaction(): Promise<void> {
160
- const response = await this.send({
161
- id: this.nextMessageId(),
162
- type: 'commit',
163
- });
164
-
165
- if (response.type === 'error') {
166
- throw new Error(response.error ?? 'Failed to commit transaction');
167
- }
168
- }
169
-
170
- async rollbackTransaction(): Promise<void> {
171
- const response = await this.send({
172
- id: this.nextMessageId(),
173
- type: 'rollback',
174
- });
175
-
176
- if (response.type === 'error') {
177
- throw new Error(response.error ?? 'Failed to rollback transaction');
178
- }
179
- }
180
-
181
- streamQuery<R>(): AsyncIterableIterator<QueryResult<R>> {
182
- throw new Error('Streaming queries are not supported over proxy');
183
- }
184
-
185
- close(): void {
186
- if (!this.closed) {
187
- this.closed = true;
188
- this.ws.close();
189
- }
190
- }
191
- }
@@ -1,76 +0,0 @@
1
- /**
2
- * @syncular/client - Proxy Dialect
3
- *
4
- * Kysely Dialect that proxies queries over WebSocket to a sync server.
5
- */
6
-
7
- import type {
8
- DatabaseIntrospector,
9
- Dialect,
10
- DialectAdapter,
11
- Driver,
12
- Kysely,
13
- QueryCompiler,
14
- } from 'kysely';
15
- import {
16
- PostgresAdapter,
17
- PostgresIntrospector,
18
- PostgresQueryCompiler,
19
- } from 'kysely';
20
- import { ProxyDriver, type ProxyDriverConfigWithFactory } from './driver';
21
-
22
- interface ProxyDialectConfig extends ProxyDriverConfigWithFactory {}
23
-
24
- /**
25
- * Creates a Kysely dialect that proxies queries over WebSocket.
26
- *
27
- * This dialect uses PostgreSQL query compilation and adapters since
28
- * the proxy server typically runs against a PostgreSQL database.
29
- *
30
- * @example
31
- * ```typescript
32
- * import { Kysely } from 'kysely';
33
- * import { createProxyDialect } from '@syncular/client/proxy';
34
- *
35
- * const db = new Kysely<MySchema>({
36
- * dialect: createProxyDialect({
37
- * endpoint: 'wss://api.example.com/proxy',
38
- * actorId: 'admin:worker',
39
- * clientId: 'cf-worker-123',
40
- * headers: { authorization: 'Bearer ...' },
41
- * }),
42
- * });
43
- *
44
- * // Full Kysely API works
45
- * const tasks = await db.selectFrom('tasks').selectAll().execute();
46
- *
47
- * // Transactions work
48
- * await db.transaction().execute(async (trx) => {
49
- * const user = await trx.selectFrom('users')
50
- * .where('id', '=', 'u1')
51
- * .executeTakeFirst();
52
- * await trx.insertInto('tasks')
53
- * .values({ id: 'x', user_id: user.id, title: 'New' })
54
- * .execute();
55
- * });
56
- * ```
57
- */
58
- export function createProxyDialect(config: ProxyDialectConfig): Dialect {
59
- return {
60
- createDriver(): Driver {
61
- return new ProxyDriver(config);
62
- },
63
-
64
- createQueryCompiler(): QueryCompiler {
65
- return new PostgresQueryCompiler();
66
- },
67
-
68
- createAdapter(): DialectAdapter {
69
- return new PostgresAdapter();
70
- },
71
-
72
- createIntrospector(db: Kysely<any>): DatabaseIntrospector {
73
- return new PostgresIntrospector(db);
74
- },
75
- };
76
- }
@@ -1,126 +0,0 @@
1
- /**
2
- * @syncular/client - Proxy Driver
3
- *
4
- * Kysely Driver that creates WebSocket connections for query execution.
5
- */
6
-
7
- import type { DatabaseConnection, Driver } from 'kysely';
8
- import { ProxyConnection } from './connection';
9
-
10
- interface ProxyDriverConfig {
11
- /** WebSocket endpoint URL (wss://...) */
12
- endpoint: string;
13
- /** Actor ID for oplog tracking */
14
- actorId: string;
15
- /** Client ID for oplog tracking */
16
- clientId: string;
17
- /** Optional headers for authentication */
18
- headers?: Record<string, string>;
19
- }
20
-
21
- /**
22
- * Custom WebSocket factory type for environments that need it.
23
- */
24
- export type WebSocketFactory = (
25
- url: string,
26
- protocols?: string | string[]
27
- ) => WebSocket;
28
-
29
- /**
30
- * Extended config with optional WebSocket factory.
31
- */
32
- export interface ProxyDriverConfigWithFactory extends ProxyDriverConfig {
33
- /** Optional WebSocket factory for custom environments */
34
- createWebSocket?: WebSocketFactory;
35
- }
36
-
37
- export class ProxyDriver implements Driver {
38
- private connection: ProxyConnection | null = null;
39
-
40
- constructor(private config: ProxyDriverConfigWithFactory) {}
41
-
42
- async init(): Promise<void> {
43
- // No initialization needed
44
- }
45
-
46
- async acquireConnection(): Promise<DatabaseConnection> {
47
- // Reuse existing connection if available
48
- if (this.connection) {
49
- return this.connection as DatabaseConnection;
50
- }
51
-
52
- const ws = await this.createWebSocket();
53
- this.connection = new ProxyConnection(
54
- ws,
55
- this.config.actorId,
56
- this.config.clientId
57
- );
58
- return this.connection as DatabaseConnection;
59
- }
60
-
61
- private async createWebSocket(): Promise<WebSocket> {
62
- return new Promise((resolve, reject) => {
63
- // Build URL with auth info if needed
64
- const url = new URL(this.config.endpoint);
65
-
66
- // Note: WebSocket doesn't support custom headers directly,
67
- // so auth is typically done via query params or after connection
68
- // The server should handle auth during the handshake
69
- if (this.config.headers?.authorization) {
70
- url.searchParams.set(
71
- 'authorization',
72
- this.config.headers.authorization
73
- );
74
- }
75
-
76
- let ws: WebSocket;
77
- if (this.config.createWebSocket) {
78
- ws = this.config.createWebSocket(url.toString());
79
- } else {
80
- ws = new WebSocket(url.toString());
81
- }
82
-
83
- ws.onopen = () => {
84
- resolve(ws);
85
- };
86
-
87
- ws.onerror = (_event) => {
88
- reject(new Error('Failed to connect to proxy endpoint'));
89
- };
90
-
91
- // Set a connection timeout
92
- const timeout = setTimeout(() => {
93
- ws.close();
94
- reject(new Error('Connection timeout'));
95
- }, 30000);
96
-
97
- ws.onopen = () => {
98
- clearTimeout(timeout);
99
- resolve(ws);
100
- };
101
- });
102
- }
103
-
104
- async beginTransaction(connection: DatabaseConnection): Promise<void> {
105
- await (connection as ProxyConnection).beginTransaction();
106
- }
107
-
108
- async commitTransaction(connection: DatabaseConnection): Promise<void> {
109
- await (connection as ProxyConnection).commitTransaction();
110
- }
111
-
112
- async rollbackTransaction(connection: DatabaseConnection): Promise<void> {
113
- await (connection as ProxyConnection).rollbackTransaction();
114
- }
115
-
116
- async releaseConnection(): Promise<void> {
117
- // Keep the connection alive for reuse
118
- }
119
-
120
- async destroy(): Promise<void> {
121
- if (this.connection) {
122
- this.connection.close();
123
- this.connection = null;
124
- }
125
- }
126
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * @syncular/client - Proxy Dialect Exports
3
- *
4
- * Kysely dialect for proxying queries over WebSocket to a sync server.
5
- */
6
-
7
- export * from './connection';
8
- export * from './dialect';
9
- export * from './driver';
10
- export * from './mutations';
@@ -1,18 +0,0 @@
1
- /**
2
- * @syncular/client - Proxy mutations
3
- *
4
- * Exposes the same Proxy-based mutation interface as the offline client,
5
- * but pushes commits to the server immediately (no local outbox/db writes).
6
- */
7
-
8
- import {
9
- createPushMutations,
10
- type MutationsApi,
11
- type PushCommitConfig,
12
- } from '../mutations';
13
-
14
- export function createProxyMutations<DB extends Record<string, any>>(
15
- config: PushCommitConfig
16
- ): MutationsApi<DB, undefined> {
17
- return createPushMutations(config);
18
- }