@loro-dev/streams-crdt 0.7.1 → 0.8.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 CHANGED
@@ -118,6 +118,43 @@ const sub = joined.value;
118
118
  checked out or detached, call `checkoutToLatest()` or `attach()` before passing
119
119
  it to `StreamsCrdt`.
120
120
 
121
+ ## Quick Start (Loro EphemeralStore)
122
+
123
+ Use a separate `EphemeralStreamCrdt` for presence-like Loro `EphemeralStore`
124
+ data. It bootstraps through the first SSE event, posts local ephemeral updates,
125
+ and periodically re-announces `store.encodeAll()` so reconnecting peers can
126
+ recover the current live state without replaying old presence history.
127
+
128
+ Ephemeral live delivery is latest-state oriented. The server may keep only a
129
+ tiny live fanout queue for current SSE subscribers and may disconnect slow
130
+ subscribers instead of buffering every intermediate cursor or presence update.
131
+ The client reconnects and receives a fresh bootstrap, so the local store
132
+ converges to current state without requiring offset replay.
133
+
134
+ ```ts
135
+ import { EphemeralStore } from "loro-crdt";
136
+ import {
137
+ EphemeralStreamCrdt,
138
+ EphemeralStoreAdaptor,
139
+ createStreamUrl,
140
+ } from "@loro-dev/streams-crdt/loro";
141
+
142
+ const store = new EphemeralStore();
143
+ const docStreamUrl = createStreamUrl({
144
+ bucketId: "my-bucket",
145
+ streamId: "doc-1",
146
+ });
147
+
148
+ const presence = new EphemeralStreamCrdt({
149
+ streamUrl: `${docStreamUrl}?ephemeral=presence`,
150
+ auth: async () => "<gateway-jwt>",
151
+ adaptor: EphemeralStoreAdaptor(store),
152
+ });
153
+
154
+ const joinedPresence = await presence.join();
155
+ if (!joinedPresence.ok) throw joinedPresence.error;
156
+ ```
157
+
121
158
  ## Quick Start (Flock)
122
159
 
