@loro-dev/streams-crdt 0.4.1 → 0.6.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 +59 -10
- 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-TsPj50Jj.js → stream-id-DBym_2Mj.js} +209 -113
- package/dist/stream-id-DBym_2Mj.js.map +1 -0
- package/dist/{stream-id-CSottEoY.d.ts → stream-id-Do4h2lOD.d.ts} +71 -11
- package/package.json +1 -1
- 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-Do4h2lOD.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-DBym_2Mj.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,52 @@ 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
|
+
function applyBundleUpdates(importFn, updates, remoterVersion) {
|
|
79
|
+
let merged = remoterVersion;
|
|
80
|
+
const bundles = [];
|
|
81
|
+
for (const update of updates) {
|
|
82
|
+
const bundle = JSON.parse(new TextDecoder().decode(update));
|
|
83
|
+
if (!hasEntries(bundle)) continue;
|
|
84
|
+
bundles.push(bundle);
|
|
85
|
+
merged = mergeVersions(merged, extractVersionFromBundle(bundle));
|
|
86
|
+
}
|
|
87
|
+
batchImportBundles(importFn, bundles);
|
|
88
|
+
return merged;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
44
91
|
* Creates a `CrdtAdapter` for one Flock replica.
|
|
45
92
|
*/
|
|
46
93
|
function createFlockAdapter(flock) {
|
|
@@ -52,6 +99,7 @@ function createFlockAdapter(flock) {
|
|
|
52
99
|
return {
|
|
53
100
|
emptyVersion: () => ({}),
|
|
54
101
|
mergeVersions,
|
|
102
|
+
flushLocalExports: () => localExportQueue,
|
|
55
103
|
exportSnapshot: () => flock.exportFile(),
|
|
56
104
|
applySnapshot: (snapshot) => {
|
|
57
105
|
flock.importFile(snapshot);
|
|
@@ -68,15 +116,16 @@ function createFlockAdapter(flock) {
|
|
|
68
116
|
version: exportedVersion
|
|
69
117
|
};
|
|
70
118
|
},
|
|
71
|
-
applyRemoteUpdates: (updates, remoterVersion) =>
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
119
|
+
applyRemoteUpdates: (updates, remoterVersion) => applyBundleUpdates((b) => flock.importJson(b), updates, remoterVersion),
|
|
120
|
+
createIsolatedAdapter: () => {
|
|
121
|
+
let isolated = new Flock("streams-crdt-isolated");
|
|
122
|
+
return {
|
|
123
|
+
applySnapshot: (snapshot) => {
|
|
124
|
+
isolated = snapshot.byteLength === 0 ? new Flock("streams-crdt-isolated") : Flock.fromFile(snapshot, "streams-crdt-isolated");
|
|
125
|
+
return isolated.version();
|
|
126
|
+
},
|
|
127
|
+
applyRemoteUpdates: (updates, remoterVersion) => applyBundleUpdates((b) => isolated.importJson(b), updates, remoterVersion)
|
|
128
|
+
};
|
|
80
129
|
},
|
|
81
130
|
subscribeLocalUpdates: (listener) => {
|
|
82
131
|
return flock.subscribe((batch) => {
|
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
|
|
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-runtime\";\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\nfunction applyBundleUpdates(\n importFn: (bundle: ExportBundle) => void,\n updates: Uint8Array[],\n remoterVersion: VersionVector,\n): VersionVector {\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(importFn, bundles);\n return merged;\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 flushLocalExports: () => localExportQueue,\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 applyBundleUpdates((b) => flock.importJson(b), updates, remoterVersion),\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 applyBundleUpdates((b) => isolated.importJson(b), updates, remoterVersion),\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-Do4h2lOD.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-DBym_2Mj.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-Do4h2lOD.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-DBym_2Mj.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-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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"}
|