@loro-dev/streams-crdt 0.6.0 → 0.7.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.
- package/README.md +305 -1
- package/dist/flock.d.ts +2 -2
- package/dist/flock.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/loro.d.ts +2 -2
- package/dist/loro.js +2 -2
- package/dist/{stream-id-Do4h2lOD.d.ts → stream-id-CS7SzuLW.d.ts} +163 -6
- package/dist/{stream-id-DBym_2Mj.js → stream-id-CbVwCS3g.js} +915 -280
- package/dist/stream-id-CbVwCS3g.js.map +1 -0
- package/package.json +5 -3
- package/dist/stream-id-DBym_2Mj.js.map +0 -1
|
@@ -109,6 +109,8 @@ interface ReconnectConfig {
|
|
|
109
109
|
readonly maxAttempts: number;
|
|
110
110
|
readonly connectTimeoutMs: number;
|
|
111
111
|
readonly pollTimeoutMs: number;
|
|
112
|
+
/** SSE inactivity timeout. Aborts and reconnects if no event arrives within this period. */
|
|
113
|
+
readonly liveInactivityTimeoutMs?: number;
|
|
112
114
|
}
|
|
113
115
|
//#endregion
|
|
114
116
|
//#region src/types.d.ts
|
|
@@ -211,7 +213,21 @@ type TransportError = {
|
|
|
211
213
|
readonly code: "unknown";
|
|
212
214
|
readonly retryable: false;
|
|
213
215
|
readonly message: string;
|
|
216
|
+
} | {
|
|
217
|
+
readonly code: "payload_protection_error";
|
|
218
|
+
readonly retryable: false;
|
|
219
|
+
readonly reason: "plaintext_forbidden" | "missing_read_key" | "decrypt_failed" | "invalid_envelope" | "wrong_payload_kind" | "encrypt_failed";
|
|
220
|
+
readonly keyId?: string;
|
|
221
|
+
readonly message: string;
|
|
214
222
|
};
|
|
223
|
+
interface WriteOnlyAppendResult<TVersion extends JsonObject> {
|
|
224
|
+
/** Whether this call found and appended local CRDT updates. */
|
|
225
|
+
readonly appended: boolean;
|
|
226
|
+
/** Server tail offset returned by the append request. Omitted when `appended` is false. */
|
|
227
|
+
readonly nextOffset?: string;
|
|
228
|
+
/** Local CRDT version exported by this runtime's append-only path. */
|
|
229
|
+
readonly localVersion: TVersion;
|
|
230
|
+
}
|
|
215
231
|
/**
|
|
216
232
|
* Result payload returned by `createStream()`.
|
|
217
233
|
*/
|
|
@@ -230,6 +246,13 @@ interface TransportDeleteStreamSuccess {
|
|
|
230
246
|
interface TransportSyncSuccess<TVersion extends JsonObject = JsonObject> {
|
|
231
247
|
readonly cursor: RemoteCursor<TVersion>;
|
|
232
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Result payload returned by the internal snapshot upload test hook.
|
|
251
|
+
*/
|
|
252
|
+
interface TransportSnapshotUploadSuccess {
|
|
253
|
+
readonly snapshotOffset: string;
|
|
254
|
+
readonly snapshotByteLength: number;
|
|
255
|
+
}
|
|
233
256
|
/**
|
|
234
257
|
* Optional parameters for a `join()` call.
|
|
235
258
|
*
|
|
@@ -280,6 +303,54 @@ interface SnapshotCodec {
|
|
|
280
303
|
readonly compress: SnapshotTransformHook;
|
|
281
304
|
readonly decompress: SnapshotTransformHook;
|
|
282
305
|
}
|
|
306
|
+
type PayloadProtectionReadPolicy = "encrypted-only" | "allow-plaintext";
|
|
307
|
+
type PayloadProtectionWritePolicy = "encrypt" | "plaintext";
|
|
308
|
+
type PayloadProtectionScope = string | {
|
|
309
|
+
readonly bucketId: string;
|
|
310
|
+
readonly streamId: string;
|
|
311
|
+
};
|
|
312
|
+
interface PayloadProtectionKey {
|
|
313
|
+
readonly id: string;
|
|
314
|
+
readonly key: CryptoKey | Uint8Array;
|
|
315
|
+
}
|
|
316
|
+
type PayloadProtectionKeyProvider = () => MaybePromise<readonly PayloadProtectionKey[]>;
|
|
317
|
+
interface PayloadProtectionEncryptionOptions {
|
|
318
|
+
/**
|
|
319
|
+
* Stable encryption context authenticated with every encrypted payload.
|
|
320
|
+
*
|
|
321
|
+
* Prefer bucket/stream identity or an application-stable string. Do not use
|
|
322
|
+
* a full gateway URL unless old encrypted bytes should become unreadable
|
|
323
|
+
* after host/proxy migration.
|
|
324
|
+
*/
|
|
325
|
+
readonly scope: PayloadProtectionScope;
|
|
326
|
+
/** Key used for new encrypted writes. Required when writePolicy is "encrypt". */
|
|
327
|
+
readonly writeKey?: PayloadProtectionKey;
|
|
328
|
+
/**
|
|
329
|
+
* Keys accepted for remote encrypted reads.
|
|
330
|
+
*
|
|
331
|
+
* When omitted, `writeKey` is also used as the only read key.
|
|
332
|
+
*/
|
|
333
|
+
readonly readKeys?: readonly PayloadProtectionKey[] | PayloadProtectionKeyProvider;
|
|
334
|
+
}
|
|
335
|
+
interface PayloadProtectionOptions {
|
|
336
|
+
/**
|
|
337
|
+
* Remote plaintext policy. Defaults to "encrypted-only" when
|
|
338
|
+
* E2EE is provided.
|
|
339
|
+
*/
|
|
340
|
+
readonly readPolicy?: PayloadProtectionReadPolicy;
|
|
341
|
+
/**
|
|
342
|
+
* Local write policy. Defaults to "encrypt" when E2EE is provided.
|
|
343
|
+
*/
|
|
344
|
+
readonly writePolicy?: PayloadProtectionWritePolicy;
|
|
345
|
+
readonly encryption?: PayloadProtectionEncryptionOptions;
|
|
346
|
+
}
|
|
347
|
+
type E2eeReadPolicy = PayloadProtectionReadPolicy;
|
|
348
|
+
type E2eeWritePolicy = PayloadProtectionWritePolicy;
|
|
349
|
+
type E2eeScope = PayloadProtectionScope;
|
|
350
|
+
type E2eeKey = PayloadProtectionKey;
|
|
351
|
+
type E2eeKeyProvider = PayloadProtectionKeyProvider;
|
|
352
|
+
type E2eeEncryptionOptions = PayloadProtectionEncryptionOptions;
|
|
353
|
+
type E2eeOptions = PayloadProtectionOptions;
|
|
283
354
|
/**
|
|
284
355
|
* Active live subscription returned by `join()`.
|
|
285
356
|
*
|
|
@@ -293,6 +364,14 @@ interface TransportSubscription {
|
|
|
293
364
|
readonly connected: boolean;
|
|
294
365
|
/** Current room status. */
|
|
295
366
|
readonly status: TransportRoomStatus;
|
|
367
|
+
/**
|
|
368
|
+
* The last transport error observed, if any.
|
|
369
|
+
*
|
|
370
|
+
* Updated when the room transitions to `"error"` or `"disconnected"`.
|
|
371
|
+
* Cleared on successful recovery. Callers can inspect this to decide
|
|
372
|
+
* whether to retry, surface auth failure, or mark the room as terminal.
|
|
373
|
+
*/
|
|
374
|
+
readonly lastError?: TransportError;
|
|
296
375
|
/** Registers a status listener. Returns an unsubscribe function. */
|
|
297
376
|
readonly onStatusChange: (listener: (status: "connecting" | TransportRoomStatus) => void) => () => void;
|
|
298
377
|
/**
|
|
@@ -307,6 +386,9 @@ interface TransportSubscription {
|
|
|
307
386
|
*
|
|
308
387
|
* The promise rejects if the flush encounters a non-recoverable error or
|
|
309
388
|
* all subscriptions are closed before all pending changes are confirmed.
|
|
389
|
+
*
|
|
390
|
+
* If the initial join sync has failed and not yet recovered, the promise
|
|
391
|
+
* rejects immediately with a structured error indicating the failure.
|
|
310
392
|
*/
|
|
311
393
|
readonly waitUntilSynced: () => Promise<void>;
|
|
312
394
|
}
|
|
@@ -435,6 +517,16 @@ interface StreamsCrdtOptions<TVersion extends JsonObject> {
|
|
|
435
517
|
* the server.
|
|
436
518
|
*/
|
|
437
519
|
readonly snapshotCodec?: SnapshotCodec;
|
|
520
|
+
/**
|
|
521
|
+
* E2EE and mixed encrypted/plaintext payload behavior.
|
|
522
|
+
*/
|
|
523
|
+
readonly e2ee?: E2eeOptions;
|
|
524
|
+
/**
|
|
525
|
+
* Deprecated alias for `e2ee`.
|
|
526
|
+
*
|
|
527
|
+
* Prefer `e2ee`. Passing both `e2ee` and `payloadProtection` is an error.
|
|
528
|
+
*/
|
|
529
|
+
readonly payloadProtection?: PayloadProtectionOptions;
|
|
438
530
|
readonly snapshotUpload?: SnapshotUploadOptions;
|
|
439
531
|
readonly reconnectConfig?: Partial<ReconnectConfig>;
|
|
440
532
|
readonly debug?: boolean;
|
|
@@ -443,9 +535,25 @@ interface StreamsCrdtLike<TVersion extends JsonObject> {
|
|
|
443
535
|
createStream(): Promise<Result<TransportCreateStreamSuccess, TransportError>>;
|
|
444
536
|
deleteStream(): Promise<Result<TransportDeleteStreamSuccess, TransportError>>;
|
|
445
537
|
sync(): Promise<Result<TransportSyncSuccess<TVersion>, TransportError>>;
|
|
538
|
+
/**
|
|
539
|
+
* Appends only locally observed batches without reading remote state first.
|
|
540
|
+
*
|
|
541
|
+
* This is the write-only path for actors that may POST but may not bootstrap,
|
|
542
|
+
* catch up, or join live reads. It must not be mixed with `sync()`/`join()`
|
|
543
|
+
* on the same instance, and `close()` permanently disables it for that
|
|
544
|
+
* instance.
|
|
545
|
+
*/
|
|
546
|
+
appendWriteOnly(): Promise<Result<WriteOnlyAppendResult<TVersion>, TransportError>>;
|
|
446
547
|
join(params?: TransportJoinParams): Promise<Result<TransportSubscription, TransportError>>;
|
|
447
548
|
/** Resets retry state and immediately attempts to reconnect all rooms. */
|
|
448
549
|
rejoin(): void;
|
|
550
|
+
/**
|
|
551
|
+
* Closes the active join and releases transport-owned resources.
|
|
552
|
+
*
|
|
553
|
+
* If this instance was using `appendWriteOnly()`, closing also tears down the
|
|
554
|
+
* write-only subscription. Create a new instance to use the write-only path
|
|
555
|
+
* again later.
|
|
556
|
+
*/
|
|
449
557
|
close(): Promise<void>;
|
|
450
558
|
getSnapshotOffset(): Promise<Result<string | null, TransportError>>;
|
|
451
559
|
readonly streamUrl: string;
|
|
@@ -466,6 +574,10 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
466
574
|
private readonly cursorManager;
|
|
467
575
|
private readonly volatileCursorManager;
|
|
468
576
|
private readonly decodeSnapshot?;
|
|
577
|
+
private readonly encodeSnapshot?;
|
|
578
|
+
private readonly decodeUpdateItems?;
|
|
579
|
+
private readonly encodeUpdateItems?;
|
|
580
|
+
private readonly measureUpdateItemsByteLength?;
|
|
469
581
|
private readonly reconnectConfig;
|
|
470
582
|
private readonly debug;
|
|
471
583
|
private readonly beforeRemoteCursorSave?;
|
|
@@ -473,6 +585,10 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
473
585
|
private readonly producerId;
|
|
474
586
|
private producerEpoch;
|
|
475
587
|
private nextProducerSeq;
|
|
588
|
+
private readonly pendingWriteOnly;
|
|
589
|
+
private writeOnlyCursor;
|
|
590
|
+
private unsubscribeWriteOnlyLocal?;
|
|
591
|
+
private writeOnlyDisabledReason?;
|
|
476
592
|
private joinState?;
|
|
477
593
|
private opQueue;
|
|
478
594
|
/**
|
|
@@ -484,6 +600,8 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
484
600
|
* Creates a transport for one stream URL and one local adapter instance.
|
|
485
601
|
*/
|
|
486
602
|
constructor(options: StreamsCrdtOptions<TVersion>);
|
|
603
|
+
private composeSnapshotDecoder;
|
|
604
|
+
private composeSnapshotEncoder;
|
|
487
605
|
/**
|
|
488
606
|
* Stops any active live join and releases transport-owned state.
|
|
489
607
|
*/
|
|
@@ -492,16 +610,24 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
492
610
|
* Resets the retry counter and immediately attempts to reconnect.
|
|
493
611
|
*
|
|
494
612
|
* Use this after device wake or network recovery to skip any pending
|
|
495
|
-
* backoff delay. No-op when the room is already
|
|
613
|
+
* backoff delay. No-op when the room is already fully healthy (both
|
|
614
|
+
* read and write sub-statuses are "ok") or no join is active.
|
|
496
615
|
*
|
|
497
|
-
* If the room reached `"disconnected"`
|
|
498
|
-
*
|
|
616
|
+
* If the room reached `"disconnected"` or `"error"` status, this
|
|
617
|
+
* restarts the affected loop(s).
|
|
499
618
|
*/
|
|
500
619
|
rejoin(): void;
|
|
501
620
|
/**
|
|
502
621
|
* Reads the latest remote snapshot offset, if the server exposes one.
|
|
503
622
|
*/
|
|
504
623
|
getSnapshotOffset(): Promise<Result<string | null, TransportError>>;
|
|
624
|
+
/**
|
|
625
|
+
* Uploads a full adapter snapshot at the current remote tail.
|
|
626
|
+
*
|
|
627
|
+
* @internal Intended for end-to-end tests that must deterministically create
|
|
628
|
+
* a remote snapshot without waiting for the best-effort join() debounce.
|
|
629
|
+
*/
|
|
630
|
+
uploadSnapshotForTesting(): Promise<Result<TransportSnapshotUploadSuccess, TransportError>>;
|
|
505
631
|
/**
|
|
506
632
|
* Explicitly creates the target stream.
|
|
507
633
|
*/
|
|
@@ -514,6 +640,14 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
514
640
|
* Runs one bootstrap/catch-up cycle without entering live mode.
|
|
515
641
|
*/
|
|
516
642
|
sync(): Promise<Result<TransportSyncSuccess<TVersion>, TransportError>>;
|
|
643
|
+
/**
|
|
644
|
+
* Appends local CRDT updates without reading the stream first.
|
|
645
|
+
*
|
|
646
|
+
* Use this for write-only actors whose auth token may POST to the stream but
|
|
647
|
+
* may not bootstrap, catch up, or join live reads. The method does not save a
|
|
648
|
+
* RemoteCursorStore cursor and does not apply remote updates.
|
|
649
|
+
*/
|
|
650
|
+
appendWriteOnly(): Promise<Result<WriteOnlyAppendResult<TVersion>, TransportError>>;
|
|
517
651
|
/**
|
|
518
652
|
* Replays remote state, flushes queued local updates, then enters live read mode.
|
|
519
653
|
*/
|
|
@@ -529,6 +663,11 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
529
663
|
private catchup;
|
|
530
664
|
private catchupOrGone;
|
|
531
665
|
private appendLocalBatch;
|
|
666
|
+
/**
|
|
667
|
+
* Ensures the target stream exists, creating it if necessary.
|
|
668
|
+
* Tolerates 409 Conflict (stream already created concurrently).
|
|
669
|
+
*/
|
|
670
|
+
private ensureStreamExists;
|
|
532
671
|
private exportUpdates;
|
|
533
672
|
private applyBootstrapItems;
|
|
534
673
|
private applyRemoteItems;
|
|
@@ -550,7 +689,9 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
550
689
|
*
|
|
551
690
|
* Returns `undefined` when the queue is empty.
|
|
552
691
|
*/
|
|
692
|
+
private drainQueuedBatch;
|
|
553
693
|
private drainPendingBatch;
|
|
694
|
+
private drainPendingWriteOnlyBatch;
|
|
554
695
|
private flushPendingLocal;
|
|
555
696
|
/**
|
|
556
697
|
* Decrements all sync waiters by the number of flushed batches and
|
|
@@ -560,12 +701,28 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
560
701
|
/** Rejects and removes all sync waiters. */
|
|
561
702
|
private rejectSyncWaiters;
|
|
562
703
|
private saveRemoteCursor;
|
|
563
|
-
private
|
|
704
|
+
private setReadSubStatus;
|
|
705
|
+
private setWriteSubStatus;
|
|
706
|
+
private recomputeJoinStatus;
|
|
707
|
+
private computeOverallStatus;
|
|
564
708
|
private enqueueExclusive;
|
|
709
|
+
private measureAppendBodyByteLength;
|
|
710
|
+
private disableWriteOnlyMode;
|
|
565
711
|
private logDebug;
|
|
566
712
|
private logError;
|
|
567
713
|
}
|
|
568
714
|
//#endregion
|
|
715
|
+
//#region src/payload-protection.d.ts
|
|
716
|
+
type PayloadProtectionFailureReason = "plaintext_forbidden" | "missing_read_key" | "decrypt_failed" | "invalid_envelope" | "wrong_payload_kind" | "encrypt_failed";
|
|
717
|
+
declare class PayloadProtectionError extends Error {
|
|
718
|
+
readonly reason: PayloadProtectionFailureReason;
|
|
719
|
+
readonly keyId?: string;
|
|
720
|
+
constructor(reason: PayloadProtectionFailureReason, message: string, options?: {
|
|
721
|
+
readonly keyId?: string;
|
|
722
|
+
readonly cause?: unknown;
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
//#endregion
|
|
569
726
|
//#region src/stream-id.d.ts
|
|
570
727
|
/**
|
|
571
728
|
* Validates a stream id accepted by `createStreamUrl()`.
|
|
@@ -584,5 +741,5 @@ declare function createStreamUrl(input: {
|
|
|
584
741
|
baseUrl?: string;
|
|
585
742
|
}): string;
|
|
586
743
|
//#endregion
|
|
587
|
-
export {
|
|
588
|
-
//# sourceMappingURL=stream-id-
|
|
744
|
+
export { StreamsAuthProvider as A, WriteOnlyAppendResult as B, PayloadProtectionScope as C, SnapshotTransformHook as D, SnapshotCodec as E, TransportJoinParams as F, IndexedDbRemoteCursorStoreOptions as G, BeforeRemoteCursorSaveHook as H, TransportRoomStatus as I, RemoteCursorStore as J, RemoteCursor as K, TransportSnapshotUploadSuccess as L, TransportCreateStreamSuccess as M, TransportDeleteStreamSuccess as N, SnapshotUploadOptions as O, TransportError as P, TransportSubscription as R, PayloadProtectionReadPolicy as S, Result as T, InMemoryRemoteCursorStore as U, BeforeRemoteCursorSaveContext as V, IndexedDbRemoteCursorStore as W, createInitialRemoteCursor as Y, JsonValue as _, StreamsCrdt as a, PayloadProtectionKeyProvider as b, E2eeEncryptionOptions as c, E2eeOptions as d, E2eeReadPolicy as f, JsonObject as g, IsolatedCrdtAdapter as h, PayloadProtectionError as i, StreamsCrdtOptions as j, StreamsAuthContext as k, E2eeKey as l, E2eeWritePolicy as m, isValidBucketId as n, CrdtAdapter as o, E2eeScope as p, RemoteCursorSaveSource as q, isValidRillId as r, CrdtUpdateBatch as s, createStreamUrl as t, E2eeKeyProvider as u, PayloadProtectionEncryptionOptions as v, PayloadProtectionWritePolicy as w, PayloadProtectionOptions as x, PayloadProtectionKey as y, TransportSyncSuccess as z };
|
|
745
|
+
//# sourceMappingURL=stream-id-CS7SzuLW.d.ts.map
|