123
160
  ```ts
package/dist/flock.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as StreamsAuthProvider, B as WriteOnlyAppendResult, C as PayloadProtectionScope, D as SnapshotTransformHook, E as SnapshotCodec, F as TransportJoinParams, G as IndexedDbRemoteCursorStoreOptions, H as BeforeRemoteCursorSaveHook, I as TransportRoomStatus, J as RemoteCursorStore, K as RemoteCursor, L as TransportSnapshotUploadSuccess, M as TransportCreateStreamSuccess, N as TransportDeleteStreamSuccess, O as SnapshotUploadOptions, P as TransportError, R as TransportSubscription, S as PayloadProtectionReadPolicy, T as Result, U as InMemoryRemoteCursorStore, V as BeforeRemoteCursorSaveContext, W as IndexedDbRemoteCursorStore, Y as createInitialRemoteCursor, _ as JsonValue, a as StreamsCrdt, b as PayloadProtectionKeyProvider, c as E2eeEncryptionOptions, d as E2eeOptions, f as E2eeReadPolicy, g as JsonObject, h as IsolatedCrdtAdapter, i as PayloadProtectionError, j as StreamsCrdtOptions, k as StreamsAuthContext, l as E2eeKey, m as E2eeWritePolicy, n as isValidBucketId, o as CrdtAdapter, p as E2eeScope, q as RemoteCursorSaveSource, r as isValidRillId, s as CrdtUpdateBatch, t as createStreamUrl, u as E2eeKeyProvider, v as PayloadProtectionEncryptionOptions, w as PayloadProtectionWritePolicy, x as PayloadProtectionOptions, y as PayloadProtectionKey, z as TransportSyncSuccess } from "./stream-id-CS7SzuLW.js";
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-BXJvwX_O.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, 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, 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 PayloadProtectionError, c as createInitialRemoteCursor, i as StreamsCrdt, n as isValidBucketId, o as InMemoryRemoteCursorStore, r as isValidRillId, s as IndexedDbRemoteCursorStore, t as createStreamUrl } from "./stream-id-D0qcbI5c.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-CF1egQDz.js";
2
2
  import { Flock } from "@loro-dev/flock-wasm";
3
3
  //#region src/flock.ts
4
4
  function isAfter(entry, current) {
@@ -149,6 +149,6 @@ function createFlockAdapter(flock) {
149
149
  };
150
150
  }
151
151
  //#endregion
152
- export { PayloadProtectionError as E2eeError, PayloadProtectionError, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createFlockAdapter, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
152
+ export { PayloadProtectionError as E2eeError, PayloadProtectionError, EphemeralStreamCrdt, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createFlockAdapter, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
153
153
 
154
154
  //# sourceMappingURL=flock.js.map
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as StreamsAuthProvider, B as WriteOnlyAppendResult, C as PayloadProtectionScope, D as SnapshotTransformHook, E as SnapshotCodec, F as TransportJoinParams, G as IndexedDbRemoteCursorStoreOptions, H as BeforeRemoteCursorSaveHook, I as TransportRoomStatus, J as RemoteCursorStore, K as RemoteCursor, L as TransportSnapshotUploadSuccess, M as TransportCreateStreamSuccess, N as TransportDeleteStreamSuccess, O as SnapshotUploadOptions, P as TransportError, R as TransportSubscription, S as PayloadProtectionReadPolicy, T as Result, U as InMemoryRemoteCursorStore, V as BeforeRemoteCursorSaveContext, W as IndexedDbRemoteCursorStore, Y as createInitialRemoteCursor, _ as JsonValue, a as StreamsCrdt, b as PayloadProtectionKeyProvider, c as E2eeEncryptionOptions, d as E2eeOptions, f as E2eeReadPolicy, g as JsonObject, h as IsolatedCrdtAdapter, i as PayloadProtectionError, j as StreamsCrdtOptions, k as StreamsAuthContext, l as E2eeKey, m as E2eeWritePolicy, n as isValidBucketId, o as CrdtAdapter, p as E2eeScope, q as RemoteCursorSaveSource, r as isValidRillId, s as CrdtUpdateBatch, t as createStreamUrl, u as E2eeKeyProvider, v as PayloadProtectionEncryptionOptions, w as PayloadProtectionWritePolicy, x as PayloadProtectionOptions, y as PayloadProtectionKey, z as TransportSyncSuccess } from "./stream-id-CS7SzuLW.js";
2
- export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, 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 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-BXJvwX_O.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 };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as PayloadProtectionError, c as createInitialRemoteCursor, i as StreamsCrdt, n as isValidBucketId, o as InMemoryRemoteCursorStore, r as isValidRillId, s as IndexedDbRemoteCursorStore, t as createStreamUrl } from "./stream-id-D0qcbI5c.js";
2
- export { PayloadProtectionError as E2eeError, PayloadProtectionError, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
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-CF1egQDz.js";
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 { A as StreamsAuthProvider, B as WriteOnlyAppendResult, C as PayloadProtectionScope, D as SnapshotTransformHook, E as SnapshotCodec, F as TransportJoinParams, G as IndexedDbRemoteCursorStoreOptions, H as BeforeRemoteCursorSaveHook, I as TransportRoomStatus, J as RemoteCursorStore, K as RemoteCursor, L as TransportSnapshotUploadSuccess, M as TransportCreateStreamSuccess, N as TransportDeleteStreamSuccess, O as SnapshotUploadOptions, P as TransportError, R as TransportSubscription, S as PayloadProtectionReadPolicy, T as Result, U as InMemoryRemoteCursorStore, V as BeforeRemoteCursorSaveContext, W as IndexedDbRemoteCursorStore, Y as createInitialRemoteCursor, _ as JsonValue, a as StreamsCrdt, b as PayloadProtectionKeyProvider, c as E2eeEncryptionOptions, d as E2eeOptions, f as E2eeReadPolicy, g as JsonObject, h as IsolatedCrdtAdapter, i as PayloadProtectionError, j as StreamsCrdtOptions, k as StreamsAuthContext, l as E2eeKey, m as E2eeWritePolicy, n as isValidBucketId, o as CrdtAdapter, p as E2eeScope, q as RemoteCursorSaveSource, r as isValidRillId, s as CrdtUpdateBatch, t as createStreamUrl, u as E2eeKeyProvider, v as PayloadProtectionEncryptionOptions, w as PayloadProtectionWritePolicy, x as PayloadProtectionOptions, y as PayloadProtectionKey, z as TransportSyncSuccess } from "./stream-id-CS7SzuLW.js";
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-BXJvwX_O.js";
2
2
  import { LoroDoc } from "loro-crdt";
3
3
 
4
4
  //#region src/loro.d.ts
@@ -17,6 +17,31 @@ import { LoroDoc } from "loro-crdt";
17
17
  * ```
