@loro-dev/streams-crdt 0.8.1 → 0.8.2

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 CHANGED
@@ -118,6 +118,23 @@ const sub = joined.value;
118
118
  checked out or detached, call `checkoutToLatest()` or `attach()` before passing
119
119
  it to `StreamsCrdt`.
120
120
 
121
+ ### Loro pending imports and cursor safety
122
+
123
+ Loro can accept an update whose dependencies are missing and keep it as a
124
+ pending import. Pending imports are not represented in `doc.version()` and are
125
+ not included in snapshots exported from that document. For that reason, the Loro
126
+ adapter must not persist a `RemoteCursor` until all pending imports are resolved
127
+ and the local document is complete at the cursor offset.
128
+
129
+ The transport may continue reading with an in-memory volatile cursor to find the
130
+ missing dependencies. If the server reports up-to-date while Loro still has
131
+ unresolved imports, the client must bootstrap again and only save the bootstrap
132
+ cursor after the batch import is clean. Snapshot upload is skipped while Loro has
133
+ unresolved imports.
134
+
135
+ See [`specs/loro-pending.md`](../../specs/loro-pending.md) for the full behavior
136
+ and implementation checklist.
137
+
121
138
  ## Quick Start (Loro EphemeralStore)
122
139
 
123
140
  Use a separate `EphemeralStreamCrdt` for presence-like Loro `EphemeralStore`
