@syncular/client 0.0.6-96 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/README.md +713 -11
  2. package/dist/auth-leases.d.ts +11 -0
  3. package/dist/auth-leases.d.ts.map +1 -0
  4. package/dist/auth-leases.js +140 -0
  5. package/dist/auth-leases.js.map +1 -0
  6. package/dist/blob-limits.d.ts +15 -0
  7. package/dist/blob-limits.d.ts.map +1 -0
  8. package/dist/blob-limits.js +66 -0
  9. package/dist/blob-limits.js.map +1 -0
  10. package/dist/bridge-client.d.ts +69 -0
  11. package/dist/bridge-client.d.ts.map +1 -0
  12. package/dist/bridge-client.js +282 -0
  13. package/dist/bridge-client.js.map +1 -0
  14. package/dist/client-config.d.ts +6 -0
  15. package/dist/client-config.d.ts.map +1 -0
  16. package/dist/client-config.js +18 -0
  17. package/dist/client-config.js.map +1 -0
  18. package/dist/client.d.ts +65 -397
  19. package/dist/client.d.ts.map +1 -1
  20. package/dist/client.js +150 -960
  21. package/dist/client.js.map +1 -1
  22. package/dist/command-history.d.ts +35 -0
  23. package/dist/command-history.d.ts.map +1 -0
  24. package/dist/command-history.js +378 -0
  25. package/dist/command-history.js.map +1 -0
  26. package/dist/console-diagnostics.d.ts +33 -0
  27. package/dist/console-diagnostics.d.ts.map +1 -0
  28. package/dist/console-diagnostics.js +496 -0
  29. package/dist/console-diagnostics.js.map +1 -0
  30. package/dist/crdt-yjs/index.d.ts +4 -0
  31. package/dist/crdt-yjs/index.d.ts.map +1 -0
  32. package/dist/crdt-yjs/index.js +4 -0
  33. package/dist/crdt-yjs/index.js.map +1 -0
  34. package/dist/crdt-yjs/webview-host-facade.d.ts +126 -0
  35. package/dist/crdt-yjs/webview-host-facade.d.ts.map +1 -0
  36. package/dist/crdt-yjs/webview-host-facade.js +284 -0
  37. package/dist/crdt-yjs/webview-host-facade.js.map +1 -0
  38. package/dist/crdt-yjs/yjs-document-field-adapter.d.ts +153 -0
  39. package/dist/crdt-yjs/yjs-document-field-adapter.d.ts.map +1 -0
  40. package/dist/crdt-yjs/yjs-document-field-adapter.js +406 -0
  41. package/dist/crdt-yjs/yjs-document-field-adapter.js.map +1 -0
  42. package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts +73 -0
  43. package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts.map +1 -0
  44. package/dist/crdt-yjs/yjs-prosemirror-bridge.js +169 -0
  45. package/dist/crdt-yjs/yjs-prosemirror-bridge.js.map +1 -0
  46. package/dist/database.d.ts +83 -0
  47. package/dist/database.d.ts.map +1 -0
  48. package/dist/database.js +916 -0
  49. package/dist/database.js.map +1 -0
  50. package/dist/diagnostics.d.ts +10 -0
  51. package/dist/diagnostics.d.ts.map +1 -0
  52. package/dist/diagnostics.js +83 -0
  53. package/dist/diagnostics.js.map +1 -0
  54. package/dist/errors.d.ts +24 -0
  55. package/dist/errors.d.ts.map +1 -0
  56. package/dist/errors.js +211 -0
  57. package/dist/errors.js.map +1 -0
  58. package/dist/generated-bridge.d.ts +365 -0
  59. package/dist/generated-bridge.d.ts.map +1 -0
  60. package/dist/generated-bridge.js +250 -0
  61. package/dist/generated-bridge.js.map +1 -0
  62. package/dist/index.d.ts +13 -23
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +14 -25
  65. package/dist/index.js.map +1 -1
  66. package/dist/mutations.d.ts +4 -72
  67. package/dist/mutations.d.ts.map +1 -1
  68. package/dist/mutations.js +2 -568
  69. package/dist/mutations.js.map +1 -1
  70. package/dist/network.d.ts +3 -0
  71. package/dist/network.d.ts.map +1 -0
  72. package/dist/network.js +17 -0
  73. package/dist/network.js.map +1 -0
  74. package/dist/react/index.d.ts +169 -0
  75. package/dist/react/index.d.ts.map +1 -0
  76. package/dist/react/index.js +628 -0
  77. package/dist/react/index.js.map +1 -0
  78. package/dist/react-native/index.d.ts +35 -0
  79. package/dist/react-native/index.d.ts.map +1 -0
  80. package/dist/react-native/index.js +49 -0
  81. package/dist/react-native/index.js.map +1 -0
  82. package/dist/runtime-contract.d.ts +13 -0
  83. package/dist/runtime-contract.d.ts.map +1 -0
  84. package/dist/runtime-contract.js +24 -0
  85. package/dist/runtime-contract.js.map +1 -0
  86. package/dist/rust-client.d.ts +106 -0
  87. package/dist/rust-client.d.ts.map +1 -0
  88. package/dist/rust-client.js +936 -0
  89. package/dist/rust-client.js.map +1 -0
  90. package/dist/sentry.d.ts +35 -0
  91. package/dist/sentry.d.ts.map +1 -0
  92. package/dist/sentry.js +155 -0
  93. package/dist/sentry.js.map +1 -0
  94. package/dist/sql-safety.d.ts +3 -0
  95. package/dist/sql-safety.d.ts.map +1 -0
  96. package/dist/sql-safety.js +62 -0
  97. package/dist/sql-safety.js.map +1 -0
  98. package/dist/syncular-runtime-artifacts.json +61 -0
  99. package/dist/tauri/index.d.ts +35 -0
  100. package/dist/tauri/index.d.ts.map +1 -0
  101. package/dist/tauri/index.js +114 -0
  102. package/dist/tauri/index.js.map +1 -0
  103. package/dist/types.d.ts +1059 -0
  104. package/dist/types.d.ts.map +1 -0
  105. package/dist/types.js +2 -0
  106. package/dist/types.js.map +1 -0
  107. package/dist/wasm/.syncular-wasm-profile +1 -0
  108. package/dist/wasm/syncular-runtime-artifact.json +21 -0
  109. package/dist/wasm/syncular.d.ts +207 -0
  110. package/dist/wasm/syncular.js +2341 -0
  111. package/dist/wasm/syncular_bg.wasm +0 -0
  112. package/dist/wasm/syncular_bg.wasm.d.ts +97 -0
  113. package/dist/wasm-bindings/runtime-contract.d.ts +22 -0
  114. package/dist/wasm-bindings/runtime-contract.d.ts.map +1 -0
  115. package/dist/wasm-bindings/runtime-contract.js +112 -0
  116. package/dist/wasm-bindings/runtime-contract.js.map +1 -0
  117. package/dist/wasm-core/.syncular-wasm-profile +1 -0
  118. package/dist/wasm-core/syncular-runtime-artifact.json +17 -0
  119. package/dist/wasm-core/syncular.d.ts +162 -0
  120. package/dist/wasm-core/syncular.js +1847 -0
  121. package/dist/wasm-core/syncular_bg.wasm +0 -0
  122. package/dist/wasm-core/syncular_bg.wasm.d.ts +77 -0
  123. package/dist/wasm-perf/.syncular-wasm-profile +1 -0
  124. package/dist/wasm-perf/syncular-runtime-artifact.json +21 -0
  125. package/dist/wasm-perf/syncular.d.ts +207 -0
  126. package/dist/wasm-perf/syncular.js +2341 -0
  127. package/dist/wasm-perf/syncular_bg.wasm +0 -0
  128. package/dist/wasm-perf/syncular_bg.wasm.d.ts +97 -0
  129. package/dist/wasm-runtime.d.ts +18 -0
  130. package/dist/wasm-runtime.d.ts.map +1 -0
  131. package/dist/wasm-runtime.js +28 -0
  132. package/dist/wasm-runtime.js.map +1 -0
  133. package/dist/worker-client.d.ts +123 -0
  134. package/dist/worker-client.d.ts.map +1 -0
  135. package/dist/worker-client.js +1686 -0
  136. package/dist/worker-client.js.map +1 -0
  137. package/dist/worker-entry.d.ts +2 -0
  138. package/dist/worker-entry.d.ts.map +1 -0
  139. package/dist/worker-entry.js +479 -0
  140. package/dist/worker-entry.js.map +1 -0
  141. package/dist/worker-protocol.d.ts +64 -0
  142. package/dist/worker-protocol.d.ts.map +1 -0
  143. package/dist/worker-protocol.js +16 -0
  144. package/dist/worker-protocol.js.map +1 -0
  145. package/dist/worker-realtime.d.ts +39 -0
  146. package/dist/worker-realtime.d.ts.map +1 -0
  147. package/dist/worker-realtime.js +677 -0
  148. package/dist/worker-realtime.js.map +1 -0
  149. package/package.json +124 -17
  150. package/src/auth-leases.ts +251 -0
  151. package/src/blob-limits.ts +98 -0
  152. package/src/bridge-client.ts +512 -0
  153. package/src/client-config.ts +29 -0
  154. package/src/client.ts +256 -1414
  155. package/src/command-history.ts +623 -0
  156. package/src/console-diagnostics.ts +617 -0
  157. package/src/crdt-yjs/index.ts +3 -0
  158. package/src/crdt-yjs/webview-host-facade.ts +477 -0
  159. package/src/crdt-yjs/yjs-document-field-adapter.ts +733 -0
  160. package/src/crdt-yjs/yjs-prosemirror-bridge.ts +272 -0
  161. package/src/database.ts +1342 -0
  162. package/src/diagnostics.ts +116 -0
  163. package/src/errors.ts +269 -0
  164. package/src/generated-bridge.ts +741 -0
  165. package/src/index.ts +21 -25
  166. package/src/mutations.ts +12 -821
  167. package/src/network.ts +32 -0
  168. package/src/react/index.ts +1036 -0
  169. package/src/react-native/index.ts +152 -0
  170. package/src/runtime-contract.ts +48 -0
  171. package/src/rust-client.ts +1488 -0
  172. package/src/sentry.ts +215 -0
  173. package/src/sql-safety.ts +61 -0
  174. package/src/tauri/index.ts +211 -0
  175. package/src/types.ts +1356 -0
  176. package/src/wasm-bindings/generated-wasm-bindings.d.ts +70 -0
  177. package/src/wasm-bindings/runtime-contract.ts +158 -0
  178. package/src/wasm-runtime.ts +76 -0
  179. package/src/worker-client.ts +2215 -0
  180. package/src/worker-entry.ts +584 -0
  181. package/src/worker-protocol.ts +109 -0
  182. package/src/worker-realtime.ts +843 -0
  183. package/dist/blobs/index.d.ts +0 -7
  184. package/dist/blobs/index.d.ts.map +0 -1
  185. package/dist/blobs/index.js +0 -7
  186. package/dist/blobs/index.js.map +0 -1
  187. package/dist/blobs/manager.d.ts +0 -345
  188. package/dist/blobs/manager.d.ts.map +0 -1
  189. package/dist/blobs/manager.js +0 -749
  190. package/dist/blobs/manager.js.map +0 -1
  191. package/dist/blobs/migrate.d.ts +0 -14
  192. package/dist/blobs/migrate.d.ts.map +0 -1
  193. package/dist/blobs/migrate.js +0 -59
  194. package/dist/blobs/migrate.js.map +0 -1
  195. package/dist/blobs/types.d.ts +0 -62
  196. package/dist/blobs/types.d.ts.map +0 -1
  197. package/dist/blobs/types.js +0 -5
  198. package/dist/blobs/types.js.map +0 -1
  199. package/dist/conflicts.d.ts +0 -31
  200. package/dist/conflicts.d.ts.map +0 -1
  201. package/dist/conflicts.js +0 -112
  202. package/dist/conflicts.js.map +0 -1
  203. package/dist/create-client.d.ts +0 -118
  204. package/dist/create-client.d.ts.map +0 -1
  205. package/dist/create-client.js +0 -178
  206. package/dist/create-client.js.map +0 -1
  207. package/dist/engine/SyncEngine.d.ts +0 -272
  208. package/dist/engine/SyncEngine.d.ts.map +0 -1
  209. package/dist/engine/SyncEngine.js +0 -1860
  210. package/dist/engine/SyncEngine.js.map +0 -1
  211. package/dist/engine/index.d.ts +0 -6
  212. package/dist/engine/index.d.ts.map +0 -1
  213. package/dist/engine/index.js +0 -6
  214. package/dist/engine/index.js.map +0 -1
  215. package/dist/engine/types.d.ts +0 -345
  216. package/dist/engine/types.d.ts.map +0 -1
  217. package/dist/engine/types.js +0 -7
  218. package/dist/engine/types.js.map +0 -1
  219. package/dist/handlers/collection.d.ts +0 -6
  220. package/dist/handlers/collection.d.ts.map +0 -1
  221. package/dist/handlers/collection.js +0 -21
  222. package/dist/handlers/collection.js.map +0 -1
  223. package/dist/handlers/create-handler.d.ts +0 -120
  224. package/dist/handlers/create-handler.d.ts.map +0 -1
  225. package/dist/handlers/create-handler.js +0 -145
  226. package/dist/handlers/create-handler.js.map +0 -1
  227. package/dist/handlers/types.d.ts +0 -83
  228. package/dist/handlers/types.d.ts.map +0 -1
  229. package/dist/handlers/types.js +0 -5
  230. package/dist/handlers/types.js.map +0 -1
  231. package/dist/migrate.d.ts +0 -19
  232. package/dist/migrate.d.ts.map +0 -1
  233. package/dist/migrate.js +0 -238
  234. package/dist/migrate.js.map +0 -1
  235. package/dist/outbox.d.ts +0 -112
  236. package/dist/outbox.d.ts.map +0 -1
  237. package/dist/outbox.js +0 -294
  238. package/dist/outbox.js.map +0 -1
  239. package/dist/plugins/incrementing-version.d.ts +0 -34
  240. package/dist/plugins/incrementing-version.d.ts.map +0 -1
  241. package/dist/plugins/incrementing-version.js +0 -83
  242. package/dist/plugins/incrementing-version.js.map +0 -1
  243. package/dist/plugins/index.d.ts +0 -3
  244. package/dist/plugins/index.d.ts.map +0 -1
  245. package/dist/plugins/index.js +0 -3
  246. package/dist/plugins/index.js.map +0 -1
  247. package/dist/plugins/types.d.ts +0 -49
  248. package/dist/plugins/types.d.ts.map +0 -1
  249. package/dist/plugins/types.js +0 -15
  250. package/dist/plugins/types.js.map +0 -1
  251. package/dist/proxy/connection.d.ts +0 -33
  252. package/dist/proxy/connection.d.ts.map +0 -1
  253. package/dist/proxy/connection.js +0 -153
  254. package/dist/proxy/connection.js.map +0 -1
  255. package/dist/proxy/dialect.d.ts +0 -46
  256. package/dist/proxy/dialect.d.ts.map +0 -1
  257. package/dist/proxy/dialect.js +0 -58
  258. package/dist/proxy/dialect.js.map +0 -1
  259. package/dist/proxy/driver.d.ts +0 -42
  260. package/dist/proxy/driver.d.ts.map +0 -1
  261. package/dist/proxy/driver.js +0 -78
  262. package/dist/proxy/driver.js.map +0 -1
  263. package/dist/proxy/index.d.ts +0 -10
  264. package/dist/proxy/index.d.ts.map +0 -1
  265. package/dist/proxy/index.js +0 -10
  266. package/dist/proxy/index.js.map +0 -1
  267. package/dist/proxy/mutations.d.ts +0 -9
  268. package/dist/proxy/mutations.d.ts.map +0 -1
  269. package/dist/proxy/mutations.js +0 -11
  270. package/dist/proxy/mutations.js.map +0 -1
  271. package/dist/pull-engine.d.ts +0 -48
  272. package/dist/pull-engine.d.ts.map +0 -1
  273. package/dist/pull-engine.js +0 -648
  274. package/dist/pull-engine.js.map +0 -1
  275. package/dist/push-engine.d.ts +0 -18
  276. package/dist/push-engine.d.ts.map +0 -1
  277. package/dist/push-engine.js +0 -167
  278. package/dist/push-engine.js.map +0 -1
  279. package/dist/query/FingerprintCollector.d.ts +0 -18
  280. package/dist/query/FingerprintCollector.d.ts.map +0 -1
  281. package/dist/query/FingerprintCollector.js +0 -28
  282. package/dist/query/FingerprintCollector.js.map +0 -1
  283. package/dist/query/QueryContext.d.ts +0 -33
  284. package/dist/query/QueryContext.d.ts.map +0 -1
  285. package/dist/query/QueryContext.js +0 -16
  286. package/dist/query/QueryContext.js.map +0 -1
  287. package/dist/query/fingerprint.d.ts +0 -61
  288. package/dist/query/fingerprint.d.ts.map +0 -1
  289. package/dist/query/fingerprint.js +0 -91
  290. package/dist/query/fingerprint.js.map +0 -1
  291. package/dist/query/index.d.ts +0 -7
  292. package/dist/query/index.d.ts.map +0 -1
  293. package/dist/query/index.js +0 -7
  294. package/dist/query/index.js.map +0 -1
  295. package/dist/query/tracked-select.d.ts +0 -18
  296. package/dist/query/tracked-select.d.ts.map +0 -1
  297. package/dist/query/tracked-select.js +0 -90
  298. package/dist/query/tracked-select.js.map +0 -1
  299. package/dist/schema.d.ts +0 -83
  300. package/dist/schema.d.ts.map +0 -1
  301. package/dist/schema.js +0 -7
  302. package/dist/schema.js.map +0 -1
  303. package/dist/subscription-state.d.ts +0 -46
  304. package/dist/subscription-state.d.ts.map +0 -1
  305. package/dist/subscription-state.js +0 -185
  306. package/dist/subscription-state.js.map +0 -1
  307. package/dist/sync-loop.d.ts +0 -34
  308. package/dist/sync-loop.d.ts.map +0 -1
  309. package/dist/sync-loop.js +0 -253
  310. package/dist/sync-loop.js.map +0 -1
  311. package/dist/sync.d.ts +0 -32
  312. package/dist/sync.d.ts.map +0 -1
  313. package/dist/sync.js +0 -55
  314. package/dist/sync.js.map +0 -1
  315. package/dist/utils/id.d.ts +0 -17
  316. package/dist/utils/id.d.ts.map +0 -1
  317. package/dist/utils/id.js +0 -46
  318. package/dist/utils/id.js.map +0 -1
  319. package/src/blobs/index.ts +0 -7
  320. package/src/blobs/manager.ts +0 -1027
  321. package/src/blobs/migrate.ts +0 -67
  322. package/src/blobs/types.ts +0 -84
  323. package/src/client.test.ts +0 -417
  324. package/src/conflicts.ts +0 -171
  325. package/src/create-client.test.ts +0 -83
  326. package/src/create-client.ts +0 -330
  327. package/src/engine/SyncEngine.test.ts +0 -254
  328. package/src/engine/SyncEngine.ts +0 -2375
  329. package/src/engine/index.ts +0 -6
  330. package/src/engine/types.ts +0 -419
  331. package/src/handlers/collection.ts +0 -36
  332. package/src/handlers/create-handler.ts +0 -330
  333. package/src/handlers/types.ts +0 -108
  334. package/src/migrate.ts +0 -318
  335. package/src/outbox.ts +0 -383
  336. package/src/plugins/incrementing-version.ts +0 -133
  337. package/src/plugins/index.ts +0 -2
  338. package/src/plugins/types.ts +0 -63
  339. package/src/proxy/connection.ts +0 -191
  340. package/src/proxy/dialect.ts +0 -76
  341. package/src/proxy/driver.ts +0 -126
  342. package/src/proxy/index.ts +0 -10
  343. package/src/proxy/mutations.ts +0 -18
  344. package/src/pull-engine.test.ts +0 -151
  345. package/src/pull-engine.ts +0 -855
  346. package/src/push-engine.ts +0 -216
  347. package/src/query/FingerprintCollector.ts +0 -29
  348. package/src/query/QueryContext.ts +0 -54
  349. package/src/query/fingerprint.ts +0 -109
  350. package/src/query/index.ts +0 -10
  351. package/src/query/tracked-select.ts +0 -139
  352. package/src/schema.ts +0 -94
  353. package/src/subscription-state.ts +0 -259
  354. package/src/sync-loop.ts +0 -379
  355. package/src/sync.ts +0 -170
  356. package/src/utils/id.ts +0 -55
