@korajs/sync 1.0.0-beta.0 → 1.0.0-beta.10
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.
- package/dist/index.cjs +837 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +228 -3
- package/dist/index.d.ts +228 -3
- package/dist/index.js +816 -55
- package/dist/index.js.map +1 -1
- package/dist/internal.d.cts +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/{transport-BMesMB6s.d.cts → transport-B2GQerqJ.d.cts} +200 -3
- package/dist/{transport-BMesMB6s.d.ts → transport-B2GQerqJ.d.ts} +200 -3
- package/package.json +4 -3
package/dist/internal.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as SyncTransport, T as TransportOptions, a as SyncMessage, d as TransportMessageHandler, e as TransportCloseHandler, f as TransportErrorHandler, Q as QueueStorage } from './transport-
|
|
1
|
+
import { c as SyncTransport, T as TransportOptions, a as SyncMessage, d as TransportMessageHandler, e as TransportCloseHandler, f as TransportErrorHandler, Q as QueueStorage } from './transport-B2GQerqJ.cjs';
|
|
2
2
|
import { Operation } from '@korajs/core';
|
|
3
3
|
|
|
4
4
|
/**
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as SyncTransport, T as TransportOptions, a as SyncMessage, d as TransportMessageHandler, e as TransportCloseHandler, f as TransportErrorHandler, Q as QueueStorage } from './transport-
|
|
1
|
+
import { c as SyncTransport, T as TransportOptions, a as SyncMessage, d as TransportMessageHandler, e as TransportCloseHandler, f as TransportErrorHandler, Q as QueueStorage } from './transport-B2GQerqJ.js';
|
|
2
2
|
import { Operation } from '@korajs/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -93,7 +93,7 @@ type SyncState = (typeof SYNC_STATES)[number];
|
|
|
93
93
|
/**
|
|
94
94
|
* Developer-facing sync status. Simplified view of the internal state.
|
|
95
95
|
*/
|
|
96
|
-
declare const SYNC_STATUSES: readonly ["connected", "syncing", "synced", "offline", "clock-error", "error", "schema-mismatch"];
|
|
96
|
+
declare const SYNC_STATUSES: readonly ["connected", "reconnecting", "syncing", "synced", "offline", "clock-error", "error", "schema-mismatch"];
|
|
97
97
|
type SyncStatus = (typeof SYNC_STATUSES)[number];
|
|
98
98
|
/**
|
|
99
99
|
* Sync status information exposed to developers.
|
|
@@ -101,6 +101,8 @@ type SyncStatus = (typeof SYNC_STATUSES)[number];
|
|
|
101
101
|
interface SyncStatusInfo {
|
|
102
102
|
/** Current developer-facing status */
|
|
103
103
|
status: SyncStatus;
|
|
104
|
+
/** True when the engine is actively trying to re-establish a transport session. */
|
|
105
|
+
reconnecting: boolean;
|
|
104
106
|
/** Number of operations waiting to be sent */
|
|
105
107
|
pendingOperations: number;
|
|
106
108
|
/** Timestamp of last successful sync (null if never synced) */
|
|
@@ -141,6 +143,13 @@ interface SyncConfig {
|
|
|
141
143
|
scopeMap?: SyncScopeMap;
|
|
142
144
|
/** Number of operations per batch. Defaults to 100. */
|
|
143
145
|
batchSize?: number;
|
|
146
|
+
/**
|
|
147
|
+
* Maximum time to wait for the server to acknowledge an outbound batch before
|
|
148
|
+
* treating the connection as stalled. The batch is returned to the durable
|
|
149
|
+
* outbound queue before disconnecting so it can be retried on reconnect.
|
|
150
|
+
* Defaults to 30000ms. Set to 0 to disable the watchdog.
|
|
151
|
+
*/
|
|
152
|
+
outboundAckTimeoutMs?: number;
|
|
144
153
|
/** Initial reconnection delay in ms. Defaults to 1000. */
|
|
145
154
|
reconnectInterval?: number;
|
|
146
155
|
/** Maximum reconnection delay in ms. Defaults to 30000. */
|
|
@@ -193,6 +202,23 @@ interface SyncStatePersistence {
|
|
|
193
202
|
/** Resume position for paginated initial sync (optional). */
|
|
194
203
|
loadDeltaCursor?(): Promise<DeltaCursor | null>;
|
|
195
204
|
saveDeltaCursor?(cursor: DeltaCursor | null): Promise<void>;
|
|
205
|
+
/**
|
|
206
|
+
* The delivery watermark for a sync view, keyed by an opaque view signature (the auth
|
|
207
|
+
* scope plus active query subscriptions). Persisted per view so a restarted client
|
|
208
|
+
* resumes each view's gap-free stream from the right point, and switching views does not
|
|
209
|
+
* re-sync a view already synced. The empty-string signature is the default, unfiltered
|
|
210
|
+
* view. Optional: a persistence layer that omits these falls back to re-sync on restart.
|
|
211
|
+
*/
|
|
212
|
+
loadDeliveryWatermark?(signature: string): Promise<number>;
|
|
213
|
+
saveDeliveryWatermark?(signature: string, watermark: number): Promise<void>;
|
|
214
|
+
/** Load every persisted view watermark, keyed by signature. */
|
|
215
|
+
loadAllDeliveryWatermarks?(): Promise<Record<string, number>>;
|
|
216
|
+
/**
|
|
217
|
+
* Delete a persisted view watermark. Used to bound the number of stored view watermarks
|
|
218
|
+
* (a cold view is evicted from the client's cache and its persisted row removed). Safe:
|
|
219
|
+
* an evicted view back-fills from 0 (deduplicated) when next visited.
|
|
220
|
+
*/
|
|
221
|
+
deleteDeliveryWatermark?(signature: string): Promise<void>;
|
|
196
222
|
}
|
|
197
223
|
/**
|
|
198
224
|
* Interface for persisting the outbound operation queue.
|
|
@@ -208,6 +234,48 @@ interface QueueStorage {
|
|
|
208
234
|
/** Return number of operations in storage */
|
|
209
235
|
count(): Promise<number>;
|
|
210
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* A record of one outbound operation the server permanently refused. Preserved so
|
|
239
|
+
* a rejected op is explainable and reconcilable rather than silently lost.
|
|
240
|
+
*/
|
|
241
|
+
interface RejectedOperation {
|
|
242
|
+
/** Content-addressed id of the rejected operation. */
|
|
243
|
+
operationId: string;
|
|
244
|
+
/** Collection the operation targeted. */
|
|
245
|
+
collection: string;
|
|
246
|
+
/** Record the operation targeted. */
|
|
247
|
+
recordId: string;
|
|
248
|
+
/** Stable, machine-readable reason code from the server. */
|
|
249
|
+
code: string;
|
|
250
|
+
/** Human-readable explanation. */
|
|
251
|
+
message: string;
|
|
252
|
+
/** Whether resubmitting the identical operation may later succeed. */
|
|
253
|
+
retriable: boolean;
|
|
254
|
+
/** Wall-clock time (ms since epoch) the rejection was recorded. Display only. */
|
|
255
|
+
rejectedAt: number;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Interface for durably persisting operations the server rejected, so the record
|
|
259
|
+
* survives a page refresh. Keyed by operation id (idempotent on re-rejection).
|
|
260
|
+
*/
|
|
261
|
+
interface RejectedOperationStorage {
|
|
262
|
+
/** Record (or overwrite) a rejected operation. */
|
|
263
|
+
record(rejected: RejectedOperation): Promise<void>;
|
|
264
|
+
/** List all recorded rejected operations. */
|
|
265
|
+
list(): Promise<RejectedOperation[]>;
|
|
266
|
+
/** Remove rejected operations by their operation ids (after the app reconciles them). */
|
|
267
|
+
remove(operationIds: string[]): Promise<void>;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* In-memory {@link RejectedOperationStorage}. The default when no durable store is
|
|
271
|
+
* provided; suitable for tests and ephemeral sessions.
|
|
272
|
+
*/
|
|
273
|
+
declare class MemoryRejectedOperationStorage implements RejectedOperationStorage {
|
|
274
|
+
private readonly rejected;
|
|
275
|
+
record(rejected: RejectedOperation): Promise<void>;
|
|
276
|
+
list(): Promise<RejectedOperation[]>;
|
|
277
|
+
remove(operationIds: string[]): Promise<void>;
|
|
278
|
+
}
|
|
211
279
|
/**
|
|
212
280
|
* Thrown when an operation violates sync scope constraints.
|
|
213
281
|
* This can happen when:
|
|
@@ -286,6 +354,14 @@ interface HandshakeMessage {
|
|
|
286
354
|
deltaCursor?: string;
|
|
287
355
|
/** Live query filters that further narrow synced data within auth/schema scope */
|
|
288
356
|
syncQueries?: SyncQuerySubset[];
|
|
357
|
+
/**
|
|
358
|
+
* The client's persisted delivery watermark: the highest server delivery sequence
|
|
359
|
+
* up to which it has contiguously applied every in-scope operation. Omitted (or 0)
|
|
360
|
+
* on first sync. When present, the server resumes the gap-free server->client
|
|
361
|
+
* stream from just after it, superseding the version-vector delta and the resume
|
|
362
|
+
* cursor for the server->client direction. Optional, so old servers ignore it.
|
|
363
|
+
*/
|
|
364
|
+
lastDeliverySequence?: number;
|
|
289
365
|
}
|
|
290
366
|
/**
|
|
291
367
|
* Server response to a handshake.
|
|
@@ -307,6 +383,19 @@ interface HandshakeResponseMessage {
|
|
|
307
383
|
acceptedScope?: Record<string, Record<string, unknown>>;
|
|
308
384
|
/** Server wall-clock time (ms since epoch) at response creation. Lets clients measure their own clock skew. */
|
|
309
385
|
serverTime?: number;
|
|
386
|
+
/**
|
|
387
|
+
* Whether the server persists blob bytes centrally. When true, a client
|
|
388
|
+
* automatically uploads the bytes behind its `blob` fields so they remain
|
|
389
|
+
* available to other devices even after the authoring device goes offline.
|
|
390
|
+
*/
|
|
391
|
+
blobStorageEnabled?: boolean;
|
|
392
|
+
/**
|
|
393
|
+
* The server's highest delivery sequence at handshake time. A client whose
|
|
394
|
+
* persisted delivery watermark exceeds this (the server's operation log was rolled
|
|
395
|
+
* back, for example by a backup restore) resets its watermark to 0 so it resyncs
|
|
396
|
+
* from the beginning instead of sitting above a frontier that no longer exists.
|
|
397
|
+
*/
|
|
398
|
+
serverMaxDeliverySequence?: number;
|
|
310
399
|
}
|
|
311
400
|
/**
|
|
312
401
|
* Batch of operations sent during delta exchange or streaming.
|
|
@@ -323,6 +412,20 @@ interface OperationBatchMessage {
|
|
|
323
412
|
cursor?: string;
|
|
324
413
|
/** Total batches in this delta exchange (for progress reporting) */
|
|
325
414
|
totalBatches?: number;
|
|
415
|
+
/**
|
|
416
|
+
* Delivery-watermark chaining (server->client). `baseDeliverySequence` is the
|
|
417
|
+
* delivery sequence the client must already hold for this batch to apply in order
|
|
418
|
+
* (it equals the previous batch's `maxDeliverySequence`, or the client's reported
|
|
419
|
+
* watermark for the first batch after a handshake). `maxDeliverySequence` is the
|
|
420
|
+
* highest delivery sequence carried by this batch. The client applies the batch
|
|
421
|
+
* only when its watermark equals `baseDeliverySequence`, then advances the
|
|
422
|
+
* watermark to `maxDeliverySequence`. A dropped batch breaks the chain and stalls
|
|
423
|
+
* the watermark, so the reconnect resend recovers it. Present only on batches the
|
|
424
|
+
* server sends as part of the delivery stream; absent on legacy version-vector
|
|
425
|
+
* batches.
|
|
426
|
+
*/
|
|
427
|
+
baseDeliverySequence?: number;
|
|
428
|
+
maxDeliverySequence?: number;
|
|
326
429
|
}
|
|
327
430
|
/**
|
|
328
431
|
* Acknowledgment of a received message.
|
|
@@ -332,6 +435,12 @@ interface AcknowledgmentMessage {
|
|
|
332
435
|
messageId: string;
|
|
333
436
|
acknowledgedMessageId: string;
|
|
334
437
|
lastSequenceNumber: number;
|
|
438
|
+
/**
|
|
439
|
+
* The acknowledged batch's `maxDeliverySequence`, echoed so the server can advance
|
|
440
|
+
* this client's durable delivery watermark. Present only when acking a delivery-
|
|
441
|
+
* stream batch. `lastSequenceNumber` stays for existing per-node behavior.
|
|
442
|
+
*/
|
|
443
|
+
deliverySequence?: number;
|
|
335
444
|
}
|
|
336
445
|
/**
|
|
337
446
|
* Error message from the server or client.
|
|
@@ -343,6 +452,33 @@ interface ErrorMessage {
|
|
|
343
452
|
message: string;
|
|
344
453
|
retriable: boolean;
|
|
345
454
|
}
|
|
455
|
+
/**
|
|
456
|
+
* Server-to-client rejection of ONE specific operation the client submitted.
|
|
457
|
+
*
|
|
458
|
+
* Unlike {@link ErrorMessage}, which is connection-level, this is tied to an
|
|
459
|
+
* `operationId` so the client can divert exactly that operation out of its
|
|
460
|
+
* pending outbound queue and into a durable rejected-operations store — kept and
|
|
461
|
+
* explainable rather than silently retried forever or lost on the batch ack. The
|
|
462
|
+
* operation never entered the server's authoritative log, so no other replica
|
|
463
|
+
* ever sees it; the submitter is told so its optimistic local copy can be
|
|
464
|
+
* reconciled (rolled back or resubmitted) by the app.
|
|
465
|
+
*/
|
|
466
|
+
interface OperationRejectedMessage {
|
|
467
|
+
type: 'operation-rejected';
|
|
468
|
+
messageId: string;
|
|
469
|
+
/** Content-addressed id of the rejected operation. */
|
|
470
|
+
operationId: string;
|
|
471
|
+
/** Collection the rejected operation targeted (for app routing / display). */
|
|
472
|
+
collection: string;
|
|
473
|
+
/** Record the rejected operation targeted. */
|
|
474
|
+
recordId: string;
|
|
475
|
+
/** Stable, machine-readable reason code. */
|
|
476
|
+
code: string;
|
|
477
|
+
/** Human-readable explanation. */
|
|
478
|
+
message: string;
|
|
479
|
+
/** Whether resubmitting the identical operation may later succeed. */
|
|
480
|
+
retriable: boolean;
|
|
481
|
+
}
|
|
346
482
|
/**
|
|
347
483
|
* Awareness state for a single client (cursor position, user info).
|
|
348
484
|
* Wire-format representation for JSON transport.
|
|
@@ -387,10 +523,55 @@ interface YjsDocUpdateMessage {
|
|
|
387
523
|
/** Base64-encoded Yjs update binary */
|
|
388
524
|
update: string;
|
|
389
525
|
}
|
|
526
|
+
/**
|
|
527
|
+
* Request for a single blob chunk by content hash. Ephemeral side channel for
|
|
528
|
+
* out-of-band blob transfer over the sync connection; never persisted in the
|
|
529
|
+
* operation log (durable state is the BlobRef inside a record's fields).
|
|
530
|
+
*
|
|
531
|
+
* Possession of a chunk hash is itself the capability to request it: hashes are
|
|
532
|
+
* only learned from BlobRefs inside records the peer already received through
|
|
533
|
+
* its (scope-filtered) sync, and SHA-256 preimage resistance makes guessing a
|
|
534
|
+
* hash infeasible. The server therefore relays chunk requests among peers
|
|
535
|
+
* without a separate ACL.
|
|
536
|
+
*/
|
|
537
|
+
interface BlobChunkRequestMessage {
|
|
538
|
+
type: 'blob-chunk-request';
|
|
539
|
+
messageId: string;
|
|
540
|
+
/** Correlates the response to this request. */
|
|
541
|
+
requestId: string;
|
|
542
|
+
/** SHA-256 hash of the requested chunk. */
|
|
543
|
+
hash: string;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Response to a {@link BlobChunkRequestMessage}. Carries the chunk bytes, or
|
|
547
|
+
* null when the responder does not hold that hash.
|
|
548
|
+
*/
|
|
549
|
+
interface BlobChunkResponseMessage {
|
|
550
|
+
type: 'blob-chunk-response';
|
|
551
|
+
messageId: string;
|
|
552
|
+
/** Echoes the request's `requestId` for correlation. */
|
|
553
|
+
requestId: string;
|
|
554
|
+
/** Base64-encoded chunk bytes, or null when the responder does not hold the hash. */
|
|
555
|
+
bytes: string | null;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Unsolicited upload of a blob chunk (or a blob manifest) from a client to the
|
|
559
|
+
* server, so the server can persist it centrally and serve it to other devices
|
|
560
|
+
* even after the authoring device disconnects. Content-addressed: the receiver
|
|
561
|
+
* verifies the bytes hash to `hash`.
|
|
562
|
+
*/
|
|
563
|
+
interface BlobChunkPushMessage {
|
|
564
|
+
type: 'blob-chunk-push';
|
|
565
|
+
messageId: string;
|
|
566
|
+
/** SHA-256 hash of the pushed bytes (a chunk hash or a manifest hash). */
|
|
567
|
+
hash: string;
|
|
568
|
+
/** Base64-encoded bytes. */
|
|
569
|
+
bytes: string;
|
|
570
|
+
}
|
|
390
571
|
/**
|
|
391
572
|
* Union of all sync protocol messages.
|
|
392
573
|
*/
|
|
393
|
-
type SyncMessage = HandshakeMessage | HandshakeResponseMessage | OperationBatchMessage | AcknowledgmentMessage | ErrorMessage | AwarenessUpdateMessage | YjsDocUpdateMessage;
|
|
574
|
+
type SyncMessage = HandshakeMessage | HandshakeResponseMessage | OperationBatchMessage | AcknowledgmentMessage | ErrorMessage | OperationRejectedMessage | AwarenessUpdateMessage | YjsDocUpdateMessage | BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage;
|
|
394
575
|
/**
|
|
395
576
|
* Check if an unknown value is a valid SyncMessage.
|
|
396
577
|
*/
|
|
@@ -415,6 +596,10 @@ declare function isAcknowledgmentMessage(value: unknown): value is Acknowledgmen
|
|
|
415
596
|
* Check if a value is an ErrorMessage.
|
|
416
597
|
*/
|
|
417
598
|
declare function isErrorMessage(value: unknown): value is ErrorMessage;
|
|
599
|
+
/**
|
|
600
|
+
* Check if a value is an OperationRejectedMessage.
|
|
601
|
+
*/
|
|
602
|
+
declare function isOperationRejectedMessage(value: unknown): value is OperationRejectedMessage;
|
|
418
603
|
/**
|
|
419
604
|
* Check if a value is an AwarenessUpdateMessage.
|
|
420
605
|
*/
|
|
@@ -423,6 +608,18 @@ declare function isAwarenessUpdateMessage(value: unknown): value is AwarenessUpd
|
|
|
423
608
|
* Check if a value is a YjsDocUpdateMessage.
|
|
424
609
|
*/
|
|
425
610
|
declare function isYjsDocUpdateMessage(value: unknown): value is YjsDocUpdateMessage;
|
|
611
|
+
/**
|
|
612
|
+
* Check if a value is a BlobChunkRequestMessage.
|
|
613
|
+
*/
|
|
614
|
+
declare function isBlobChunkRequestMessage(value: unknown): value is BlobChunkRequestMessage;
|
|
615
|
+
/**
|
|
616
|
+
* Check if a value is a BlobChunkResponseMessage.
|
|
617
|
+
*/
|
|
618
|
+
declare function isBlobChunkResponseMessage(value: unknown): value is BlobChunkResponseMessage;
|
|
619
|
+
/**
|
|
620
|
+
* Check if a value is a BlobChunkPushMessage.
|
|
621
|
+
*/
|
|
622
|
+
declare function isBlobChunkPushMessage(value: unknown): value is BlobChunkPushMessage;
|
|
426
623
|
|
|
427
624
|
/**
|
|
428
625
|
* Options for transport connection.
|
|
@@ -466,4 +663,4 @@ interface SyncTransport {
|
|
|
466
663
|
isConnected(): boolean;
|
|
467
664
|
}
|
|
468
665
|
|
|
469
|
-
export { type AcknowledgmentMessage as A,
|
|
666
|
+
export { isYjsDocUpdateMessage as $, type AcknowledgmentMessage as A, type BlobChunkRequestMessage as B, dedupeQuerySubsets as C, type DeltaCursor as D, type EncryptedPayload as E, isAcknowledgmentMessage as F, isAwarenessUpdateMessage as G, type HandshakeMessage as H, InvalidScopeError as I, isBlobChunkPushMessage as J, isBlobChunkRequestMessage as K, isBlobChunkResponseMessage as L, MemoryRejectedOperationStorage as M, isErrorMessage as N, type OperationBatchMessage as O, isHandshakeMessage as P, type QueueStorage as Q, type RejectedOperationStorage as R, type SyncScopeMap as S, type TransportOptions as T, isHandshakeResponseMessage as U, type VersionedKey as V, type WireFormat as W, isOperationBatchMessage as X, type YjsDocUpdateMessage as Y, isOperationRejectedMessage as Z, isSyncMessage as _, type SyncMessage as a, operationMatchesQuerySubsets as a0, 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 AwarenessStateWire as p, type AwarenessUpdateMessage as q, type ErrorMessage as r, type HandshakeResponseMessage as s, type OperationRejectedMessage as t, SYNC_STATES as u, SYNC_STATUSES as v, ScopeViolationError as w, type SyncEncryptionAlgorithm as x, type SyncScopeContext as y, type SyncStatus as z };
|
|
@@ -93,7 +93,7 @@ type SyncState = (typeof SYNC_STATES)[number];
|
|
|
93
93
|
/**
|
|
94
94
|
* Developer-facing sync status. Simplified view of the internal state.
|
|
95
95
|
*/
|
|
96
|
-
declare const SYNC_STATUSES: readonly ["connected", "syncing", "synced", "offline", "clock-error", "error", "schema-mismatch"];
|
|
96
|
+
declare const SYNC_STATUSES: readonly ["connected", "reconnecting", "syncing", "synced", "offline", "clock-error", "error", "schema-mismatch"];
|
|
97
97
|
type SyncStatus = (typeof SYNC_STATUSES)[number];
|
|
98
98
|
/**
|
|
99
99
|
* Sync status information exposed to developers.
|
|
@@ -101,6 +101,8 @@ type SyncStatus = (typeof SYNC_STATUSES)[number];
|
|
|
101
101
|
interface SyncStatusInfo {
|
|
102
102
|
/** Current developer-facing status */
|
|
103
103
|
status: SyncStatus;
|
|
104
|
+
/** True when the engine is actively trying to re-establish a transport session. */
|
|
105
|
+
reconnecting: boolean;
|
|
104
106
|
/** Number of operations waiting to be sent */
|
|
105
107
|
pendingOperations: number;
|
|
106
108
|
/** Timestamp of last successful sync (null if never synced) */
|
|
@@ -141,6 +143,13 @@ interface SyncConfig {
|
|
|
141
143
|
scopeMap?: SyncScopeMap;
|
|
142
144
|
/** Number of operations per batch. Defaults to 100. */
|
|
143
145
|
batchSize?: number;
|
|
146
|
+
/**
|
|
147
|
+
* Maximum time to wait for the server to acknowledge an outbound batch before
|
|
148
|
+
* treating the connection as stalled. The batch is returned to the durable
|
|
149
|
+
* outbound queue before disconnecting so it can be retried on reconnect.
|
|
150
|
+
* Defaults to 30000ms. Set to 0 to disable the watchdog.
|
|
151
|
+
*/
|
|
152
|
+
outboundAckTimeoutMs?: number;
|
|
144
153
|
/** Initial reconnection delay in ms. Defaults to 1000. */
|
|
145
154
|
reconnectInterval?: number;
|
|
146
155
|
/** Maximum reconnection delay in ms. Defaults to 30000. */
|
|
@@ -193,6 +202,23 @@ interface SyncStatePersistence {
|
|
|
193
202
|
/** Resume position for paginated initial sync (optional). */
|
|
194
203
|
loadDeltaCursor?(): Promise<DeltaCursor | null>;
|
|
195
204
|
saveDeltaCursor?(cursor: DeltaCursor | null): Promise<void>;
|
|
205
|
+
/**
|
|
206
|
+
* The delivery watermark for a sync view, keyed by an opaque view signature (the auth
|
|
207
|
+
* scope plus active query subscriptions). Persisted per view so a restarted client
|
|
208
|
+
* resumes each view's gap-free stream from the right point, and switching views does not
|
|
209
|
+
* re-sync a view already synced. The empty-string signature is the default, unfiltered
|
|
210
|
+
* view. Optional: a persistence layer that omits these falls back to re-sync on restart.
|
|
211
|
+
*/
|
|
212
|
+
loadDeliveryWatermark?(signature: string): Promise<number>;
|
|
213
|
+
saveDeliveryWatermark?(signature: string, watermark: number): Promise<void>;
|
|
214
|
+
/** Load every persisted view watermark, keyed by signature. */
|
|
215
|
+
loadAllDeliveryWatermarks?(): Promise<Record<string, number>>;
|
|
216
|
+
/**
|
|
217
|
+
* Delete a persisted view watermark. Used to bound the number of stored view watermarks
|
|
218
|
+
* (a cold view is evicted from the client's cache and its persisted row removed). Safe:
|
|
219
|
+
* an evicted view back-fills from 0 (deduplicated) when next visited.
|
|
220
|
+
*/
|
|
221
|
+
deleteDeliveryWatermark?(signature: string): Promise<void>;
|
|
196
222
|
}
|
|
197
223
|
/**
|
|
198
224
|
* Interface for persisting the outbound operation queue.
|
|
@@ -208,6 +234,48 @@ interface QueueStorage {
|
|
|
208
234
|
/** Return number of operations in storage */
|
|
209
235
|
count(): Promise<number>;
|
|
210
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* A record of one outbound operation the server permanently refused. Preserved so
|
|
239
|
+
* a rejected op is explainable and reconcilable rather than silently lost.
|
|
240
|
+
*/
|
|
241
|
+
interface RejectedOperation {
|
|
242
|
+
/** Content-addressed id of the rejected operation. */
|
|
243
|
+
operationId: string;
|
|
244
|
+
/** Collection the operation targeted. */
|
|
245
|
+
collection: string;
|
|
246
|
+
/** Record the operation targeted. */
|
|
247
|
+
recordId: string;
|
|
248
|
+
/** Stable, machine-readable reason code from the server. */
|
|
249
|
+
code: string;
|
|
250
|
+
/** Human-readable explanation. */
|
|
251
|
+
message: string;
|
|
252
|
+
/** Whether resubmitting the identical operation may later succeed. */
|
|
253
|
+
retriable: boolean;
|
|
254
|
+
/** Wall-clock time (ms since epoch) the rejection was recorded. Display only. */
|
|
255
|
+
rejectedAt: number;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Interface for durably persisting operations the server rejected, so the record
|
|
259
|
+
* survives a page refresh. Keyed by operation id (idempotent on re-rejection).
|
|
260
|
+
*/
|
|
261
|
+
interface RejectedOperationStorage {
|
|
262
|
+
/** Record (or overwrite) a rejected operation. */
|
|
263
|
+
record(rejected: RejectedOperation): Promise<void>;
|
|
264
|
+
/** List all recorded rejected operations. */
|
|
265
|
+
list(): Promise<RejectedOperation[]>;
|
|
266
|
+
/** Remove rejected operations by their operation ids (after the app reconciles them). */
|
|
267
|
+
remove(operationIds: string[]): Promise<void>;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* In-memory {@link RejectedOperationStorage}. The default when no durable store is
|
|
271
|
+
* provided; suitable for tests and ephemeral sessions.
|
|
272
|
+
*/
|
|
273
|
+
declare class MemoryRejectedOperationStorage implements RejectedOperationStorage {
|
|
274
|
+
private readonly rejected;
|
|
275
|
+
record(rejected: RejectedOperation): Promise<void>;
|
|
276
|
+
list(): Promise<RejectedOperation[]>;
|
|
277
|
+
remove(operationIds: string[]): Promise<void>;
|
|
278
|
+
}
|
|
211
279
|
/**
|
|
212
280
|
* Thrown when an operation violates sync scope constraints.
|
|
213
281
|
* This can happen when:
|
|
@@ -286,6 +354,14 @@ interface HandshakeMessage {
|
|
|
286
354
|
deltaCursor?: string;
|
|
287
355
|
/** Live query filters that further narrow synced data within auth/schema scope */
|
|
288
356
|
syncQueries?: SyncQuerySubset[];
|
|
357
|
+
/**
|
|
358
|
+
* The client's persisted delivery watermark: the highest server delivery sequence
|
|
359
|
+
* up to which it has contiguously applied every in-scope operation. Omitted (or 0)
|
|
360
|
+
* on first sync. When present, the server resumes the gap-free server->client
|
|
361
|
+
* stream from just after it, superseding the version-vector delta and the resume
|
|
362
|
+
* cursor for the server->client direction. Optional, so old servers ignore it.
|
|
363
|
+
*/
|
|
364
|
+
lastDeliverySequence?: number;
|
|
289
365
|
}
|
|
290
366
|
/**
|
|
291
367
|
* Server response to a handshake.
|
|
@@ -307,6 +383,19 @@ interface HandshakeResponseMessage {
|
|
|
307
383
|
acceptedScope?: Record<string, Record<string, unknown>>;
|
|
308
384
|
/** Server wall-clock time (ms since epoch) at response creation. Lets clients measure their own clock skew. */
|
|
309
385
|
serverTime?: number;
|
|
386
|
+
/**
|
|
387
|
+
* Whether the server persists blob bytes centrally. When true, a client
|
|
388
|
+
* automatically uploads the bytes behind its `blob` fields so they remain
|
|
389
|
+
* available to other devices even after the authoring device goes offline.
|
|
390
|
+
*/
|
|
391
|
+
blobStorageEnabled?: boolean;
|
|
392
|
+
/**
|
|
393
|
+
* The server's highest delivery sequence at handshake time. A client whose
|
|
394
|
+
* persisted delivery watermark exceeds this (the server's operation log was rolled
|
|
395
|
+
* back, for example by a backup restore) resets its watermark to 0 so it resyncs
|
|
396
|
+
* from the beginning instead of sitting above a frontier that no longer exists.
|
|
397
|
+
*/
|
|
398
|
+
serverMaxDeliverySequence?: number;
|
|
310
399
|
}
|
|
311
400
|
/**
|
|
312
401
|
* Batch of operations sent during delta exchange or streaming.
|
|
@@ -323,6 +412,20 @@ interface OperationBatchMessage {
|
|
|
323
412
|
cursor?: string;
|
|
324
413
|
/** Total batches in this delta exchange (for progress reporting) */
|
|
325
414
|
totalBatches?: number;
|
|
415
|
+
/**
|
|
416
|
+
* Delivery-watermark chaining (server->client). `baseDeliverySequence` is the
|
|
417
|
+
* delivery sequence the client must already hold for this batch to apply in order
|
|
418
|
+
* (it equals the previous batch's `maxDeliverySequence`, or the client's reported
|
|
419
|
+
* watermark for the first batch after a handshake). `maxDeliverySequence` is the
|
|
420
|
+
* highest delivery sequence carried by this batch. The client applies the batch
|
|
421
|
+
* only when its watermark equals `baseDeliverySequence`, then advances the
|
|
422
|
+
* watermark to `maxDeliverySequence`. A dropped batch breaks the chain and stalls
|
|
423
|
+
* the watermark, so the reconnect resend recovers it. Present only on batches the
|
|
424
|
+
* server sends as part of the delivery stream; absent on legacy version-vector
|
|
425
|
+
* batches.
|
|
426
|
+
*/
|
|
427
|
+
baseDeliverySequence?: number;
|
|
428
|
+
maxDeliverySequence?: number;
|
|
326
429
|
}
|
|
327
430
|
/**
|
|
328
431
|
* Acknowledgment of a received message.
|
|
@@ -332,6 +435,12 @@ interface AcknowledgmentMessage {
|
|
|
332
435
|
messageId: string;
|
|
333
436
|
acknowledgedMessageId: string;
|
|
334
437
|
lastSequenceNumber: number;
|
|
438
|
+
/**
|
|
439
|
+
* The acknowledged batch's `maxDeliverySequence`, echoed so the server can advance
|
|
440
|
+
* this client's durable delivery watermark. Present only when acking a delivery-
|
|
441
|
+
* stream batch. `lastSequenceNumber` stays for existing per-node behavior.
|
|
442
|
+
*/
|
|
443
|
+
deliverySequence?: number;
|
|
335
444
|
}
|
|
336
445
|
/**
|
|
337
446
|
* Error message from the server or client.
|
|
@@ -343,6 +452,33 @@ interface ErrorMessage {
|
|
|
343
452
|
message: string;
|
|
344
453
|
retriable: boolean;
|
|
345
454
|
}
|
|
455
|
+
/**
|
|
456
|
+
* Server-to-client rejection of ONE specific operation the client submitted.
|
|
457
|
+
*
|
|
458
|
+
* Unlike {@link ErrorMessage}, which is connection-level, this is tied to an
|
|
459
|
+
* `operationId` so the client can divert exactly that operation out of its
|
|
460
|
+
* pending outbound queue and into a durable rejected-operations store — kept and
|
|
461
|
+
* explainable rather than silently retried forever or lost on the batch ack. The
|
|
462
|
+
* operation never entered the server's authoritative log, so no other replica
|
|
463
|
+
* ever sees it; the submitter is told so its optimistic local copy can be
|
|
464
|
+
* reconciled (rolled back or resubmitted) by the app.
|
|
465
|
+
*/
|
|
466
|
+
interface OperationRejectedMessage {
|
|
467
|
+
type: 'operation-rejected';
|
|
468
|
+
messageId: string;
|
|
469
|
+
/** Content-addressed id of the rejected operation. */
|
|
470
|
+
operationId: string;
|
|
471
|
+
/** Collection the rejected operation targeted (for app routing / display). */
|
|
472
|
+
collection: string;
|
|
473
|
+
/** Record the rejected operation targeted. */
|
|
474
|
+
recordId: string;
|
|
475
|
+
/** Stable, machine-readable reason code. */
|
|
476
|
+
code: string;
|
|
477
|
+
/** Human-readable explanation. */
|
|
478
|
+
message: string;
|
|
479
|
+
/** Whether resubmitting the identical operation may later succeed. */
|
|
480
|
+
retriable: boolean;
|
|
481
|
+
}
|
|
346
482
|
/**
|
|
347
483
|
* Awareness state for a single client (cursor position, user info).
|
|
348
484
|
* Wire-format representation for JSON transport.
|
|
@@ -387,10 +523,55 @@ interface YjsDocUpdateMessage {
|
|
|
387
523
|
/** Base64-encoded Yjs update binary */
|
|
388
524
|
update: string;
|
|
389
525
|
}
|
|
526
|
+
/**
|
|
527
|
+
* Request for a single blob chunk by content hash. Ephemeral side channel for
|
|
528
|
+
* out-of-band blob transfer over the sync connection; never persisted in the
|
|
529
|
+
* operation log (durable state is the BlobRef inside a record's fields).
|
|
530
|
+
*
|
|
531
|
+
* Possession of a chunk hash is itself the capability to request it: hashes are
|
|
532
|
+
* only learned from BlobRefs inside records the peer already received through
|
|
533
|
+
* its (scope-filtered) sync, and SHA-256 preimage resistance makes guessing a
|
|
534
|
+
* hash infeasible. The server therefore relays chunk requests among peers
|
|
535
|
+
* without a separate ACL.
|
|
536
|
+
*/
|
|
537
|
+
interface BlobChunkRequestMessage {
|
|
538
|
+
type: 'blob-chunk-request';
|
|
539
|
+
messageId: string;
|
|
540
|
+
/** Correlates the response to this request. */
|
|
541
|
+
requestId: string;
|
|
542
|
+
/** SHA-256 hash of the requested chunk. */
|
|
543
|
+
hash: string;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Response to a {@link BlobChunkRequestMessage}. Carries the chunk bytes, or
|
|
547
|
+
* null when the responder does not hold that hash.
|
|
548
|
+
*/
|
|
549
|
+
interface BlobChunkResponseMessage {
|
|
550
|
+
type: 'blob-chunk-response';
|
|
551
|
+
messageId: string;
|
|
552
|
+
/** Echoes the request's `requestId` for correlation. */
|
|
553
|
+
requestId: string;
|
|
554
|
+
/** Base64-encoded chunk bytes, or null when the responder does not hold the hash. */
|
|
555
|
+
bytes: string | null;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Unsolicited upload of a blob chunk (or a blob manifest) from a client to the
|
|
559
|
+
* server, so the server can persist it centrally and serve it to other devices
|
|
560
|
+
* even after the authoring device disconnects. Content-addressed: the receiver
|
|
561
|
+
* verifies the bytes hash to `hash`.
|
|
562
|
+
*/
|
|
563
|
+
interface BlobChunkPushMessage {
|
|
564
|
+
type: 'blob-chunk-push';
|
|
565
|
+
messageId: string;
|
|
566
|
+
/** SHA-256 hash of the pushed bytes (a chunk hash or a manifest hash). */
|
|
567
|
+
hash: string;
|
|
568
|
+
/** Base64-encoded bytes. */
|
|
569
|
+
bytes: string;
|
|
570
|
+
}
|
|
390
571
|
/**
|
|
391
572
|
* Union of all sync protocol messages.
|
|
392
573
|
*/
|
|
393
|
-
type SyncMessage = HandshakeMessage | HandshakeResponseMessage | OperationBatchMessage | AcknowledgmentMessage | ErrorMessage | AwarenessUpdateMessage | YjsDocUpdateMessage;
|
|
574
|
+
type SyncMessage = HandshakeMessage | HandshakeResponseMessage | OperationBatchMessage | AcknowledgmentMessage | ErrorMessage | OperationRejectedMessage | AwarenessUpdateMessage | YjsDocUpdateMessage | BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage;
|
|
394
575
|
/**
|
|
395
576
|
* Check if an unknown value is a valid SyncMessage.
|
|
396
577
|
*/
|
|
@@ -415,6 +596,10 @@ declare function isAcknowledgmentMessage(value: unknown): value is Acknowledgmen
|
|
|
415
596
|
* Check if a value is an ErrorMessage.
|
|
416
597
|
*/
|
|
417
598
|
declare function isErrorMessage(value: unknown): value is ErrorMessage;
|
|
599
|
+
/**
|
|
600
|
+
* Check if a value is an OperationRejectedMessage.
|
|
601
|
+
*/
|
|
602
|
+
declare function isOperationRejectedMessage(value: unknown): value is OperationRejectedMessage;
|
|
418
603
|
/**
|
|
419
604
|
* Check if a value is an AwarenessUpdateMessage.
|
|
420
605
|
*/
|
|
@@ -423,6 +608,18 @@ declare function isAwarenessUpdateMessage(value: unknown): value is AwarenessUpd
|
|
|
423
608
|
* Check if a value is a YjsDocUpdateMessage.
|
|
424
609
|
*/
|
|
425
610
|
declare function isYjsDocUpdateMessage(value: unknown): value is YjsDocUpdateMessage;
|
|
611
|
+
/**
|
|
612
|
+
* Check if a value is a BlobChunkRequestMessage.
|
|
613
|
+
*/
|
|
614
|
+
declare function isBlobChunkRequestMessage(value: unknown): value is BlobChunkRequestMessage;
|
|
615
|
+
/**
|
|
616
|
+
* Check if a value is a BlobChunkResponseMessage.
|
|
617
|
+
*/
|
|
618
|
+
declare function isBlobChunkResponseMessage(value: unknown): value is BlobChunkResponseMessage;
|
|
619
|
+
/**
|
|
620
|
+
* Check if a value is a BlobChunkPushMessage.
|
|
621
|
+
*/
|
|
622
|
+
declare function isBlobChunkPushMessage(value: unknown): value is BlobChunkPushMessage;
|
|
426
623
|
|
|
427
624
|
/**
|
|
428
625
|
* Options for transport connection.
|
|
@@ -466,4 +663,4 @@ interface SyncTransport {
|
|
|
466
663
|
isConnected(): boolean;
|
|
467
664
|
}
|
|
468
665
|
|
|
469
|
-
export { type AcknowledgmentMessage as A,
|
|
666
|
+
export { isYjsDocUpdateMessage as $, type AcknowledgmentMessage as A, type BlobChunkRequestMessage as B, dedupeQuerySubsets as C, type DeltaCursor as D, type EncryptedPayload as E, isAcknowledgmentMessage as F, isAwarenessUpdateMessage as G, type HandshakeMessage as H, InvalidScopeError as I, isBlobChunkPushMessage as J, isBlobChunkRequestMessage as K, isBlobChunkResponseMessage as L, MemoryRejectedOperationStorage as M, isErrorMessage as N, type OperationBatchMessage as O, isHandshakeMessage as P, type QueueStorage as Q, type RejectedOperationStorage as R, type SyncScopeMap as S, type TransportOptions as T, isHandshakeResponseMessage as U, type VersionedKey as V, type WireFormat as W, isOperationBatchMessage as X, type YjsDocUpdateMessage as Y, isOperationRejectedMessage as Z, isSyncMessage as _, type SyncMessage as a, operationMatchesQuerySubsets as a0, 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 AwarenessStateWire as p, type AwarenessUpdateMessage as q, type ErrorMessage as r, type HandshakeResponseMessage as s, type OperationRejectedMessage as t, SYNC_STATES as u, SYNC_STATUSES as v, ScopeViolationError as w, type SyncEncryptionAlgorithm as x, type SyncScopeContext as y, type SyncStatus as z };
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@korajs/sync",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.10",
|
|
4
4
|
"description": "Sync protocol and transports for Kora.js with delta sync, causal ordering, and automatic reconnection",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
|
+
"./package.json": "./package.json",
|
|
10
11
|
".": {
|
|
11
12
|
"import": {
|
|
12
13
|
"types": "./dist/index.d.ts",
|
|
@@ -33,8 +34,8 @@
|
|
|
33
34
|
],
|
|
34
35
|
"dependencies": {
|
|
35
36
|
"protobufjs": "^8.0.1",
|
|
36
|
-
"@korajs/
|
|
37
|
-
"@korajs/
|
|
37
|
+
"@korajs/core": "1.0.0-beta.10",
|
|
38
|
+
"@korajs/merge": "1.0.0-beta.10"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"@fast-check/vitest": "0.2.0",
|