@syncular/client 0.1.3 → 0.2.1

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
package/src/index.ts CHANGED
@@ -1,23 +1,28 @@
1
- export * from './blob-limits';
2
- export * from './bridge-client';
1
+ /**
2
+ * @syncular/client — the B3 TypeScript client protocol core
3
+ * (SPEC.md is normative; REVISE.md B3 is the architectural mandate).
4
+ *
5
+ * Browser-safe root: database backends live behind subpath exports
6
+ * (`./bun` for bun:sqlite tests, `./wasm` for sqlite-wasm + OPFS); the
7
+ * worker-side bootstrap lives behind `./worker`. The main-thread handle
8
+ * (`worker-host`) and the RPC protocol types are root exports — they
9
+ * import no SQLite.
10
+ */
11
+ export * from './apply';
12
+ export * from './blob';
3
13
  export * from './client';
4
- export * from './client-config';
5
- export * from './command-history';
6
- export * from './console-diagnostics';
14
+ export * from './content-type';
7
15
  export * from './database';
16
+ export * from './encryption';
8
17
  export * from './errors';
9
- export * from './mutations';
10
- export * from './network';
11
- export * from './runtime-contract';
12
- export * from './types';
13
- export type { SyncularWasmArtifactVariant } from './wasm-runtime';
14
- export {
15
- getSyncularPackagedRuntimeArtifacts,
16
- getSyncularRuntimeArtifact,
17
- getSyncularRuntimeArtifactCatalogUrl,
18
- getSyncularWasmGlueUrl,
19
- getSyncularWasmUrl,
20
- resolveSyncularRuntimeArtifactCatalog,
21
- selectSyncularRuntimeArtifact,
22
- } from './wasm-runtime';
23
- export * from './worker-client';
18
+ export * from './http';
19
+ export * from './invalidation';
20
+ export * from './leader-lock';
21
+ export * from './multi-tab';
22
+ export * from './outbox';
23
+ export * from './schema';
24
+ export * from './state';
25
+ export * from './transport';
26
+ export * from './window';
27
+ export * from './worker-host';
28
+ export * from './worker-protocol';
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The ONE apply-path invalidation choke point (TODO 3.1 / DESIGN-eviction
3
+ * I1–I4). Every local mutation — `COMMIT` apply, segment apply (rows +
4
+ * sqlite images), optimistic overlay rebuild, revocation purge, schema-bump
5
+ * reset, and (future) window eviction — routes its touched keys through a
6
+ * single {@link Invalidation} accumulator, and the client emits exactly ONE
7
+ * {@link InvalidationEvent} per apply batch (never per row). Live queries
8
+ * subscribe via `SyncClient.onInvalidate` and re-run only when a table they
9
+ * depend on appears.
10
+ *
11
+ * Granularity truth (honest to the wire, §4.5 / §5.2):
12
+ * - `COMMIT` changes carry per-row `scopes` (variable → value), so their
13
+ * `prefix:value` scope keys (§3.1 vocabulary, I2) are emitted precisely.
14
+ * - Segments carry only a table + `scopeDigest`, NOT per-row scope keys, so
15
+ * a segment apply invalidates at the **table** granularity plus the
16
+ * subscription's requested/effective scope keys (the coarsest honest key
17
+ * the wire supports for bulk data).
18
+ * - Purge / reset / optimistic / eviction are keyed by table (and effective
19
+ * scope keys where a scope map is in hand).
20
+ *
21
+ * `tables` is therefore always the reliable floor; `scopeKeys` is a
22
+ * best-effort refinement present where the source carried it. A live query
23
+ * that cannot express its scope footprint keys off `tables` alone.
24
+ */
25
+ import type { ScopeMap } from '@syncular/core';
26
+ import type { CompiledClientTable } from './schema';
27
+
28
+ /** One coalesced invalidation batch (I1). Empty batches are not emitted. */
29
+ export interface InvalidationEvent {
30
+ /** Tables whose local rows changed this batch — the reliable floor. */
31
+ readonly tables: ReadonlySet<string>;
32
+ /** `prefix:value` scope keys touched, where the source carried them (I2). */
33
+ readonly scopeKeys: ReadonlySet<string>;
34
+ }
35
+
36
+ export type InvalidationListener = (event: InvalidationEvent) => void;
37
+
38
+ /**
39
+ * A per-batch accumulator. One is created at the top of each apply batch,
40
+ * fed by the apply paths, and flushed once at the batch boundary. Mutable
41
+ * and cheap on purpose — no allocation until something is actually touched.
42
+ */
43
+ export class Invalidation {
44
+ #tables: Set<string> | undefined;
45
+ #scopeKeys: Set<string> | undefined;
46
+
47
+ /** Mark a whole table dirty (the floor for any apply). */
48
+ table(name: string): void {
49
+ if (this.#tables === undefined) this.#tables = new Set();
50
+ this.#tables.add(name);
51
+ }
52
+
53
+ /** Add a raw `prefix:value` scope key (§3.1). */
54
+ scopeKey(key: string): void {
55
+ if (this.#scopeKeys === undefined) this.#scopeKeys = new Set();
56
+ this.#scopeKeys.add(key);
57
+ }
58
+
59
+ /**
60
+ * Add the `prefix:value` keys for an effective/requested scope map on
61
+ * `table` (variable → list(value)), skipping variables the table has no
62
+ * prefix for (never guesses a key).
63
+ */
64
+ scopeMap(table: CompiledClientTable, scopes: ScopeMap): void {
65
+ for (const [variable, values] of Object.entries(scopes)) {
66
+ const prefix = table.scopePrefixByVariable.get(variable);
67
+ if (prefix === undefined) continue;
68
+ for (const v of values) this.scopeKey(`${prefix}:${v}`);
69
+ }
70
+ }
71
+
72
+ /** A COMMIT change's stored scopes (variable → single value, §4.5). */
73
+ changeScopes(
74
+ table: CompiledClientTable,
75
+ scopes: Record<string, string>,
76
+ ): void {
77
+ for (const [variable, value] of Object.entries(scopes)) {
78
+ const prefix = table.scopePrefixByVariable.get(variable);
79
+ if (prefix !== undefined) this.scopeKey(`${prefix}:${value}`);
80
+ }
81
+ }
82
+
83
+ get touched(): boolean {
84
+ return this.#tables !== undefined || this.#scopeKeys !== undefined;
85
+ }
86
+
87
+ /** Freeze into an event, or undefined when nothing was touched. */
88
+ finish(): InvalidationEvent | undefined {
89
+ if (!this.touched) return undefined;
90
+ return {
91
+ tables: this.#tables ?? EMPTY,
92
+ scopeKeys: this.#scopeKeys ?? EMPTY,
93
+ };
94
+ }
95
+ }
96
+
97
+ const EMPTY: ReadonlySet<string> = new Set();
98
+
99
+ /**
100
+ * A tiny subscribable listener set. Notification is synchronous and
101
+ * exception-isolated (one throwing listener never starves the rest, and
102
+ * never corrupts the emitting apply path). Reused by the worker handle so
103
+ * both cores expose the identical `onInvalidate` surface.
104
+ */
105
+ export class InvalidationEmitter {
106
+ readonly #listeners = new Set<InvalidationListener>();
107
+
108
+ on(listener: InvalidationListener): () => void {
109
+ this.#listeners.add(listener);
110
+ return () => {
111
+ this.#listeners.delete(listener);
112
+ };
113
+ }
114
+
115
+ emit(event: InvalidationEvent): void {
116
+ for (const listener of this.#listeners) {
117
+ try {
118
+ listener(event);
119
+ } catch {
120
+ // A UI listener must never break the apply path (I1).
121
+ }
122
+ }
123
+ }
124
+
125
+ get size(): number {
126
+ return this.#listeners.size;
127
+ }
128
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Multi-tab ownership seam (REVISE B3): exactly one core instance owns the
3
+ * local database. The interface is the whole B3 deliverable — cross-tab
4
+ * follower fanout is post-gate. Browsers use Web Locks; tests use the
5
+ * no-op single-owner lock.
6
+ */
7
+ export interface LeaderLease {
8
+ release(): void | Promise<void>;
9
+ }
10
+
11
+ export interface LeaderLock {
12
+ /** Resolves when this instance holds leadership for `name`. */
13
+ acquire(name: string): Promise<LeaderLease>;
14
+ /**
15
+ * Resolves immediately: the lease when leadership was free, `undefined`
16
+ * when another owner holds it. The worker handle uses this so a second
17
+ * tab gets a clear not-leader state instead of blocking forever
18
+ * (followers are post-gate, TODO 3.2).
19
+ */
20
+ tryAcquire?(name: string): Promise<LeaderLease | undefined>;
21
+ }
22
+
23
+ /** Single-owner environments (tests, dedicated workers): always leader. */
24
+ export function singleOwnerLock(): LeaderLock {
25
+ return {
26
+ acquire: () => Promise.resolve({ release: () => {} }),
27
+ tryAcquire: () => Promise.resolve({ release: () => {} }),
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Web Locks leader election: resolves once the exclusive lock is granted
33
+ * and holds it until the lease is released (tab close releases it
34
+ * implicitly, letting the next tab take over).
35
+ */
36
+ export function webLocksLeaderLock(locks?: LockManager): LeaderLock {
37
+ const manager = locks ?? navigator.locks;
38
+ return {
39
+ acquire: (name) =>
40
+ new Promise<LeaderLease>((resolveAcquire, rejectAcquire) => {
41
+ manager
42
+ .request(
43
+ name,
44
+ { mode: 'exclusive' },
45
+ () =>
46
+ new Promise<void>((resolveHold) => {
47
+ resolveAcquire({ release: () => resolveHold() });
48
+ }),
49
+ )
50
+ .catch((error: unknown) => rejectAcquire(error));
51
+ }),
52
+ tryAcquire: (name) =>
53
+ new Promise<LeaderLease | undefined>((resolveTry, rejectTry) => {
54
+ manager
55
+ .request(name, { mode: 'exclusive', ifAvailable: true }, (lock) => {
56
+ // `ifAvailable` grants `null` instead of waiting (Web Locks).
57
+ if (lock === null) {
58
+ resolveTry(undefined);
59
+ return undefined;
60
+ }
61
+ return new Promise<void>((resolveHold) => {
62
+ resolveTry({ release: () => resolveHold() });
63
+ });
64
+ })
65
+ .catch((error: unknown) => rejectTry(error));
66
+ }),
67
+ };
68
+ }