@syncular/client 0.1.3 → 0.2.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 (233) hide show
  1. package/README.md +227 -709
  2. package/dist/apply.d.ts +80 -0
  3. package/dist/apply.js +301 -0
  4. package/dist/blob.d.ts +127 -0
  5. package/dist/blob.js +201 -0
  6. package/dist/bun-database.d.ts +22 -0
  7. package/dist/bun-database.js +61 -0
  8. package/dist/client.d.ts +320 -69
  9. package/dist/client.js +1627 -163
  10. package/dist/content-type.d.ts +2 -0
  11. package/dist/content-type.js +2 -0
  12. package/dist/database.d.ts +35 -80
  13. package/dist/database.js +32 -947
  14. package/dist/encryption.d.ts +47 -0
  15. package/dist/encryption.js +75 -0
  16. package/dist/errors.d.ts +8 -22
  17. package/dist/errors.js +10 -207
  18. package/dist/http.d.ts +32 -0
  19. package/dist/http.js +196 -0
  20. package/dist/index.d.ts +28 -16
  21. package/dist/index.js +25 -12
  22. package/dist/invalidation.d.ts +69 -0
  23. package/dist/invalidation.js +84 -0
  24. package/dist/leader-lock.d.ts +28 -0
  25. package/dist/leader-lock.js +38 -0
  26. package/dist/multi-tab.d.ts +134 -0
  27. package/dist/multi-tab.js +399 -0
  28. package/dist/node-database.d.ts +41 -0
  29. package/dist/node-database.js +133 -0
  30. package/dist/outbox.d.ts +56 -0
  31. package/dist/outbox.js +140 -0
  32. package/dist/schema.d.ts +107 -0
  33. package/dist/schema.js +267 -0
  34. package/dist/state.d.ts +40 -0
  35. package/dist/state.js +77 -0
  36. package/dist/transport.d.ts +44 -0
  37. package/dist/transport.js +7 -0
  38. package/dist/wasm-database.d.ts +30 -0
  39. package/dist/wasm-database.js +170 -0
  40. package/dist/window.d.ts +64 -0
  41. package/dist/window.js +0 -0
  42. package/dist/worker-entry.d.ts +16 -2
  43. package/dist/worker-entry.js +300 -456
  44. package/dist/worker-host.d.ts +174 -0
  45. package/dist/worker-host.js +536 -0
  46. package/dist/worker-protocol.d.ts +173 -61
  47. package/dist/worker-protocol.js +7 -16
  48. package/package.json +45 -112
  49. package/src/apply.ts +435 -0
  50. package/src/blob.ts +333 -0
  51. package/src/bun-database.ts +75 -0
  52. package/src/client.ts +2247 -277
  53. package/src/content-type.ts +2 -0
  54. package/src/database.ts +63 -1389
  55. package/src/encryption.ts +123 -0
  56. package/src/errors.ts +11 -265
  57. package/src/http.ts +254 -0
  58. package/src/index.ts +25 -20
  59. package/src/invalidation.ts +128 -0
  60. package/src/leader-lock.ts +68 -0
  61. package/src/multi-tab.ts +550 -0
  62. package/src/node-database.ts +180 -0
  63. package/src/outbox.ts +206 -0
  64. package/src/schema.ts +397 -0
  65. package/src/state.ts +126 -0
  66. package/src/transport.ts +52 -0
  67. package/src/wasm-database.ts +279 -0
  68. package/src/window.ts +0 -0
  69. package/src/worker-entry.ts +391 -545
  70. package/src/worker-host.ts +800 -0
  71. package/src/worker-protocol.ts +204 -99
  72. package/dist/auth-leases.d.ts +0 -11
  73. package/dist/auth-leases.d.ts.map +0 -1
  74. package/dist/auth-leases.js +0 -140
  75. package/dist/auth-leases.js.map +0 -1
  76. package/dist/blob-limits.d.ts +0 -15
  77. package/dist/blob-limits.d.ts.map +0 -1
  78. package/dist/blob-limits.js +0 -66
  79. package/dist/blob-limits.js.map +0 -1
  80. package/dist/bridge-client.d.ts +0 -69
  81. package/dist/bridge-client.d.ts.map +0 -1
  82. package/dist/bridge-client.js +0 -282
  83. package/dist/bridge-client.js.map +0 -1
  84. package/dist/client-config.d.ts +0 -6
  85. package/dist/client-config.d.ts.map +0 -1
  86. package/dist/client-config.js +0 -18
  87. package/dist/client-config.js.map +0 -1
  88. package/dist/client.d.ts.map +0 -1
  89. package/dist/client.js.map +0 -1
  90. package/dist/command-history.d.ts +0 -35
  91. package/dist/command-history.d.ts.map +0 -1
  92. package/dist/command-history.js +0 -378
  93. package/dist/command-history.js.map +0 -1
  94. package/dist/console-diagnostics.d.ts +0 -33
  95. package/dist/console-diagnostics.d.ts.map +0 -1
  96. package/dist/console-diagnostics.js +0 -496
  97. package/dist/console-diagnostics.js.map +0 -1
  98. package/dist/crdt-yjs/index.d.ts +0 -4
  99. package/dist/crdt-yjs/index.d.ts.map +0 -1
  100. package/dist/crdt-yjs/index.js +0 -4
  101. package/dist/crdt-yjs/index.js.map +0 -1
  102. package/dist/crdt-yjs/webview-host-facade.d.ts +0 -126
  103. package/dist/crdt-yjs/webview-host-facade.d.ts.map +0 -1
  104. package/dist/crdt-yjs/webview-host-facade.js +0 -284
  105. package/dist/crdt-yjs/webview-host-facade.js.map +0 -1
  106. package/dist/crdt-yjs/yjs-document-field-adapter.d.ts +0 -153
  107. package/dist/crdt-yjs/yjs-document-field-adapter.d.ts.map +0 -1
  108. package/dist/crdt-yjs/yjs-document-field-adapter.js +0 -406
  109. package/dist/crdt-yjs/yjs-document-field-adapter.js.map +0 -1
  110. package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts +0 -73
  111. package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts.map +0 -1
  112. package/dist/crdt-yjs/yjs-prosemirror-bridge.js +0 -169
  113. package/dist/crdt-yjs/yjs-prosemirror-bridge.js.map +0 -1
  114. package/dist/database.d.ts.map +0 -1
  115. package/dist/database.js.map +0 -1
  116. package/dist/diagnostics.d.ts +0 -10
  117. package/dist/diagnostics.d.ts.map +0 -1
  118. package/dist/diagnostics.js +0 -83
  119. package/dist/diagnostics.js.map +0 -1
  120. package/dist/errors.d.ts.map +0 -1
  121. package/dist/errors.js.map +0 -1
  122. package/dist/generated-bridge.d.ts +0 -365
  123. package/dist/generated-bridge.d.ts.map +0 -1
  124. package/dist/generated-bridge.js +0 -250
  125. package/dist/generated-bridge.js.map +0 -1
  126. package/dist/index.d.ts.map +0 -1
  127. package/dist/index.js.map +0 -1
  128. package/dist/mutations.d.ts +0 -72
  129. package/dist/mutations.d.ts.map +0 -1
  130. package/dist/mutations.js +0 -63
  131. package/dist/mutations.js.map +0 -1
  132. package/dist/network.d.ts +0 -3
  133. package/dist/network.d.ts.map +0 -1
  134. package/dist/network.js +0 -17
  135. package/dist/network.js.map +0 -1
  136. package/dist/react/index.d.ts +0 -169
  137. package/dist/react/index.d.ts.map +0 -1
  138. package/dist/react/index.js +0 -628
  139. package/dist/react/index.js.map +0 -1
  140. package/dist/react-native/index.d.ts +0 -35
  141. package/dist/react-native/index.d.ts.map +0 -1
  142. package/dist/react-native/index.js +0 -49
  143. package/dist/react-native/index.js.map +0 -1
  144. package/dist/runtime-contract.d.ts +0 -13
  145. package/dist/runtime-contract.d.ts.map +0 -1
  146. package/dist/runtime-contract.js +0 -24
  147. package/dist/runtime-contract.js.map +0 -1
  148. package/dist/rust-client.d.ts +0 -106
  149. package/dist/rust-client.d.ts.map +0 -1
  150. package/dist/rust-client.js +0 -938
  151. package/dist/rust-client.js.map +0 -1
  152. package/dist/sentry.d.ts +0 -35
  153. package/dist/sentry.d.ts.map +0 -1
  154. package/dist/sentry.js +0 -155
  155. package/dist/sentry.js.map +0 -1
  156. package/dist/sql-safety.d.ts +0 -3
  157. package/dist/sql-safety.d.ts.map +0 -1
  158. package/dist/sql-safety.js +0 -62
  159. package/dist/sql-safety.js.map +0 -1
  160. package/dist/syncular-runtime-artifacts.json +0 -61
  161. package/dist/tauri/index.d.ts +0 -35
  162. package/dist/tauri/index.d.ts.map +0 -1
  163. package/dist/tauri/index.js +0 -114
  164. package/dist/tauri/index.js.map +0 -1
  165. package/dist/types.d.ts +0 -1096
  166. package/dist/types.d.ts.map +0 -1
  167. package/dist/types.js +0 -2
  168. package/dist/types.js.map +0 -1
  169. package/dist/wasm/.syncular-wasm-profile +0 -1
  170. package/dist/wasm/syncular-runtime-artifact.json +0 -21
  171. package/dist/wasm/syncular.d.ts +0 -207
  172. package/dist/wasm/syncular.js +0 -2341
  173. package/dist/wasm/syncular_bg.wasm +0 -0
  174. package/dist/wasm/syncular_bg.wasm.d.ts +0 -97
  175. package/dist/wasm-bindings/runtime-contract.d.ts +0 -22
  176. package/dist/wasm-bindings/runtime-contract.d.ts.map +0 -1
  177. package/dist/wasm-bindings/runtime-contract.js +0 -112
  178. package/dist/wasm-bindings/runtime-contract.js.map +0 -1
  179. package/dist/wasm-core/.syncular-wasm-profile +0 -1
  180. package/dist/wasm-core/syncular-runtime-artifact.json +0 -17
  181. package/dist/wasm-core/syncular.d.ts +0 -162
  182. package/dist/wasm-core/syncular.js +0 -1847
  183. package/dist/wasm-core/syncular_bg.wasm +0 -0
  184. package/dist/wasm-core/syncular_bg.wasm.d.ts +0 -77
  185. package/dist/wasm-perf/.syncular-wasm-profile +0 -1
  186. package/dist/wasm-perf/syncular-runtime-artifact.json +0 -21
  187. package/dist/wasm-perf/syncular.d.ts +0 -207
  188. package/dist/wasm-perf/syncular.js +0 -2341
  189. package/dist/wasm-perf/syncular_bg.wasm +0 -0
  190. package/dist/wasm-perf/syncular_bg.wasm.d.ts +0 -97
  191. package/dist/wasm-runtime.d.ts +0 -23
  192. package/dist/wasm-runtime.d.ts.map +0 -1
  193. package/dist/wasm-runtime.js +0 -69
  194. package/dist/wasm-runtime.js.map +0 -1
  195. package/dist/worker-client.d.ts +0 -123
  196. package/dist/worker-client.d.ts.map +0 -1
  197. package/dist/worker-client.js +0 -1735
  198. package/dist/worker-client.js.map +0 -1
  199. package/dist/worker-entry.d.ts.map +0 -1
  200. package/dist/worker-entry.js.map +0 -1
  201. package/dist/worker-protocol.d.ts.map +0 -1
  202. package/dist/worker-protocol.js.map +0 -1
  203. package/dist/worker-realtime.d.ts +0 -39
  204. package/dist/worker-realtime.d.ts.map +0 -1
  205. package/dist/worker-realtime.js +0 -677
  206. package/dist/worker-realtime.js.map +0 -1
  207. package/src/auth-leases.ts +0 -251
  208. package/src/blob-limits.ts +0 -98
  209. package/src/bridge-client.ts +0 -512
  210. package/src/client-config.ts +0 -29
  211. package/src/command-history.ts +0 -623
  212. package/src/console-diagnostics.ts +0 -617
  213. package/src/crdt-yjs/index.ts +0 -3
  214. package/src/crdt-yjs/webview-host-facade.ts +0 -477
  215. package/src/crdt-yjs/yjs-document-field-adapter.ts +0 -733
  216. package/src/crdt-yjs/yjs-prosemirror-bridge.ts +0 -272
  217. package/src/diagnostics.ts +0 -116
  218. package/src/generated-bridge.ts +0 -741
  219. package/src/mutations.ts +0 -168
  220. package/src/network.ts +0 -32
  221. package/src/react/index.ts +0 -1036
  222. package/src/react-native/index.ts +0 -152
  223. package/src/runtime-contract.ts +0 -48
  224. package/src/rust-client.ts +0 -1491
  225. package/src/sentry.ts +0 -215
  226. package/src/sql-safety.ts +0 -61
  227. package/src/tauri/index.ts +0 -211
  228. package/src/types.ts +0 -1397
  229. package/src/wasm-bindings/generated-wasm-bindings.d.ts +0 -70
  230. package/src/wasm-bindings/runtime-contract.ts +0 -158
  231. package/src/wasm-runtime.ts +0 -145
  232. package/src/worker-client.ts +0 -2289
  233. package/src/worker-realtime.ts +0 -843
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Main-thread side of the worker mode (Direction decision 2) and the
3
+ * multi-tab topology (TODO 3.2, REVISE B3).
4
+ *
5
+ * `createSyncClientHandle` acquires the Web Locks leader lock and, when it
6
+ * wins, spawns the worker running the WHOLE core — so exactly one core runs
7
+ * per origin (the lock IS the invariant: a worker is NEVER spawned without
8
+ * holding the lock). The returned {@link SyncClientHandle} is a thin, fully
9
+ * async proxy over the `worker-protocol` RPC.
10
+ *
11
+ * With `multiTab: true`, a tab that LOSES the election does not resolve to a
12
+ * dead not-leader handle: it becomes a FOLLOWER (`role === 'follower'`) that
13
+ * proxies every call to the leader tab over a BroadcastChannel (see
14
+ * `multi-tab.ts`). When the leader tab closes, its lock releases; the
15
+ * followers contest, the winner PROMOTES in place — spawns the worker over
16
+ * the persisted OPFS database and re-announces — and the handle's `role`
17
+ * flips to `'leader'` with `onRoleChange` firing. The same handle object is
18
+ * kept across the transition so React bindings hold a stable reference.
19
+ *
20
+ * With `multiTab` off (default), behavior is unchanged: the loser is an
21
+ * `isLeader === false` handle whose calls reject with `client.not_leader`.
22
+ */
23
+ import type { WakeReason } from '@syncular/core';
24
+ import type { BlobRef, CachedBlob } from './blob.js';
25
+ import type { ConflictRecord, LeaseState, MutationInput, PresencePeer, RejectionRecord, SchemaFloor, SubscribeInput, SyncClientLimits, SyncSummary, WindowState } from './client.js';
26
+ import type { SqlRow, SqlValue } from './database.js';
27
+ import { InvalidationEmitter, type InvalidationListener } from './invalidation.js';
28
+ import { type LeaderLease, type LeaderLock } from './leader-lock.js';
29
+ import { type CrossTabChannel, FollowerLink, LeaderBridge } from './multi-tab.js';
30
+ import type { OutboxCommit } from './outbox.js';
31
+ import type { ClientSchema } from './schema.js';
32
+ import type { SubscriptionRecord } from './state.js';
33
+ import type { WindowBase } from './window.js';
34
+ import { type SyncWorkerEvent, type WorkerDatabaseInit, type WorkerEndpoints, type WorkerErrorShape } from './worker-protocol.js';
35
+ export type HandleRole = 'leader' | 'follower';
36
+ export interface SyncClientHandleConfig {
37
+ /**
38
+ * Spawns the worker running `startSyncWorker()` (a factory so bundlers
39
+ * see `new Worker(new URL(...))` at the call site, and so no worker is
40
+ * spawned when this tab loses the leader election). Called again on a
41
+ * follower's promotion.
42
+ */
43
+ readonly worker: () => Worker;
44
+ readonly schema: ClientSchema;
45
+ readonly database: WorkerDatabaseInit;
46
+ readonly endpoints: WorkerEndpoints;
47
+ readonly clientId?: string;
48
+ readonly limits?: SyncClientLimits;
49
+ /** Worker-side host loop (§8.4); default true. */
50
+ readonly autoSync?: boolean;
51
+ readonly wakeJitterMs?: number;
52
+ /** Default: Web Locks when available, else single-owner. */
53
+ readonly leaderLock?: LeaderLock;
54
+ readonly lockName?: string;
55
+ /**
56
+ * Multi-tab followers (TODO 3.2). When true, a tab that loses the leader
57
+ * election becomes a FOLLOWER that proxies to the leader over a
58
+ * BroadcastChannel, and contests + promotes when the leader closes. When
59
+ * false (default), the loser is a dead `isLeader === false` handle.
60
+ */
61
+ readonly multiTab?: boolean;
62
+ /** Cross-tab channel factory (default `BroadcastChannel`); injectable for tests. */
63
+ readonly channelFactory?: (name: string) => CrossTabChannel;
64
+ /** Deadline for a follower call (covers the leader-handover gap). */
65
+ readonly followerCallTimeoutMs?: number;
66
+ /** Fires when this handle's role changes (follower → leader on promotion). */
67
+ readonly onRoleChange?: (role: HandleRole) => void;
68
+ readonly onSyncNeeded?: (reason: 'hello' | WakeReason) => void;
69
+ readonly onConflict?: (conflict: ConflictRecord) => void;
70
+ /** A worker-side autoSync round finished (or failed). */
71
+ readonly onSynced?: (result: {
72
+ readonly summary?: SyncSummary;
73
+ readonly error?: WorkerErrorShape;
74
+ }) => void;
75
+ /** §7.4.5: schema-bump `upgrading` state changed (reset began/completed). */
76
+ readonly onUpgrading?: (upgrading: boolean) => void;
77
+ /** §8.6: presence on a scope key changed. */
78
+ readonly onPresence?: (scopeKey: string) => void;
79
+ }
80
+ /**
81
+ * A running worker core owned by THIS tab (the leader). Wraps the worker,
82
+ * the RPC pending map, and (in multi-tab mode) the `LeaderBridge` relaying
83
+ * follower requests. Torn down on `close()` or on a graceful demotion.
84
+ */
85
+ interface LeaderCore {
86
+ readonly clientId: string;
87
+ readonly invoke: (method: string, args: readonly unknown[]) => Promise<unknown>;
88
+ readonly bridge: LeaderBridge | undefined;
89
+ readonly lease: LeaderLease;
90
+ close(terminate: boolean): Promise<void>;
91
+ }
92
+ /**
93
+ * The main-thread proxy: the same logical API as `SyncClient`, every method a
94
+ * promise. `role` is `'leader'` (owns the worker) or `'follower'` (proxies to
95
+ * the leader over the channel). Constructed via {@link createSyncClientHandle}.
96
+ */
97
+ export declare class SyncClientHandle {
98
+ #private;
99
+ /** True only for a leader handle. Kept for the pre-multiTab contract. */
100
+ get isLeader(): boolean;
101
+ get role(): HandleRole;
102
+ /** Resolved client id — the leader's; shared by all tabs on this origin. */
103
+ get clientId(): string;
104
+ /** @internal — use {@link createSyncClientHandle}. */
105
+ constructor(internals: {
106
+ role: HandleRole;
107
+ clientId: string;
108
+ core?: LeaderCore;
109
+ follower?: FollowerLink;
110
+ invalidation: InvalidationEmitter;
111
+ presence: Set<(scopeKey: string) => void>;
112
+ roleListeners?: Set<(role: HandleRole) => void>;
113
+ });
114
+ /** @internal — swap this handle from follower to leader (promotion). */
115
+ __becomeLeader(core: LeaderCore): void;
116
+ /** @internal — dispatch a worker/relayed event to handle-local listeners. */
117
+ __dispatchEvent(event: SyncWorkerEvent): void;
118
+ /**
119
+ * TODO 3.1 / I1: subscribe to fine-grained invalidation — the identical
120
+ * surface as `SyncClient.onInvalidate`, so React bindings target one
121
+ * interface across direct, worker-leader, and follower modes. Returns an
122
+ * unsubscribe function.
123
+ */
124
+ onInvalidate(listener: InvalidationListener): () => void;
125
+ /**
126
+ * §8.6: subscribe to presence changes — the identical surface as
127
+ * `SyncClient.onPresence`. Returns an unsubscribe function.
128
+ */
129
+ onPresence(listener: (scopeKey: string) => void): () => void;
130
+ /** Subscribe to role transitions (follower → leader on promotion). */
131
+ onRoleChange(listener: (role: HandleRole) => void): () => void;
132
+ subscribe(input: SubscribeInput): Promise<void>;
133
+ unsubscribe(id: string): Promise<void>;
134
+ setWindow(base: WindowBase, units: readonly string[]): Promise<void>;
135
+ windowState(base: WindowBase): Promise<WindowState>;
136
+ mutate(mutations: readonly MutationInput[]): Promise<string>;
137
+ sync(): Promise<SyncSummary>;
138
+ syncUntilIdle(maxRounds?: number): Promise<SyncSummary>;
139
+ query(sql: string, params?: readonly SqlValue[]): Promise<SqlRow[]>;
140
+ conflicts(): Promise<readonly ConflictRecord[]>;
141
+ rejections(): Promise<readonly RejectionRecord[]>;
142
+ schemaFloor(): Promise<SchemaFloor | undefined>;
143
+ leaseState(): Promise<LeaseState | undefined>;
144
+ /** §7.4.5: true while a schema-bump reset + first re-bootstrap runs. */
145
+ upgrading(): Promise<boolean>;
146
+ syncNeeded(): Promise<boolean>;
147
+ pendingCommits(): Promise<OutboxCommit[]>;
148
+ subscriptions(): Promise<SubscriptionRecord[]>;
149
+ subscription(id: string): Promise<SubscriptionRecord | undefined>;
150
+ connectRealtime(): Promise<void>;
151
+ disconnectRealtime(): Promise<void>;
152
+ /** §8.6: publish/clear a scope-keyed presence document. */
153
+ setPresence(scopeKey: string, doc: Record<string, unknown> | null): Promise<void>;
154
+ /** §8.6: the peers currently present on a scope key. */
155
+ presence(scopeKey: string): Promise<readonly PresencePeer[]>;
156
+ uploadBlob(bytes: Uint8Array, options?: {
157
+ readonly mediaType?: string;
158
+ readonly name?: string;
159
+ }): Promise<BlobRef>;
160
+ fetchBlob(blobIdOrRef: string): Promise<CachedBlob>;
161
+ /** Sever/restore transport + realtime inside the worker (demos). */
162
+ setOffline(offline: boolean): Promise<void>;
163
+ /** Close the core (leader) or unbind the link (follower), release leadership. */
164
+ close(): Promise<void>;
165
+ }
166
+ /**
167
+ * Acquire leadership, spawn the worker, initialize the core inside it.
168
+ *
169
+ * With `multiTab` off: a losing tab resolves to a dead not-leader handle.
170
+ * With `multiTab` on: a losing tab becomes a FOLLOWER proxying to the leader,
171
+ * and promotes itself if the leader later closes.
172
+ */
173
+ export declare function createSyncClientHandle(config: SyncClientHandleConfig): Promise<SyncClientHandle>;
174
+ export {};