@korajs/sync 1.0.0-beta.12 → 1.0.0-beta.14

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.
@@ -1,745 +0,0 @@
1
- import * as _korajs_core from '@korajs/core';
2
- import { KoraError, Operation, VersionVector, OperationType, HLCTimestamp, AtomicOp } from '@korajs/core';
3
-
4
- /**
5
- * Encryption types for the Kora.js sync layer.
6
- *
7
- * These types define the configuration and wire format for end-to-end
8
- * encryption of operation data during sync. Only `data` and `previousData`
9
- * fields are encrypted — metadata stays in cleartext for routing and ordering.
10
- */
11
- /**
12
- * Supported encryption algorithms. Currently only AES-256-GCM is supported,
13
- * but the type is extensible for future algorithms.
14
- */
15
- type SyncEncryptionAlgorithm = 'aes-256-gcm';
16
- /**
17
- * Configuration for sync-layer encryption.
18
- *
19
- * When enabled, the sync engine encrypts `data` and `previousData` fields
20
- * of every operation before sending over the wire. The server never sees
21
- * plaintext user data.
22
- *
23
- * @example
24
- * ```typescript
25
- * const app = createApp({
26
- * schema,
27
- * sync: {
28
- * url: 'wss://my-server.com/kora',
29
- * encryption: {
30
- * enabled: true,
31
- * key: 'my-secure-passphrase'
32
- * }
33
- * }
34
- * })
35
- * ```
36
- */
37
- interface SyncEncryptionConfig {
38
- /** Whether encryption is enabled. When false, all other fields are ignored. */
39
- enabled: boolean;
40
- /**
41
- * Passphrase or async key provider function.
42
- *
43
- * - If a string, used as a passphrase for PBKDF2 key derivation.
44
- * - If a function, called to retrieve the passphrase (e.g., from a vault or user prompt).
45
- */
46
- key: string | (() => Promise<string>);
47
- /**
48
- * Encryption algorithm. Defaults to 'aes-256-gcm'.
49
- * Currently only AES-256-GCM is supported.
50
- */
51
- algorithm?: SyncEncryptionAlgorithm;
52
- }
53
- /**
54
- * Encrypted payload structure embedded in operation `data` and `previousData`
55
- * fields when encryption is enabled.
56
- *
57
- * This structure replaces the original field values on the wire. The server
58
- * stores and relays these opaque payloads without being able to read the
59
- * plaintext contents.
60
- */
61
- interface EncryptedPayload {
62
- /** Encryption key version. Supports key rotation: older operations may use older key versions. */
63
- v: number;
64
- /** Base64-encoded initialization vector (12 bytes for AES-GCM). Unique per operation field. */
65
- iv: string;
66
- /** Base64-encoded ciphertext (AES-256-GCM output including authentication tag). */
67
- ct: string;
68
- /** Encryption algorithm identifier. */
69
- alg: SyncEncryptionAlgorithm;
70
- }
71
- /**
72
- * A versioned encryption key with its associated salt for key derivation.
73
- *
74
- * Key versions enable rotation: when the passphrase changes, a new key version
75
- * is created. Operations encrypted with older key versions include their version
76
- * number in the {@link EncryptedPayload}, allowing the decryptor to select the
77
- * correct key.
78
- */
79
- interface VersionedKey {
80
- /** Key version number (monotonically increasing, starting at 1). */
81
- version: number;
82
- /** The derived CryptoKey for AES-256-GCM operations. */
83
- key: CryptoKey;
84
- /** The salt used during PBKDF2 key derivation. Required to re-derive the same key. */
85
- salt: Uint8Array;
86
- }
87
-
88
- /**
89
- * Internal sync engine states. Used for state machine transitions.
90
- */
91
- declare const SYNC_STATES: readonly ["disconnected", "connecting", "handshaking", "syncing", "streaming", "error"];
92
- type SyncState = (typeof SYNC_STATES)[number];
93
- /**
94
- * Developer-facing sync status. Simplified view of the internal state.
95
- */
96
- declare const SYNC_STATUSES: readonly ["connected", "reconnecting", "syncing", "synced", "offline", "clock-error", "error", "schema-mismatch", "auth-required"];
97
- type SyncStatus = (typeof SYNC_STATUSES)[number];
98
- type SyncPhase = 'suspended' | 'offline' | 'connecting' | 'authenticating' | 'handshaking' | 'uploading' | 'receiving' | 'applying' | 'streaming' | 'blocked';
99
- interface ActiveApplyFailure {
100
- operationId: string;
101
- collection: string;
102
- recordId: string;
103
- code: string;
104
- message: string;
105
- retriable: boolean;
106
- firstSeenAt: number;
107
- retryCount: number;
108
- }
109
- /**
110
- * Sync status information exposed to developers.
111
- */
112
- interface SyncStatusInfo {
113
- /** Current developer-facing status */
114
- status: SyncStatus;
115
- phase?: SyncPhase;
116
- reason?: string;
117
- /** True when the engine is actively trying to re-establish a transport session. */
118
- reconnecting: boolean;
119
- /** Number of operations waiting to be sent */
120
- pendingOperations: number;
121
- /** Timestamp of last successful sync (null if never synced) */
122
- lastSyncedAt: number | null;
123
- /** Timestamp of last successful push to the server (null if never pushed) */
124
- lastSuccessfulPush: number | null;
125
- /** Timestamp of last successful pull from the server (null if never pulled) */
126
- lastSuccessfulPull: number | null;
127
- /** Number of merge conflicts encountered during this session */
128
- conflicts: number;
129
- /** serverTime - localTime in ms measured at the last handshake, or null before first connect. Negative = this device's clock is fast. */
130
- clockSkewMs: number | null;
131
- inFlightUploadOperations?: number;
132
- hasInFlightDeliveryBatch?: boolean;
133
- activeViewId?: string;
134
- activeViewComplete?: boolean;
135
- initialSync?: {
136
- complete: boolean;
137
- receivedBatches: number;
138
- totalBatches: number | null;
139
- progress: number | null;
140
- };
141
- deliveryWatermark?: number;
142
- serverFrontier?: number | null;
143
- blockedFailure?: ActiveApplyFailure | null;
144
- }
145
- interface SyncSettlementOptions {
146
- upload?: boolean;
147
- download?: 'active-view' | false;
148
- timeoutMs?: number;
149
- signal?: AbortSignal;
150
- }
151
- type SyncSettlementResult = {
152
- outcome: 'settled';
153
- status: SyncStatusInfo;
154
- } | {
155
- outcome: 'offline';
156
- status: SyncStatusInfo;
157
- } | {
158
- outcome: 'suspended';
159
- reason: string;
160
- status: SyncStatusInfo;
161
- } | {
162
- outcome: 'blocked';
163
- failure: ActiveApplyFailure;
164
- status: SyncStatusInfo;
165
- } | {
166
- outcome: 'timeout';
167
- status: SyncStatusInfo;
168
- } | {
169
- outcome: 'aborted';
170
- status: SyncStatusInfo;
171
- };
172
- /**
173
- * Per-collection sync scope map. Maps collection names to field-value filters.
174
- * Empty filter `{}` means no restriction (all records visible).
175
- * Missing collection means hidden (no records visible for that collection).
176
- */
177
- type SyncScopeMap = Record<string, Record<string, unknown>>;
178
- /**
179
- * Sync configuration provided by the developer.
180
- */
181
- interface SyncConfig {
182
- /** WebSocket or HTTP URL for the sync server */
183
- url: string;
184
- /** Transport type to use. Defaults to 'websocket'. */
185
- transport?: 'websocket' | 'http';
186
- /** Auth provider function. Called before each connection attempt. */
187
- auth?: () => Promise<{
188
- token: string;
189
- }>;
190
- /** Auth readiness gate. A suspended result prevents transport creation and retries. */
191
- authState?: () => Promise<{
192
- state: 'loading' | 'signed-out' | 'anonymous' | 'authenticated';
193
- mayConnectAnonymously?: boolean;
194
- }>;
195
- querySubsets?: {
196
- mode?: 'reactive' | 'static' | 'disabled';
197
- };
198
- /** Client-local behavior for records leaving the accepted downlink scope. Defaults to `retain`. */
199
- scopeExit?: 'retain' | 'retract';
200
- /** Sync scopes per collection. Limits which records sync to this client. */
201
- scopes?: Record<string, (ctx: SyncScopeContext) => Record<string, unknown>>;
202
- /**
203
- * Pre-computed per-collection sync scope map. Sent to the server in the handshake.
204
- * Built automatically by createApp from schema scope declarations + flat scope values.
205
- */
206
- scopeMap?: SyncScopeMap;
207
- /** Number of operations per batch. Defaults to 100. */
208
- batchSize?: number;
209
- /**
210
- * Maximum time to wait for the server to acknowledge an outbound batch before
211
- * treating the connection as stalled. The batch is returned to the durable
212
- * outbound queue before disconnecting so it can be retried on reconnect.
213
- * Defaults to 30000ms. Set to 0 to disable the watchdog.
214
- */
215
- outboundAckTimeoutMs?: number;
216
- /** Initial delay before resending a transiently rejected outbound operation. Defaults to 250ms. */
217
- outboundRetryBaseDelayMs?: number;
218
- /** Maximum transient-operation retry delay. Defaults to 30000ms. */
219
- outboundRetryMaxDelayMs?: number;
220
- /** Initial reconnection delay in ms. Defaults to 1000. */
221
- reconnectInterval?: number;
222
- /** Maximum reconnection delay in ms. Defaults to 30000. */
223
- maxReconnectInterval?: number;
224
- /** Schema version of this client. */
225
- schemaVersion?: number;
226
- /** Start sync automatically when the engine is created. Defaults to false. */
227
- autoConnect?: boolean;
228
- /**
229
- * When true, wait for server ACKs on all outbound handshake delta batches before
230
- * entering streaming. Improves backpressure for large initial syncs.
231
- */
232
- strictHandshake?: boolean;
233
- /** Optional operation transforms for cross-schema-version sync. */
234
- operationTransforms?: _korajs_core.OperationTransform[];
235
- /**
236
- * Richtext snapshot size (bytes) at which the optional Yjs doc channel is used.
237
- * Defaults to 4096.
238
- */
239
- richtextDocChannelThreshold?: number;
240
- /**
241
- * End-to-end encryption configuration.
242
- * When enabled, `data` and `previousData` fields are encrypted before sending
243
- * over the wire. The server never sees plaintext user data.
244
- */
245
- encryption?: SyncEncryptionConfig;
246
- }
247
- /**
248
- * Context passed to sync scope functions.
249
- */
250
- interface SyncScopeContext {
251
- userId?: string;
252
- [key: string]: unknown;
253
- }
254
- /**
255
- * Persists last-acked server version vector and computes unsynced operations from the op log.
256
- */
257
- interface DeltaCursor {
258
- /** ID of the last fully applied operation in the previous delta stream */
259
- lastOperationId: string;
260
- /** Zero-based batch index where the cursor was recorded */
261
- batchIndex: number;
262
- }
263
- interface SyncStatePersistence {
264
- loadLastAckedServerVector(): Promise<VersionVector>;
265
- saveLastAckedServerVector(vector: VersionVector): Promise<void>;
266
- mergeServerVectors(a: VersionVector, b: VersionVector): VersionVector;
267
- countUnsyncedOperations(serverVector: VersionVector): Promise<number>;
268
- getUnsyncedOperations(serverVector: VersionVector): Promise<Operation[]>;
269
- /** Resume position for paginated initial sync (optional). */
270
- loadDeltaCursor?(): Promise<DeltaCursor | null>;
271
- saveDeltaCursor?(cursor: DeltaCursor | null): Promise<void>;
272
- /**
273
- * The delivery watermark for a sync view, keyed by an opaque view signature (the auth
274
- * scope plus active query subscriptions). Persisted per view so a restarted client
275
- * resumes each view's gap-free stream from the right point, and switching views does not
276
- * re-sync a view already synced. The empty-string signature is the default, unfiltered
277
- * view. Optional: a persistence layer that omits these falls back to re-sync on restart.
278
- */
279
- loadDeliveryWatermark?(signature: string): Promise<number>;
280
- saveDeliveryWatermark?(signature: string, watermark: number): Promise<void>;
281
- /** Load every persisted view watermark, keyed by signature. */
282
- loadAllDeliveryWatermarks?(): Promise<Record<string, number>>;
283
- /**
284
- * Delete a persisted view watermark. Used to bound the number of stored view watermarks
285
- * (a cold view is evicted from the client's cache and its persisted row removed). Safe:
286
- * an evicted view back-fills from 0 (deduplicated) when next visited.
287
- */
288
- deleteDeliveryWatermark?(signature: string): Promise<void>;
289
- }
290
- /**
291
- * Interface for persisting the outbound operation queue.
292
- * Operations must survive page refreshes and be sent when connection is re-established.
293
- */
294
- interface QueueStorage {
295
- /** Load all queued operations from persistent storage */
296
- load(): Promise<Operation[]>;
297
- /** Persist an operation to the queue */
298
- enqueue(op: Operation): Promise<void>;
299
- /** Remove acknowledged operations by their IDs */
300
- dequeue(ids: string[]): Promise<void>;
301
- /** Return number of operations in storage */
302
- count(): Promise<number>;
303
- }
304
- /**
305
- * A record of one outbound operation the server permanently refused. Preserved so
306
- * a rejected op is explainable and reconcilable rather than silently lost.
307
- */
308
- interface RejectedOperation {
309
- /** Content-addressed id of the rejected operation. */
310
- operationId: string;
311
- /** Collection the operation targeted. */
312
- collection: string;
313
- /** Record the operation targeted. */
314
- recordId: string;
315
- /** Stable, machine-readable reason code from the server. */
316
- code: string;
317
- /** Human-readable explanation. */
318
- message: string;
319
- /** Whether resubmitting the identical operation may later succeed. */
320
- retriable: boolean;
321
- /** Wall-clock time (ms since epoch) the rejection was recorded. Display only. */
322
- rejectedAt: number;
323
- }
324
- /**
325
- * Interface for durably persisting operations the server rejected, so the record
326
- * survives a page refresh. Keyed by operation id (idempotent on re-rejection).
327
- */
328
- interface RejectedOperationStorage {
329
- /** Record (or overwrite) a rejected operation. */
330
- record(rejected: RejectedOperation): Promise<void>;
331
- /** List all recorded rejected operations. */
332
- list(): Promise<RejectedOperation[]>;
333
- /** Remove rejected operations by their operation ids (after the app reconciles them). */
334
- remove(operationIds: string[]): Promise<void>;
335
- }
336
- /**
337
- * In-memory {@link RejectedOperationStorage}. The default when no durable store is
338
- * provided; suitable for tests and ephemeral sessions.
339
- */
340
- declare class MemoryRejectedOperationStorage implements RejectedOperationStorage {
341
- private readonly rejected;
342
- record(rejected: RejectedOperation): Promise<void>;
343
- list(): Promise<RejectedOperation[]>;
344
- remove(operationIds: string[]): Promise<void>;
345
- }
346
- /**
347
- * Thrown when an operation violates sync scope constraints.
348
- * This can happen when:
349
- * - A client tries to push an operation outside its configured scope
350
- * - The server rejects an operation because it falls outside the client's scope
351
- */
352
- declare class ScopeViolationError extends KoraError {
353
- readonly operationId: string;
354
- readonly collection: string;
355
- readonly scope: Record<string, unknown>;
356
- constructor(operationId: string, collection: string, scope: Record<string, unknown>, message?: string);
357
- }
358
- /**
359
- * Thrown when a sync scope configuration is invalid.
360
- */
361
- declare class InvalidScopeError extends KoraError {
362
- constructor(message: string, context?: Record<string, unknown>);
363
- }
364
-
365
- /**
366
- * A live query filter that narrows which operations sync for a collection.
367
- */
368
- interface SyncQuerySubset {
369
- collection: string;
370
- where: Record<string, unknown>;
371
- }
372
- /**
373
- * Returns true when an operation matches at least one active query subset
374
- * for its collection. Collections without query subsets pass through.
375
- */
376
- declare function operationMatchesQuerySubsets(op: Operation, subsets: SyncQuerySubset[] | undefined, fullRecord?: Record<string, unknown> | null): boolean;
377
- /** Canonicalize equality subsets and remove subsets already covered by a broader one. */
378
- declare function dedupeQuerySubsets(subsets: SyncQuerySubset[]): SyncQuerySubset[];
379
- /** True when every record selected by `narrow` is also selected by `broad`. */
380
- declare function querySubsetContains(broad: SyncQuerySubset[], narrow: SyncQuerySubset[]): boolean;
381
-
382
- type WireFormat = 'json' | 'protobuf';
383
- /**
384
- * Wire-format operation. Plain object (no Map) for JSON serialization.
385
- * Maps 1:1 with Operation, but uses Record instead of Map for version vectors.
386
- */
387
- interface SerializedOperation {
388
- id: string;
389
- nodeId: string;
390
- type: OperationType;
391
- collection: string;
392
- recordId: string;
393
- data: Record<string, unknown> | null;
394
- previousData: Record<string, unknown> | null;
395
- timestamp: HLCTimestamp;
396
- sequenceNumber: number;
397
- causalDeps: string[];
398
- schemaVersion: number;
399
- /** Atomic operation intents, present only when atomic ops were used. */
400
- atomicOps?: Record<string, AtomicOp>;
401
- /** Groups this operation with others in an atomic transaction. */
402
- transactionId?: string;
403
- /** Human-readable name for the mutation group. For DevTools display. */
404
- mutationName?: string;
405
- }
406
- /**
407
- * Handshake message sent by client to initiate sync.
408
- */
409
- interface HandshakeMessage {
410
- type: 'handshake';
411
- messageId: string;
412
- nodeId: string;
413
- /** Version vector as plain object (nodeId -> sequence number) */
414
- versionVector: Record<string, number>;
415
- schemaVersion: number;
416
- authToken?: string;
417
- supportedWireFormats?: WireFormat[];
418
- /** Per-collection sync scope filters. Limits which records are synced to this client. */
419
- syncScope?: Record<string, Record<string, unknown>>;
420
- /** Base64url-encoded delta cursor for resuming paginated initial sync */
421
- deltaCursor?: string;
422
- /** Live query filters that further narrow synced data within auth/schema scope */
423
- syncQueries?: SyncQuerySubset[];
424
- /**
425
- * The client's persisted delivery watermark: the highest server delivery sequence
426
- * up to which it has contiguously applied every in-scope operation. Omitted (or 0)
427
- * on first sync. When present, the server resumes the gap-free server->client
428
- * stream from just after it, superseding the version-vector delta and the resume
429
- * cursor for the server->client direction. Optional, so old servers ignore it.
430
- */
431
- lastDeliverySequence?: number;
432
- /** Opt in to client-local removal when records leave the accepted downlink view. */
433
- scopeExitPolicy?: 'retain' | 'retract';
434
- }
435
- /**
436
- * Server response to a handshake.
437
- */
438
- interface HandshakeResponseMessage {
439
- type: 'handshake-response';
440
- messageId: string;
441
- nodeId: string;
442
- versionVector: Record<string, number>;
443
- schemaVersion: number;
444
- accepted: boolean;
445
- rejectReason?: string;
446
- /** Inclusive minimum schema version the server accepts (present when rejected for schema mismatch). */
447
- supportedSchemaMin?: number;
448
- /** Inclusive maximum schema version the server accepts (present when rejected for schema mismatch). */
449
- supportedSchemaMax?: number;
450
- selectedWireFormat?: WireFormat;
451
- /** The server-accepted per-collection sync scope. Confirms what data will be synced. */
452
- acceptedScope?: Record<string, Record<string, unknown>>;
453
- /** Server-authoritative scope for records delivered to this client. */
454
- acceptedDownlinkScopes?: Record<string, Record<string, unknown>>;
455
- /** Server-authoritative scope for operations uploaded by this client. */
456
- acceptedUplinkScopes?: Record<string, Record<string, unknown>>;
457
- /** Server wall-clock time (ms since epoch) at response creation. Lets clients measure their own clock skew. */
458
- serverTime?: number;
459
- /**
460
- * Whether the server persists blob bytes centrally. When true, a client
461
- * automatically uploads the bytes behind its `blob` fields so they remain
462
- * available to other devices even after the authoring device goes offline.
463
- */
464
- blobStorageEnabled?: boolean;
465
- /**
466
- * The server's highest delivery sequence at handshake time. A client whose
467
- * persisted delivery watermark exceeds this (the server's operation log was rolled
468
- * back, for example by a backup restore) resets its watermark to 0 so it resyncs
469
- * from the beginning instead of sitting above a frontier that no longer exists.
470
- */
471
- serverMaxDeliverySequence?: number;
472
- }
473
- /**
474
- * Batch of operations sent during delta exchange or streaming.
475
- */
476
- interface OperationBatchMessage {
477
- type: 'operation-batch';
478
- messageId: string;
479
- operations: SerializedOperation[];
480
- /** Client-local authorization removals; these are never domain delete operations. */
481
- retractions?: ScopeRetraction[];
482
- /** True if this is the last batch in the delta exchange phase */
483
- isFinal: boolean;
484
- /** Index of this batch (0-based) for ordering */
485
- batchIndex: number;
486
- /** Base64url-encoded cursor marking the last operation in this batch */
487
- cursor?: string;
488
- /** Total batches in this delta exchange (for progress reporting) */
489
- totalBatches?: number;
490
- /**
491
- * Delivery-watermark chaining (server->client). `baseDeliverySequence` is the
492
- * delivery sequence the client must already hold for this batch to apply in order
493
- * (it equals the previous batch's `maxDeliverySequence`, or the client's reported
494
- * watermark for the first batch after a handshake). `maxDeliverySequence` is the
495
- * highest delivery sequence carried by this batch. The client applies the batch
496
- * only when its watermark equals `baseDeliverySequence`, then advances the
497
- * watermark to `maxDeliverySequence`. A dropped batch breaks the chain and stalls
498
- * the watermark, so the reconnect resend recovers it. Present only on batches the
499
- * server sends as part of the delivery stream; absent on legacy version-vector
500
- * batches.
501
- */
502
- baseDeliverySequence?: number;
503
- maxDeliverySequence?: number;
504
- }
505
- interface ScopeRetraction {
506
- collection: string;
507
- recordId: string;
508
- }
509
- /**
510
- * Acknowledgment of a received message.
511
- */
512
- interface AcknowledgmentMessage {
513
- type: 'acknowledgment';
514
- messageId: string;
515
- acknowledgedMessageId: string;
516
- lastSequenceNumber: number;
517
- /**
518
- * The acknowledged batch's `maxDeliverySequence`, echoed so the server can advance
519
- * this client's durable delivery watermark. Present only when acking a delivery-
520
- * stream batch. `lastSequenceNumber` stays for existing per-node behavior.
521
- */
522
- deliverySequence?: number;
523
- }
524
- /**
525
- * Error message from the server or client.
526
- */
527
- interface ErrorMessage {
528
- type: 'error';
529
- messageId: string;
530
- code: string;
531
- message: string;
532
- retriable: boolean;
533
- }
534
- /**
535
- * Server-to-client rejection of ONE specific operation the client submitted.
536
- *
537
- * Unlike {@link ErrorMessage}, which is connection-level, this is tied to an
538
- * `operationId` so the client can divert exactly that operation out of its
539
- * pending outbound queue and into a durable rejected-operations store — kept and
540
- * explainable rather than silently retried forever or lost on the batch ack. The
541
- * operation never entered the server's authoritative log, so no other replica
542
- * ever sees it; the submitter is told so its optimistic local copy can be
543
- * reconciled (rolled back or resubmitted) by the app.
544
- */
545
- interface OperationRejectedMessage {
546
- type: 'operation-rejected';
547
- messageId: string;
548
- /** Content-addressed id of the rejected operation. */
549
- operationId: string;
550
- /** Collection the rejected operation targeted (for app routing / display). */
551
- collection: string;
552
- /** Record the rejected operation targeted. */
553
- recordId: string;
554
- /** Stable, machine-readable reason code. */
555
- code: string;
556
- /** Human-readable explanation. */
557
- message: string;
558
- /** Whether resubmitting the identical operation may later succeed. */
559
- retriable: boolean;
560
- }
561
- /**
562
- * Awareness state for a single client (cursor position, user info).
563
- * Wire-format representation for JSON transport.
564
- * Ephemeral -- not persisted, only shared with connected peers.
565
- */
566
- interface AwarenessStateWire {
567
- user: {
568
- name: string;
569
- color: string;
570
- avatar?: string;
571
- };
572
- cursor?: {
573
- collection: string;
574
- recordId: string;
575
- field: string;
576
- anchor: number;
577
- head: number;
578
- };
579
- }
580
- /**
581
- * Awareness update message. Carries ephemeral presence data (cursors, user info).
582
- * Processed separately from operation sync -- never persisted.
583
- */
584
- interface AwarenessUpdateMessage {
585
- type: 'awareness-update';
586
- messageId: string;
587
- /** Client ID of the sender */
588
- clientId: number;
589
- /** Map of clientId -> state (null means removal) */
590
- states: Record<string, AwarenessStateWire | null>;
591
- }
592
- /**
593
- * Incremental Yjs update for a richtext field. Ephemeral side channel for large documents.
594
- * JSON transport only; not persisted on the server operation log.
595
- */
596
- interface YjsDocUpdateMessage {
597
- type: 'yjs-doc-update';
598
- messageId: string;
599
- collection: string;
600
- recordId: string;
601
- field: string;
602
- /** Base64-encoded Yjs update binary */
603
- update: string;
604
- }
605
- /**
606
- * Request for a single blob chunk by content hash. Ephemeral side channel for
607
- * out-of-band blob transfer over the sync connection; never persisted in the
608
- * operation log (durable state is the BlobRef inside a record's fields).
609
- *
610
- * Possession of a chunk hash is itself the capability to request it: hashes are
611
- * only learned from BlobRefs inside records the peer already received through
612
- * its (scope-filtered) sync, and SHA-256 preimage resistance makes guessing a
613
- * hash infeasible. The server therefore relays chunk requests among peers
614
- * without a separate ACL.
615
- */
616
- interface BlobChunkRequestMessage {
617
- type: 'blob-chunk-request';
618
- messageId: string;
619
- /** Correlates the response to this request. */
620
- requestId: string;
621
- /** SHA-256 hash of the requested chunk. */
622
- hash: string;
623
- }
624
- /**
625
- * Response to a {@link BlobChunkRequestMessage}. Carries the chunk bytes, or
626
- * null when the responder does not hold that hash.
627
- */
628
- interface BlobChunkResponseMessage {
629
- type: 'blob-chunk-response';
630
- messageId: string;
631
- /** Echoes the request's `requestId` for correlation. */
632
- requestId: string;
633
- /** Base64-encoded chunk bytes, or null when the responder does not hold the hash. */
634
- bytes: string | null;
635
- }
636
- /**
637
- * Unsolicited upload of a blob chunk (or a blob manifest) from a client to the
638
- * server, so the server can persist it centrally and serve it to other devices
639
- * even after the authoring device disconnects. Content-addressed: the receiver
640
- * verifies the bytes hash to `hash`.
641
- */
642
- interface BlobChunkPushMessage {
643
- type: 'blob-chunk-push';
644
- messageId: string;
645
- /** SHA-256 hash of the pushed bytes (a chunk hash or a manifest hash). */
646
- hash: string;
647
- /** Base64-encoded bytes. */
648
- bytes: string;
649
- }
650
- /**
651
- * Union of all sync protocol messages.
652
- */
653
- type SyncMessage = HandshakeMessage | HandshakeResponseMessage | OperationBatchMessage | AcknowledgmentMessage | ErrorMessage | OperationRejectedMessage | AwarenessUpdateMessage | YjsDocUpdateMessage | BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage;
654
- /**
655
- * Check if an unknown value is a valid SyncMessage.
656
- */
657
- declare function isSyncMessage(value: unknown): value is SyncMessage;
658
- /**
659
- * Check if a value is a HandshakeMessage.
660
- */
661
- declare function isHandshakeMessage(value: unknown): value is HandshakeMessage;
662
- /**
663
- * Check if a value is a HandshakeResponseMessage.
664
- */
665
- declare function isHandshakeResponseMessage(value: unknown): value is HandshakeResponseMessage;
666
- /**
667
- * Check if a value is an OperationBatchMessage.
668
- */
669
- declare function isOperationBatchMessage(value: unknown): value is OperationBatchMessage;
670
- /**
671
- * Check if a value is an AcknowledgmentMessage.
672
- */
673
- declare function isAcknowledgmentMessage(value: unknown): value is AcknowledgmentMessage;
674
- /**
675
- * Check if a value is an ErrorMessage.
676
- */
677
- declare function isErrorMessage(value: unknown): value is ErrorMessage;
678
- /**
679
- * Check if a value is an OperationRejectedMessage.
680
- */
681
- declare function isOperationRejectedMessage(value: unknown): value is OperationRejectedMessage;
682
- /**
683
- * Check if a value is an AwarenessUpdateMessage.
684
- */
685
- declare function isAwarenessUpdateMessage(value: unknown): value is AwarenessUpdateMessage;
686
- /**
687
- * Check if a value is a YjsDocUpdateMessage.
688
- */
689
- declare function isYjsDocUpdateMessage(value: unknown): value is YjsDocUpdateMessage;
690
- /**
691
- * Check if a value is a BlobChunkRequestMessage.
692
- */
693
- declare function isBlobChunkRequestMessage(value: unknown): value is BlobChunkRequestMessage;
694
- /**
695
- * Check if a value is a BlobChunkResponseMessage.
696
- */
697
- declare function isBlobChunkResponseMessage(value: unknown): value is BlobChunkResponseMessage;
698
- /**
699
- * Check if a value is a BlobChunkPushMessage.
700
- */
701
- declare function isBlobChunkPushMessage(value: unknown): value is BlobChunkPushMessage;
702
-
703
- /**
704
- * Options for transport connection.
705
- */
706
- interface TransportOptions {
707
- /** Authentication token sent during connection */
708
- authToken?: string;
709
- /** Additional headers (for HTTP-based transports) */
710
- headers?: Record<string, string>;
711
- }
712
- /**
713
- * Transport-level error handler.
714
- */
715
- type TransportErrorHandler = (error: Error) => void;
716
- /**
717
- * Transport-level close handler.
718
- */
719
- type TransportCloseHandler = (reason: string) => void;
720
- /**
721
- * Transport-level message handler.
722
- */
723
- type TransportMessageHandler = (message: SyncMessage) => void;
724
- /**
725
- * Abstract transport interface for sync communication.
726
- * Implementations can use WebSocket, HTTP, Bluetooth, or any other transport.
727
- */
728
- interface SyncTransport {
729
- /** Connect to the sync server at the given URL */
730
- connect(url: string, options?: TransportOptions): Promise<void>;
731
- /** Disconnect from the server */
732
- disconnect(): Promise<void>;
733
- /** Send a sync message */
734
- send(message: SyncMessage): void;
735
- /** Register a handler for incoming messages */
736
- onMessage(handler: TransportMessageHandler): void;
737
- /** Register a handler for connection close events */
738
- onClose(handler: TransportCloseHandler): void;
739
- /** Register a handler for transport errors */
740
- onError(handler: TransportErrorHandler): void;
741
- /** Returns true if the transport is currently connected */
742
- isConnected(): boolean;
743
- }
744
-
745
- export { isHandshakeResponseMessage as $, type AcknowledgmentMessage as A, type BlobChunkRequestMessage as B, type SyncScopeContext as C, type DeltaCursor as D, type EncryptedPayload as E, type SyncSettlementOptions as F, type SyncSettlementResult as G, type HandshakeMessage as H, InvalidScopeError as I, type SyncStatus as J, dedupeQuerySubsets as K, isAcknowledgmentMessage as L, MemoryRejectedOperationStorage as M, isAwarenessUpdateMessage as N, type OperationBatchMessage as O, isBlobChunkPushMessage as P, type QueueStorage as Q, type RejectedOperationStorage as R, type SyncScopeMap as S, type TransportOptions as T, isBlobChunkRequestMessage as U, type VersionedKey as V, type WireFormat as W, isBlobChunkResponseMessage as X, type YjsDocUpdateMessage as Y, isErrorMessage as Z, isHandshakeMessage as _, type SyncMessage as a, isOperationBatchMessage as a0, isOperationRejectedMessage as a1, isSyncMessage as a2, isYjsDocUpdateMessage as a3, operationMatchesQuerySubsets as a4, querySubsetContains as a5, type SerializedOperation as b, type SyncTransport as c, type TransportMessageHandler as d, type TransportCloseHandler as e, type TransportErrorHandler as f, type BlobChunkResponseMessage as g, type BlobChunkPushMessage as h, type SyncEncryptionConfig as i, type SyncState as j, type SyncStatusInfo as k, type SyncConfig as l, type SyncStatePersistence as m, type SyncQuerySubset as n, type RejectedOperation as o, type ActiveApplyFailure as p, type AwarenessStateWire as q, type AwarenessUpdateMessage as r, type ErrorMessage as s, type HandshakeResponseMessage as t, type OperationRejectedMessage as u, SYNC_STATES as v, SYNC_STATUSES as w, ScopeViolationError as x, type SyncEncryptionAlgorithm as y, type SyncPhase as z };