18
18
  */
19
19
  declare function createLoroDocAdapter(doc: LoroDoc): CrdtAdapter<JsonObject>;
20
+ /**
21
+ * Structural subset of Loro's `EphemeralStore`.
22
+ */
23
+ interface LoroEphemeralStoreLike {
24
+ /** Encodes the current non-expired store state for bootstrap/lease publish. */
25
+ encodeAll(): Uint8Array;
26
+ /** Applies one opaque Loro ephemeral update. */
27
+ apply(update: Uint8Array): void;
28
+ /** Subscribes to locally produced ephemeral updates. */
29
+ subscribeLocalUpdates(listener: (update: Uint8Array) => void): () => void;
30
+ }
31
+ /**
32
+ * Creates an ephemeral stream adaptor for a Loro `EphemeralStore`.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * const room = new EphemeralStreamCrdt({
37
+ * streamUrl,
38
+ * auth,
39
+ * adaptor: EphemeralStoreAdaptor(store),
40
+ * });
41
+ * ```
42
+ */
43
+ declare function EphemeralStoreAdaptor(store: LoroEphemeralStoreLike): EphemeralStreamAdaptor;
44
+ declare const createLoroEphemeralStoreAdaptor: typeof EphemeralStoreAdaptor;
20
45
  //#endregion
21
- export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, 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, createLoroDocAdapter, createStreamUrl, isValidBucketId, isValidRillId };
46
+ 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 };
22
47
  //# sourceMappingURL=loro.d.ts.map
package/dist/loro.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as PayloadProtectionError, c as createInitialRemoteCursor, i as StreamsCrdt, n as isValidBucketId, o as InMemoryRemoteCursorStore, r as isValidRillId, s as IndexedDbRemoteCursorStore, t as createStreamUrl } from "./stream-id-D0qcbI5c.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-CF1egQDz.js";
2
2
  import { LoroDoc, VersionVector, decodeImportBlobMeta } from "loro-crdt";
3
3
  //#region src/loro.ts
4
4
  function batchImportUpdates(doc, updates) {
@@ -125,7 +125,29 @@ function createLoroDocAdapter(doc) {
125
125
  }
126
126
  };
127
127
  }
128
+ /**
129
+ * Creates an ephemeral stream adaptor for a Loro `EphemeralStore`.
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * const room = new EphemeralStreamCrdt({
134
+ * streamUrl,
135
+ * auth,
136
+ * adaptor: EphemeralStoreAdaptor(store),
137
+ * });
138
+ * ```
139
+ */
140
+ function EphemeralStoreAdaptor(store) {
141
+ return {
142
+ encodeAll: () => store.encodeAll(),
143
+ applyRemoteUpdates: (updates) => {
144
+ for (const update of updates) store.apply(update);
145
+ },
146
+ subscribeLocalUpdates: (listener) => store.subscribeLocalUpdates(listener)
147
+ };
148
+ }
149
+ const createLoroEphemeralStoreAdaptor = EphemeralStoreAdaptor;
128
150
  //#endregion
129
- export { PayloadProtectionError as E2eeError, PayloadProtectionError, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createInitialRemoteCursor, createLoroDocAdapter, createStreamUrl, isValidBucketId, isValidRillId };
151
+ export { PayloadProtectionError as E2eeError, PayloadProtectionError, EphemeralStoreAdaptor, EphemeralStreamCrdt, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createInitialRemoteCursor, createLoroDocAdapter, createLoroEphemeralStoreAdaptor, createStreamUrl, isValidBucketId, isValidRillId };
130
152
 
