@loro-dev/streams-crdt 0.7.1 → 0.8.1
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 +97 -0
- package/dist/flock.d.ts +2 -2
- package/dist/flock.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/loro.d.ts +25 -2
- package/dist/loro.js +37 -2
- package/dist/loro.js.map +1 -1
- package/dist/{stream-id-CS7SzuLW.d.ts → stream-id-B6FzWgkM.d.ts} +106 -2
- package/dist/{stream-id-D0qcbI5c.js → stream-id-CCPEBb6G.js} +1011 -400
- package/dist/stream-id-CCPEBb6G.js.map +1 -0
- package/package.json +4 -2
- package/dist/stream-id-D0qcbI5c.js.map +0 -1
package/README.md
CHANGED
|
@@ -118,6 +118,103 @@ 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 never republishes state imported from other peers. If a client needs to keep
|
|
126
|
+
its own presence alive, update that client's own keys with `store.set()` so the
|
|
127
|
+
store emits a fresh local update.
|
|
128
|
+
|
|
129
|
+
Ephemeral live delivery is latest-state oriented. The server may keep only a
|
|
130
|
+
tiny live fanout queue for current SSE subscribers and may disconnect slow
|
|
131
|
+
subscribers instead of buffering every intermediate cursor or presence update.
|
|
132
|
+
The client reconnects and receives a fresh bootstrap, so the local store
|
|
133
|
+
converges to current state without requiring offset replay.
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { EphemeralStore } from "loro-crdt";
|
|
137
|
+
import {
|
|
138
|
+
EphemeralStreamCrdt,
|
|
139
|
+
EphemeralStoreAdaptor,
|
|
140
|
+
createStreamUrl,
|
|
141
|
+
} from "@loro-dev/streams-crdt/loro";
|
|
142
|
+
|
|
143
|
+
const store = new EphemeralStore();
|
|
144
|
+
const docStreamUrl = createStreamUrl({
|
|
145
|
+
bucketId: "my-bucket",
|
|
146
|
+
streamId: "doc-1",
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const presence = new EphemeralStreamCrdt({
|
|
150
|
+
streamUrl: `${docStreamUrl}?ephemeral=presence`,
|
|
151
|
+
auth: async () => "<gateway-jwt>",
|
|
152
|
+
adaptor: EphemeralStoreAdaptor(store),
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const joinedPresence = await presence.join();
|
|
156
|
+
if (!joinedPresence.ok) throw joinedPresence.error;
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Ephemeral stream URL format
|
|
160
|
+
|
|
161
|
+
An ephemeral stream URL is the durable stream URL for the same document plus a
|
|
162
|
+
channel query parameter:
|
|
163
|
+
|
|
164
|
+
```txt
|
|
165
|
+
{baseUrl}/ds/{bucketId}/{streamId}?ephemeral={channel}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
For example, if the durable Flock document stream is:
|
|
169
|
+
|
|
170
|
+
```txt
|
|
171
|
+
https://streams-api.loro.dev/ds/my-bucket/flock-cm9vbS0x
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
then the matching presence stream is:
|
|
175
|
+
|
|
176
|
+
```txt
|
|
177
|
+
https://streams-api.loro.dev/ds/my-bucket/flock-cm9vbS0x?ephemeral=presence
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Use the same `bucketId` and `streamId` as the durable `StreamsCrdt` room. The
|
|
181
|
+
`ephemeral` value names a side-channel for that room, such as `presence`,
|
|
182
|
+
`cursor`, or `selection`. Different channels under the same durable stream are
|
|
183
|
+
separate in-memory rooms. The compatibility spelling `?awareness=presence` is
|
|
184
|
+
also accepted, but new code should use `?ephemeral=presence`.
|
|
185
|
+
|
|
186
|
+
When an app exposes a room link such as `/rooms/design-review-42`, the link
|
|
187
|
+
normally maps to one durable stream id first, and both durable and ephemeral
|
|
188
|
+
sync derive from that same id:
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
const roomId = "design-review-42";
|
|
192
|
+
const durableStreamUrl = createStreamUrl({
|
|
193
|
+
bucketId: "my-bucket",
|
|
194
|
+
streamId: `flock-${roomId}`,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const presenceStreamUrl = new URL(durableStreamUrl);
|
|
198
|
+
presenceStreamUrl.searchParams.set("ephemeral", "presence");
|
|
199
|
+
|
|
200
|
+
const flockTransport = new StreamsCrdt({
|
|
201
|
+
streamUrl: durableStreamUrl,
|
|
202
|
+
auth,
|
|
203
|
+
adapter: createFlockAdapter(flock),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const presenceTransport = new EphemeralStreamCrdt({
|
|
207
|
+
streamUrl: presenceStreamUrl.toString(),
|
|
208
|
+
auth,
|
|
209
|
+
adaptor: EphemeralStoreAdaptor(store),
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`EphemeralStreamCrdt` does not create or delete the durable stream. It posts
|
|
214
|
+
updates to the ephemeral URL and opens SSE on the same URL with `live=sse`.
|
|
215
|
+
Ephemeral state is not persisted with the Flock document stream; it is scoped by
|
|
216
|
+
the URL path plus channel and is meant for current live state only.
|
|
217
|
+
|
|
121
218
|
## Quick Start (Flock)
|
|
122
219
|
|
|
123
220
|
```ts
|
package/dist/flock.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
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
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
|
|
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";
|
|
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
|
|
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-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 };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
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-CCPEBb6G.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
|
|
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
2
|
import { LoroDoc } from "loro-crdt";
|
|
3
3
|
|
|
4
4
|
//#region src/loro.d.ts
|
|
@@ -17,6 +17,29 @@ 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
|
+
/** Applies one opaque Loro ephemeral update. */
|
|
25
|
+
apply(update: Uint8Array): void;
|
|
26
|
+
/** Subscribes to locally produced ephemeral updates. */
|
|
27
|
+
subscribeLocalUpdates(listener: (update: Uint8Array) => void): () => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Creates an ephemeral stream adaptor for a Loro `EphemeralStore`.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const room = new EphemeralStreamCrdt({
|
|
35
|
+
* streamUrl,
|
|
36
|
+
* auth,
|
|
37
|
+
* adaptor: EphemeralStoreAdaptor(store),
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
declare function EphemeralStoreAdaptor(store: LoroEphemeralStoreLike): EphemeralStreamAdaptor;
|
|
42
|
+
declare const createLoroEphemeralStoreAdaptor: typeof EphemeralStoreAdaptor;
|
|
20
43
|
//#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 };
|
|
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 };
|
|
22
45
|
//# sourceMappingURL=loro.d.ts.map
|
package/dist/loro.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
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";
|
|
2
2
|
import { LoroDoc, VersionVector, decodeImportBlobMeta } from "loro-crdt";
|
|
3
3
|
//#region src/loro.ts
|
|
4
4
|
function batchImportUpdates(doc, updates) {
|
|
@@ -125,7 +125,42 @@ 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
|
+
let applyingRemoteDepth = 0;
|
|
142
|
+
return {
|
|
143
|
+
applyRemoteUpdates: (updates) => {
|
|
144
|
+
applyingRemoteDepth += 1;
|
|
145
|
+
try {
|
|
146
|
+
for (const update of updates) store.apply(update);
|
|
147
|
+
} finally {
|
|
148
|
+
applyingRemoteDepth -= 1;
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
subscribeLocalUpdates: (listener) => {
|
|
152
|
+
const unsubscribeLocalUpdates = store.subscribeLocalUpdates((update) => {
|
|
153
|
+
if (applyingRemoteDepth > 0) return;
|
|
154
|
+
listener(update);
|
|
155
|
+
});
|
|
156
|
+
return () => {
|
|
157
|
+
unsubscribeLocalUpdates();
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const createLoroEphemeralStoreAdaptor = EphemeralStoreAdaptor;
|
|
128
163
|
//#endregion
|
|
129
|
-
export { PayloadProtectionError as E2eeError, PayloadProtectionError, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createInitialRemoteCursor, createLoroDocAdapter, createStreamUrl, isValidBucketId, isValidRillId };
|
|
164
|
+
export { PayloadProtectionError as E2eeError, PayloadProtectionError, EphemeralStoreAdaptor, EphemeralStreamCrdt, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createInitialRemoteCursor, createLoroDocAdapter, createLoroEphemeralStoreAdaptor, createStreamUrl, isValidBucketId, isValidRillId };
|
|
130
165
|
|
|
131
166
|
//# 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 /** 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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"}
|
|
@@ -392,6 +392,25 @@ 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. Outbound data is limited to updates emitted by
|
|
401
|
+
* `subscribeLocalUpdates()` so imported remote presence is never republished.
|
|
402
|
+
*/
|
|
403
|
+
interface EphemeralStreamAdaptor {
|
|
404
|
+
/** Applies updates received from the remote ephemeral stream. */
|
|
405
|
+
applyRemoteUpdates(updates: readonly Uint8Array[]): MaybePromise<void>;
|
|
406
|
+
/** Subscribes to local ephemeral updates. Returns an unsubscribe function. */
|
|
407
|
+
subscribeLocalUpdates(listener: (update: Uint8Array) => void): () => void;
|
|
408
|
+
}
|
|
409
|
+
interface EphemeralStreamJoinParams {
|
|
410
|
+
/** Called when the live ephemeral subscription status changes. */
|
|
411
|
+
readonly onStatusChange?: (status: "connecting" | TransportRoomStatus) => void;
|
|
412
|
+
}
|
|
413
|
+
interface EphemeralStreamSubscription extends TransportSubscription {}
|
|
395
414
|
/**
|
|
396
415
|
* Local CRDT updates ready to append to Durable Streams.
|
|
397
416
|
*/
|
|
@@ -462,6 +481,29 @@ interface CrdtAdapter<TVersion extends JsonObject> {
|
|
|
462
481
|
/** Optional headers to include when creating the stream (e.g. retention config). */
|
|
463
482
|
getCreateStreamHeaders?(): MaybePromise<Record<string, string>>;
|
|
464
483
|
}
|
|
484
|
+
interface EphemeralStreamCrdtOptions {
|
|
485
|
+
/**
|
|
486
|
+
* Full ephemeral stream URL.
|
|
487
|
+
*
|
|
488
|
+
* The URL must include the channel query, usually
|
|
489
|
+
* `?ephemeral=presence` or the compatibility alias `?awareness=presence`.
|
|
490
|
+
*/
|
|
491
|
+
readonly streamUrl: string;
|
|
492
|
+
/**
|
|
493
|
+
* Ephemeral store adaptor.
|
|
494
|
+
*
|
|
495
|
+
* The public spelling intentionally matches Loro's "adaptor" examples.
|
|
496
|
+
*/
|
|
497
|
+
readonly adaptor: EphemeralStreamAdaptor;
|
|
498
|
+
/** Auth token source used for ephemeral POST and SSE requests. */
|
|
499
|
+
readonly auth?: StreamsAuthProvider;
|
|
500
|
+
/** Optional fetch implementation for tests, workers, or custom runtimes. */
|
|
501
|
+
readonly fetch?: (input: string, init?: RequestInit) => Promise<Response>;
|
|
502
|
+
/** Reconnect policy for the SSE read loop and local POST retry loop. */
|
|
503
|
+
readonly reconnectConfig?: Partial<ReconnectConfig>;
|
|
504
|
+
/** Enables diagnostic console logging for terminal read/write failures. */
|
|
505
|
+
readonly debug?: boolean;
|
|
506
|
+
}
|
|
465
507
|
/**
|
|
466
508
|
* Construction options for one `StreamsCrdt` instance.
|
|
467
509
|
*
|
|
@@ -585,6 +627,8 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
585
627
|
private readonly producerId;
|
|
586
628
|
private producerEpoch;
|
|
587
629
|
private nextProducerSeq;
|
|
630
|
+
private frozenLocalAppend?;
|
|
631
|
+
private committedPendingCount?;
|
|
588
632
|
private readonly pendingWriteOnly;
|
|
589
633
|
private writeOnlyCursor;
|
|
590
634
|
private unsubscribeWriteOnlyLocal?;
|
|
@@ -663,6 +707,15 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
663
707
|
private catchup;
|
|
664
708
|
private catchupOrGone;
|
|
665
709
|
private appendLocalBatch;
|
|
710
|
+
private appendLocalBatchDurably;
|
|
711
|
+
private appendLocalBatchRemote;
|
|
712
|
+
private getOrCreateFrozenLocalAppend;
|
|
713
|
+
private appendFrozenLocalBatchRemote;
|
|
714
|
+
private finalizeDirectLocalAppendIfNeeded;
|
|
715
|
+
private createCurrentProducerSession;
|
|
716
|
+
private commitProducerAck;
|
|
717
|
+
private abandonFrozenLocalAppend;
|
|
718
|
+
private consumeCommittedPendingCount;
|
|
666
719
|
/**
|
|
667
720
|
* Ensures the target stream exists, creating it if necessary.
|
|
668
721
|
* Tolerates 409 Conflict (stream already created concurrently).
|
|
@@ -712,6 +765,57 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
712
765
|
private logError;
|
|
713
766
|
}
|
|
714
767
|
//#endregion
|
|
768
|
+
//#region src/ephemeral-runtime.d.ts
|
|
769
|
+
/**
|
|
770
|
+
* Realtime transport for one ephemeral CRDT stream.
|
|
771
|
+
*
|
|
772
|
+
* This is intentionally separate from `StreamsCrdt`: it bootstraps through
|
|
773
|
+
* the first SSE event, then tails live deltas, and never stores a replay
|
|
774
|
+
* offset, snapshot checkpoint, or remote cursor.
|
|
775
|
+
*/
|
|
776
|
+
declare class EphemeralStreamCrdt {
|
|
777
|
+
/** Full ephemeral stream URL, including `?ephemeral=` or `?awareness=`. */
|
|
778
|
+
readonly streamUrl: string;
|
|
779
|
+
/** Store-specific adapter used to encode local state and apply remote bytes. */
|
|
780
|
+
readonly adaptor: EphemeralStreamAdaptor;
|
|
781
|
+
/** Low-level HTTP/SSE transport. It is reused only for auth, fetch, and parsing helpers. */
|
|
782
|
+
private readonly client;
|
|
783
|
+
/** Retry policy shared by the read loop and local write queue. */
|
|
784
|
+
private readonly reconnectConfig;
|
|
785
|
+
/** Enables diagnostic console errors for failed read/write loops. */
|
|
786
|
+
private readonly debug;
|
|
787
|
+
/** Current shared subscription state, if the stream is joined. */
|
|
788
|
+
private joinState?;
|
|
789
|
+
constructor(options: EphemeralStreamCrdtOptions);
|
|
790
|
+
join(params?: EphemeralStreamJoinParams): Promise<{
|
|
791
|
+
readonly ok: true;
|
|
792
|
+
readonly value: EphemeralStreamSubscription;
|
|
793
|
+
} | {
|
|
794
|
+
readonly ok: false;
|
|
795
|
+
readonly error: TransportError;
|
|
796
|
+
}>;
|
|
797
|
+
rejoin(): void;
|
|
798
|
+
close(): Promise<void>;
|
|
799
|
+
private createJoinState;
|
|
800
|
+
private startJoin;
|
|
801
|
+
private waitForFirstOpen;
|
|
802
|
+
private createSubscription;
|
|
803
|
+
private onStatusChange;
|
|
804
|
+
private disposeJoinState;
|
|
805
|
+
private resolveFirstOpen;
|
|
806
|
+
private rejectFirstOpen;
|
|
807
|
+
private runReadLoop;
|
|
808
|
+
private enqueueLocal;
|
|
809
|
+
private flushPendingLocal;
|
|
810
|
+
private notifySyncWaiters;
|
|
811
|
+
private rejectSyncWaiters;
|
|
812
|
+
private setReadStatus;
|
|
813
|
+
private setWriteStatus;
|
|
814
|
+
private recomputeStatus;
|
|
815
|
+
private computeStatus;
|
|
816
|
+
private logError;
|
|
817
|
+
}
|
|
818
|
+
//#endregion
|
|
715
819
|
//#region src/payload-protection.d.ts
|
|
716
820
|
type PayloadProtectionFailureReason = "plaintext_forbidden" | "missing_read_key" | "decrypt_failed" | "invalid_envelope" | "wrong_payload_kind" | "encrypt_failed";
|
|
717
821
|
declare class PayloadProtectionError extends Error {
|
|
@@ -741,5 +845,5 @@ declare function createStreamUrl(input: {
|
|
|
741
845
|
baseUrl?: string;
|
|
742
846
|
}): string;
|
|
743
847
|
//#endregion
|
|
744
|
-
export {
|
|
745
|
-
//# sourceMappingURL=stream-id-
|
|
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
|