@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
package/src/conflicts.ts DELETED
@@ -1,171 +0,0 @@
1
- /**
2
- * @syncular/client - Sync conflict storage helpers
3
- */
4
-
5
- import type { SyncOperationResult, SyncPushResponse } from '@syncular/core';
6
- import { randomId } from '@syncular/core';
7
- import type { Kysely } from 'kysely';
8
- import { sql } from 'kysely';
9
- import type { SyncClientDb } from './schema';
10
-
11
- function messageFromResult(
12
- r: Extract<SyncOperationResult, { status: 'conflict' | 'error' }>
13
- ): {
14
- message: string;
15
- code: string | null;
16
- serverVersion: number | null;
17
- serverRowJson: string | null;
18
- } {
19
- if (r.status === 'conflict') {
20
- return {
21
- message: r.message,
22
- code: 'CONFLICT',
23
- serverVersion: r.server_version,
24
- serverRowJson: JSON.stringify(r.server_row),
25
- };
26
- }
27
-
28
- return {
29
- message: r.error,
30
- code: r.code ?? null,
31
- serverVersion: null,
32
- serverRowJson: null,
33
- };
34
- }
35
-
36
- export async function upsertConflictsForRejectedCommit<DB extends SyncClientDb>(
37
- db: Kysely<DB>,
38
- args: {
39
- outboxCommitId: string;
40
- clientCommitId: string;
41
- response: SyncPushResponse;
42
- nowMs?: number;
43
- }
44
- ): Promise<number> {
45
- const now = args.nowMs ?? Date.now();
46
-
47
- // Remove any previous conflict rows for this outbox commit.
48
- await sql`
49
- delete from ${sql.table('sync_conflicts')}
50
- where ${sql.ref('outbox_commit_id')} = ${sql.val(args.outboxCommitId)}
51
- `.execute(db);
52
-
53
- const conflictResults = args.response.results.filter(
54
- (r) => r.status === 'conflict' || r.status === 'error'
55
- );
56
-
57
- if (conflictResults.length === 0) return 0;
58
-
59
- const rows = conflictResults.map((r) => {
60
- const info = messageFromResult(r);
61
- return {
62
- id: randomId(),
63
- outbox_commit_id: args.outboxCommitId,
64
- client_commit_id: args.clientCommitId,
65
- op_index: r.opIndex,
66
- result_status: r.status,
67
- message: info.message,
68
- code: info.code,
69
- server_version: info.serverVersion,
70
- server_row_json: info.serverRowJson,
71
- created_at: now,
72
- resolved_at: null,
73
- resolution: null,
74
- };
75
- });
76
-
77
- const insertColumns = [
78
- 'id',
79
- 'outbox_commit_id',
80
- 'client_commit_id',
81
- 'op_index',
82
- 'result_status',
83
- 'message',
84
- 'code',
85
- 'server_version',
86
- 'server_row_json',
87
- 'created_at',
88
- 'resolved_at',
89
- 'resolution',
90
- ] as const;
91
-
92
- await sql`
93
- insert into ${sql.table('sync_conflicts')} (
94
- ${sql.join(insertColumns.map((c) => sql.ref(c)))}
95
- ) values ${sql.join(
96
- rows.map(
97
- (row) =>
98
- sql`(${sql.join(
99
- [
100
- sql.val(row.id),
101
- sql.val(row.outbox_commit_id),
102
- sql.val(row.client_commit_id),
103
- sql.val(row.op_index),
104
- sql.val(row.result_status),
105
- sql.val(row.message),
106
- sql.val(row.code),
107
- sql.val(row.server_version),
108
- sql.val(row.server_row_json),
109
- sql.val(row.created_at),
110
- sql.val(row.resolved_at),
111
- sql.val(row.resolution),
112
- ],
113
- sql`, `
114
- )})`
115
- ),
116
- sql`, `
117
- )}
118
- `.execute(db);
119
-
120
- return conflictResults.length;
121
- }
122
-
123
- export type PendingConflictRow = {
124
- id: string;
125
- outbox_commit_id: string;
126
- client_commit_id: string;
127
- op_index: number;
128
- result_status: string;
129
- message: string;
130
- code: string | null;
131
- server_version: number | null;
132
- server_row_json: string | null;
133
- created_at: number;
134
- };
135
-
136
- export async function listPendingConflicts<DB extends SyncClientDb>(
137
- db: Kysely<DB>
138
- ): Promise<PendingConflictRow[]> {
139
- const res = await sql<PendingConflictRow>`
140
- select
141
- ${sql.ref('id')},
142
- ${sql.ref('outbox_commit_id')},
143
- ${sql.ref('client_commit_id')},
144
- ${sql.ref('op_index')},
145
- ${sql.ref('result_status')},
146
- ${sql.ref('message')},
147
- ${sql.ref('code')},
148
- ${sql.ref('server_version')},
149
- ${sql.ref('server_row_json')},
150
- ${sql.ref('created_at')}
151
- from ${sql.table('sync_conflicts')}
152
- where ${sql.ref('resolved_at')} is null
153
- order by ${sql.ref('created_at')} desc
154
- `.execute(db);
155
-
156
- return res.rows;
157
- }
158
-
159
- export async function resolveConflict<DB extends SyncClientDb>(
160
- db: Kysely<DB>,
161
- args: { id: string; resolution: string; nowMs?: number }
162
- ): Promise<void> {
163
- const now = args.nowMs ?? Date.now();
164
- await sql`
165
- update ${sql.table('sync_conflicts')}
166
- set
167
- ${sql.ref('resolved_at')} = ${sql.val(now)},
168
- ${sql.ref('resolution')} = ${sql.val(args.resolution)}
169
- where ${sql.ref('id')} = ${sql.val(args.id)}
170
- `.execute(db);
171
- }
@@ -1,83 +0,0 @@
1
- import { afterEach, describe, expect, it } from 'bun:test';
2
- import { createDatabase } from '@syncular/core';
3
- import type { Kysely } from 'kysely';
4
- import { createBunSqliteDialect } from '../../dialect-bun-sqlite/src';
5
- import { createClient } from './create-client';
6
- import type { SyncClientDb } from './schema';
7
-
8
- interface TasksTable {
9
- id: string;
10
- user_id: string;
11
- title: string;
12
- server_version: number;
13
- }
14
-
15
- interface TestDb extends SyncClientDb {
16
- tasks: TasksTable;
17
- }
18
-
19
- async function createTestDb(): Promise<Kysely<TestDb>> {
20
- const db = createDatabase<TestDb>({
21
- dialect: createBunSqliteDialect({ path: ':memory:' }),
22
- family: 'sqlite',
23
- });
24
- await db.schema
25
- .createTable('tasks')
26
- .addColumn('id', 'text', (col) => col.primaryKey())
27
- .addColumn('user_id', 'text', (col) => col.notNull())
28
- .addColumn('title', 'text', (col) => col.notNull())
29
- .addColumn('server_version', 'integer', (col) => col.notNull().defaultTo(0))
30
- .execute();
31
- return db;
32
- }
33
-
34
- describe('createClient url normalization', () => {
35
- const originalFetch = globalThis.fetch;
36
-
37
- afterEach(() => {
38
- globalThis.fetch = originalFetch;
39
- });
40
-
41
- it('accepts sync endpoint URLs without duplicating /sync', async () => {
42
- const requests: string[] = [];
43
- globalThis.fetch = (async (input: RequestInfo | URL) => {
44
- const request = input instanceof Request ? input : new Request(input);
45
- requests.push(request.url);
46
- return new Response(
47
- JSON.stringify({
48
- ok: true,
49
- pull: { ok: true, subscriptions: [] },
50
- }),
51
- {
52
- status: 200,
53
- headers: { 'content-type': 'application/json' },
54
- }
55
- );
56
- }) as typeof fetch;
57
-
58
- const db = await createTestDb();
59
- try {
60
- const { destroy } = await createClient<TestDb>({
61
- db,
62
- actorId: 'user-1',
63
- clientId: 'client-1',
64
- url: 'http://localhost:4311/api/sync',
65
- handlers: [
66
- {
67
- table: 'tasks',
68
- subscribe: false,
69
- async applySnapshot() {},
70
- async clearAll() {},
71
- async applyChange() {},
72
- },
73
- ],
74
- });
75
-
76
- destroy();
77
- expect(requests).toContain('http://localhost:4311/api/sync');
78
- expect(requests).not.toContain('http://localhost:4311/api/sync/sync');
79
- } finally {
80
- await db.destroy();
81
- }
82
- });
83
- });
@@ -1,330 +0,0 @@
1
- /**
2
- * Simplified client factory
3
- *
4
- * Breaking changes from legacy Client:
5
- * - handlers: plain array (no registry class)
6
- * - url: string instead of transport (transport auto-created)
7
- * - subscriptions: derived from handler.subscribe (no separate param)
8
- * - clientId: auto-generated (no longer required)
9
- */
10
-
11
- import type {
12
- ColumnCodecDialect,
13
- ColumnCodecSource,
14
- ScopeDefinition,
15
- ScopeValues,
16
- SyncTransport,
17
- } from '@syncular/core';
18
- import { extractScopeVars } from '@syncular/core';
19
- import { createHttpTransport } from '@syncular/transport-http';
20
- import type { Kysely } from 'kysely';
21
- import { Client } from './client';
22
- import { createClientHandlerCollection } from './handlers/collection';
23
- import { createClientHandler } from './handlers/create-handler';
24
- import type { ClientTableHandler } from './handlers/types';
25
- import type { SyncClientDb } from './schema';
26
- import { randomUUID } from './utils/id';
27
-
28
- function deriveDefaultSubscriptionScopes<DB>(args: {
29
- handler: Pick<ClientTableHandler<DB>, 'table' | 'scopePatterns'>;
30
- actorId: string;
31
- }): ScopeValues {
32
- const patterns = args.handler.scopePatterns ?? [];
33
- const vars = new Set<string>();
34
- for (const pattern of patterns) {
35
- for (const v of extractScopeVars(pattern)) {
36
- vars.add(v);
37
- }
38
- }
39
-
40
- const allVars = Array.from(vars);
41
- if (allVars.length !== 1) {
42
- throw new Error(
43
- `Handler "${args.handler.table}" has subscribe=true but no explicit subscription scopes. ` +
44
- 'Set subscribe: { scopes: { ... } } on the handler. ' +
45
- `(Cannot infer defaults from scopePatterns: ${
46
- patterns.length > 0 ? patterns.join(', ') : '(none)'
47
- })`
48
- );
49
- }
50
-
51
- const varName = allVars[0]!;
52
- return { [varName]: args.actorId };
53
- }
54
-
55
- /**
56
- * Auto-generate a simple handler for a table.
57
- * Uses 'id' as primary key and provided scopes.
58
- */
59
- function createAutoHandler<
60
- DB extends SyncClientDb,
61
- TableName extends keyof DB & string,
62
- >(
63
- table: string,
64
- scopes: string[],
65
- options: {
66
- codecs?: ColumnCodecSource;
67
- codecDialect?: ColumnCodecDialect;
68
- }
69
- ): ClientTableHandler<DB, TableName> {
70
- return createClientHandler<DB, TableName>({
71
- table: table as TableName,
72
- scopes: scopes as ScopeDefinition[],
73
- codecs: options.codecs,
74
- codecDialect: options.codecDialect,
75
- });
76
- }
77
-
78
- function normalizeTransportBaseUrl(url: string): string {
79
- const trimmed = url.trim().replace(/\/+$/, '');
80
- if (!trimmed.endsWith('/sync')) {
81
- return trimmed;
82
- }
83
-
84
- const baseUrl = trimmed.slice(0, -'/sync'.length);
85
- return baseUrl.length > 0 ? baseUrl : '/';
86
- }
87
-
88
- interface CreateClientOptions<DB extends SyncClientDb> {
89
- /** Kysely database instance */
90
- db: Kysely<DB>;
91
-
92
- /**
93
- * Sync URL (e.g., '/api/sync') or base API URL (e.g., '/api').
94
- * Defaults to '/api/sync' if not provided.
95
- * Ignored if transport is provided.
96
- */
97
- url?: string;
98
-
99
- /**
100
- * Table handlers for applying snapshots and changes.
101
- * Handlers with `subscribe: true` (or an object) are synced.
102
- * Handlers with `subscribe: false` are local-only.
103
- * Either handlers or tables must be provided.
104
- */
105
- handlers?: Array<ClientTableHandler<DB>>;
106
-
107
- /**
108
- * Table names to auto-generate handlers for.
109
- * Uses default scopes and primary key 'id'.
110
- * Either handlers or tables must be provided.
111
- *
112
- * @example
113
- * ```typescript
114
- * tables: ['tasks', 'notes', 'projects']
115
- * ```
116
- */
117
- tables?: string[];
118
-
119
- /**
120
- * Default scopes for auto-generated table handlers.
121
- * Required when using tables option.
122
- * Ignored when handlers are provided.
123
- */
124
- scopes?: string[];
125
-
126
- /** Current actor/user identifier */
127
- actorId: string;
128
-
129
- /** Optional: Custom client ID (auto-generated UUID if not provided) */
130
- clientId?: string;
131
-
132
- /** Optional: Custom transport (overrides url) */
133
- transport?: SyncTransport;
134
-
135
- /** Optional: Function to get auth headers */
136
- getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
137
-
138
- /** Optional: Sync configuration */
139
- sync?: {
140
- /** Enable realtime/WebSocket mode (default: true) */
141
- realtime?: boolean;
142
- /** Polling interval in ms (default: 10000) */
143
- pollIntervalMs?: number;
144
- };
145
-
146
- /** Optional: Local blob storage adapter */
147
- blobStorage?: import('./client').ClientBlobStorage;
148
-
149
- /** Optional: Sync plugins */
150
- plugins?: import('./plugins').SyncClientPlugin[];
151
-
152
- /** Optional: State ID for multi-tenant scenarios */
153
- stateId?: string;
154
-
155
- /** Optional: Column codec resolver */
156
- codecs?: ColumnCodecSource;
157
-
158
- /** Optional: Codec dialect override (default: 'sqlite') */
159
- codecDialect?: ColumnCodecDialect;
160
-
161
- /** Optional: Auto-start sync (default: true) */
162
- autoStart?: boolean;
163
- }
164
-
165
- interface CreateClientResult<DB extends SyncClientDb> {
166
- /** The client instance */
167
- client: Client<DB>;
168
- /** Stop sync */
169
- stop: () => void;
170
- /** Destroy client and cleanup */
171
- destroy: () => void;
172
- }
173
-
174
- /**
175
- * Create a simplified sync client.
176
- *
177
- * Auto-generates clientId, creates transport from URL (default: '/api/sync'),
178
- * builds subscriptions from handlers.
179
- *
180
- * @example
181
- * ```typescript
182
- * // Auto-generate handlers for simple tables (default URL: '/api/sync')
183
- * const { client } = await createClient({
184
- * db,
185
- * actorId: 'user-123',
186
- * tables: ['tasks', 'notes'],
187
- * scopes: ['user:{user_id}'],
188
- * });
189
- *
190
- * // With custom handlers for advanced cases
191
- * const { client } = await createClient({
192
- * db,
193
- * actorId: 'user-123',
194
- * handlers: [
195
- * tasksHandler, // subscribe: true by default
196
- * notesHandler, // subscribe: true by default
197
- * draftsHandler, // subscribe: false (local-only)
198
- * ],
199
- * });
200
- *
201
- * // Listen for events
202
- * client.on('sync:error', (err) => console.error(err));
203
- * client.on('data:change', (scopes) => console.log('Data changed:', scopes));
204
- * ```
205
- */
206
- export async function createClient<DB extends SyncClientDb>(
207
- options: CreateClientOptions<DB>
208
- ): Promise<CreateClientResult<DB>> {
209
- const {
210
- db,
211
- url = '/api/sync',
212
- handlers: providedHandlers,
213
- tables,
214
- scopes,
215
- actorId,
216
- clientId = randomUUID(),
217
- transport: customTransport,
218
- getHeaders,
219
- sync = {},
220
- blobStorage,
221
- plugins,
222
- stateId,
223
- codecs,
224
- codecDialect,
225
- autoStart = true,
226
- } = options;
227
-
228
- // Validate options
229
- if (!providedHandlers && !tables) {
230
- throw new Error('Either handlers or tables must be provided');
231
- }
232
- if (tables && !scopes) {
233
- throw new Error('scopes is required when using tables option');
234
- }
235
-
236
- // Auto-generate handlers from tables if needed
237
- const handlers =
238
- providedHandlers ??
239
- tables!.map((table) =>
240
- createAutoHandler<DB, keyof DB & string>(table, scopes!, {
241
- codecs,
242
- codecDialect,
243
- })
244
- );
245
-
246
- const tableHandlers = createClientHandlerCollection(handlers);
247
-
248
- // Create transport from URL if not provided
249
- let transport = customTransport;
250
- if (!transport && url) {
251
- transport = createHttpTransport({
252
- baseUrl: normalizeTransportBaseUrl(url),
253
- getHeaders,
254
- });
255
- }
256
-
257
- if (!transport) {
258
- throw new Error('Either url or transport must be provided');
259
- }
260
-
261
- // Build subscriptions from handlers
262
- const subscriptions = handlers
263
- .map((handler) => {
264
- const sub = handler.subscribe;
265
- // Skip handlers that are explicitly disabled
266
- if (sub === false) return null;
267
-
268
- if (sub === true || sub === undefined) {
269
- // Default: subscribe to the handler's single scope var using actorId.
270
- // This avoids sending `{}` which would be treated as revoked by the server.
271
- const scopes = deriveDefaultSubscriptionScopes({
272
- handler,
273
- actorId,
274
- });
275
- return {
276
- id: handler.table,
277
- table: handler.table,
278
- scopes,
279
- params: {},
280
- };
281
- }
282
- // Custom subscription config
283
- const scopes: ScopeValues = {};
284
- for (const [scopeKey, scopeValue] of Object.entries(sub.scopes ?? {})) {
285
- if (scopeValue === undefined) continue;
286
- scopes[scopeKey] = scopeValue;
287
- }
288
- if (Object.keys(scopes).length === 0) {
289
- throw new Error(
290
- `Handler "${handler.table}" subscription scopes cannot be empty. ` +
291
- 'Set subscribe: false or provide subscribe.scopes.'
292
- );
293
- }
294
- return {
295
- id: handler.table,
296
- table: handler.table,
297
- scopes,
298
- params: sub.params ?? {},
299
- };
300
- })
301
- .filter((sub): sub is NonNullable<typeof sub> => sub !== null);
302
-
303
- // Create client
304
- const client = new Client({
305
- db,
306
- transport,
307
- tableHandlers,
308
- clientId,
309
- actorId,
310
- subscriptions,
311
- blobStorage,
312
- plugins,
313
- stateId,
314
- codecs,
315
- codecDialect,
316
- realtimeEnabled: sync.realtime ?? true,
317
- pollIntervalMs: sync.pollIntervalMs,
318
- });
319
-
320
- // Auto-start
321
- if (autoStart) {
322
- await client.start();
323
- }
324
-
325
- return {
326
- client,
327
- stop: () => client.stop(),
328
- destroy: () => client.destroy(),
329
- };
330
- }