@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
package/dist/client.d.ts CHANGED
@@ -1,78 +1,329 @@
1
- import type { BlobRef, SyncAuthLeaseIssueRequest } from '@syncular/core';
2
- import type { SyncularDatabase } from './database';
3
- import type { MutationsApi } from './mutations';
4
- import type { SyncularAuthLeaseRecord, SyncularBlobUploadQueueProcessOptions, SyncularBlobUploadQueueStats, SyncularClientEventSink, SyncularClientEventType, SyncularConflictResolution, SyncularConflictStats, SyncularConflictSummary, SyncularConnectionState, SyncularDiagnosticSnapshot, SyncularLifecycleState, SyncularNetworkStatusSource, SyncularOutboxStats, SyncularPresenceEntry, SyncularPresenceSink, SyncularRealtimeOptions, SyncularRuntimeClient, SyncularSubscriptionSpec, SyncularSyncRequestOptions, SyncularSyncResult } from './types';
5
- export interface SyncularClientLifecycleOptions {
6
- initialSync?: boolean;
7
- realtime?: boolean | SyncularRealtimeOptions;
8
- syncOnRealtimeConnect?: boolean;
9
- pollIntervalMs?: number | false;
10
- network?: SyncularNetworkStatusSource | false;
11
- subscriptions?: readonly SyncularSubscriptionSpec[];
1
+ /**
2
+ * SyncClient the B3 client protocol core (SPEC.md §§3–8 client side).
3
+ *
4
+ * A plain library running on whatever thread it is constructed on
5
+ * (worker-OPTIONAL, REVISE B3): storage behind `ClientDatabase`, network
6
+ * behind `SyncTransport`/`SegmentDownloader`/`RealtimeConnector`, multi-tab
7
+ * ownership behind `LeaderLock`. One combined push+pull request per
8
+ * `sync()` round (§7.2); local reads go straight to the database.
9
+ */
10
+ import { type RowValue, type ScopeMap, type WakeReason } from '@syncular/core';
11
+ import { type BlobRef, type BlobTransport, type CachedBlob } from './blob.js';
12
+ import type { ClientDatabase, SqlRow, SqlValue } from './database.js';
13
+ import type { EncryptionConfig } from './encryption.js';
14
+ import { type InvalidationListener } from './invalidation.js';
15
+ import { type LeaderLock } from './leader-lock.js';
16
+ import { type OutboxCommit, type OutboxOperation } from './outbox.js';
17
+ import { type ClientSchema } from './schema.js';
18
+ import { type SubscriptionRecord } from './state.js';
19
+ import type { RealtimeConnector, SegmentDownloader, SyncTransport } from './transport.js';
20
+ import { type WindowBase } from './window.js';
21
+ export type MutationInput = {
22
+ readonly table: string;
23
+ readonly op: 'upsert';
24
+ /** Full-row values keyed by column name (§6.1: full row payloads). */
25
+ readonly values: Readonly<Record<string, unknown>>;
26
+ readonly baseVersion?: number;
27
+ } | {
28
+ readonly table: string;
29
+ readonly op: 'delete';
30
+ readonly rowId: string;
31
+ readonly baseVersion?: number;
32
+ };
33
+ /** A §6.3 conflict result, surfaced to the app — never auto-resolved. */
34
+ export interface ConflictRecord {
35
+ readonly clientCommitId: string;
36
+ readonly opIndex: number;
37
+ readonly table: string;
38
+ readonly rowId: string;
39
+ readonly code: string;
40
+ readonly message: string;
41
+ readonly serverVersion: number;
42
+ /** The current server row, decoded — resolve without a round-trip. */
43
+ readonly serverRow: Readonly<Record<string, RowValue>>;
44
+ /** The losing local operation (absent only for malformed op indexes). */
45
+ readonly operation?: OutboxOperation;
12
46
  }
13
- export interface SyncularClientStatus {
14
- lifecycle: SyncularLifecycleState;
15
- connection: SyncularConnectionState;
16
- outbox: SyncularOutboxStats | null;
17
- conflicts: SyncularConflictStats | null;
18
- isConnected: boolean;
19
- isSyncing: boolean;
20
- hasPendingMutations: boolean;
21
- hasConflicts: boolean;
22
- requiresAction: boolean;
47
+ /** A non-conflict `error` result from a rejected commit (§6.3). */
48
+ export interface RejectionRecord {
49
+ readonly clientCommitId: string;
50
+ readonly opIndex: number;
51
+ readonly code: string;
52
+ readonly message: string;
53
+ readonly retryable: boolean;
54
+ readonly operation?: OutboxOperation;
23
55
  }
24
- export interface SyncularBlobClientLike {
25
- getUploadQueueStats(): Promise<SyncularBlobUploadQueueStats>;
26
- processUploadQueue(options?: SyncularBlobUploadQueueProcessOptions): Promise<{
27
- uploaded: number;
28
- failed: number;
29
- }>;
30
- retrieve(ref: BlobRef): Promise<Uint8Array>;
56
+ export interface SchemaFloor {
57
+ readonly requiredSchemaVersion?: number;
58
+ readonly latestSchemaVersion?: number;
31
59
  }
32
- export interface SyncularPresenceClientLike {
33
- get<TMetadata = Record<string, unknown>>(scopeKey: string): SyncularPresenceEntry<TMetadata>[];
34
- join(scopeKey: string, metadata?: Record<string, unknown>): void;
35
- leave(scopeKey: string): void;
36
- updateMetadata(scopeKey: string, metadata: Record<string, unknown>): void;
37
- onChange<TMetadata = Record<string, unknown>>(listener: SyncularPresenceSink<TMetadata>): () => void;
60
+ /**
61
+ * §7.3.5: the client's view of its current auth lease — opaque state, the
62
+ * mirror of `schemaFloor`. `leaseId`/`expiresAtMs` come from the last
63
+ * `LEASE` frame; `errorCode` is set (and syncing stops on the lease) when
64
+ * the server rejects a round with a request-level lease code (§7.3.4).
65
+ * The lease is never cryptographically verified by the client (§7.3
66
+ * non-goal 1), and lease errors never purge local data (§7.3.4).
67
+ */
68
+ export interface LeaseState {
69
+ readonly leaseId?: string;
70
+ readonly expiresAtMs?: number;
71
+ /** `sync.auth_lease_required` | `sync.auth_lease_revoked` when stopped. */
72
+ readonly errorCode?: string;
38
73
  }
39
- export interface SyncularConflictsClientLike {
40
- list(): Promise<SyncularConflictSummary[]>;
41
- retryKeepLocal(id: string): Promise<string>;
42
- resolve(id: string, resolution: SyncularConflictResolution): Promise<void>;
74
+ export interface SyncSummary {
75
+ /** Commits sent in this round's push half. */
76
+ readonly pushed: number;
77
+ /** clientCommitIds drained (`applied` or `cached`, §6.3). */
78
+ readonly applied: readonly string[];
79
+ /** clientCommitIds rejected and dropped from the outbox. */
80
+ readonly rejected: readonly string[];
81
+ /** clientCommitIds kept for retry (`sync.idempotency_cache_miss`). */
82
+ readonly retryable: readonly string[];
83
+ /** New conflict records surfaced this round. */
84
+ readonly conflicts: readonly ConflictRecord[];
85
+ /** `COMMIT` frames applied locally. */
86
+ readonly commitsApplied: number;
87
+ /** Snapshot rows applied from segments. */
88
+ readonly segmentRowsApplied: number;
89
+ /** Subscriptions still mid-bootstrap (resume token pending). */
90
+ readonly bootstrapping: readonly string[];
91
+ /** Subscriptions reset via `sync.cursor_expired` (re-pull needed). */
92
+ readonly resets: readonly string[];
93
+ /** Subscriptions revoked this round (§3.3 purge ran). */
94
+ readonly revoked: readonly string[];
95
+ /** Subscriptions stopped by a fatal configuration error (§3.3/§5.6
96
+ * fail-closed: no local scope-column mapping). */
97
+ readonly failed: readonly string[];
98
+ /** Present when the server declared a schema floor — syncing stopped. */
99
+ readonly schemaFloor?: SchemaFloor;
100
+ /** §6.1 splitBatch: outbox commits held back from THIS request because
101
+ * including them would exceed the per-request operation cap. They remain
102
+ * queued; sync-needed stays raised and `syncUntilIdle` keeps going. */
103
+ readonly deferredCommits?: number;
43
104
  }
44
- export interface SyncularClientLike<DB> {
45
- db: SyncularDatabase<DB>['db'];
46
- dialect?: SyncularDatabase<DB>['dialect'] | unknown;
47
- mutations: MutationsApi<DB, any>;
48
- leasedMutations: MutationsApi<DB, any>;
49
- blobs: SyncularBlobClientLike;
50
- on<T extends SyncularClientEventType>(event: T, listener: SyncularClientEventSink<T>): () => void;
51
- getStatus(): SyncularClientStatus;
52
- setSubscriptions(subscriptions: readonly SyncularSubscriptionSpec[]): Promise<void>;
53
- resumeFromBackground(options?: SyncularSyncRequestOptions): Promise<SyncularSyncResult>;
54
- issueAuthLease(request: SyncAuthLeaseIssueRequest): Promise<SyncularAuthLeaseRecord>;
55
- upsertAuthLease(lease: SyncularAuthLeaseRecord): Promise<void>;
56
- authLease(leaseId: string): Promise<SyncularAuthLeaseRecord | null>;
57
- activeAuthLeases(actorId?: string | null, nowMs?: number): Promise<SyncularAuthLeaseRecord[]>;
58
- diagnosticSnapshot(): Promise<SyncularDiagnosticSnapshot>;
59
- presence: SyncularPresenceClientLike;
60
- conflicts: SyncularConflictsClientLike;
61
- start(): Promise<void>;
62
- stop(): Promise<void>;
63
- sync(): Promise<SyncularSyncResult>;
64
- close(): Promise<void>;
105
+ export interface SyncClientLimits {
106
+ readonly limitCommits?: number;
107
+ readonly limitSnapshotRows?: number;
108
+ readonly maxSnapshotPages?: number;
109
+ /**
110
+ * §4.2 accept bitmask; defaults to inline + external rows (0b0011)
111
+ * plus sqlite images (bit 2) when the database backend implements
112
+ * `withSqliteImage` and a segment downloader is configured (§5.3).
113
+ */
114
+ readonly accept?: number;
115
+ }
116
+ export interface SyncClientConfig {
117
+ readonly database: ClientDatabase;
118
+ readonly schema: ClientSchema;
119
+ readonly transport: SyncTransport;
120
+ readonly segments?: SegmentDownloader;
121
+ /** Blob upload/download (§5.9). Required to use `uploadBlob`/`fetchBlob`. */
122
+ readonly blobs?: BlobTransport;
123
+ /**
124
+ * §5.9.7 B1 blob-cache size cap (bytes). When the sum of cached body sizes
125
+ * exceeds this, zero-ref, non-pinned bodies are evicted LRU-first after each
126
+ * cache write (referenced/pinned bodies are never evicted — correctness
127
+ * beats the cap). Absent ⇒ retain until storage pressure (the shipped
128
+ * default; a referenced body always stays resolvable without a re-download).
129
+ */
130
+ readonly blobCacheMaxBytes?: number;
131
+ readonly realtime?: RealtimeConnector;
132
+ /** Stable per-device id (§1.5); defaults to a persisted random UUID. */
133
+ readonly clientId?: string;
134
+ readonly leaderLock?: LeaderLock;
135
+ readonly lockName?: string;
136
+ readonly limits?: SyncClientLimits;
137
+ readonly now?: () => number;
138
+ /** §8: hello `requiresSync` or a wake-up — run a pull soon. */
139
+ readonly onSyncNeeded?: (reason: 'hello' | WakeReason) => void;
140
+ readonly onConflict?: (conflict: ConflictRecord) => void;
141
+ /**
142
+ * §7.4.5: the schema-bump `upgrading` state changed. `true` when a reset
143
+ * (wipe + re-bootstrap) began, `false` when the first post-reset
144
+ * bootstrap round reached idle — the app's cue to re-run live queries.
145
+ */
146
+ readonly onUpgrading?: (upgrading: boolean) => void;
147
+ /**
148
+ * §8.6 presence: a scope-mate's presence on a key this client holds
149
+ * changed (join/update/leave). Fired after the local presence map is
150
+ * updated — the app's cue to re-render who's-online.
151
+ */
152
+ readonly onPresence?: (scopeKey: string) => void;
153
+ /**
154
+ * §5.11 client-side encryption. Supplies the key material (`keyProvider`)
155
+ * and key selection (`keyIdFor`) for columns the generated schema marks
156
+ * `encrypted`. Absent ⇒ no columns are encrypted (a schema with encrypted
157
+ * columns then fails loud on the first encode/apply — a missing key is
158
+ * `client.decrypt_failed`, never silent plaintext).
159
+ */
160
+ readonly encryption?: EncryptionConfig;
161
+ }
162
+ /** §8.6 a peer's ephemeral presence document on a scope key. */
163
+ export interface PresencePeer {
164
+ readonly actorId: string;
165
+ readonly clientId: string;
166
+ readonly doc: Record<string, unknown>;
65
167
  }
66
- type LifecycleClient = Pick<SyncularRuntimeClient, 'addDiagnosticListener' | 'connectionState' | 'forceSubscriptionsBootstrap' | 'setSubscriptions' | 'startRealtime' | 'stopRealtime' | 'syncOnce'>;
67
- export declare function getSyncularClientStatus(client: Pick<SyncularRuntimeClient, 'connectionState' | 'lifecycleState'>): SyncularClientStatus;
68
- export declare class SyncularClientLifecycle {
168
+ export interface SubscribeInput {
169
+ readonly id: string;
170
+ readonly table: string;
171
+ readonly scopes: ScopeMap;
172
+ readonly params?: string;
173
+ }
174
+ /**
175
+ * The live state of a window base (§4.8) — the completeness oracle (I3),
176
+ * as plain serializable data so it crosses the worker/follower boundary
177
+ * unchanged. `units` are the scope values currently windowed-in; a query
178
+ * touching only these is answerable in full locally. Use
179
+ * {@link windowComplete} for the per-value verdict a live query renders
180
+ * "this data may be partial" from.
181
+ */
182
+ export interface WindowState {
183
+ /** Windowed-in units for this base, ordered by value. */
184
+ readonly units: readonly string[];
185
+ }
186
+ /** True iff `unit` is windowed-in for this snapshot (a registry hit, I3). */
187
+ export declare function windowComplete(state: WindowState, unit: string): boolean;
188
+ export declare class SyncClient {
69
189
  #private;
70
- private readonly client;
71
- private readonly options;
72
- constructor(client: LifecycleClient, options?: SyncularClientLifecycleOptions);
190
+ constructor(config: SyncClientConfig);
191
+ /** Acquire leadership, create local tables, resolve the clientId. */
73
192
  start(): Promise<void>;
74
- stop(): Promise<void>;
75
- sync(): Promise<SyncularSyncResult>;
193
+ close(): Promise<void>;
194
+ get clientId(): string;
195
+ /** The underlying database — raw SQL is the local query API (B3). */
196
+ get database(): ClientDatabase;
197
+ query(sql: string, params?: readonly SqlValue[]): SqlRow[];
198
+ /**
199
+ * Subscribe to fine-grained invalidation. The callback fires ONCE per
200
+ * apply batch (never per row, I1) with the `{tables, scopeKeys}` touched
201
+ * this batch (§3.1 vocabulary, I2). Returns an unsubscribe function.
202
+ *
203
+ * Every local mutation flows through the same choke point: `COMMIT`
204
+ * apply, segment apply, the optimistic overlay rebuild, the §3.3 purge,
205
+ * the §7.4.3 schema-bump reset, and local `mutate`. `tables` is the
206
+ * reliable floor; `scopeKeys` refines it wherever the source carried
207
+ * per-row scopes (COMMIT changes) or a scope map (segments/purge).
208
+ */
209
+ onInvalidate(listener: InvalidationListener): () => void;
210
+ /**
211
+ * Stage a blob for attachment (§5.9.7): hash the bytes into the content
212
+ * address, cache them locally, and queue the upload (flushed before the
213
+ * next push — B4). Returns the canonical `BlobRef` **string** to store in
214
+ * a `blob_ref` column of a mutation. The referencing row MUST be written
215
+ * (via `mutate`) after this call so upload-before-push holds (§5.9.3).
216
+ */
217
+ uploadBlob(bytes: Uint8Array, options?: {
218
+ readonly mediaType?: string;
219
+ readonly name?: string;
220
+ }): Promise<BlobRef>;
221
+ /** Serialize a BlobRef to the canonical string a `blob_ref` column holds. */
222
+ blobRefString(ref: BlobRef): string;
223
+ /**
224
+ * Resolve blob bytes for a `blobId` (§5.9.7): a content-addressed cache
225
+ * hit serves without a network fetch (B1); a miss downloads via the blob
226
+ * transport (§5.9.5), verifies the content address, caches, and returns.
227
+ * Accepts a raw `blob_ref` column string or a bare `blobId`.
228
+ */
229
+ fetchBlob(blobIdOrRef: string): Promise<CachedBlob>;
230
+ /** Flush any queued blob uploads (§5.9.7 B4); safe to call standalone. */
231
+ flushBlobUploads(): Promise<void>;
232
+ get conflicts(): readonly ConflictRecord[];
233
+ get rejections(): readonly RejectionRecord[];
234
+ /** Non-undefined once the server declared a schema floor (§1.6). */
235
+ get schemaFloor(): SchemaFloor | undefined;
236
+ /**
237
+ * §7.4.5: true while a schema-bump reset + first re-bootstrap is in
238
+ * flight — the app's "upgrading…" cue. Clears when the first post-reset
239
+ * bootstrap round reaches idle (every subscription past its fresh
240
+ * bootstrap).
241
+ */
242
+ get upgrading(): boolean;
243
+ /**
244
+ * §7.3.5: the current auth-lease state (opaque). Undefined until a
245
+ * `LEASE` frame arrives. `errorCode` is set when a round was rejected
246
+ * with a request-level lease code — syncing on the lease has stopped.
247
+ */
248
+ get leaseState(): LeaseState | undefined;
249
+ /** §7.3.5: remaining lease validity in ms (`expiresAtMs − now`), or
250
+ * `undefined` if no lease is held. Negative once expired. */
251
+ leaseRemainingMs(now?: number): number | undefined;
252
+ /** True when syncing is stopped pending a client upgrade. */
253
+ get stopped(): boolean;
254
+ /** §8: a hello/wake-up asked for a pull that has not run yet. */
255
+ get syncNeeded(): boolean;
256
+ /**
257
+ * §8.6 presence on a scope key: the current peers present there (a map
258
+ * of `actorId clientId` → peer). Empty for a key with no present peers.
259
+ * Ephemeral — reflects only what the socket has delivered.
260
+ */
261
+ presence(scopeKey: string): readonly PresencePeer[];
262
+ /** Every scope key this client currently has presence state for. */
263
+ presenceKeys(): string[];
264
+ /**
265
+ * §8.6: subscribe to presence changes (join/update/leave on any held key).
266
+ * The subscribable twin of the `onPresence` config callback — React's
267
+ * `usePresence` targets this so many components can watch one client.
268
+ * Returns an unsubscribe function.
269
+ */
270
+ onPresence(listener: (scopeKey: string) => void): () => void;
271
+ /**
272
+ * §8.6.2 publish (or clear, `doc: null`) this client's presence document
273
+ * for `scopeKey`. Requires a live socket; the document is ephemeral and
274
+ * lost on disconnect (the server emits leave). Authorization is the
275
+ * connection's registration (§8.6.3) — an unheld key is rejected loudly
276
+ * by the server with `presence.forbidden`.
277
+ */
278
+ setPresence(scopeKey: string, doc: Record<string, unknown> | null): void;
279
+ subscriptions(): SubscriptionRecord[];
280
+ subscription(id: string): SubscriptionRecord | undefined;
281
+ pendingCommits(): OutboxCommit[];
282
+ subscribe(input: SubscribeInput): void;
283
+ unsubscribe(id: string): void;
284
+ /**
285
+ * Set the live window units for a base (§4.8): a value-sharded family of
286
+ * subscriptions, one per unit. Computes the diff against the registry —
287
+ * added units get fresh subscriptions (image-lane bootstrap on the next
288
+ * sync); removed units are unsubscribed and evicted, fused in one local
289
+ * transaction (E1–E4). Idempotent: calling with the same units is a
290
+ * no-op. Re-entry (a unit removed then re-added) cancels any deferred
291
+ * eviction and fresh-bootstraps.
292
+ *
293
+ * The change takes effect on the next `sync()`/socket round — the pull's
294
+ * subscription list (now with the added unit, without the removed one)
295
+ * re-registers realtime at round end (§8.7). No socket cycle needed.
296
+ */
297
+ setWindow(base: WindowBase, units: readonly string[]): Promise<void>;
298
+ /**
299
+ * The completeness oracle (§4.8 I3): which units of a base are windowed-in
300
+ * locally, and a per-unit verdict a live query renders "may be partial"
301
+ * from. A query whose scope footprint includes an un-windowed unit is
302
+ * NOT answerable in full — the host widens or shows partial, never
303
+ * silently-complete.
304
+ */
305
+ windowState(base: WindowBase): WindowState;
306
+ /**
307
+ * Record one atomic local commit (§7.1): appended to the outbox in
308
+ * schema-agnostic form and applied optimistically to the local mirror.
309
+ * Returns the generated `clientCommitId`.
310
+ */
311
+ mutate(mutations: readonly MutationInput[]): string;
312
+ /**
313
+ * One combined push+pull round (§1.5, §7.2). The core owns one loop: a
314
+ * concurrent `sync()` while one is already outstanding is rejected loudly
315
+ * (the app must coalesce its own wake-ups, §8.4) — this check is
316
+ * SYNCHRONOUS so the second caller sees the first still in flight before
317
+ * the op chain would otherwise queue it. The round then runs serialized on
318
+ * the operation chain so it never interleaves with a delta apply or a
319
+ * `setWindow` at an await point.
320
+ */
321
+ sync(): Promise<SyncSummary>;
322
+ /**
323
+ * Pull repeatedly until quiescent: no commits delivered, no bootstrap
324
+ * pages pending, no resets to recover (§4.5 "pull again" SHOULD).
325
+ */
326
+ syncUntilIdle(maxRounds?: number): Promise<SyncSummary>;
327
+ connectRealtime(): Promise<void>;
328
+ disconnectRealtime(): void;
76
329
  }
77
- export {};
78
- //# sourceMappingURL=client.d.ts.map