131
153
  //# sourceMappingURL=loro.js.map
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\";\n\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"],"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 { 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 /** Encodes the current non-expired store state for bootstrap/lease publish. */\n encodeAll(): Uint8Array;\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 return {\n encodeAll: () => store.encodeAll(),\n applyRemoteUpdates: (updates) => {\n for (const update of updates) {\n store.apply(update);\n }\n },\n subscribeLocalUpdates: (listener) =>\n store.subscribeLocalUpdates(listener),\n };\n}\n\nexport const createLoroEphemeralStoreAdaptor = EphemeralStoreAdaptor;\n"],"mappings":"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"}
@@ -392,6 +392,27 @@ interface TransportSubscription {
392
392
  */
393
393
  readonly waitUntilSynced: () => Promise<void>;
394
394
  }
395
+ /**
396
+ * Adapter contract for CRDT-like ephemeral stores.
397
+ *
398
+ * Ephemeral transports bootstrap through the first SSE event, then continue
399
+ * with live deltas only. They do not expose checkpoints, snapshots, or
400
+ * replayable offset history. `encodeAll()` is sent on join/reconnect/heartbeat
401
+ * so peers can converge on the caller's current presence state.
402
+ */
403
+ interface EphemeralStreamAdaptor {
404
+ /** Encodes all currently live local ephemeral state. */
405
+ encodeAll(): MaybePromise<Uint8Array | null | undefined>;
406
+ /** Applies updates received from the remote ephemeral stream. */
407
+ applyRemoteUpdates(updates: readonly Uint8Array[]): MaybePromise<void>;
408
+ /** Subscribes to local ephemeral updates. Returns an unsubscribe function. */
409
+ subscribeLocalUpdates(listener: (update: Uint8Array) => void): () => void;
410
+ }
411
+ interface EphemeralStreamJoinParams {
412
+ /** Called when the live ephemeral subscription status changes. */
413
+ readonly onStatusChange?: (status: "connecting" | TransportRoomStatus) => void;
414
+ }
415
+ interface EphemeralStreamSubscription extends TransportSubscription {}
395
416
  /**
396
417
  * Local CRDT updates ready to append to Durable Streams.
397
418
  */
@@ -462,6 +483,34 @@ interface CrdtAdapter<TVersion extends JsonObject> {
462
483
  /** Optional headers to include when creating the stream (e.g. retention config). */
463
484
  getCreateStreamHeaders?(): MaybePromise<Record<string, string>>;
464
485
  }