@@ -0,0 +1,512 @@
1
+ import {
2
+ randomId,
3
+ type SyncAuthLeaseIssueRequest,
4
+ type SyncOperation,
5
+ } from '@syncular/core';
6
+ import {
7
+ type CompiledQuery,
8
+ type DatabaseConnection,
9
+ type Dialect,
10
+ Kysely,
11
+ type QueryResult,
12
+ } from 'kysely';
13
+ import { BaseSqliteDialect, BaseSqliteDriver } from 'kysely-generic-sqlite';
14
+ import type {
15
+ SyncularBlobClientLike,
16
+ SyncularClientLike,
17
+ SyncularClientStatus,
18
+ } from './client';
19
+ import {
20
+ createMutationsApi,
21
+ type MutationReceipt,
22
+ type MutationsApi,
23
+ type MutationsCommitFn,
24
+ type MutationsTx,
25
+ } from './mutations';
26
+ import { assertSyncularReadonlySql } from './sql-safety';
27
+ import type {
28
+ SyncularAuthLeaseRecord,
29
+ SyncularClientEventSink,
30
+ SyncularClientEventType,
31
+ SyncularConflictResolution,
32
+ SyncularConflictStats,
33
+ SyncularConflictSummary,
34
+ SyncularConnectionState,
35
+ SyncularDiagnosticSnapshot,
36
+ SyncularLifecycleState,
37
+ SyncularOutboxStats,
38
+ SyncularPresenceEntry,
39
+ SyncularPresenceSink,
40
+ SyncularSubscriptionSpec,
41
+ SyncularSyncRequestOptions,
42
+ SyncularSyncResult,
43
+ } from './types';
44
+
45
+ export interface SyncularBridgeQueryRequest {
46
+ sql: string;
47
+ parameters: readonly unknown[];
48
+ }
49
+
50
+ export interface SyncularBridgeQueryResult<
51
+ Row extends Record<string, unknown> = Record<string, unknown>,
52
+ > {
53
+ rows: Row[];
54
+ numAffectedRows?: bigint | number | string;
55
+ insertId?: bigint | number | string | null;
56
+ }
57
+
58
+ export interface SyncularBridgeMutationBatch {
59
+ operations: SyncOperation[];
60
+ }
61
+
62
+ export interface SyncularBridgeStatus {
63
+ lifecycle?: SyncularLifecycleState;
64
+ connection?: SyncularConnectionState;
65
+ outbox?: SyncularOutboxStats | null;
66
+ conflicts?: SyncularConflictStats | null;
67
+ }
68
+
69
+ export interface SyncularBridgePresence {
70
+ get<TMetadata = Record<string, unknown>>(
71
+ scopeKey: string
72
+ ): SyncularPresenceEntry<TMetadata>[];
73
+ join(scopeKey: string, metadata?: Record<string, unknown>): void;
74
+ leave(scopeKey: string): void;
75
+ updateMetadata(scopeKey: string, metadata: Record<string, unknown>): void;
76
+ onChange<TMetadata = Record<string, unknown>>(
77
+ listener: SyncularPresenceSink<TMetadata>
78
+ ): () => void;
79
+ }
80
+
81
+ export interface SyncularBridgeConflicts {
82
+ list(): Promise<SyncularConflictSummary[]>;
83
+ retryKeepLocal(id: string): Promise<string>;
84
+ resolve(id: string, resolution: SyncularConflictResolution): Promise<void>;
85
+ }
86
+
87
+ export interface SyncularBridge {
88
+ open?(): Promise<void> | void;
89
+ close?(): Promise<void> | void;
90
+ executeSql<Row extends Record<string, unknown> = Record<string, unknown>>(
91
+ request: SyncularBridgeQueryRequest
92
+ ): Promise<SyncularBridgeQueryResult<Row>> | SyncularBridgeQueryResult<Row>;
93
+ applyMutationsCommit(
94
+ batch: SyncularBridgeMutationBatch
95
+ ): Promise<string | MutationReceipt> | string | MutationReceipt;
96
+ applyLeasedMutationsCommit?(
97
+ batch: SyncularBridgeMutationBatch
98
+ ): Promise<string | MutationReceipt> | string | MutationReceipt;
99
+ sync?(): Promise<SyncularSyncResult>;
100
+ resumeFromBackground?(
101
+ options?: SyncularSyncRequestOptions
102
+ ): Promise<SyncularSyncResult>;
103
+ start?(): Promise<void>;
104
+ stop?(): Promise<void>;
105
+ setSubscriptions?(
106
+ subscriptions: readonly SyncularSubscriptionSpec[]
107
+ ): Promise<void>;
108
+ getStatus?(): Promise<SyncularBridgeStatus> | SyncularBridgeStatus;
109
+ issueAuthLease?(
110
+ request: SyncAuthLeaseIssueRequest
111
+ ): Promise<SyncularAuthLeaseRecord>;
112
+ upsertAuthLease?(lease: SyncularAuthLeaseRecord): Promise<void>;
113
+ authLease?(leaseId: string): Promise<SyncularAuthLeaseRecord | null>;
114
+ activeAuthLeases?(
115
+ actorId?: string | null,
116
+ nowMs?: number
117
+ ): Promise<SyncularAuthLeaseRecord[]>;
118
+ diagnosticSnapshot?(): Promise<SyncularDiagnosticSnapshot>;
119
+ on?<T extends SyncularClientEventType>(
120
+ event: T,
121
+ listener: SyncularClientEventSink<T>
122
+ ): () => void;
123
+ presence?: SyncularBridgePresence;
124
+ conflicts?: SyncularBridgeConflicts;
125
+ blobs?: Partial<SyncularBlobClientLike>;
126
+ }
127
+
128
+ export interface CreateSyncularBridgeClientOptions {
129
+ bridge: SyncularBridge;
130
+ sync?: {
131
+ autoSyncAfterMutation?: boolean;
132
+ };
133
+ idColumn?: string;
134
+ versionColumn?: string | null;
135
+ omitColumns?: readonly string[];
136
+ }
137
+
138
+ export function createSyncularBridgeDialect(bridge: SyncularBridge): Dialect {
139
+ return new BaseSqliteDialect(() => new SyncularBridgeDriver(bridge));
140
+ }
141
+
142
+ export async function createSyncularBridgeClient<DB>(
143
+ options: CreateSyncularBridgeClientOptions
144
+ ): Promise<SyncularClientLike<DB>> {
145
+ await options.bridge.open?.();
146
+ const dialect = createSyncularBridgeDialect(options.bridge);
147
+ const db = new Kysely<DB>({ dialect });
148
+ let closed = false;
149
+ const syncAfterMutation = options.sync?.autoSyncAfterMutation !== false;
150
+
151
+ const client: SyncularClientLike<DB> = {
152
+ db,
153
+ dialect,
154
+ mutations: createSyncularBridgeMutations<DB>(options, async () => {
155
+ if (syncAfterMutation) await client.sync().catch(() => undefined);
156
+ }),
157
+ leasedMutations: createSyncularBridgeMutations<DB>(
158
+ options,
159
+ async () => {
160
+ if (syncAfterMutation) await client.sync().catch(() => undefined);
161
+ },
162
+ { leased: true }
163
+ ),
164
+ blobs: createBridgeBlobs(options.bridge),
165
+ on: (event, listener) => options.bridge.on?.(event, listener) ?? noop,
166
+ getStatus: () => resolveBridgeStatusSnapshot(options.bridge),
167
+ setSubscriptions: async (subscriptions) => {
168
+ await options.bridge.setSubscriptions?.(subscriptions);
169
+ },
170
+ resumeFromBackground: (syncOptions) =>
171
+ requireBridgeMethod(
172
+ options.bridge.resumeFromBackground,
173
+ 'resumeFromBackground'
174
+ )(syncOptions),
175
+ issueAuthLease: (request) =>
176
+ requireBridgeMethod(
177
+ options.bridge.issueAuthLease,
178
+ 'issueAuthLease'
179
+ )(request),
180
+ upsertAuthLease: (lease) =>
181
+ requireBridgeMethod(
182
+ options.bridge.upsertAuthLease,
183
+ 'upsertAuthLease'
184
+ )(lease),
185
+ authLease: (leaseId) =>
186
+ requireBridgeMethod(options.bridge.authLease, 'authLease')(leaseId),
187
+ activeAuthLeases: (actorId, nowMs) =>
188
+ requireBridgeMethod(options.bridge.activeAuthLeases, 'activeAuthLeases')(
189
+ actorId,
190
+ nowMs
191
+ ),
192
+ diagnosticSnapshot: () =>
193
+ requireBridgeMethod(
194
+ options.bridge.diagnosticSnapshot,
195
+ 'diagnosticSnapshot'
196
+ )(),
197
+ presence: createBridgePresence(options.bridge),
198
+ conflicts: createBridgeConflicts(options.bridge),
199
+ start: async () => {
200
+ await options.bridge.start?.();
201
+ },
202
+ stop: async () => {
203
+ await options.bridge.stop?.();
204
+ },
205
+ sync: async () => {
206
+ if (!options.bridge.sync) {
207
+ throw new Error('Syncular bridge does not implement sync().');
208
+ }
209
+ return await options.bridge.sync();
210
+ },
211
+ async close() {
212
+ if (closed) return;
213
+ closed = true;
214
+ await options.bridge.stop?.();
215
+ await db.destroy();
216
+ await options.bridge.close?.();
217
+ },
218
+ };
219
+
220
+ return client;
221
+ }
222
+
223
+ function createSyncularBridgeMutations<DB>(
224
+ options: CreateSyncularBridgeClientOptions,
225
+ afterCommit: () => Promise<void>,
226
+ mode: { leased?: boolean } = {}
227
+ ): MutationsApi<DB, undefined> {
228
+ const commit = createSyncularBridgeCommit<DB>(options, afterCommit, mode);
229
+ return createMutationsApi(commit);
230
+ }
231
+
232
+ function createSyncularBridgeCommit<DB>(
233
+ options: CreateSyncularBridgeClientOptions,
234
+ afterCommit: () => Promise<void>,
235
+ mode: { leased?: boolean } = {}
236
+ ): MutationsCommitFn<DB, { operations: SyncOperation[] }, undefined> {
237
+ const idColumn = options.idColumn ?? 'id';
238
+ const versionColumn = options.versionColumn ?? 'server_version';
239
+ const omitColumns = options.omitColumns ?? [];
240
+
241
+ return async (fn) => {
242
+ const operations: SyncOperation[] = [];
243
+ const makeTxTable = (table: string) => ({
244
+ async insert(values: unknown) {
245
+ const raw = objectRecord(values);
246
+ const id =
247
+ typeof raw[idColumn] === 'string' && raw[idColumn]
248
+ ? raw[idColumn]
249
+ : randomId();
250
+ operations.push({
251
+ table,
252
+ row_id: id,
253
+ op: 'upsert',
254
+ payload: sanitizeOperationPayload({ ...raw, [idColumn]: id }, [
255
+ idColumn,
256
+ ...(versionColumn ? [versionColumn] : []),
257
+ ...omitColumns,
258
+ ]),
259
+ base_version: null,
260
+ });
261
+ return id;
262
+ },
263
+ async insertMany(rows: unknown[]) {
264
+ const ids: string[] = [];
265
+ for (const row of rows) ids.push(await this.insert(row));
266
+ return ids;
267
+ },
268
+ async update(
269
+ id: string,
270
+ patch: unknown,
271
+ opts?: { baseVersion?: number | null }
272
+ ) {
273
+ operations.push(upsertOperation(table, id, patch, opts));
274
+ },
275
+ async delete(id: string, opts?: { baseVersion?: number | null }) {
276
+ operations.push({
277
+ table,
278
+ row_id: id,
279
+ op: 'delete',
280
+ payload: null,
281
+ base_version: opts?.baseVersion ?? null,
282
+ });
283
+ },
284
+ async upsert(
285
+ id: string,
286
+ patch: unknown,
287
+ opts?: { baseVersion?: number | null }
288
+ ) {
289
+ operations.push(upsertOperation(table, id, patch, opts));
290
+ },
291
+ });
292
+
293
+ const tx = new Proxy(
294
+ {},
295
+ {
296
+ get(_target, prop) {
297
+ if (prop === 'then') return undefined;
298
+ return typeof prop === 'string' ? makeTxTable(prop) : undefined;
299
+ },
300
+ }
301
+ ) as MutationsTx<DB>;
302
+
303
+ const result = await fn(tx);
304
+ if (operations.length === 0) throw new Error('No mutations were enqueued');
305
+ const commitFn = mode.leased
306
+ ? requireBridgeMethod(
307
+ options.bridge.applyLeasedMutationsCommit,
308
+ 'applyLeasedMutationsCommit'
309
+ )
310
+ : options.bridge.applyMutationsCommit.bind(options.bridge);
311
+ const receipt = normalizeMutationReceipt(await commitFn({ operations }));
312
+ await afterCommit();
313
+ return { result, receipt, meta: { operations } };
314
+ };
315
+
316
+ function upsertOperation(
317
+ table: string,
318
+ id: string,
319
+ patch: unknown,
320
+ opts?: { baseVersion?: number | null }
321
+ ): SyncOperation {
322
+ return {
323
+ table,
324
+ row_id: id,
325
+ op: 'upsert',
326
+ payload: sanitizeOperationPayload(objectRecord(patch), [
327
+ idColumn,
328
+ ...(versionColumn ? [versionColumn] : []),
329
+ ...omitColumns,
330
+ ]),
331
+ base_version: opts?.baseVersion ?? null,
332
+ };
333
+ }
334
+ }
335
+
336
+ class SyncularBridgeDriver extends BaseSqliteDriver {
337
+ constructor(bridge: SyncularBridge) {
338
+ super(async () => {
339
+ this.conn = new SyncularBridgeConnection(bridge);
340
+ });
341
+ }
342
+
343
+ async destroy(): Promise<void> {
344
+ this.conn = undefined;
345
+ }
346
+ }
347
+
348
+ class SyncularBridgeConnection implements DatabaseConnection {
349
+ constructor(private readonly bridge: SyncularBridge) {}
350
+
351
+ async executeQuery<R>(compiledQuery: CompiledQuery): Promise<QueryResult<R>> {
352
+ assertSyncularReadonlySql(compiledQuery.sql);
353
+ const result = await this.bridge.executeSql<R & Record<string, unknown>>({
354
+ sql: compiledQuery.sql,
355
+ parameters: compiledQuery.parameters,
356
+ });
357
+ return {
358
+ rows: result.rows as R[],
359
+ numAffectedRows:
360
+ result.numAffectedRows == null
361
+ ? undefined
362
+ : BigInt(result.numAffectedRows),
363
+ insertId: result.insertId == null ? undefined : BigInt(result.insertId),
364
+ };
365
+ }
366
+
367
+ streamQuery<R>(
368
+ _compiledQuery: CompiledQuery,
369
+ _chunkSize?: number
370
+ ): AsyncIterableIterator<QueryResult<R>> {
371
+ throw new Error('Syncular bridge dialect does not support streaming.');
372
+ }
373
+ }
374
+
375
+ function createBridgePresence(bridge: SyncularBridge): SyncularBridgePresence {
376
+ return (
377
+ bridge.presence ?? {
378
+ get: () => [],
379
+ join: unsupportedFn('presence.join'),
380
+ leave: unsupportedFn('presence.leave'),
381
+ updateMetadata: unsupportedFn('presence.updateMetadata'),
382
+ onChange: () => noop,
383
+ }
384
+ );
385
+ }
386
+
387
+ function createBridgeConflicts(
388
+ bridge: SyncularBridge
389
+ ): SyncularBridgeConflicts {
390
+ return (
391
+ bridge.conflicts ?? {
392
+ list: async () => [],
393
+ retryKeepLocal: unsupportedFn('conflicts.retryKeepLocal'),
394
+ resolve: unsupportedFn('conflicts.resolve'),
395
+ }
396
+ );
397
+ }
398
+
399
+ function createBridgeBlobs(bridge: SyncularBridge): SyncularBlobClientLike {
400
+ return {
401
+ getUploadQueueStats: async () =>
402
+ bridge.blobs?.getUploadQueueStats?.() ?? {
403
+ pending: 0,
404
+ uploading: 0,
405
+ failed: 0,
406
+ },
407
+ processUploadQueue: async (options) =>
408
+ bridge.blobs?.processUploadQueue?.(options) ?? { uploaded: 0, failed: 0 },
409
+ retrieve:
410
+ bridge.blobs?.retrieve ??
411
+ (async () => {
412
+ throw new Error('Syncular bridge does not implement blob retrieval.');
413
+ }),
414
+ };
415
+ }
416
+
417
+ function resolveBridgeStatusSnapshot(
418
+ bridge: SyncularBridge
419
+ ): SyncularClientStatus {
420
+ const snapshot = readSynchronousStatus(bridge);
421
+ const connection = snapshot.connection ?? {
422
+ closed: false,
423
+ pendingRequests: 0,
424
+ realtime: 'disconnected',
425
+ };
426
+ const lifecycle = snapshot.lifecycle ?? {
427
+ phase: connection.closed ? 'closed' : 'offline',
428
+ realtime: connection.realtime,
429
+ online: connection.realtime === 'connected',
430
+ requiresAction: false,
431
+ pendingRequests: connection.pendingRequests,
432
+ ...(snapshot.outbox ? { outbox: snapshot.outbox } : {}),
433
+ ...(snapshot.conflicts ? { conflicts: snapshot.conflicts } : {}),
434
+ };
435
+ const outbox = snapshot.outbox ?? lifecycle.outbox ?? null;
436
+ const conflicts = snapshot.conflicts ?? lifecycle.conflicts ?? null;
437
+ return {
438
+ lifecycle,
439
+ connection,
440
+ outbox,
441
+ conflicts,
442
+ isConnected: connection.realtime === 'connected' && !connection.closed,
443
+ isSyncing:
444
+ lifecycle.phase === 'syncing' || lifecycle.phase === 'recovering',
445
+ hasPendingMutations: (outbox?.pending ?? 0) + (outbox?.sending ?? 0) > 0,
446
+ hasConflicts: (conflicts?.unresolved ?? 0) > 0,
447
+ requiresAction: lifecycle.requiresAction,
448
+ };
449
+ }
450
+
451
+ function readSynchronousStatus(bridge: SyncularBridge): SyncularBridgeStatus {
452
+ if (!bridge.getStatus) return {};
453
+ const snapshot = bridge.getStatus();
454
+ if (isPromiseLike(snapshot)) {
455
+ throw new Error(
456
+ 'Syncular bridge getStatus() must be synchronous for React snapshots.'
457
+ );
458
+ }
459
+ return snapshot;
460
+ }
461
+
462
+ function normalizeMutationReceipt(
463
+ value: string | MutationReceipt
464
+ ): MutationReceipt {
465
+ if (typeof value === 'string')
466
+ return { commitId: value, clientCommitId: value };
467
+ return value;
468
+ }
469
+
470
+ function sanitizeOperationPayload(
471
+ payload: Record<string, unknown>,
472
+ omitColumns: readonly string[]
473
+ ): Record<string, unknown> {
474
+ const omit = new Set(omitColumns);
475
+ const out: Record<string, unknown> = {};
476
+ for (const [key, value] of Object.entries(payload)) {
477
+ if (!omit.has(key)) out[key] = value;
478
+ }
479
+ return out;
480
+ }
481
+
482
+ function objectRecord(value: unknown): Record<string, unknown> {
483
+ return value && typeof value === 'object' && !Array.isArray(value)
484
+ ? { ...(value as Record<string, unknown>) }
485
+ : {};
486
+ }
487
+
488
+ function unsupportedFn<Args extends unknown[], Result>(
489
+ method: string
490
+ ): (...args: Args) => Result {
491
+ return () => {
492
+ throw new Error(`Syncular bridge does not implement ${method}.`);
493
+ };
494
+ }
495
+
496
+ function requireBridgeMethod<Fn extends (...args: any[]) => any>(
497
+ fn: Fn | undefined,
498
+ method: string
499
+ ): Fn {
500
+ if (typeof fn === 'function') return fn;
501
+ return unsupportedFn(method) as Fn;
502
+ }
503
+
504
+ function isPromiseLike(value: unknown): value is Promise<unknown> {
505
+ return (
506
+ value != null &&
507
+ typeof value === 'object' &&
508
+ typeof (value as { then?: unknown }).then === 'function'
509
+ );
510
+ }
511
+
512
+ function noop(): void {}
@@ -0,0 +1,29 @@
1
+ import type {
2
+ ResolvedSyncularClientConfig,
3
+ SyncularClientConfig,
4
+ SyncularStorage,
5
+ } from './types';
6
+
7
+ export const SYNCULAR_DEFAULT_STORAGE = 'opfsSahPool' satisfies SyncularStorage;
8
+ export const SYNCULAR_LOCAL_DISABLED_BASE_URL = 'syncular-local://disabled';
9
+
10
+ export function resolveSyncularClientConfig(
11
+ config: SyncularClientConfig
12
+ ): ResolvedSyncularClientConfig {
13
+ const mode = config.mode ?? 'remote';
14
+ if (mode === 'remote' && !config.baseUrl) {
15
+ throw new Error('Syncular remote clients require config.baseUrl');
16
+ }
17
+ return {
18
+ ...config,
19
+ mode,
20
+ baseUrl: config.baseUrl ?? SYNCULAR_LOCAL_DISABLED_BASE_URL,
21
+ storage: config.storage ?? SYNCULAR_DEFAULT_STORAGE,
22
+ };
23
+ }
24
+
25
+ export function isSyncularRemoteMode(
26
+ config: Pick<ResolvedSyncularClientConfig, 'mode'>
27
+ ): boolean {
28
+ return config.mode === 'remote';
29
+ }