@@ -859,9 +876,13 @@ const transport = new StreamsCrdt({
859
876
  - `CrdtAdapter` — adapter contract interface
860
877
  - `IsolatedCrdtAdapter` — optional apply-only adapter used for safe 410 recovery
861
878
  - `CrdtUpdateBatch` — local update batch type
879
+ - `CrdtApplyOutcome`, `CrdtApplyReturn`, `CrdtUnresolvedSpan` — adapter apply
880
+ result types used to report incomplete imports such as Loro pending updates
862
881
  - `Result` — Rust-style `Ok | Err` result
863
882
  - `StreamsAuthProvider` — auth callback type
864
883
  - `TransportError` — discriminated error union
884
+ `apply_incomplete` is returned when remote data was read but the CRDT adapter
885
+ still has unresolved dependencies and the cursor cannot be saved.
865
886
  - `TransportCreateStreamSuccess`, `TransportDeleteStreamSuccess`, `TransportSyncSuccess` — result payloads
866
887
  - `TransportJoinParams` — `join()` options
867
888
  - `TransportSubscription` — active live subscription handle
package/dist/flock.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as RemoteCursorStore, A as Result, B as TransportJoinParams, C as PayloadProtectionEncryptionOptions, D as PayloadProtectionReadPolicy, E as PayloadProtectionOptions, F as StreamsAuthProvider, G as WriteOnlyAppendResult, H as TransportSnapshotUploadSuccess, I as StreamsCrdtOptions, J as InMemoryRemoteCursorStore, K as BeforeRemoteCursorSaveContext, L as TransportCreateStreamSuccess, M as SnapshotTransformHook, N as SnapshotUploadOptions, O as PayloadProtectionScope, P as StreamsAuthContext, Q as RemoteCursorSaveSource, R as TransportDeleteStreamSuccess, S as JsonValue, T as PayloadProtectionKeyProvider, U as TransportSubscription, V as TransportRoomStatus, W as TransportSyncSuccess, X as IndexedDbRemoteCursorStoreOptions, Y as IndexedDbRemoteCursorStore, Z as RemoteCursor, _ as EphemeralStreamCrdtOptions, a as EphemeralStreamCrdt, b as IsolatedCrdtAdapter, c as CrdtUpdateBatch, d as E2eeKeyProvider, et as createInitialRemoteCursor, f as E2eeOptions, g as EphemeralStreamAdaptor, h as E2eeWritePolicy, i as PayloadProtectionError, j as SnapshotCodec, k as PayloadProtectionWritePolicy, l as E2eeEncryptionOptions, m as E2eeScope, n as isValidBucketId, o as StreamsCrdt, p as E2eeReadPolicy, q as BeforeRemoteCursorSaveHook, r as isValidRillId, s as CrdtAdapter, t as createStreamUrl, u as E2eeKey, v as EphemeralStreamJoinParams, w as PayloadProtectionKey, x as JsonObject, y as EphemeralStreamSubscription, z as TransportError } from "./stream-id-B6FzWgkM.js";
1
+ import { $ as IndexedDbRemoteCursorStoreOptions, A as PayloadProtectionReadPolicy, B as TransportCreateStreamSuccess, C as IsolatedCrdtAdapter, D as PayloadProtectionKey, E as PayloadProtectionEncryptionOptions, F as SnapshotTransformHook, G as TransportSnapshotUploadSuccess, H as TransportError, I as SnapshotUploadOptions, J as WriteOnlyAppendResult, K as TransportSubscription, L as StreamsAuthContext, M as PayloadProtectionWritePolicy, N as Result, O as PayloadProtectionKeyProvider, P as SnapshotCodec, Q as IndexedDbRemoteCursorStore, R as StreamsAuthProvider, S as EphemeralStreamSubscription, T as JsonValue, U as TransportJoinParams, V as TransportDeleteStreamSuccess, W as TransportRoomStatus, X as BeforeRemoteCursorSaveHook, Y as BeforeRemoteCursorSaveContext, Z as InMemoryRemoteCursorStore, _ as E2eeScope, a as EphemeralStreamCrdt, b as EphemeralStreamCrdtOptions, c as CrdtApplyOutcome, d as CrdtUpdateBatch, et as RemoteCursor, f as E2eeEncryptionOptions, g as E2eeReadPolicy, h as E2eeOptions, i as PayloadProtectionError, j as PayloadProtectionScope, k as PayloadProtectionOptions, l as CrdtApplyReturn, m as E2eeKeyProvider, n as isValidBucketId, nt as RemoteCursorStore, o as StreamsCrdt, p as E2eeKey, q as TransportSyncSuccess, r as isValidRillId, rt as createInitialRemoteCursor, s as CrdtAdapter, t as createStreamUrl, tt as RemoteCursorSaveSource, u as CrdtUnresolvedSpan, v as E2eeWritePolicy, w as JsonObject, x as EphemeralStreamJoinParams, y as EphemeralStreamAdaptor, z as StreamsCrdtOptions } from "./stream-id-Bi5rAZfP.js";
2
2
  import { ExportBundle, Flock, VersionVector, VersionVector as VersionVector$1 } from "@loro-dev/flock-wasm";
3
3
 
4
4
  //#region src/flock.d.ts
@@ -7,5 +7,5 @@ import { ExportBundle, Flock, VersionVector, VersionVector as VersionVector$1 }
7
7
  */
8
8
  declare function createFlockAdapter(flock: Flock): CrdtAdapter<VersionVector$1>;
9
9
  //#endregion
10
- export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, type ExportBundle, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, PayloadProtectionEncryptionOptions, PayloadProtectionKey, PayloadProtectionKeyProvider, PayloadProtectionOptions, PayloadProtectionReadPolicy, PayloadProtectionScope, PayloadProtectionWritePolicy, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSnapshotUploadSuccess, TransportSubscription, TransportSyncSuccess, type VersionVector, WriteOnlyAppendResult, createFlockAdapter, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
10
+ export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtApplyOutcome, CrdtApplyReturn, CrdtUnresolvedSpan, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, type ExportBundle, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, PayloadProtectionEncryptionOptions, PayloadProtectionKey, PayloadProtectionKeyProvider, PayloadProtectionOptions, PayloadProtectionReadPolicy, PayloadProtectionScope, PayloadProtectionWritePolicy, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSnapshotUploadSuccess, TransportSubscription, TransportSyncSuccess, type VersionVector, WriteOnlyAppendResult, createFlockAdapter, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
11
11
  //# sourceMappingURL=flock.d.ts.map
package/dist/flock.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as InMemoryRemoteCursorStore, c as EphemeralStreamCrdt, i as StreamsCrdt, l as PayloadProtectionError, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-CCPEBb6G.js";
1
+ import { a as InMemoryRemoteCursorStore, c as EphemeralStreamCrdt, i as StreamsCrdt, l as PayloadProtectionError, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-BjmGvObf.js";
2
2
  import { Flock } from "@loro-dev/flock-wasm";
3
3
  //#region src/flock.ts
4
4
  function isAfter(entry, current) {
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as RemoteCursorStore, A as Result, B as TransportJoinParams, C as PayloadProtectionEncryptionOptions, D as PayloadProtectionReadPolicy, E as PayloadProtectionOptions, F as StreamsAuthProvider, G as WriteOnlyAppendResult, H as TransportSnapshotUploadSuccess, I as StreamsCrdtOptions, J as InMemoryRemoteCursorStore, K as BeforeRemoteCursorSaveContext, L as TransportCreateStreamSuccess, M as SnapshotTransformHook, N as SnapshotUploadOptions, O as PayloadProtectionScope, P as StreamsAuthContext, Q as RemoteCursorSaveSource, R as TransportDeleteStreamSuccess, S as JsonValue, T as PayloadProtectionKeyProvider, U as TransportSubscription, V as TransportRoomStatus, W as TransportSyncSuccess, X as IndexedDbRemoteCursorStoreOptions, Y as IndexedDbRemoteCursorStore, Z as RemoteCursor, _ as EphemeralStreamCrdtOptions, a as EphemeralStreamCrdt, b as IsolatedCrdtAdapter, c as CrdtUpdateBatch, d as E2eeKeyProvider, et as createInitialRemoteCursor, f as E2eeOptions, g as EphemeralStreamAdaptor, h as E2eeWritePolicy, i as PayloadProtectionError, j as SnapshotCodec, k as PayloadProtectionWritePolicy, l as E2eeEncryptionOptions, m as E2eeScope, n as isValidBucketId, o as StreamsCrdt, p as E2eeReadPolicy, q as BeforeRemoteCursorSaveHook, r as isValidRillId, s as CrdtAdapter, t as createStreamUrl, u as E2eeKey, v as EphemeralStreamJoinParams, w as PayloadProtectionKey, x as JsonObject, y as EphemeralStreamSubscription, z as TransportError } from "./stream-id-B6FzWgkM.js";
2
- export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, PayloadProtectionEncryptionOptions, PayloadProtectionKey, PayloadProtectionKeyProvider, PayloadProtectionOptions, PayloadProtectionReadPolicy, PayloadProtectionScope, PayloadProtectionWritePolicy, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSnapshotUploadSuccess, TransportSubscription, TransportSyncSuccess, WriteOnlyAppendResult, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
1
+ import { $ as IndexedDbRemoteCursorStoreOptions, A as PayloadProtectionReadPolicy, B as TransportCreateStreamSuccess, C as IsolatedCrdtAdapter, D as PayloadProtectionKey, E as PayloadProtectionEncryptionOptions, F as SnapshotTransformHook, G as TransportSnapshotUploadSuccess, H as TransportError, I as SnapshotUploadOptions, J as WriteOnlyAppendResult, K as TransportSubscription, L as StreamsAuthContext, M as PayloadProtectionWritePolicy, N as Result, O as PayloadProtectionKeyProvider, P as SnapshotCodec, Q as IndexedDbRemoteCursorStore, R as StreamsAuthProvider, S as EphemeralStreamSubscription, T as JsonValue, U as TransportJoinParams, V as TransportDeleteStreamSuccess, W as TransportRoomStatus, X as BeforeRemoteCursorSaveHook, Y as BeforeRemoteCursorSaveContext, Z as InMemoryRemoteCursorStore, _ as E2eeScope, a as EphemeralStreamCrdt, b as EphemeralStreamCrdtOptions, c as CrdtApplyOutcome, d as CrdtUpdateBatch, et as RemoteCursor, f as E2eeEncryptionOptions, g as E2eeReadPolicy, h as E2eeOptions, i as PayloadProtectionError, j as PayloadProtectionScope, k as PayloadProtectionOptions, l as CrdtApplyReturn, m as E2eeKeyProvider, n as isValidBucketId, nt as RemoteCursorStore, o as StreamsCrdt, p as E2eeKey, q as TransportSyncSuccess, r as isValidRillId, rt as createInitialRemoteCursor, s as CrdtAdapter, t as createStreamUrl, tt as RemoteCursorSaveSource, u as CrdtUnresolvedSpan, v as E2eeWritePolicy, w as JsonObject, x as EphemeralStreamJoinParams, y as EphemeralStreamAdaptor, z as StreamsCrdtOptions } from "./stream-id-Bi5rAZfP.js";
2
+ export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtApplyOutcome, CrdtApplyReturn, CrdtUnresolvedSpan, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, PayloadProtectionEncryptionOptions, PayloadProtectionKey, PayloadProtectionKeyProvider, PayloadProtectionOptions, PayloadProtectionReadPolicy, PayloadProtectionScope, PayloadProtectionWritePolicy, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSnapshotUploadSuccess, TransportSubscription, TransportSyncSuccess, WriteOnlyAppendResult, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as InMemoryRemoteCursorStore, c as EphemeralStreamCrdt, i as StreamsCrdt, l as PayloadProtectionError, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-CCPEBb6G.js";
1
+ import { a as InMemoryRemoteCursorStore, c as EphemeralStreamCrdt, i as StreamsCrdt, l as PayloadProtectionError, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-BjmGvObf.js";
2
2
  export { PayloadProtectionError as E2eeError, PayloadProtectionError, EphemeralStreamCrdt, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
package/dist/loro.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as RemoteCursorStore, A as Result, B as TransportJoinParams, C as PayloadProtectionEncryptionOptions, D as PayloadProtectionReadPolicy, E as PayloadProtectionOptions, F as StreamsAuthProvider, G as WriteOnlyAppendResult, H as TransportSnapshotUploadSuccess, I as StreamsCrdtOptions, J as InMemoryRemoteCursorStore, K as BeforeRemoteCursorSaveContext, L as TransportCreateStreamSuccess, M as SnapshotTransformHook, N as SnapshotUploadOptions, O as PayloadProtectionScope, P as StreamsAuthContext, Q as RemoteCursorSaveSource, R as TransportDeleteStreamSuccess, S as JsonValue, T as PayloadProtectionKeyProvider, U as TransportSubscription, V as TransportRoomStatus, W as TransportSyncSuccess, X as IndexedDbRemoteCursorStoreOptions, Y as IndexedDbRemoteCursorStore, Z as RemoteCursor, _ as EphemeralStreamCrdtOptions, a as EphemeralStreamCrdt, b as IsolatedCrdtAdapter, c as CrdtUpdateBatch, d as E2eeKeyProvider, et as createInitialRemoteCursor, f as E2eeOptions, g as EphemeralStreamAdaptor, h as E2eeWritePolicy, i as PayloadProtectionError, j as SnapshotCodec, k as PayloadProtectionWritePolicy, l as E2eeEncryptionOptions, m as E2eeScope, n as isValidBucketId, o as StreamsCrdt, p as E2eeReadPolicy, q as BeforeRemoteCursorSaveHook, r as isValidRillId, s as CrdtAdapter, t as createStreamUrl, u as E2eeKey, v as EphemeralStreamJoinParams, w as PayloadProtectionKey, x as JsonObject, y as EphemeralStreamSubscription, z as TransportError } from "./stream-id-B6FzWgkM.js";
1
+ import { $ as IndexedDbRemoteCursorStoreOptions, A as PayloadProtectionReadPolicy, B as TransportCreateStreamSuccess, C as IsolatedCrdtAdapter, D as PayloadProtectionKey, E as PayloadProtectionEncryptionOptions, F as SnapshotTransformHook, G as TransportSnapshotUploadSuccess, H as TransportError, I as SnapshotUploadOptions, J as WriteOnlyAppendResult, K as TransportSubscription, L as StreamsAuthContext, M as PayloadProtectionWritePolicy, N as Result, O as PayloadProtectionKeyProvider, P as SnapshotCodec, Q as IndexedDbRemoteCursorStore, R as StreamsAuthProvider, S as EphemeralStreamSubscription, T as JsonValue, U as TransportJoinParams, V as TransportDeleteStreamSuccess, W as TransportRoomStatus, X as BeforeRemoteCursorSaveHook, Y as BeforeRemoteCursorSaveContext, Z as InMemoryRemoteCursorStore, _ as E2eeScope, a as EphemeralStreamCrdt, b as EphemeralStreamCrdtOptions, c as CrdtApplyOutcome, d as CrdtUpdateBatch, et as RemoteCursor, f as E2eeEncryptionOptions, g as E2eeReadPolicy, h as E2eeOptions, i as PayloadProtectionError, j as PayloadProtectionScope, k as PayloadProtectionOptions, l as CrdtApplyReturn, m as E2eeKeyProvider, n as isValidBucketId, nt as RemoteCursorStore, o as StreamsCrdt, p as E2eeKey, q as TransportSyncSuccess, r as isValidRillId, rt as createInitialRemoteCursor, s as CrdtAdapter, t as createStreamUrl, tt as RemoteCursorSaveSource, u as CrdtUnresolvedSpan, v as E2eeWritePolicy, w as JsonObject, x as EphemeralStreamJoinParams, y as EphemeralStreamAdaptor, z as StreamsCrdtOptions } from "./stream-id-Bi5rAZfP.js";
2
2
  import { LoroDoc } from "loro-crdt";
3
3
 
4
4
  //#region src/loro.d.ts
@@ -41,5 +41,5 @@ interface LoroEphemeralStoreLike {
41
41
  declare function EphemeralStoreAdaptor(store: LoroEphemeralStoreLike): EphemeralStreamAdaptor;
42
42
  declare const createLoroEphemeralStoreAdaptor: typeof EphemeralStoreAdaptor;
43
43
  //#endregion
44
- export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, EphemeralStoreAdaptor, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, LoroEphemeralStoreLike, PayloadProtectionEncryptionOptions, PayloadProtectionKey, PayloadProtectionKeyProvider, PayloadProtectionOptions, PayloadProtectionReadPolicy, PayloadProtectionScope, PayloadProtectionWritePolicy, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSnapshotUploadSuccess, TransportSubscription, TransportSyncSuccess, WriteOnlyAppendResult, createInitialRemoteCursor, createLoroDocAdapter, createLoroEphemeralStoreAdaptor, createStreamUrl, isValidBucketId, isValidRillId };
44
+ export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtApplyOutcome, CrdtApplyReturn, CrdtUnresolvedSpan, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, EphemeralStoreAdaptor, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, LoroEphemeralStoreLike, PayloadProtectionEncryptionOptions, PayloadProtectionKey, PayloadProtectionKeyProvider, PayloadProtectionOptions, PayloadProtectionReadPolicy, PayloadProtectionScope, PayloadProtectionWritePolicy, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSnapshotUploadSuccess, TransportSubscription, TransportSyncSuccess, WriteOnlyAppendResult, createInitialRemoteCursor, createLoroDocAdapter, createLoroEphemeralStoreAdaptor, createStreamUrl, isValidBucketId, isValidRillId };
45
45
  //# sourceMappingURL=loro.d.ts.map
package/dist/loro.js CHANGED
@@ -1,9 +1,9 @@
1
- import { a as InMemoryRemoteCursorStore, c as EphemeralStreamCrdt, i as StreamsCrdt, l as PayloadProtectionError, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-CCPEBb6G.js";
1
+ import { a as InMemoryRemoteCursorStore, c as EphemeralStreamCrdt, i as StreamsCrdt, l as PayloadProtectionError, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-BjmGvObf.js";
2
2
  import { LoroDoc, VersionVector, decodeImportBlobMeta } from "loro-crdt";
3
3
  //#region src/loro.ts
4
4
  function batchImportUpdates(doc, updates) {
5
- if ("batchImport" in doc && typeof doc.batchImport === "function") doc.batchImport(updates);
6
- else doc.importBatch(updates);
5
+ if ("batchImport" in doc && typeof doc.batchImport === "function") return doc.batchImport(updates);
6
+ return doc.importBatch(updates);
7
7
  }
8
8
  function toDocVersionRecord(version) {
9
9
  const out = {};
@@ -39,6 +39,106 @@ function assertDocAttached(doc) {
39
39
  if (!doc.isDetached()) return;
40
40
  throw new Error("LoroDoc must be attached before using streams-crdt; call checkoutToLatest() or attach() first");
41
41
  }
42
+ function normalizePeerId(peer) {
43
+ return `${peer}`;
44
+ }
45
+ function isValidSpan(span) {
46
+ return Number.isSafeInteger(span.start) && Number.isSafeInteger(span.end) && span.start < span.end;
47
+ }
48
+ var UnresolvedSpanTracker = class {
49
+ spans = /* @__PURE__ */ new Map();
50
+ addAll(pending) {
51
+ if (pending == null) return;
52
+ for (const [peer, span] of pending) this.add(normalizePeerId(peer), span);
53
+ }
54
+ resolveAll(success) {
55
+ for (const [peer, span] of success) this.resolve(normalizePeerId(peer), span);
56
+ }
57
+ isEmpty() {
58
+ return this.spans.size === 0;
59
+ }
60
+ toArray() {
61
+ const out = [];
62
+ for (const [peer, spans] of this.spans) for (const span of spans) out.push({
63
+ peer,
64
+ start: span.start,
65
+ end: span.end
66
+ });
67
+ return out;
68
+ }
69
+ reset() {
70
+ this.spans.clear();
71
+ }
72
+ add(peer, incoming) {
73
+ if (!isValidSpan(incoming)) return;
74
+ const existing = this.spans.get(peer) ?? [];
75
+ existing.push({
76
+ start: incoming.start,
77
+ end: incoming.end
78
+ });
79
+ existing.sort((a, b) => a.start - b.start || a.end - b.end);
80
+ const merged = [];
81
+ for (const span of existing) {
82
+ const last = merged[merged.length - 1];
83
+ if (last == null || span.start > last.end) merged.push({
84
+ start: span.start,
85
+ end: span.end
86
+ });
87
+ else merged[merged.length - 1] = {
88
+ start: last.start,
89
+ end: Math.max(last.end, span.end)
90
+ };
91
+ }
92
+ this.spans.set(peer, merged);
93
+ }
94
+ resolve(peer, applied) {
95
+ if (!isValidSpan(applied)) return;
96
+ const existing = this.spans.get(peer);
97
+ if (existing == null) return;
98
+ const remaining = [];
99
+ for (const span of existing) {
100
+ if (applied.end <= span.start || applied.start >= span.end) {
101
+ remaining.push(span);
102
+ continue;
103
+ }
104
+ if (span.start < applied.start) remaining.push({
105
+ start: span.start,
106
+ end: applied.start
107
+ });
108
+ if (applied.end < span.end) remaining.push({
109
+ start: applied.end,
110
+ end: span.end
111
+ });
112
+ }
113
+ if (remaining.length === 0) this.spans.delete(peer);
114
+ else this.spans.set(peer, remaining);
115
+ }
116
+ };
117
+ function trackImportStatus(tracker, status) {
118
+ tracker.addAll(status.pending);
119
+ tracker.resolveAll(status.success);
120
+ }
121
+ function versionFromImportSuccess(status) {
122
+ const version = {};
123
+ for (const [peer, span] of status.success) {
124
+ if (!isValidSpan(span)) continue;
125
+ const peerId = normalizePeerId(peer);
126
+ version[peerId] = Math.max(version[peerId] ?? 0, span.end);
127
+ }
128
+ return version;
129
+ }
130
+ function versionFromSnapshotImport(snapshot, status, tracker) {
131
+ if (!tracker.isEmpty()) return versionFromImportSuccess(status);
132
+ return extractVersionFromUpdate(snapshot) ?? versionFromImportSuccess(status);
133
+ }
134
+ function applyOutcome(version, tracker) {
135
+ const unresolved = tracker.toArray();
136
+ return {
137
+ version,
138
+ complete: unresolved.length === 0,
139
+ ...unresolved.length === 0 ? {} : { unresolved }
140
+ };
141
+ }
42
142
  /**
43
143
  * Creates a `CrdtAdapter` for one `LoroDoc`.
44
144
  *
@@ -54,6 +154,7 @@ function assertDocAttached(doc) {
54
154
  * ```
55
155
  */
56
156
  function createLoroDocAdapter(doc) {
157
+ const unresolved = new UnresolvedSpanTracker();
57
158
  return {
58
159
  emptyVersion: () => ({}),
59
160
  mergeVersions: (base, incoming) => mergeDocVersions(base, incoming),
@@ -63,8 +164,10 @@ function createLoroDocAdapter(doc) {
63
164
  },
64
165
  applySnapshot: (snapshot) => {
65
166
  assertDocAttached(doc);
66
- doc.import(snapshot);
67
- return toDocVersionRecord(doc.version());
167
+ const status = doc.import(snapshot);
168
+ unresolved.reset();
169
+ trackImportStatus(unresolved, status);
170
+ return applyOutcome(versionFromSnapshotImport(snapshot, status, unresolved), unresolved);
68
171
  },
69
172
  exportUpdates: (fromVersion) => {
70
173
  assertDocAttached(doc);
@@ -86,30 +189,34 @@ function createLoroDocAdapter(doc) {
86
189
  },
87
190
  applyRemoteUpdates: (updates, remoterVersion) => {
88
191
  assertDocAttached(doc);
89
- if (updates.length > 0) batchImportUpdates(doc, updates);
90
- let version = remoterVersion;
91
- for (const update of updates) {
92
- const next = extractVersionFromUpdate(update);
93
- if (next != null) version = mergeDocVersions(version, next);
192
+ if (updates.length > 0) {
193
+ const status = batchImportUpdates(doc, updates);
194
+ trackImportStatus(unresolved, status);
195
+ return applyOutcome(mergeDocVersions(remoterVersion, versionFromImportSuccess(status)), unresolved);
94
196
  }
95
- return version;
197
+ return applyOutcome(remoterVersion, unresolved);
96
198
  },
97
199
  createIsolatedAdapter: () => {
98
200
  let isolatedDoc = new LoroDoc();
201
+ const isolatedUnresolved = new UnresolvedSpanTracker();
99
202
  return {
100
203
  applySnapshot: (snapshot) => {
101
204
  isolatedDoc = new LoroDoc();
102
- if (snapshot.byteLength > 0) isolatedDoc.import(snapshot);
103
- return toDocVersionRecord(isolatedDoc.version());
205
+ isolatedUnresolved.reset();
206
+ if (snapshot.byteLength > 0) {
207
+ const status = isolatedDoc.import(snapshot);
208
+ trackImportStatus(isolatedUnresolved, status);
209
+ return applyOutcome(versionFromSnapshotImport(snapshot, status, isolatedUnresolved), isolatedUnresolved);
210
+ }
211
+ return applyOutcome({}, isolatedUnresolved);
104
212
  },
105
213
  applyRemoteUpdates: (updates, remoterVersion) => {
106
- if (updates.length > 0) batchImportUpdates(isolatedDoc, updates);
107
- let version = remoterVersion;
108
- for (const update of updates) {
109
- const next = extractVersionFromUpdate(update);
110
- if (next != null) version = mergeDocVersions(version, next);
214
+ if (updates.length > 0) {
215
+ const status = batchImportUpdates(isolatedDoc, updates);
216
+ trackImportStatus(isolatedUnresolved, status);
217
+ return applyOutcome(mergeDocVersions(remoterVersion, versionFromImportSuccess(status)), isolatedUnresolved);
111
218
  }
112
- return version;
219
+ return applyOutcome(remoterVersion, isolatedUnresolved);
113
220
  }
114
221
  };
115
222
  },
package/dist/loro.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"loro.js","names":[],"sources":["../src/loro.ts"],"sourcesContent":["import {\n decodeImportBlobMeta,\n LoroDoc,\n VersionVector,\n} from \"loro-crdt\";\n\nimport type { CrdtAdapter, JsonObject } from \"./types\";\nimport type { EphemeralStreamAdaptor } from \"./types\";\n\nexport { EphemeralStreamCrdt } from \"./ephemeral-runtime\";\nexport { StreamsCrdt } from \"./transport-runtime\";\nexport * from \"./shared\";\n\nfunction batchImportUpdates(doc: LoroDoc, updates: Uint8Array[]): void {\n if (\"batchImport\" in doc && typeof doc.batchImport === \"function\") {\n doc.batchImport(updates);\n } else {\n doc.importBatch(updates);\n }\n}\n\ntype PeerId = `${number}`;\ntype DocVersionRecord = Record<PeerId, number>;\n\nfunction toDocVersionRecord(version: VersionVector): DocVersionRecord {\n const out = {} as DocVersionRecord;\n for (const [peer, counter] of version.toJSON()) {\n out[peer as PeerId] = counter;\n }\n return out;\n}\n\nfunction toDocVersionMap(version: JsonObject): Map<PeerId, number> {\n const out = new Map<PeerId, number>();\n for (const [peer, counter] of Object.entries(version)) {\n if (typeof counter === \"number\" && Number.isFinite(counter)) {\n out.set(peer as PeerId, counter);\n }\n }\n return out;\n}\n\nfunction mergeDocVersions(\n base: DocVersionRecord,\n incoming: DocVersionRecord,\n): DocVersionRecord {\n const merged = new Map<PeerId, number>();\n for (const [peer, counter] of Object.entries(base)) {\n merged.set(peer as PeerId, counter);\n }\n for (const [peer, counter] of Object.entries(incoming)) {\n const peerId = peer as PeerId;\n merged.set(peerId, Math.max(merged.get(peerId) ?? 0, counter));\n }\n return toDocVersionRecord(VersionVector.parseJSON(merged));\n}\n\nfunction hasUpdatesSince(\n fromVersion: DocVersionRecord,\n currentVersion: DocVersionRecord,\n): boolean {\n for (const [peer, counter] of Object.entries(currentVersion)) {\n if (counter > (fromVersion[peer as PeerId] ?? 0)) {\n return true;\n }\n }\n return false;\n}\n\nfunction extractVersionFromUpdate(update: Uint8Array): DocVersionRecord | null {\n try {\n const meta = decodeImportBlobMeta(update, false);\n return toDocVersionRecord(meta.partialEndVersionVector);\n } catch {\n return null;\n }\n}\n\nfunction assertDocAttached(doc: LoroDoc): void {\n if (!doc.isDetached()) {\n return;\n }\n throw new Error(\n \"LoroDoc must be attached before using streams-crdt; \" +\n \"call checkoutToLatest() or attach() first\",\n );\n}\n\n/**\n * Creates a `CrdtAdapter` for one `LoroDoc`.\n *\n * The `LoroDoc` must be attached (not detached or checked out to a historical\n * version). If the document is detached, call `doc.checkoutToLatest()` or\n * `doc.attach()` before passing it here.\n *\n * @example\n * ```ts\n * const doc = new LoroDoc();\n * const adapter = createLoroDocAdapter(doc);\n * const transport = new StreamsCrdt({ streamUrl, adapter, auth });\n * ```\n */\nexport function createLoroDocAdapter(\n doc: LoroDoc,\n): CrdtAdapter<JsonObject> {\n return {\n emptyVersion: () => ({}),\n mergeVersions: (base, incoming) =>\n mergeDocVersions(base as DocVersionRecord, incoming as DocVersionRecord),\n exportSnapshot: () => {\n assertDocAttached(doc);\n return doc.export({ mode: \"snapshot\" });\n },\n applySnapshot: (snapshot) => {\n assertDocAttached(doc);\n doc.import(snapshot);\n return toDocVersionRecord(doc.version());\n },\n exportUpdates: (fromVersion) => {\n assertDocAttached(doc);\n const currentVersion = toDocVersionRecord(doc.version());\n if (\n !hasUpdatesSince(\n fromVersion as DocVersionRecord,\n currentVersion,\n )\n ) {\n return undefined;\n }\n const updates = [\n doc.export({\n mode: \"update\",\n from: VersionVector.parseJSON(toDocVersionMap(fromVersion)),\n }),\n ].filter((update) => update.byteLength > 0);\n if (updates.length === 0) {\n return undefined;\n }\n let version: DocVersionRecord = {};\n for (const update of updates) {\n const next = extractVersionFromUpdate(update);\n if (next != null) {\n version = mergeDocVersions(version, next);\n }\n }\n return {\n updates,\n version,\n };\n },\n applyRemoteUpdates: (updates, remoterVersion) => {\n assertDocAttached(doc);\n if (updates.length > 0) {\n batchImportUpdates(doc, updates);\n }\n let version = remoterVersion as DocVersionRecord;\n for (const update of updates) {\n const next = extractVersionFromUpdate(update);\n if (next != null) {\n version = mergeDocVersions(version, next);\n }\n }\n return version;\n },\n createIsolatedAdapter: () => {\n let isolatedDoc = new LoroDoc();\n return {\n applySnapshot: (snapshot) => {\n isolatedDoc = new LoroDoc();\n if (snapshot.byteLength > 0) {\n isolatedDoc.import(snapshot);\n }\n return toDocVersionRecord(isolatedDoc.version());\n },\n applyRemoteUpdates: (updates, remoterVersion) => {\n if (updates.length > 0) {\n batchImportUpdates(isolatedDoc, updates);\n }\n let version = remoterVersion as DocVersionRecord;\n for (const update of updates) {\n const next = extractVersionFromUpdate(update);\n if (next != null) {\n version = mergeDocVersions(version, next);\n }\n }\n return version;\n },\n };\n },\n subscribeLocalUpdates: (listener) => {\n assertDocAttached(doc);\n return doc.subscribeLocalUpdates((update) => {\n const version = extractVersionFromUpdate(update) ?? {};\n listener({ updates: [update], version });\n });\n },\n };\n}\n\n/**\n * Structural subset of Loro's `EphemeralStore`.\n */\nexport interface LoroEphemeralStoreLike {\n /** Applies one opaque Loro ephemeral update. */\n apply(update: Uint8Array): void;\n /** Subscribes to locally produced ephemeral updates. */\n subscribeLocalUpdates(listener: (update: Uint8Array) => void): () => void;\n}\n\n/**\n * Creates an ephemeral stream adaptor for a Loro `EphemeralStore`.\n *\n * @example\n * ```ts\n * const room = new EphemeralStreamCrdt({\n * streamUrl,\n * auth,\n * adaptor: EphemeralStoreAdaptor(store),\n * });\n * ```\n */\nexport function EphemeralStoreAdaptor(\n store: LoroEphemeralStoreLike,\n): EphemeralStreamAdaptor {\n let applyingRemoteDepth = 0;\n return {\n applyRemoteUpdates: (updates) => {\n applyingRemoteDepth += 1;\n try {\n for (const update of updates) {\n store.apply(update);\n }\n } finally {\n applyingRemoteDepth -= 1;\n }\n },\n subscribeLocalUpdates: (listener) => {\n const unsubscribeLocalUpdates = store.subscribeLocalUpdates((update) => {\n if (applyingRemoteDepth > 0) {\n return;\n }\n listener(update);\n });\n return () => {\n unsubscribeLocalUpdates();\n };\n },\n };\n}\n\nexport const createLoroEphemeralStoreAdaptor = EphemeralStoreAdaptor;\n"],"mappings":"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"}
1
+ {"version":3,"file":"loro.js","names":[],"sources":["../src/loro.ts"],"sourcesContent":["import {\n decodeImportBlobMeta,\n LoroDoc,\n VersionVector,\n} from \"loro-crdt\";\n\nimport type {\n CrdtAdapter,\n CrdtApplyOutcome,\n CrdtUnresolvedSpan,\n JsonObject,\n} from \"./types\";\nimport type { EphemeralStreamAdaptor } from \"./types\";\n\nexport { EphemeralStreamCrdt } from \"./ephemeral-runtime\";\nexport { StreamsCrdt } from \"./transport-runtime\";\nexport * from \"./shared\";\n\ntype CounterSpan = {\n readonly start: number;\n readonly end: number;\n};\n\ntype LoroImportStatus = {\n readonly success: Map<string | number | bigint, CounterSpan>;\n readonly pending: Map<string | number | bigint, CounterSpan> | null;\n};\n\nfunction batchImportUpdates(\n doc: LoroDoc,\n updates: Uint8Array[],\n): LoroImportStatus {\n if (\"batchImport\" in doc && typeof doc.batchImport === \"function\") {\n return doc.batchImport(updates) as LoroImportStatus;\n }\n return doc.importBatch(updates) as LoroImportStatus;\n}\n\ntype PeerId = `${number}`;\ntype DocVersionRecord = Record<PeerId, number>;\n\nfunction toDocVersionRecord(version: VersionVector): DocVersionRecord {\n const out = {} as DocVersionRecord;\n for (const [peer, counter] of version.toJSON()) {\n out[peer as PeerId] = counter;\n }\n return out;\n}\n\nfunction toDocVersionMap(version: JsonObject): Map<PeerId, number> {\n const out = new Map<PeerId, number>();\n for (const [peer, counter] of Object.entries(version)) {\n if (typeof counter === \"number\" && Number.isFinite(counter)) {\n out.set(peer as PeerId, counter);\n }\n }\n return out;\n}\n\nfunction mergeDocVersions(\n base: DocVersionRecord,\n incoming: DocVersionRecord,\n): DocVersionRecord {\n const merged = new Map<PeerId, number>();\n for (const [peer, counter] of Object.entries(base)) {\n merged.set(peer as PeerId, counter);\n }\n for (const [peer, counter] of Object.entries(incoming)) {\n const peerId = peer as PeerId;\n merged.set(peerId, Math.max(merged.get(peerId) ?? 0, counter));\n }\n return toDocVersionRecord(VersionVector.parseJSON(merged));\n}\n\nfunction hasUpdatesSince(\n fromVersion: DocVersionRecord,\n currentVersion: DocVersionRecord,\n): boolean {\n for (const [peer, counter] of Object.entries(currentVersion)) {\n if (counter > (fromVersion[peer as PeerId] ?? 0)) {\n return true;\n }\n }\n return false;\n}\n\nfunction extractVersionFromUpdate(update: Uint8Array): DocVersionRecord | null {\n try {\n const meta = decodeImportBlobMeta(update, false);\n return toDocVersionRecord(meta.partialEndVersionVector);\n } catch {\n return null;\n }\n}\n\nfunction assertDocAttached(doc: LoroDoc): void {\n if (!doc.isDetached()) {\n return;\n }\n throw new Error(\n \"LoroDoc must be attached before using streams-crdt; \" +\n \"call checkoutToLatest() or attach() first\",\n );\n}\n\nfunction normalizePeerId(peer: string | number | bigint): PeerId {\n return `${peer}` as PeerId;\n}\n\nfunction isValidSpan(span: CounterSpan): boolean {\n return (\n Number.isSafeInteger(span.start) &&\n Number.isSafeInteger(span.end) &&\n span.start < span.end\n );\n}\n\nclass UnresolvedSpanTracker {\n private readonly spans = new Map<PeerId, CounterSpan[]>();\n\n addAll(pending: LoroImportStatus[\"pending\"]): void {\n if (pending == null) {\n return;\n }\n for (const [peer, span] of pending) {\n this.add(normalizePeerId(peer), span);\n }\n }\n\n resolveAll(success: LoroImportStatus[\"success\"]): void {\n for (const [peer, span] of success) {\n this.resolve(normalizePeerId(peer), span);\n }\n }\n\n isEmpty(): boolean {\n return this.spans.size === 0;\n }\n\n toArray(): CrdtUnresolvedSpan[] {\n const out: CrdtUnresolvedSpan[] = [];\n for (const [peer, spans] of this.spans) {\n for (const span of spans) {\n out.push({ peer, start: span.start, end: span.end });\n }\n }\n return out;\n }\n\n reset(): void {\n this.spans.clear();\n }\n\n private add(peer: PeerId, incoming: CounterSpan): void {\n if (!isValidSpan(incoming)) {\n return;\n }\n const existing = this.spans.get(peer) ?? [];\n existing.push({ start: incoming.start, end: incoming.end });\n existing.sort((a, b) => a.start - b.start || a.end - b.end);\n const merged: CounterSpan[] = [];\n for (const span of existing) {\n const last = merged[merged.length - 1];\n if (last == null || span.start > last.end) {\n merged.push({ start: span.start, end: span.end });\n } else {\n merged[merged.length - 1] = {\n start: last.start,\n end: Math.max(last.end, span.end),\n };\n }\n }\n this.spans.set(peer, merged);\n }\n\n private resolve(peer: PeerId, applied: CounterSpan): void {\n if (!isValidSpan(applied)) {\n return;\n }\n const existing = this.spans.get(peer);\n if (existing == null) {\n return;\n }\n const remaining: CounterSpan[] = [];\n for (const span of existing) {\n if (applied.end <= span.start || applied.start >= span.end) {\n remaining.push(span);\n continue;\n }\n if (span.start < applied.start) {\n remaining.push({ start: span.start, end: applied.start });\n }\n if (applied.end < span.end) {\n remaining.push({ start: applied.end, end: span.end });\n }\n }\n if (remaining.length === 0) {\n this.spans.delete(peer);\n } else {\n this.spans.set(peer, remaining);\n }\n }\n}\n\nfunction trackImportStatus(\n tracker: UnresolvedSpanTracker,\n status: LoroImportStatus,\n): void {\n tracker.addAll(status.pending);\n tracker.resolveAll(status.success);\n}\n\nfunction versionFromImportSuccess(status: LoroImportStatus): DocVersionRecord {\n const version: DocVersionRecord = {};\n for (const [peer, span] of status.success) {\n if (!isValidSpan(span)) {\n continue;\n }\n const peerId = normalizePeerId(peer);\n version[peerId] = Math.max(version[peerId] ?? 0, span.end);\n }\n return version;\n}\n\nfunction versionFromSnapshotImport(\n snapshot: Uint8Array,\n status: LoroImportStatus,\n tracker: UnresolvedSpanTracker,\n): DocVersionRecord {\n if (!tracker.isEmpty()) {\n return versionFromImportSuccess(status);\n }\n return extractVersionFromUpdate(snapshot) ?? versionFromImportSuccess(status);\n}\n\nfunction applyOutcome(\n version: DocVersionRecord,\n tracker: UnresolvedSpanTracker,\n): CrdtApplyOutcome<JsonObject> {\n const unresolved = tracker.toArray();\n return {\n version,\n complete: unresolved.length === 0,\n ...(unresolved.length === 0 ? {} : { unresolved }),\n };\n}\n\n/**\n * Creates a `CrdtAdapter` for one `LoroDoc`.\n *\n * The `LoroDoc` must be attached (not detached or checked out to a historical\n * version). If the document is detached, call `doc.checkoutToLatest()` or\n * `doc.attach()` before passing it here.\n *\n * @example\n * ```ts\n * const doc = new LoroDoc();\n * const adapter = createLoroDocAdapter(doc);\n * const transport = new StreamsCrdt({ streamUrl, adapter, auth });\n * ```\n */\nexport function createLoroDocAdapter(\n doc: LoroDoc,\n): CrdtAdapter<JsonObject> {\n const unresolved = new UnresolvedSpanTracker();\n return {\n emptyVersion: () => ({}),\n mergeVersions: (base, incoming) =>\n mergeDocVersions(base as DocVersionRecord, incoming as DocVersionRecord),\n exportSnapshot: () => {\n assertDocAttached(doc);\n return doc.export({ mode: \"snapshot\" });\n },\n applySnapshot: (snapshot) => {\n assertDocAttached(doc);\n const status = doc.import(snapshot) as LoroImportStatus;\n unresolved.reset();\n trackImportStatus(unresolved, status);\n return applyOutcome(\n versionFromSnapshotImport(snapshot, status, unresolved),\n unresolved,\n );\n },\n exportUpdates: (fromVersion) => {\n assertDocAttached(doc);\n const currentVersion = toDocVersionRecord(doc.version());\n if (\n !hasUpdatesSince(\n fromVersion as DocVersionRecord,\n currentVersion,\n )\n ) {\n return undefined;\n }\n const updates = [\n doc.export({\n mode: \"update\",\n from: VersionVector.parseJSON(toDocVersionMap(fromVersion)),\n }),\n ].filter((update) => update.byteLength > 0);\n if (updates.length === 0) {\n return undefined;\n }\n let version: DocVersionRecord = {};\n for (const update of updates) {\n const next = extractVersionFromUpdate(update);\n if (next != null) {\n version = mergeDocVersions(version, next);\n }\n }\n return {\n updates,\n version,\n };\n },\n applyRemoteUpdates: (updates, remoterVersion) => {\n assertDocAttached(doc);\n if (updates.length > 0) {\n const status = batchImportUpdates(doc, updates);\n trackImportStatus(unresolved, status);\n const version = mergeDocVersions(\n remoterVersion as DocVersionRecord,\n versionFromImportSuccess(status),\n );\n return applyOutcome(version, unresolved);\n }\n return applyOutcome(remoterVersion as DocVersionRecord, unresolved);\n },\n createIsolatedAdapter: () => {\n let isolatedDoc = new LoroDoc();\n const isolatedUnresolved = new UnresolvedSpanTracker();\n return {\n applySnapshot: (snapshot) => {\n isolatedDoc = new LoroDoc();\n isolatedUnresolved.reset();\n if (snapshot.byteLength > 0) {\n const status = isolatedDoc.import(snapshot) as LoroImportStatus;\n trackImportStatus(isolatedUnresolved, status);\n return applyOutcome(\n versionFromSnapshotImport(snapshot, status, isolatedUnresolved),\n isolatedUnresolved,\n );\n }\n return applyOutcome({}, isolatedUnresolved);\n },\n applyRemoteUpdates: (updates, remoterVersion) => {\n if (updates.length > 0) {\n const status = batchImportUpdates(isolatedDoc, updates);\n trackImportStatus(isolatedUnresolved, status);\n const version = mergeDocVersions(\n remoterVersion as DocVersionRecord,\n versionFromImportSuccess(status),\n );\n return applyOutcome(version, isolatedUnresolved);\n }\n return applyOutcome(\n remoterVersion as DocVersionRecord,\n isolatedUnresolved,\n );\n },\n };\n },\n subscribeLocalUpdates: (listener) => {\n assertDocAttached(doc);\n return doc.subscribeLocalUpdates((update) => {\n const version = extractVersionFromUpdate(update) ?? {};\n listener({ updates: [update], version });\n });\n },\n };\n}\n\n/**\n * Structural subset of Loro's `EphemeralStore`.\n */\nexport interface LoroEphemeralStoreLike {\n /** Applies one opaque Loro ephemeral update. */\n apply(update: Uint8Array): void;\n /** Subscribes to locally produced ephemeral updates. */\n subscribeLocalUpdates(listener: (update: Uint8Array) => void): () => void;\n}\n\n/**\n * Creates an ephemeral stream adaptor for a Loro `EphemeralStore`.\n *\n * @example\n * ```ts\n * const room = new EphemeralStreamCrdt({\n * streamUrl,\n * auth,\n * adaptor: EphemeralStoreAdaptor(store),\n * });\n * ```\n */\nexport function EphemeralStoreAdaptor(\n store: LoroEphemeralStoreLike,\n): EphemeralStreamAdaptor {\n let applyingRemoteDepth = 0;\n return {\n applyRemoteUpdates: (updates) => {\n applyingRemoteDepth += 1;\n try {\n for (const update of updates) {\n store.apply(update);\n }\n } finally {\n applyingRemoteDepth -= 1;\n }\n },\n subscribeLocalUpdates: (listener) => {\n const unsubscribeLocalUpdates = store.subscribeLocalUpdates((update) => {\n if (applyingRemoteDepth > 0) {\n return;\n }\n listener(update);\n });\n return () => {\n unsubscribeLocalUpdates();\n };\n },\n };\n}\n\nexport const createLoroEphemeralStoreAdaptor = EphemeralStoreAdaptor;\n"],"mappings":"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"}
@@ -176,6 +176,7 @@ type TransportRoomStatus = "joined" | "reconnecting" | "disconnected" | "error";
176
176
  * - `network_error` — fetch-level failure (DNS, connection refused, etc.).
177
177
  * - `timeout` — connect or poll deadline exceeded.
178
178
  * - `gone` — the server indicated the stream was deleted or moved (410).
179
+ * - `apply_incomplete` — a CRDT adapter could not fully apply remote data.
179
180
  * - `internal_error` — adapter or runtime logic failed locally.
180
181
  * - `unknown` — catch-all for unexpected errors.
181
182
  */
@@ -205,6 +206,11 @@ type TransportError = {
205
206
  readonly code: "gone";
206
207
  readonly retryable: true;
207
208
  readonly message: string;
209
+ } | {
210
+ readonly code: "apply_incomplete";
211
+ readonly retryable: true;
212
+ readonly reason: "loro_pending_updates";
213
+ readonly message: string;
208
214
  } | {
209
215
  readonly code: "internal_error";
210
216
  readonly retryable: false;
@@ -419,6 +425,23 @@ interface CrdtUpdateBatch<TVersion extends JsonObject> {
419
425
  readonly version: TVersion;
420
426
  }
421
427
  type ExportedCrdtUpdateBatch<TVersion extends JsonObject> = CrdtUpdateBatch<TVersion> | null | undefined;
428
+ interface CrdtUnresolvedSpan {
429
+ /** CRDT peer/client id that still has an unresolved counter span. */
430
+ readonly peer: string;
431
+ /** Inclusive start counter. */
432
+ readonly start: number;
433
+ /** Exclusive end counter. */
434
+ readonly end: number;
435
+ }
436
+ interface CrdtApplyOutcome<TVersion extends JsonObject> {
437
+ /** Inferred remote version that is actually applied to local durable CRDT state. */
438
+ readonly version: TVersion;
439
+ /** `false` means remote bytes were seen but cannot yet be durably checkpointed. */
440
+ readonly complete: boolean;
441
+ /** Adapter-specific unresolved dependency spans, when known. */
442
+ readonly unresolved?: readonly CrdtUnresolvedSpan[];
443
+ }
444
+ type CrdtApplyReturn<TVersion extends JsonObject> = TVersion | CrdtApplyOutcome<TVersion>;
422
445
  /**
423
446
  * Isolated apply-only adapter used for 410/bootstrap recovery.
424
447
  *
@@ -426,9 +449,9 @@ type ExportedCrdtUpdateBatch<TVersion extends JsonObject> = CrdtUpdateBatch<TVer
426
449
  */
427
450
  interface IsolatedCrdtAdapter<TVersion extends JsonObject> {
428
451
  /** Imports a full snapshot into isolated state and returns its version. */
429
- applySnapshot(snapshot: Uint8Array): MaybePromise<TVersion>;
452
+ applySnapshot(snapshot: Uint8Array): MaybePromise<CrdtApplyReturn<TVersion>>;
430
453
  /** Applies retained remote updates into isolated state and returns the new version. */
431
- applyRemoteUpdates(updates: Uint8Array[], remoterVersion: TVersion): MaybePromise<TVersion>;
454
+ applyRemoteUpdates(updates: Uint8Array[], remoterVersion: TVersion): MaybePromise<CrdtApplyReturn<TVersion>>;
432
455
  }
433
456
  /**
434
457
  * Adapter contract between `StreamsCrdt` and a concrete CRDT runtime.
@@ -441,7 +464,7 @@ interface CrdtAdapter<TVersion extends JsonObject> {
441
464
  /** Exports a full snapshot of the current local CRDT state. */
442
465
  exportSnapshot(): Uint8Array;
443
466
  /** Imports a full snapshot, replacing local state. Returns the version after import. */
444
- applySnapshot(snapshot: Uint8Array): MaybePromise<TVersion>;
467
+ applySnapshot(snapshot: Uint8Array): MaybePromise<CrdtApplyReturn<TVersion>>;
445
468
  /** Exports incremental updates since `fromVersion`. Returns `null`/`undefined` when there are no new updates. */
446
469
  exportUpdates(fromVersion: TVersion): ExportedCrdtUpdateBatch<TVersion>;
447
470
  /**
@@ -458,7 +481,7 @@ interface CrdtAdapter<TVersion extends JsonObject> {
458
481
  * invariant to distinguish self-replay from another writer advancing the
459
482
  * remote version.
460
483
  */
461
- applyRemoteUpdates(updates: Uint8Array[], remoterVersion: TVersion): MaybePromise<TVersion>;
484
+ applyRemoteUpdates(updates: Uint8Array[], remoterVersion: TVersion): MaybePromise<CrdtApplyReturn<TVersion>>;
462
485
  /**
463
486
  * Creates an isolated apply-only adapter for 410 recovery.
464
487
  *
@@ -629,6 +652,7 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
629
652
  private nextProducerSeq;
630
653
  private frozenLocalAppend?;
631
654
  private committedPendingCount?;
655
+ private committedLocalStreamCursor?;
632
656
  private readonly pendingWriteOnly;
633
657
  private writeOnlyCursor;
634
658
  private unsubscribeWriteOnlyLocal?;
@@ -706,8 +730,11 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
706
730
  private bootstrapState;
707
731
  private catchup;
708
732
  private catchupOrGone;
733
+ private recoverLiveIncompleteRemoteApply;
709
734
  private appendLocalBatch;
710
735
  private appendLocalBatchDurably;
736
+ private localAppendReadCursor;
737
+ private catchupLocalAppend;
711
738
  private appendLocalBatchRemote;
712
739
  private getOrCreateFrozenLocalAppend;
713
740
  private appendFrozenLocalBatchRemote;
@@ -716,6 +743,7 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
716
743
  private commitProducerAck;
717
744
  private abandonFrozenLocalAppend;
718
745
  private consumeCommittedPendingCount;
746
+ private consumeCommittedLocalStreamCursor;
719
747
  /**
720
748
  * Ensures the target stream exists, creating it if necessary.
721
749
  * Tolerates 409 Conflict (stream already created concurrently).
@@ -845,5 +873,5 @@ declare function createStreamUrl(input: {
845
873
  baseUrl?: string;
846
874
  }): string;
847
875
  //#endregion
848
- export { RemoteCursorStore as $, Result as A, TransportJoinParams as B, PayloadProtectionEncryptionOptions as C, PayloadProtectionReadPolicy as D, PayloadProtectionOptions as E, StreamsAuthProvider as F, WriteOnlyAppendResult as G, TransportSnapshotUploadSuccess as H, StreamsCrdtOptions as I, InMemoryRemoteCursorStore as J, BeforeRemoteCursorSaveContext as K, TransportCreateStreamSuccess as L, SnapshotTransformHook as M, SnapshotUploadOptions as N, PayloadProtectionScope as O, StreamsAuthContext as P, RemoteCursorSaveSource as Q, TransportDeleteStreamSuccess as R, JsonValue as S, PayloadProtectionKeyProvider as T, TransportSubscription as U, TransportRoomStatus as V, TransportSyncSuccess as W, IndexedDbRemoteCursorStoreOptions as X, IndexedDbRemoteCursorStore as Y, RemoteCursor as Z, EphemeralStreamCrdtOptions as _, EphemeralStreamCrdt as a, IsolatedCrdtAdapter as b, CrdtUpdateBatch as c, E2eeKeyProvider as d, createInitialRemoteCursor as et, E2eeOptions as f, EphemeralStreamAdaptor as g, E2eeWritePolicy as h, PayloadProtectionError as i, SnapshotCodec as j, PayloadProtectionWritePolicy as k, E2eeEncryptionOptions as l, E2eeScope as m, isValidBucketId as n, StreamsCrdt as o, E2eeReadPolicy as p, BeforeRemoteCursorSaveHook as q, isValidRillId as r, CrdtAdapter as s, createStreamUrl as t, E2eeKey as u, EphemeralStreamJoinParams as v, PayloadProtectionKey as w, JsonObject as x, EphemeralStreamSubscription as y, TransportError as z };
849
- //# sourceMappingURL=stream-id-B6FzWgkM.d.ts.map
876
+ export { IndexedDbRemoteCursorStoreOptions as $, PayloadProtectionReadPolicy as A, TransportCreateStreamSuccess as B, IsolatedCrdtAdapter as C, PayloadProtectionKey as D, PayloadProtectionEncryptionOptions as E, SnapshotTransformHook as F, TransportSnapshotUploadSuccess as G, TransportError as H, SnapshotUploadOptions as I, WriteOnlyAppendResult as J, TransportSubscription as K, StreamsAuthContext as L, PayloadProtectionWritePolicy as M, Result as N, PayloadProtectionKeyProvider as O, SnapshotCodec as P, IndexedDbRemoteCursorStore as Q, StreamsAuthProvider as R, EphemeralStreamSubscription as S, JsonValue as T, TransportJoinParams as U, TransportDeleteStreamSuccess as V, TransportRoomStatus as W, BeforeRemoteCursorSaveHook as X, BeforeRemoteCursorSaveContext as Y, InMemoryRemoteCursorStore as Z, E2eeScope as _, EphemeralStreamCrdt as a, EphemeralStreamCrdtOptions as b, CrdtApplyOutcome as c, CrdtUpdateBatch as d, RemoteCursor as et, E2eeEncryptionOptions as f, E2eeReadPolicy as g, E2eeOptions as h, PayloadProtectionError as i, PayloadProtectionScope as j, PayloadProtectionOptions as k, CrdtApplyReturn as l, E2eeKeyProvider as m, isValidBucketId as n, RemoteCursorStore as nt, StreamsCrdt as o, E2eeKey as p, TransportSyncSuccess as q, isValidRillId as r, createInitialRemoteCursor as rt, CrdtAdapter as s, createStreamUrl as t, RemoteCursorSaveSource as tt, CrdtUnresolvedSpan as u, E2eeWritePolicy as v, JsonObject as w, EphemeralStreamJoinParams as x, EphemeralStreamAdaptor as y, StreamsCrdtOptions as z };
877
+ //# sourceMappingURL=stream-id-Bi5rAZfP.d.ts.map