486
+ interface EphemeralStreamCrdtOptions {
487
+ /**
488
+ * Full ephemeral stream URL.
489
+ *
490
+ * The URL must include the channel query, usually
491
+ * `?ephemeral=presence` or the compatibility alias `?awareness=presence`.
492
+ */
493
+ readonly streamUrl: string;
494
+ /**
495
+ * Ephemeral store adaptor.
496
+ *
497
+ * The public spelling intentionally matches Loro's "adaptor" examples.
498
+ */
499
+ readonly adaptor: EphemeralStreamAdaptor;
500
+ /** Auth token source used for ephemeral POST and SSE requests. */
501
+ readonly auth?: StreamsAuthProvider;
502
+ /** Optional fetch implementation for tests, workers, or custom runtimes. */
503
+ readonly fetch?: (input: string, init?: RequestInit) => Promise<Response>;
504
+ /** Reconnect policy for the SSE read loop and local POST retry loop. */
505
+ readonly reconnectConfig?: Partial<ReconnectConfig>;
506
+ /**
507
+ * Interval for resending `adaptor.encodeAll()`. Set to `false` to disable.
508
+ * Defaults to 15 seconds, matching presence-style leases.
509
+ */
510
+ readonly heartbeatMs?: number | false;
511
+ /** Enables diagnostic console logging for terminal read/write failures. */
512
+ readonly debug?: boolean;
513
+ }
465
514
  /**
466
515
  * Construction options for one `StreamsCrdt` instance.
467
516
  *
@@ -585,6 +634,8 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
585
634
  private readonly producerId;
586
635
  private producerEpoch;
587
636
  private nextProducerSeq;
637
+ private frozenLocalAppend?;
638
+ private committedPendingCount?;
588
639
  private readonly pendingWriteOnly;
589
640
  private writeOnlyCursor;
590
641
  private unsubscribeWriteOnlyLocal?;
@@ -663,6 +714,15 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
663
714
  private catchup;
664
715
  private catchupOrGone;
665
716
  private appendLocalBatch;
717
+ private appendLocalBatchDurably;
718
+ private appendLocalBatchRemote;
719
+ private getOrCreateFrozenLocalAppend;
720
+ private appendFrozenLocalBatchRemote;
721
+ private finalizeDirectLocalAppendIfNeeded;
722
+ private createCurrentProducerSession;
723
+ private commitProducerAck;
724
+ private abandonFrozenLocalAppend;
725
+ private consumeCommittedPendingCount;
666
726
  /**
667
727
  * Ensures the target stream exists, creating it if necessary.
668
728
  * Tolerates 409 Conflict (stream already created concurrently).
@@ -712,6 +772,61 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
712
772
  private logError;
713
773
  }
714
774
  //#endregion
775
+ //#region src/ephemeral-runtime.d.ts
776
+ /**
777
+ * Realtime transport for one ephemeral CRDT stream.
778
+ *
779
+ * This is intentionally separate from `StreamsCrdt`: it bootstraps through
780
+ * the first SSE event, then tails live deltas, and never stores a replay
781
+ * offset, snapshot checkpoint, or remote cursor.
782
+ */
783
+ declare class EphemeralStreamCrdt {
784
+ /** Full ephemeral stream URL, including `?ephemeral=` or `?awareness=`. */
785
+ readonly streamUrl: string;
786
+ /** Store-specific adapter used to encode local state and apply remote bytes. */
787
+ readonly adaptor: EphemeralStreamAdaptor;
788
+ /** Low-level HTTP/SSE transport. It is reused only for auth, fetch, and parsing helpers. */
789
+ private readonly client;
790
+ /** Retry policy shared by the read loop and local write queue. */
791
+ private readonly reconnectConfig;
792
+ /** Period for republishing full local ephemeral state, or `false` to disable it. */
793
+ private readonly heartbeatMs;
794
+ /** Enables diagnostic console errors for failed read/write loops. */
795
+ private readonly debug;
796
+ /** Current shared subscription state, if the stream is joined. */
797
+ private joinState?;
798
+ constructor(options: EphemeralStreamCrdtOptions);
799
+ join(params?: EphemeralStreamJoinParams): Promise<{
800
+ readonly ok: true;
801
+ readonly value: EphemeralStreamSubscription;
802
+ } | {
803
+ readonly ok: false;
804
+ readonly error: TransportError;
805
+ }>;
806
+ rejoin(): void;
807
+ close(): Promise<void>;
808
+ private createJoinState;
809
+ private startJoin;
810
+ private waitForFirstOpen;
811
+ private createSubscription;
812
+ private onStatusChange;
813
+ private disposeJoinState;
814
+ private resolveFirstOpen;
815
+ private rejectFirstOpen;
816
+ private runReadLoop;
817
+ private startHeartbeat;
818
+ private enqueueFullState;
819
+ private enqueueLocal;
820
+ private flushPendingLocal;
821
+ private notifySyncWaiters;
822
+ private rejectSyncWaiters;
823
+ private setReadStatus;
824
+ private setWriteStatus;
825
+ private recomputeStatus;
826
+ private computeStatus;
827
+ private logError;
828
+ }
829
+ //#endregion
715
830
  //#region src/payload-protection.d.ts
716
831
  type PayloadProtectionFailureReason = "plaintext_forbidden" | "missing_read_key" | "decrypt_failed" | "invalid_envelope" | "wrong_payload_kind" | "encrypt_failed";
717
832
  declare class PayloadProtectionError extends Error {
@@ -741,5 +856,5 @@ declare function createStreamUrl(input: {
741
856
  baseUrl?: string;
742
857
  }): string;
743
858
  //#endregion
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
859
+ 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 };
860
+ //# sourceMappingURL=stream-id-BXJvwX_O.d.ts.map