@loro-dev/streams-crdt 0.4.0 → 0.5.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 +2 -1
- package/dist/flock.d.ts +2 -2
- package/dist/flock.js +60 -2
- package/dist/flock.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/loro.d.ts +2 -2
- package/dist/loro.js +26 -3
- package/dist/loro.js.map +1 -1
- package/dist/{stream-id-CSottEoY.d.ts → stream-id-BCCfzKNm.d.ts} +34 -3
- package/dist/{stream-id-TsPj50Jj.js → stream-id-nPEdFo7c.js} +156 -80
- package/dist/stream-id-nPEdFo7c.js.map +1 -0
- package/dist/zstd.wasm +0 -0
- package/package.json +5 -6
- package/dist/stream-id-TsPj50Jj.js.map +0 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Transport layer for synchronizing CRDT state over
|
|
|
10
10
|
| Class | Responsibility |
|
|
11
11
|
| --- | --- |
|
|
12
12
|
| `StreamsCrdt` | Transport runtime bound to **one** stream URL. Handles auth, reconnect, bootstrap, SSE/long-poll, binary framing, and remote cursor persistence. |
|
|
13
|
-
| `CrdtAdapter` | Binding for **one** local CRDT instance. Handles snapshot export/import, version tracking, local update export,
|
|
13
|
+
| `CrdtAdapter` | Binding for **one** local CRDT instance. Handles snapshot export/import, version tracking, local update export, remote update application, and optional isolated 410 recovery inference. |
|
|
14
14
|
|
|
15
15
|
### Operations
|
|
16
16
|
|
|
@@ -463,6 +463,7 @@ const transport = new StreamsCrdt({
|
|
|
463
463
|
- `StreamsCrdt` — transport runtime class
|
|
464
464
|
- `StreamsCrdtOptions` — constructor options
|
|
465
465
|
- `CrdtAdapter` — adapter contract interface
|
|
466
|
+
- `IsolatedCrdtAdapter` — optional apply-only adapter used for safe 410 recovery
|
|
466
467
|
- `CrdtUpdateBatch` — local update batch type
|
|
467
468
|
- `Result` — Rust-style `Ok | Err` result
|
|
468
469
|
- `StreamsAuthProvider` — auth callback type
|
package/dist/flock.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as RemoteCursorSaveSource, C as TransportSyncSuccess, D as IndexedDbRemoteCursorStore, E as InMemoryRemoteCursorStore, M as createInitialRemoteCursor, O as IndexedDbRemoteCursorStoreOptions, S as TransportSubscription, T as BeforeRemoteCursorSaveHook, _ as TransportCreateStreamSuccess, a as CrdtAdapter, b as TransportJoinParams, c as JsonObject, d as SnapshotCodec, f as SnapshotTransformHook, g as StreamsCrdtOptions, h as StreamsAuthProvider, i as StreamsCrdt, j as RemoteCursorStore, k as RemoteCursor, l as JsonValue, m as StreamsAuthContext, n as isValidBucketId, o as CrdtUpdateBatch, p as SnapshotUploadOptions, r as isValidRillId, s as IsolatedCrdtAdapter, t as createStreamUrl, u as Result, v as TransportDeleteStreamSuccess, w as BeforeRemoteCursorSaveContext, x as TransportRoomStatus, y as TransportError } from "./stream-id-BCCfzKNm.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, type ExportBundle, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, type VersionVector, createFlockAdapter, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
|
|
10
|
+
export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, type ExportBundle, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, type VersionVector, createFlockAdapter, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
|
|
11
11
|
//# sourceMappingURL=flock.d.ts.map
|
package/dist/flock.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-
|
|
1
|
+
import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-nPEdFo7c.js";
|
|
2
|
+
import { Flock } from "@loro-dev/flock-wasm";
|
|
2
3
|
//#region src/flock.ts
|
|
3
4
|
function isAfter(entry, current) {
|
|
4
5
|
return entry.physicalTime > current.physicalTime || entry.physicalTime === current.physicalTime && entry.logicalCounter > current.logicalCounter;
|
|
@@ -41,6 +42,40 @@ function hasEntries(bundle) {
|
|
|
41
42
|
return Object.keys(bundle.entries).length > 0;
|
|
42
43
|
}
|
|
43
44
|
/**
|
|
45
|
+
* Try to merge parsed bundles into a single combined bundle.
|
|
46
|
+
* Falls back to per-bundle import when any key appears in more than one
|
|
47
|
+
* bundle, so Flock's LWW conflict resolution sees every entry.
|
|
48
|
+
*/
|
|
49
|
+
function batchImportBundles(importFn, bundles) {
|
|
50
|
+
if (bundles.length === 0) return;
|
|
51
|
+
if (bundles.length === 1) {
|
|
52
|
+
importFn(bundles[0]);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
56
|
+
let hasCollision = false;
|
|
57
|
+
for (const bundle of bundles) {
|
|
58
|
+
for (const key of Object.keys(bundle.entries)) {
|
|
59
|
+
if (seenKeys.has(key)) {
|
|
60
|
+
hasCollision = true;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
seenKeys.add(key);
|
|
64
|
+
}
|
|
65
|
+
if (hasCollision) break;
|
|
66
|
+
}
|
|
67
|
+
if (hasCollision) {
|
|
68
|
+
for (const bundle of bundles) importFn(bundle);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const combined = {
|
|
72
|
+
...bundles[0],
|
|
73
|
+
entries: { ...bundles[0].entries }
|
|
74
|
+
};
|
|
75
|
+
for (let i = 1; i < bundles.length; i++) Object.assign(combined.entries, bundles[i].entries);
|
|
76
|
+
importFn(combined);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
44
79
|
* Creates a `CrdtAdapter` for one Flock replica.
|
|
45
80
|
*/
|
|
46
81
|
function createFlockAdapter(flock) {
|
|
@@ -70,14 +105,37 @@ function createFlockAdapter(flock) {
|
|
|
70
105
|
},
|
|
71
106
|
applyRemoteUpdates: (updates, remoterVersion) => {
|
|
72
107
|
let merged = remoterVersion;
|
|
108
|
+
const bundles = [];
|
|
73
109
|
for (const update of updates) {
|
|
74
110
|
const bundle = JSON.parse(new TextDecoder().decode(update));
|
|
75
111
|
if (!hasEntries(bundle)) continue;
|
|
76
|
-
|
|
112
|
+
bundles.push(bundle);
|
|
77
113
|
merged = mergeVersions(merged, extractVersionFromBundle(bundle));
|
|
78
114
|
}
|
|
115
|
+
batchImportBundles((b) => flock.importJson(b), bundles);
|
|
79
116
|
return merged;
|
|
80
117
|
},
|
|
118
|
+
createIsolatedAdapter: () => {
|
|
119
|
+
let isolated = new Flock("streams-crdt-isolated");
|
|
120
|
+
return {
|
|
121
|
+
applySnapshot: (snapshot) => {
|
|
122
|
+
isolated = snapshot.byteLength === 0 ? new Flock("streams-crdt-isolated") : Flock.fromFile(snapshot, "streams-crdt-isolated");
|
|
123
|
+
return isolated.version();
|
|
124
|
+
},
|
|
125
|
+
applyRemoteUpdates: (updates, remoterVersion) => {
|
|
126
|
+
let merged = remoterVersion;
|
|
127
|
+
const bundles = [];
|
|
128
|
+
for (const update of updates) {
|
|
129
|
+
const bundle = JSON.parse(new TextDecoder().decode(update));
|
|
130
|
+
if (!hasEntries(bundle)) continue;
|
|
131
|
+
bundles.push(bundle);
|
|
132
|
+
merged = mergeVersions(merged, extractVersionFromBundle(bundle));
|
|
133
|
+
}
|
|
134
|
+
batchImportBundles((b) => isolated.importJson(b), bundles);
|
|
135
|
+
return merged;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
},
|
|
81
139
|
subscribeLocalUpdates: (listener) => {
|
|
82
140
|
return flock.subscribe((batch) => {
|
|
83
141
|
if (batch.source !== "local") return;
|
package/dist/flock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flock.js","names":[],"sources":["../src/flock.ts"],"sourcesContent":["import { Flock } from \"@loro-dev/flock-wasm\";\nimport type {\n EventBatch,\n ExportBundle,\n ExportRecord,\n VersionVector,\n VersionVectorEntry,\n} from \"@loro-dev/flock-wasm\";\nimport type { CrdtAdapter } from \"./types\";\n\nexport { StreamsCrdt } from \"./transport\";\nexport * from \"./shared\";\nexport type { ExportBundle, VersionVector } from \"@loro-dev/flock-wasm\";\n\nfunction isAfter(\n entry: VersionVectorEntry,\n current: VersionVectorEntry,\n): boolean {\n return (\n entry.physicalTime > current.physicalTime ||\n (entry.physicalTime === current.physicalTime &&\n entry.logicalCounter > current.logicalCounter)\n );\n}\n\nfunction mergeVersions(\n base: VersionVector,\n incoming: VersionVector,\n): VersionVector {\n const merged: VersionVector = { ...base };\n for (const [peer, entry] of Object.entries(incoming)) {\n if (entry == null) {\n continue;\n }\n const current = merged[peer];\n if (current == null || isAfter(entry, current)) {\n merged[peer] = { ...entry };\n }\n }\n return merged;\n}\n\nfunction parseClock(\n record: ExportRecord,\n): { peerId: string; entry: VersionVectorEntry } | null {\n const firstComma = record.c.indexOf(\",\");\n const secondComma = record.c.indexOf(\",\", firstComma + 1);\n if (firstComma < 0 || secondComma < 0) {\n return null;\n }\n\n const physicalTime = Number(record.c.slice(0, firstComma));\n const logicalCounter = Number(record.c.slice(firstComma + 1, secondComma));\n if (!Number.isFinite(physicalTime) || !Number.isFinite(logicalCounter)) {\n return null;\n }\n\n return {\n peerId: record.c.slice(secondComma + 1),\n entry: { physicalTime, logicalCounter },\n };\n}\n\nfunction extractVersionFromBundle(bundle: ExportBundle): VersionVector {\n const out: VersionVector = {};\n for (const record of Object.values(bundle.entries)) {\n const parsed = parseClock(record);\n if (parsed == null) {\n continue;\n }\n const current = out[parsed.peerId];\n if (current == null || isAfter(parsed.entry, current)) {\n out[parsed.peerId] = parsed.entry;\n }\n }\n return out;\n}\n\nfunction hasEntries(bundle: ExportBundle): boolean {\n return Object.keys(bundle.entries).length > 0;\n}\n\n/**\n * Creates a `CrdtAdapter` for one Flock replica.\n */\nexport function createFlockAdapter(flock: Flock): CrdtAdapter<VersionVector> {\n let lastExportedVersion: VersionVector = {};\n let localExportQueue = Promise.resolve();\n\n const recordExportFrontier = (version: VersionVector): void => {\n lastExportedVersion = mergeVersions(lastExportedVersion, version);\n };\n\n return {\n emptyVersion: () => ({}),\n mergeVersions,\n exportSnapshot: () => flock.exportFile(),\n applySnapshot: (snapshot) => {\n flock.importFile(snapshot);\n return flock.version();\n },\n exportUpdates: (fromVersion) => {\n const bundle = flock.exportJson(fromVersion);\n recordExportFrontier(fromVersion);\n if (!hasEntries(bundle)) {\n return undefined;\n }\n const exportedVersion = extractVersionFromBundle(bundle);\n recordExportFrontier(exportedVersion);\n return {\n updates: [new TextEncoder().encode(JSON.stringify(bundle))],\n version: exportedVersion,\n };\n },\n applyRemoteUpdates: (updates, remoterVersion) => {\n let merged = remoterVersion;\n for (const update of updates) {\n const bundle = JSON.parse(\n new TextDecoder().decode(update),\n ) as ExportBundle;\n if (!hasEntries(bundle)) {\n continue;\n }\n flock.importJson(bundle);\n merged = mergeVersions(merged, extractVersionFromBundle(bundle));\n }\n return merged;\n },\n subscribeLocalUpdates: (listener) => {\n const unsubscribe = flock.subscribe((batch) => {\n if (batch.source !== \"local\") {\n return;\n }\n const drain = async () => {\n const batchPayload = await flock.exportJson({\n from: lastExportedVersion,\n peerId: flock.peerId(),\n });\n if (!hasEntries(batchPayload)) {\n return;\n }\n const exportedVersion = extractVersionFromBundle(batchPayload);\n recordExportFrontier(exportedVersion);\n listener({\n updates: [new TextEncoder().encode(JSON.stringify(batchPayload))],\n version: exportedVersion,\n });\n };\n\n localExportQueue = localExportQueue.then(drain).catch(() => {\n /* swallow to keep queue alive */\n });\n });\n return unsubscribe;\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"flock.js","names":[],"sources":["../src/flock.ts"],"sourcesContent":["import { Flock } from \"@loro-dev/flock-wasm\";\nimport type {\n EventBatch,\n ExportBundle,\n ExportRecord,\n VersionVector,\n VersionVectorEntry,\n} from \"@loro-dev/flock-wasm\";\nimport type { CrdtAdapter } from \"./types\";\n\nexport { StreamsCrdt } from \"./transport\";\nexport * from \"./shared\";\nexport type { ExportBundle, VersionVector } from \"@loro-dev/flock-wasm\";\n\nfunction isAfter(\n entry: VersionVectorEntry,\n current: VersionVectorEntry,\n): boolean {\n return (\n entry.physicalTime > current.physicalTime ||\n (entry.physicalTime === current.physicalTime &&\n entry.logicalCounter > current.logicalCounter)\n );\n}\n\nfunction mergeVersions(\n base: VersionVector,\n incoming: VersionVector,\n): VersionVector {\n const merged: VersionVector = { ...base };\n for (const [peer, entry] of Object.entries(incoming)) {\n if (entry == null) {\n continue;\n }\n const current = merged[peer];\n if (current == null || isAfter(entry, current)) {\n merged[peer] = { ...entry };\n }\n }\n return merged;\n}\n\nfunction parseClock(\n record: ExportRecord,\n): { peerId: string; entry: VersionVectorEntry } | null {\n const firstComma = record.c.indexOf(\",\");\n const secondComma = record.c.indexOf(\",\", firstComma + 1);\n if (firstComma < 0 || secondComma < 0) {\n return null;\n }\n\n const physicalTime = Number(record.c.slice(0, firstComma));\n const logicalCounter = Number(record.c.slice(firstComma + 1, secondComma));\n if (!Number.isFinite(physicalTime) || !Number.isFinite(logicalCounter)) {\n return null;\n }\n\n return {\n peerId: record.c.slice(secondComma + 1),\n entry: { physicalTime, logicalCounter },\n };\n}\n\nfunction extractVersionFromBundle(bundle: ExportBundle): VersionVector {\n const out: VersionVector = {};\n for (const record of Object.values(bundle.entries)) {\n const parsed = parseClock(record);\n if (parsed == null) {\n continue;\n }\n const current = out[parsed.peerId];\n if (current == null || isAfter(parsed.entry, current)) {\n out[parsed.peerId] = parsed.entry;\n }\n }\n return out;\n}\n\nfunction hasEntries(bundle: ExportBundle): boolean {\n return Object.keys(bundle.entries).length > 0;\n}\n\n/**\n * Try to merge parsed bundles into a single combined bundle.\n * Falls back to per-bundle import when any key appears in more than one\n * bundle, so Flock's LWW conflict resolution sees every entry.\n */\nfunction batchImportBundles(\n importFn: (bundle: ExportBundle) => void,\n bundles: ExportBundle[],\n): void {\n if (bundles.length === 0) {\n return;\n }\n if (bundles.length === 1) {\n importFn(bundles[0]!);\n return;\n }\n\n // Detect key collisions across bundles.\n const seenKeys = new Set<string>();\n let hasCollision = false;\n for (const bundle of bundles) {\n for (const key of Object.keys(bundle.entries)) {\n if (seenKeys.has(key)) {\n hasCollision = true;\n break;\n }\n seenKeys.add(key);\n }\n if (hasCollision) {\n break;\n }\n }\n\n if (hasCollision) {\n for (const bundle of bundles) {\n importFn(bundle);\n }\n return;\n }\n\n // No collisions — merge entries into the first bundle and import once.\n const combined: ExportBundle = {\n ...bundles[0]!,\n entries: { ...bundles[0]!.entries },\n };\n for (let i = 1; i < bundles.length; i++) {\n Object.assign(combined.entries, bundles[i]!.entries);\n }\n importFn(combined);\n}\n\n/**\n * Creates a `CrdtAdapter` for one Flock replica.\n */\nexport function createFlockAdapter(flock: Flock): CrdtAdapter<VersionVector> {\n let lastExportedVersion: VersionVector = {};\n let localExportQueue = Promise.resolve();\n\n const recordExportFrontier = (version: VersionVector): void => {\n lastExportedVersion = mergeVersions(lastExportedVersion, version);\n };\n\n return {\n emptyVersion: () => ({}),\n mergeVersions,\n exportSnapshot: () => flock.exportFile(),\n applySnapshot: (snapshot) => {\n flock.importFile(snapshot);\n return flock.version();\n },\n exportUpdates: (fromVersion) => {\n const bundle = flock.exportJson(fromVersion);\n recordExportFrontier(fromVersion);\n if (!hasEntries(bundle)) {\n return undefined;\n }\n const exportedVersion = extractVersionFromBundle(bundle);\n recordExportFrontier(exportedVersion);\n return {\n updates: [new TextEncoder().encode(JSON.stringify(bundle))],\n version: exportedVersion,\n };\n },\n applyRemoteUpdates: (updates, remoterVersion) => {\n let merged = remoterVersion;\n const bundles: ExportBundle[] = [];\n for (const update of updates) {\n const bundle = JSON.parse(\n new TextDecoder().decode(update),\n ) as ExportBundle;\n if (!hasEntries(bundle)) {\n continue;\n }\n bundles.push(bundle);\n merged = mergeVersions(merged, extractVersionFromBundle(bundle));\n }\n batchImportBundles((b) => flock.importJson(b), bundles);\n return merged;\n },\n createIsolatedAdapter: () => {\n let isolated = new Flock(\"streams-crdt-isolated\");\n return {\n applySnapshot: (snapshot) => {\n isolated =\n snapshot.byteLength === 0\n ? new Flock(\"streams-crdt-isolated\")\n : Flock.fromFile(snapshot, \"streams-crdt-isolated\");\n return isolated.version();\n },\n applyRemoteUpdates: (updates, remoterVersion) => {\n let merged = remoterVersion;\n const bundles: ExportBundle[] = [];\n for (const update of updates) {\n const bundle = JSON.parse(\n new TextDecoder().decode(update),\n ) as ExportBundle;\n if (!hasEntries(bundle)) {\n continue;\n }\n bundles.push(bundle);\n merged = mergeVersions(merged, extractVersionFromBundle(bundle));\n }\n batchImportBundles((b) => isolated.importJson(b), bundles);\n return merged;\n },\n };\n },\n subscribeLocalUpdates: (listener) => {\n const unsubscribe = flock.subscribe((batch) => {\n if (batch.source !== \"local\") {\n return;\n }\n const drain = async () => {\n const batchPayload = await flock.exportJson({\n from: lastExportedVersion,\n peerId: flock.peerId(),\n });\n if (!hasEntries(batchPayload)) {\n return;\n }\n const exportedVersion = extractVersionFromBundle(batchPayload);\n recordExportFrontier(exportedVersion);\n listener({\n updates: [new TextEncoder().encode(JSON.stringify(batchPayload))],\n version: exportedVersion,\n });\n };\n\n localExportQueue = localExportQueue.then(drain).catch(() => {\n /* swallow to keep queue alive */\n });\n });\n return unsubscribe;\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
|
|
1
|
+
import { A as RemoteCursorSaveSource, C as TransportSyncSuccess, D as IndexedDbRemoteCursorStore, E as InMemoryRemoteCursorStore, M as createInitialRemoteCursor, O as IndexedDbRemoteCursorStoreOptions, S as TransportSubscription, T as BeforeRemoteCursorSaveHook, _ as TransportCreateStreamSuccess, a as CrdtAdapter, b as TransportJoinParams, c as JsonObject, d as SnapshotCodec, f as SnapshotTransformHook, g as StreamsCrdtOptions, h as StreamsAuthProvider, i as StreamsCrdt, j as RemoteCursorStore, k as RemoteCursor, l as JsonValue, m as StreamsAuthContext, n as isValidBucketId, o as CrdtUpdateBatch, p as SnapshotUploadOptions, r as isValidRillId, s as IsolatedCrdtAdapter, t as createStreamUrl, u as Result, v as TransportDeleteStreamSuccess, w as BeforeRemoteCursorSaveContext, x as TransportRoomStatus, y as TransportError } from "./stream-id-BCCfzKNm.js";
|
|
2
|
+
export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-
|
|
1
|
+
import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-nPEdFo7c.js";
|
|
2
2
|
export { InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
|
package/dist/loro.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as RemoteCursorSaveSource, C as TransportSyncSuccess, D as IndexedDbRemoteCursorStore, E as InMemoryRemoteCursorStore, M as createInitialRemoteCursor, O as IndexedDbRemoteCursorStoreOptions, S as TransportSubscription, T as BeforeRemoteCursorSaveHook, _ as TransportCreateStreamSuccess, a as CrdtAdapter, b as TransportJoinParams, c as JsonObject, d as SnapshotCodec, f as SnapshotTransformHook, g as StreamsCrdtOptions, h as StreamsAuthProvider, i as StreamsCrdt, j as RemoteCursorStore, k as RemoteCursor, l as JsonValue, m as StreamsAuthContext, n as isValidBucketId, o as CrdtUpdateBatch, p as SnapshotUploadOptions, r as isValidRillId, s as IsolatedCrdtAdapter, t as createStreamUrl, u as Result, v as TransportDeleteStreamSuccess, w as BeforeRemoteCursorSaveContext, x as TransportRoomStatus, y as TransportError } from "./stream-id-BCCfzKNm.js";
|
|
2
2
|
import { LoroDoc } from "loro-crdt";
|
|
3
3
|
|
|
4
4
|
//#region src/loro.d.ts
|
|
@@ -18,5 +18,5 @@ import { LoroDoc } from "loro-crdt";
|
|
|
18
18
|
*/
|
|
19
19
|
declare function createLoroDocAdapter(doc: LoroDoc): CrdtAdapter<JsonObject>;
|
|
20
20
|
//#endregion
|
|
21
|
-
export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, createInitialRemoteCursor, createLoroDocAdapter, createStreamUrl, isValidBucketId, isValidRillId };
|
|
21
|
+
export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, createInitialRemoteCursor, createLoroDocAdapter, createStreamUrl, isValidBucketId, isValidRillId };
|
|
22
22
|
//# sourceMappingURL=loro.d.ts.map
|
package/dist/loro.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-
|
|
2
|
-
import { VersionVector, decodeImportBlobMeta } from "loro-crdt";
|
|
1
|
+
import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-nPEdFo7c.js";
|
|
2
|
+
import { LoroDoc, VersionVector, decodeImportBlobMeta } from "loro-crdt";
|
|
3
3
|
//#region src/loro.ts
|
|
4
|
+
function batchImportUpdates(doc, updates) {
|
|
5
|
+
if ("batchImport" in doc && typeof doc.batchImport === "function") doc.batchImport(updates);
|
|
6
|
+
else doc.importBatch(updates);
|
|
7
|
+
}
|
|
4
8
|
function toDocVersionRecord(version) {
|
|
5
9
|
const out = {};
|
|
6
10
|
for (const [peer, counter] of version.toJSON()) out[peer] = counter;
|
|
@@ -82,7 +86,7 @@ function createLoroDocAdapter(doc) {
|
|
|
82
86
|
},
|
|
83
87
|
applyRemoteUpdates: (updates, remoterVersion) => {
|
|
84
88
|
assertDocAttached(doc);
|
|
85
|
-
if (updates.length > 0) doc
|
|
89
|
+
if (updates.length > 0) batchImportUpdates(doc, updates);
|
|
86
90
|
let version = remoterVersion;
|
|
87
91
|
for (const update of updates) {
|
|
88
92
|
const next = extractVersionFromUpdate(update);
|
|
@@ -90,6 +94,25 @@ function createLoroDocAdapter(doc) {
|
|
|
90
94
|
}
|
|
91
95
|
return version;
|
|
92
96
|
},
|
|
97
|
+
createIsolatedAdapter: () => {
|
|
98
|
+
let isolatedDoc = new LoroDoc();
|
|
99
|
+
return {
|
|
100
|
+
applySnapshot: (snapshot) => {
|
|
101
|
+
isolatedDoc = new LoroDoc();
|
|
102
|
+
if (snapshot.byteLength > 0) isolatedDoc.import(snapshot);
|
|
103
|
+
return toDocVersionRecord(isolatedDoc.version());
|
|
104
|
+
},
|
|
105
|
+
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);
|
|
111
|
+
}
|
|
112
|
+
return version;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
},
|
|
93
116
|
subscribeLocalUpdates: (listener) => {
|
|
94
117
|
assertDocAttached(doc);
|
|
95
118
|
return doc.subscribeLocalUpdates((update) => {
|
package/dist/loro.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loro.js","names":[],"sources":["../src/loro.ts"],"sourcesContent":["import {\n decodeImportBlobMeta,\n
|
|
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\";\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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"}
|
|
@@ -304,6 +304,17 @@ interface CrdtUpdateBatch<TVersion extends JsonObject> {
|
|
|
304
304
|
readonly version: TVersion;
|
|
305
305
|
}
|
|
306
306
|
type ExportedCrdtUpdateBatch<TVersion extends JsonObject> = CrdtUpdateBatch<TVersion> | null | undefined;
|
|
307
|
+
/**
|
|
308
|
+
* Isolated apply-only adapter used for 410/bootstrap recovery.
|
|
309
|
+
*
|
|
310
|
+
* Implementations must not mutate the caller's live CRDT state.
|
|
311
|
+
*/
|
|
312
|
+
interface IsolatedCrdtAdapter<TVersion extends JsonObject> {
|
|
313
|
+
/** Imports a full snapshot into isolated state and returns its version. */
|
|
314
|
+
applySnapshot(snapshot: Uint8Array): MaybePromise<TVersion>;
|
|
315
|
+
/** Applies retained remote updates into isolated state and returns the new version. */
|
|
316
|
+
applyRemoteUpdates(updates: Uint8Array[], remoterVersion: TVersion): MaybePromise<TVersion>;
|
|
317
|
+
}
|
|
307
318
|
/**
|
|
308
319
|
* Adapter contract between `StreamsCrdt` and a concrete CRDT runtime.
|
|
309
320
|
*/
|
|
@@ -333,6 +344,13 @@ interface CrdtAdapter<TVersion extends JsonObject> {
|
|
|
333
344
|
* remote version.
|
|
334
345
|
*/
|
|
335
346
|
applyRemoteUpdates(updates: Uint8Array[], remoterVersion: TVersion): MaybePromise<TVersion>;
|
|
347
|
+
/**
|
|
348
|
+
* Creates an isolated apply-only adapter for 410 recovery.
|
|
349
|
+
*
|
|
350
|
+
* When present, transport uses it to infer the remote version from
|
|
351
|
+
* bootstrap data without mutating the live CRDT instance first.
|
|
352
|
+
*/
|
|
353
|
+
createIsolatedAdapter?(): MaybePromise<IsolatedCrdtAdapter<TVersion>>;
|
|
336
354
|
/** Subscribes to local CRDT mutations. Returns an unsubscribe function. */
|
|
337
355
|
subscribeLocalUpdates(listener: (batch: CrdtUpdateBatch<TVersion>) => void): () => void;
|
|
338
356
|
/** Optional headers to include when creating the stream (e.g. retention config). */
|
|
@@ -422,6 +440,8 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
422
440
|
private readonly createStreamIfMissing;
|
|
423
441
|
private readonly client;
|
|
424
442
|
private readonly cursorManager;
|
|
443
|
+
private readonly volatileCursorManager;
|
|
444
|
+
private readonly decodeSnapshot?;
|
|
425
445
|
private readonly reconnectConfig;
|
|
426
446
|
private readonly debug;
|
|
427
447
|
private readonly beforeRemoteCursorSave?;
|
|
@@ -431,6 +451,11 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
431
451
|
private nextProducerSeq;
|
|
432
452
|
private joinState?;
|
|
433
453
|
private opQueue;
|
|
454
|
+
/**
|
|
455
|
+
* Internal catch-up result used when callers need to handle 410 by
|
|
456
|
+
* preserving local state before rebuilding through bootstrap.
|
|
457
|
+
*/
|
|
458
|
+
private static readonly GONE_CATCHUP;
|
|
434
459
|
/**
|
|
435
460
|
* Creates a transport for one stream URL and one local adapter instance.
|
|
436
461
|
*/
|
|
@@ -474,12 +499,18 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
|
|
|
474
499
|
private startJoin;
|
|
475
500
|
private syncOnce;
|
|
476
501
|
private performInitialJoinSync;
|
|
477
|
-
private
|
|
502
|
+
private createInitialCursor;
|
|
503
|
+
private resolveInitialRemoteState;
|
|
504
|
+
private bootstrapState;
|
|
478
505
|
private catchup;
|
|
506
|
+
private catchupOrGone;
|
|
479
507
|
private appendLocalBatch;
|
|
480
508
|
private exportUpdates;
|
|
481
509
|
private applyBootstrapItems;
|
|
482
510
|
private applyRemoteItems;
|
|
511
|
+
private createCursorManager;
|
|
512
|
+
private createIsolatedCursorManager;
|
|
513
|
+
private recoverFromGoneWithLocalBootstrap;
|
|
483
514
|
private runReadLoop;
|
|
484
515
|
/** Maximum encoded size (bytes) for a single batched append. */
|
|
485
516
|
private static readonly MAX_BATCH_BYTES;
|
|
@@ -524,5 +555,5 @@ declare function createStreamUrl(input: {
|
|
|
524
555
|
baseUrl?: string;
|
|
525
556
|
}): string;
|
|
526
557
|
//#endregion
|
|
527
|
-
export {
|
|
528
|
-
//# sourceMappingURL=stream-id-
|
|
558
|
+
export { RemoteCursorSaveSource as A, TransportSyncSuccess as C, IndexedDbRemoteCursorStore as D, InMemoryRemoteCursorStore as E, createInitialRemoteCursor as M, IndexedDbRemoteCursorStoreOptions as O, TransportSubscription as S, BeforeRemoteCursorSaveHook as T, TransportCreateStreamSuccess as _, CrdtAdapter as a, TransportJoinParams as b, JsonObject as c, SnapshotCodec as d, SnapshotTransformHook as f, StreamsCrdtOptions as g, StreamsAuthProvider as h, StreamsCrdt as i, RemoteCursorStore as j, RemoteCursor as k, JsonValue as l, StreamsAuthContext as m, isValidBucketId as n, CrdtUpdateBatch as o, SnapshotUploadOptions as p, isValidRillId as r, IsolatedCrdtAdapter as s, createStreamUrl as t, Result as u, TransportDeleteStreamSuccess as v, BeforeRemoteCursorSaveContext as w, TransportRoomStatus as x, TransportError as y };
|
|
559
|
+
//# sourceMappingURL=stream-id-BCCfzKNm.d.ts.map
|