@loro-dev/streams-crdt 0.13.0 → 0.14.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/dist/flock.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as BeforeRemoteCursorSaveHook, A as PayloadProtectionReadPolicy, B as StreamsCrdtShardUrlsOptions, C as IsolatedCrdtAdapter, D as PayloadProtectionKey, E as PayloadProtectionEncryptionOptions, F as SnapshotTransformHook, G as TransportError, H as TransportCatchupSuccess, I as SnapshotUploadOptions, J as TransportSnapshotUploadSuccess, K as TransportJoinParams, L as StreamsAuthContext, M as PayloadProtectionWritePolicy, N as Result, O as PayloadProtectionKeyProvider, P as SnapshotCodec, Q as BeforeRemoteCursorSaveContext, R as StreamsAuthProvider, S as EphemeralStreamSubscription, T as JsonValue, U as TransportCreateStreamSuccess, V as TransportCatchupParams, W as TransportDeleteStreamSuccess, X as TransportSyncSuccess, Y as TransportSubscription, Z as WriteOnlyAppendResult, _ as E2eeScope, a as EphemeralStreamCrdt, at as RemoteCursorStore, b as EphemeralStreamCrdtOptions, c as CrdtApplyOutcome, d as CrdtUpdateBatch, et as InMemoryRemoteCursorStore, f as E2eeEncryptionOptions, g as E2eeReadPolicy, h as E2eeOptions, i as PayloadProtectionError, it as RemoteCursorSaveSource, j as PayloadProtectionScope, k as PayloadProtectionOptions, l as CrdtApplyReturn, m as E2eeKeyProvider, n as isValidBucketId, nt as IndexedDbRemoteCursorStoreOptions, o as StreamsCrdt, ot as createInitialRemoteCursor, p as E2eeKey, q as TransportRoomStatus, r as isValidRillId, rt as RemoteCursor, s as CrdtAdapter, t as createStreamUrl, tt as IndexedDbRemoteCursorStore, u as CrdtUnresolvedSpan, v as E2eeWritePolicy, w as JsonObject, x as EphemeralStreamJoinParams, y as EphemeralStreamAdaptor, z as StreamsCrdtOptions } from "./stream-id-DJYQlTE2.js";
1
+ import { $ as WriteOnlyAppendResult, A as PayloadProtectionKeyProvider, B as StreamsAuthProvider, C as EphemeralStreamSubscription, D as LocalSyncStats, E as JsonValue, F as Result, G as TransportCreateStreamSuccess, H as StreamsCrdtShardUrlsOptions, I as SnapshotCodec, J as TransportJoinParams, K as TransportDeleteStreamSuccess, L as SnapshotTransformHook, M as PayloadProtectionReadPolicy, N as PayloadProtectionScope, O as PayloadProtectionEncryptionOptions, P as PayloadProtectionWritePolicy, Q as TransportSyncSuccess, R as SnapshotUploadOptions, S as EphemeralStreamJoinParams, T as JsonObject, U as TransportCatchupParams, V as StreamsCrdtOptions, W as TransportCatchupSuccess, X as TransportSnapshotUploadSuccess, Y as TransportRoomStatus, Z as TransportSubscription, _ as E2eeReadPolicy, a as LocalAppendFailedError, at as RemoteCursor, b as EphemeralStreamAdaptor, c as CrdtAdapter, ct as createInitialRemoteCursor, d as CrdtUnresolvedSpan, et as BeforeRemoteCursorSaveContext, f as CrdtUpdateBatch, g as E2eeOptions, h as E2eeKeyProvider, i as PayloadProtectionError, it as IndexedDbRemoteCursorStoreOptions, j as PayloadProtectionOptions, k as PayloadProtectionKey, l as CrdtApplyOutcome, m as E2eeKey, n as isValidBucketId, nt as InMemoryRemoteCursorStore, o as EphemeralStreamCrdt, ot as RemoteCursorSaveSource, p as E2eeEncryptionOptions, q as TransportError, r as isValidRillId, rt as IndexedDbRemoteCursorStore, s as StreamsCrdt, st as RemoteCursorStore, t as createStreamUrl, tt as BeforeRemoteCursorSaveHook, u as CrdtApplyReturn, v as E2eeScope, w as IsolatedCrdtAdapter, x as EphemeralStreamCrdtOptions, y as E2eeWritePolicy, z as StreamsAuthContext } from "./stream-id-BuXMoCvo.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, CrdtApplyOutcome, CrdtApplyReturn, CrdtUnresolvedSpan, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, type ExportBundle, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, PayloadProtectionEncryptionOptions, PayloadProtectionKey, PayloadProtectionKeyProvider, PayloadProtectionOptions, PayloadProtectionReadPolicy, PayloadProtectionScope, PayloadProtectionWritePolicy, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, StreamsCrdtShardUrlsOptions, TransportCatchupParams, TransportCatchupSuccess, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSnapshotUploadSuccess, TransportSubscription, TransportSyncSuccess, type VersionVector, WriteOnlyAppendResult, createFlockAdapter, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
10
+ export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtApplyOutcome, CrdtApplyReturn, CrdtUnresolvedSpan, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, type ExportBundle, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, LocalAppendFailedError, LocalSyncStats, PayloadProtectionEncryptionOptions, PayloadProtectionKey, PayloadProtectionKeyProvider, PayloadProtectionOptions, PayloadProtectionReadPolicy, PayloadProtectionScope, PayloadProtectionWritePolicy, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, StreamsCrdtShardUrlsOptions, TransportCatchupParams, TransportCatchupSuccess, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSnapshotUploadSuccess, TransportSubscription, TransportSyncSuccess, type VersionVector, WriteOnlyAppendResult, createFlockAdapter, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
11
11
  //# sourceMappingURL=flock.d.ts.map
package/dist/flock.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as InMemoryRemoteCursorStore, c as EphemeralStreamCrdt, i as StreamsCrdt, l as PayloadProtectionError, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-CvJLq-0s.js";
1
+ import { a as InMemoryRemoteCursorStore, c as EphemeralStreamCrdt, i as StreamsCrdt, l as LocalAppendFailedError, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl, u as PayloadProtectionError } from "./stream-id-CEp4zEQJ.js";
2
2
  import { Flock } from "@loro-dev/flock-wasm";
3
3
  //#region src/flock.ts
4
4
  function isAfter(entry, current) {
@@ -103,7 +103,8 @@ function createFlockAdapter(flock) {
103
103
  exportSnapshot: () => flock.exportFile(),
104
104
  applySnapshot: (snapshot) => {
105
105
  flock.importFile(snapshot);
106
- return flock.version();
106
+ if (snapshot.byteLength === 0) return {};
107
+ return Flock.fromFile(snapshot, "streams-crdt-snapshot-version").version();
107
108
  },
108
109
  exportUpdates: (fromVersion) => {
109
110
  const bundle = flock.exportJson(fromVersion);
@@ -149,6 +150,6 @@ function createFlockAdapter(flock) {
149
150
  };
150
151
  }
151
152
  //#endregion
152
- export { PayloadProtectionError as E2eeError, PayloadProtectionError, EphemeralStreamCrdt, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createFlockAdapter, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
153
+ export { PayloadProtectionError as E2eeError, PayloadProtectionError, EphemeralStreamCrdt, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, LocalAppendFailedError, StreamsCrdt, createFlockAdapter, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
153
154
 
154
155
  //# sourceMappingURL=flock.js.map
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-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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"}
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 if (snapshot.byteLength === 0) {\n return {};\n }\n // Return only the version carried by the snapshot itself. Returning\n // `flock.version()` here would fold the local peer's pre-join clocks\n // into `serverLowerBoundVersion`, so entries committed before joining\n // would never be exported (they'd read as already server-known).\n return Flock.fromFile(snapshot, \"streams-crdt-snapshot-version\").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 { $ as BeforeRemoteCursorSaveHook, A as PayloadProtectionReadPolicy, B as StreamsCrdtShardUrlsOptions, C as IsolatedCrdtAdapter, D as PayloadProtectionKey, E as PayloadProtectionEncryptionOptions, F as SnapshotTransformHook, G as TransportError, H as TransportCatchupSuccess, I as SnapshotUploadOptions, J as TransportSnapshotUploadSuccess, K as TransportJoinParams, L as StreamsAuthContext, M as PayloadProtectionWritePolicy, N as Result, O as PayloadProtectionKeyProvider, P as SnapshotCodec, Q as BeforeRemoteCursorSaveContext, R as StreamsAuthProvider, S as EphemeralStreamSubscription, T as JsonValue, U as TransportCreateStreamSuccess, V as TransportCatchupParams, W as TransportDeleteStreamSuccess, X as TransportSyncSuccess, Y as TransportSubscription, Z as WriteOnlyAppendResult, _ as E2eeScope, a as EphemeralStreamCrdt, at as RemoteCursorStore, b as EphemeralStreamCrdtOptions, c as CrdtApplyOutcome, d as CrdtUpdateBatch, et as InMemoryRemoteCursorStore, f as E2eeEncryptionOptions, g as E2eeReadPolicy, h as E2eeOptions, i as PayloadProtectionError, it as RemoteCursorSaveSource, j as PayloadProtectionScope, k as PayloadProtectionOptions, l as CrdtApplyReturn, m as E2eeKeyProvider, n as isValidBucketId, nt as IndexedDbRemoteCursorStoreOptions, o as StreamsCrdt, ot as createInitialRemoteCursor, p as E2eeKey, q as TransportRoomStatus, r as isValidRillId, rt as RemoteCursor, s as CrdtAdapter, t as createStreamUrl, tt as IndexedDbRemoteCursorStore, u as CrdtUnresolvedSpan, v as E2eeWritePolicy, w as JsonObject, x as EphemeralStreamJoinParams, y as EphemeralStreamAdaptor, z as StreamsCrdtOptions } from "./stream-id-DJYQlTE2.js";
2
- export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtApplyOutcome, CrdtApplyReturn, CrdtUnresolvedSpan, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, PayloadProtectionEncryptionOptions, PayloadProtectionKey, PayloadProtectionKeyProvider, PayloadProtectionOptions, PayloadProtectionReadPolicy, PayloadProtectionScope, PayloadProtectionWritePolicy, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, StreamsCrdtShardUrlsOptions, TransportCatchupParams, TransportCatchupSuccess, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSnapshotUploadSuccess, TransportSubscription, TransportSyncSuccess, WriteOnlyAppendResult, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
1
+ import { $ as WriteOnlyAppendResult, A as PayloadProtectionKeyProvider, B as StreamsAuthProvider, C as EphemeralStreamSubscription, D as LocalSyncStats, E as JsonValue, F as Result, G as TransportCreateStreamSuccess, H as StreamsCrdtShardUrlsOptions, I as SnapshotCodec, J as TransportJoinParams, K as TransportDeleteStreamSuccess, L as SnapshotTransformHook, M as PayloadProtectionReadPolicy, N as PayloadProtectionScope, O as PayloadProtectionEncryptionOptions, P as PayloadProtectionWritePolicy, Q as TransportSyncSuccess, R as SnapshotUploadOptions, S as EphemeralStreamJoinParams, T as JsonObject, U as TransportCatchupParams, V as StreamsCrdtOptions, W as TransportCatchupSuccess, X as TransportSnapshotUploadSuccess, Y as TransportRoomStatus, Z as TransportSubscription, _ as E2eeReadPolicy, a as LocalAppendFailedError, at as RemoteCursor, b as EphemeralStreamAdaptor, c as CrdtAdapter, ct as createInitialRemoteCursor, d as CrdtUnresolvedSpan, et as BeforeRemoteCursorSaveContext, f as CrdtUpdateBatch, g as E2eeOptions, h as E2eeKeyProvider, i as PayloadProtectionError, it as IndexedDbRemoteCursorStoreOptions, j as PayloadProtectionOptions, k as PayloadProtectionKey, l as CrdtApplyOutcome, m as E2eeKey, n as isValidBucketId, nt as InMemoryRemoteCursorStore, o as EphemeralStreamCrdt, ot as RemoteCursorSaveSource, p as E2eeEncryptionOptions, q as TransportError, r as isValidRillId, rt as IndexedDbRemoteCursorStore, s as StreamsCrdt, st as RemoteCursorStore, t as createStreamUrl, tt as BeforeRemoteCursorSaveHook, u as CrdtApplyReturn, v as E2eeScope, w as IsolatedCrdtAdapter, x as EphemeralStreamCrdtOptions, y as E2eeWritePolicy, z as StreamsAuthContext } from "./stream-id-BuXMoCvo.js";
2
+ export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtApplyOutcome, CrdtApplyReturn, CrdtUnresolvedSpan, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, LocalAppendFailedError, LocalSyncStats, PayloadProtectionEncryptionOptions, PayloadProtectionKey, PayloadProtectionKeyProvider, PayloadProtectionOptions, PayloadProtectionReadPolicy, PayloadProtectionScope, PayloadProtectionWritePolicy, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, StreamsCrdtShardUrlsOptions, TransportCatchupParams, TransportCatchupSuccess, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSnapshotUploadSuccess, TransportSubscription, TransportSyncSuccess, WriteOnlyAppendResult, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as InMemoryRemoteCursorStore, c as EphemeralStreamCrdt, i as StreamsCrdt, l as PayloadProtectionError, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-CvJLq-0s.js";
2
- export { PayloadProtectionError as E2eeError, PayloadProtectionError, EphemeralStreamCrdt, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
1
+ import { a as InMemoryRemoteCursorStore, c as EphemeralStreamCrdt, i as StreamsCrdt, l as LocalAppendFailedError, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl, u as PayloadProtectionError } from "./stream-id-CEp4zEQJ.js";
2
+ export { PayloadProtectionError as E2eeError, PayloadProtectionError, EphemeralStreamCrdt, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, LocalAppendFailedError, StreamsCrdt, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
package/dist/loro.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as BeforeRemoteCursorSaveHook, A as PayloadProtectionReadPolicy, B as StreamsCrdtShardUrlsOptions, C as IsolatedCrdtAdapter, D as PayloadProtectionKey, E as PayloadProtectionEncryptionOptions, F as SnapshotTransformHook, G as TransportError, H as TransportCatchupSuccess, I as SnapshotUploadOptions, J as TransportSnapshotUploadSuccess, K as TransportJoinParams, L as StreamsAuthContext, M as PayloadProtectionWritePolicy, N as Result, O as PayloadProtectionKeyProvider, P as SnapshotCodec, Q as BeforeRemoteCursorSaveContext, R as StreamsAuthProvider, S as EphemeralStreamSubscription, T as JsonValue, U as TransportCreateStreamSuccess, V as TransportCatchupParams, W as TransportDeleteStreamSuccess, X as TransportSyncSuccess, Y as TransportSubscription, Z as WriteOnlyAppendResult, _ as E2eeScope, a as EphemeralStreamCrdt, at as RemoteCursorStore, b as EphemeralStreamCrdtOptions, c as CrdtApplyOutcome, d as CrdtUpdateBatch, et as InMemoryRemoteCursorStore, f as E2eeEncryptionOptions, g as E2eeReadPolicy, h as E2eeOptions, i as PayloadProtectionError, it as RemoteCursorSaveSource, j as PayloadProtectionScope, k as PayloadProtectionOptions, l as CrdtApplyReturn, m as E2eeKeyProvider, n as isValidBucketId, nt as IndexedDbRemoteCursorStoreOptions, o as StreamsCrdt, ot as createInitialRemoteCursor, p as E2eeKey, q as TransportRoomStatus, r as isValidRillId, rt as RemoteCursor, s as CrdtAdapter, t as createStreamUrl, tt as IndexedDbRemoteCursorStore, u as CrdtUnresolvedSpan, v as E2eeWritePolicy, w as JsonObject, x as EphemeralStreamJoinParams, y as EphemeralStreamAdaptor, z as StreamsCrdtOptions } from "./stream-id-DJYQlTE2.js";
1
+ import { $ as WriteOnlyAppendResult, A as PayloadProtectionKeyProvider, B as StreamsAuthProvider, C as EphemeralStreamSubscription, D as LocalSyncStats, E as JsonValue, F as Result, G as TransportCreateStreamSuccess, H as StreamsCrdtShardUrlsOptions, I as SnapshotCodec, J as TransportJoinParams, K as TransportDeleteStreamSuccess, L as SnapshotTransformHook, M as PayloadProtectionReadPolicy, N as PayloadProtectionScope, O as PayloadProtectionEncryptionOptions, P as PayloadProtectionWritePolicy, Q as TransportSyncSuccess, R as SnapshotUploadOptions, S as EphemeralStreamJoinParams, T as JsonObject, U as TransportCatchupParams, V as StreamsCrdtOptions, W as TransportCatchupSuccess, X as TransportSnapshotUploadSuccess, Y as TransportRoomStatus, Z as TransportSubscription, _ as E2eeReadPolicy, a as LocalAppendFailedError, at as RemoteCursor, b as EphemeralStreamAdaptor, c as CrdtAdapter, ct as createInitialRemoteCursor, d as CrdtUnresolvedSpan, et as BeforeRemoteCursorSaveContext, f as CrdtUpdateBatch, g as E2eeOptions, h as E2eeKeyProvider, i as PayloadProtectionError, it as IndexedDbRemoteCursorStoreOptions, j as PayloadProtectionOptions, k as PayloadProtectionKey, l as CrdtApplyOutcome, m as E2eeKey, n as isValidBucketId, nt as InMemoryRemoteCursorStore, o as EphemeralStreamCrdt, ot as RemoteCursorSaveSource, p as E2eeEncryptionOptions, q as TransportError, r as isValidRillId, rt as IndexedDbRemoteCursorStore, s as StreamsCrdt, st as RemoteCursorStore, t as createStreamUrl, tt as BeforeRemoteCursorSaveHook, u as CrdtApplyReturn, v as E2eeScope, w as IsolatedCrdtAdapter, x as EphemeralStreamCrdtOptions, y as E2eeWritePolicy, z as StreamsAuthContext } from "./stream-id-BuXMoCvo.js";
2
2
  import { LoroDoc } from "loro-crdt";
3
3
 
4
4
  //#region src/loro.d.ts
@@ -41,5 +41,5 @@ interface LoroEphemeralStoreLike {
41
41
  declare function EphemeralStoreAdaptor(store: LoroEphemeralStoreLike): EphemeralStreamAdaptor;
42
42
  declare const createLoroEphemeralStoreAdaptor: typeof EphemeralStoreAdaptor;
43
43
  //#endregion
44
- export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtApplyOutcome, CrdtApplyReturn, CrdtUnresolvedSpan, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, EphemeralStoreAdaptor, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, LoroEphemeralStoreLike, PayloadProtectionEncryptionOptions, PayloadProtectionKey, PayloadProtectionKeyProvider, PayloadProtectionOptions, PayloadProtectionReadPolicy, PayloadProtectionScope, PayloadProtectionWritePolicy, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, StreamsCrdtShardUrlsOptions, TransportCatchupParams, TransportCatchupSuccess, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSnapshotUploadSuccess, TransportSubscription, TransportSyncSuccess, WriteOnlyAppendResult, createInitialRemoteCursor, createLoroDocAdapter, createLoroEphemeralStoreAdaptor, createStreamUrl, isValidBucketId, isValidRillId };
44
+ export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtApplyOutcome, CrdtApplyReturn, CrdtUnresolvedSpan, CrdtUpdateBatch, E2eeEncryptionOptions, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeKey, E2eeKeyProvider, E2eeOptions, E2eeReadPolicy, E2eeScope, E2eeWritePolicy, EphemeralStoreAdaptor, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, LocalAppendFailedError, LocalSyncStats, LoroEphemeralStoreLike, PayloadProtectionEncryptionOptions, PayloadProtectionKey, PayloadProtectionKeyProvider, PayloadProtectionOptions, PayloadProtectionReadPolicy, PayloadProtectionScope, PayloadProtectionWritePolicy, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, StreamsCrdtShardUrlsOptions, TransportCatchupParams, TransportCatchupSuccess, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSnapshotUploadSuccess, TransportSubscription, TransportSyncSuccess, WriteOnlyAppendResult, createInitialRemoteCursor, createLoroDocAdapter, createLoroEphemeralStoreAdaptor, createStreamUrl, isValidBucketId, isValidRillId };
45
45
  //# sourceMappingURL=loro.d.ts.map
package/dist/loro.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as InMemoryRemoteCursorStore, c as EphemeralStreamCrdt, i as StreamsCrdt, l as PayloadProtectionError, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-CvJLq-0s.js";
1
+ import { a as InMemoryRemoteCursorStore, c as EphemeralStreamCrdt, i as StreamsCrdt, l as LocalAppendFailedError, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl, u as PayloadProtectionError } from "./stream-id-CEp4zEQJ.js";
2
2
  import { LoroDoc, VersionVector, decodeImportBlobMeta } from "loro-crdt";
3
3
  //#region src/loro.ts
4
4
  function batchImportUpdates(doc, updates) {
@@ -268,6 +268,6 @@ function EphemeralStoreAdaptor(store) {
268
268
  }
269
269
  const createLoroEphemeralStoreAdaptor = EphemeralStoreAdaptor;
270
270
  //#endregion
271
- export { PayloadProtectionError as E2eeError, PayloadProtectionError, EphemeralStoreAdaptor, EphemeralStreamCrdt, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createInitialRemoteCursor, createLoroDocAdapter, createLoroEphemeralStoreAdaptor, createStreamUrl, isValidBucketId, isValidRillId };
271
+ export { PayloadProtectionError as E2eeError, PayloadProtectionError, EphemeralStoreAdaptor, EphemeralStreamCrdt, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, LocalAppendFailedError, StreamsCrdt, createInitialRemoteCursor, createLoroDocAdapter, createLoroEphemeralStoreAdaptor, createStreamUrl, isValidBucketId, isValidRillId };
272
272
 
273
273
  //# sourceMappingURL=loro.js.map
@@ -203,21 +203,26 @@ type TransportRoomStatus = "joined" | "reconnecting" | "disconnected" | "error";
203
203
  * live mode. For permanent missing-credential cases, return `undefined`
204
204
  * from your auth callback instead of throwing.
205
205
  * - `protocol_error` — 400/405/409 or malformed response; treat as a bug.
206
+ * - `payload_too_large` — 413; a single append body exceeds the server limit.
206
207
  * - `network_error` — fetch-level failure (DNS, connection refused). Retry.
207
208
  * - `timeout` — connect or poll deadline exceeded. Retry.
208
209
  * - `initial_sync_timeout` — initial bootstrap/catch-up exceeded its hard cap.
209
210
  * - `gone` — 410; persisted cursor is no longer valid. Bootstrap from scratch.
210
211
  * - `apply_incomplete` — a CRDT adapter could not fully apply remote data.
211
- * - `server_error` — 5xx; transient backend failure, retry with backoff.
212
+ * - `server_error` — 5xx or 429; transient backend failure, retry with backoff.
212
213
  * - `internal_error` — local adapter/runtime failure (not from the network).
213
214
  * - `unknown` — uncategorized error.
214
215
  * - `payload_protection_error` — payload encryption/decryption failed.
215
216
  *
216
217
  * In live mode, `join()`'s read/write loops automatically retry every
217
- * `retryable: true` error using the configured backoff. Operation-level methods
218
- * (`sync()`, `createStream()`, `appendWriteOnly()`) return after a single
219
- * attempt — applications decide whether to retry based on the `retryable`
220
- * flag.
218
+ * `retryable: true` error using the configured backoff. Errors with
219
+ * `retryable: false` do not terminate the live loops either: the room surfaces
220
+ * `"error"` status (with this error in `lastError`) and keeps retrying in the
221
+ * background on a capped exponential backoff, re-running the auth callback on
222
+ * each attempt so refreshed credentials are picked up automatically.
223
+ * Operation-level methods (`sync()`, `createStream()`, `appendWriteOnly()`)
224
+ * return after a single attempt — applications decide whether to retry based
225
+ * on the `retryable` flag.
221
226
  */
222
227
  type TransportError =
223
228
  /**
@@ -273,6 +278,22 @@ type TransportError =
273
278
  readonly message: string;
274
279
  readonly body?: string;
275
280
  }
281
+ /**
282
+ * The server (or an intermediary) rejected the request body as too large
283
+ * (413). Deterministic for a given payload: retrying identical bytes cannot
284
+ * succeed while the server limit is unchanged. When the live write queue
285
+ * hits this on an unsplittable single batch, it surfaces the error here and
286
+ * in `lastWriteError` while keeping the batch queued (strict FIFO — the
287
+ * head is never dropped), retrying only on the slow capped backoff in case
288
+ * the server limit changes.
289
+ */
290
+ | {
291
+ readonly code: "payload_too_large";
292
+ readonly retryable: false;
293
+ readonly status: 413;
294
+ readonly message: string;
295
+ readonly body?: string;
296
+ }
276
297
  /**
277
298
  * A `fetch` failure (DNS, connection refused, TLS error, etc.). Safe to
278
299
  * retry after a short delay.
@@ -328,11 +349,11 @@ type TransportError =
328
349
  readonly message: string;
329
350
  }
330
351
  /**
331
- * A 5xx response from the backend. Likely transient — the live read/write
332
- * loops retry automatically with exponential backoff (default cap 30s, with
333
- * jitter). When the server provides a `Retry-After` header, the runtime
334
- * honors it. `requestId` (when present) is suitable for surfacing to end
335
- * users for support tickets.
352
+ * A 5xx (or 429 rate-limit) response from the backend. Likely transient —
353
+ * the live read/write loops retry automatically with exponential backoff
354
+ * (default cap 30s, with jitter). When the server provides a `Retry-After`
355
+ * header, the runtime honors it. `requestId` (when present) is suitable for
356
+ * surfacing to end users for support tickets.
336
357
  */
337
358
  | {
338
359
  readonly code: "server_error";
@@ -571,6 +592,27 @@ interface TransportSubscription {
571
592
  * whether to retry, surface auth failure, or mark the room as terminal.
572
593
  */
573
594
  readonly lastError?: TransportError;
595
+ /**
596
+ * Number of local update batches queued but not yet durably appended.
597
+ *
598
+ * Includes any batch currently in flight (batches are only dequeued after
599
+ * a durable append). Suitable for "N updates not yet uploaded" UIs.
600
+ */
601
+ readonly pendingLocalCount: number;
602
+ /**
603
+ * Wall-clock timestamp (`Date.now()` ms) of the last durably committed
604
+ * local append on this transport, or `undefined` if none succeeded yet.
605
+ */
606
+ readonly lastLocalAppendAtMs?: number;
607
+ /**
608
+ * Last error observed by the local write path, if uploads are stuck.
609
+ *
610
+ * Unlike `lastError`, this is only written by the write queue and only
611
+ * cleared by a successful append, so it always explains a non-zero
612
+ * `pendingLocalCount` even while the read side is healthy or failing
613
+ * with a different error.
614
+ */
615
+ readonly lastWriteError?: TransportError;
574
616
  /** Registers a status listener. Returns an unsubscribe function. */
575
617
  readonly onStatusChange: (listener: (status: "connecting" | TransportRoomStatus) => void) => () => void;
576
618
  /**
@@ -591,6 +633,34 @@ interface TransportSubscription {
591
633
  */
592
634
  readonly waitUntilSynced: () => Promise<void>;
593
635
  }
636
+ /**
637
+ * Point-in-time local upload health for one transport (one stream/room).
638
+ *
639
+ * Returned by `StreamsCrdt.getLocalSyncStats()`. Intended for downstream
640
+ * tooling (e.g. a CLI showing "N updates not yet uploaded" with the blocking
641
+ * error) without requiring a live `TransportSubscription` reference.
642
+ */
643
+ interface LocalSyncStats {
644
+ /**
645
+ * Number of local update batches queued but not yet durably appended.
646
+ *
647
+ * In join mode this is the live room's pending queue (including any batch
648
+ * currently in flight). In write-only mode it is the write-only queue.
649
+ */
650
+ readonly pendingLocalCount: number;
651
+ /**
652
+ * Wall-clock timestamp (`Date.now()` ms) of the last durably committed
653
+ * local append, or `undefined` if none succeeded yet.
654
+ */
655
+ readonly lastLocalAppendAtMs?: number;
656
+ /** Last transport error observed on the room (read or write side). */
657
+ readonly lastError?: TransportError;
658
+ /**
659
+ * Last error observed by the local write path specifically. Only cleared
660
+ * by a successful append; explains a stuck non-zero `pendingLocalCount`.
661
+ */
662
+ readonly lastWriteError?: TransportError;
663
+ }
594
664
  /**
595
665
  * Adapter contract for CRDT-like ephemeral stores.
596
666
  *
@@ -850,6 +920,8 @@ interface StreamsCrdtLike<TVersion extends JsonObject> {
850
920
  */
851
921
  close(): Promise<void>;
852
922
  getSnapshotOffset(): Promise<Result<string | null, TransportError>>;
923
+ /** Reports current local upload health (pending count, last append, last error). */
924
+ getLocalSyncStats(): LocalSyncStats;
853
925
  readonly streamUrl: string;
854
926
  readonly adapter: CrdtAdapter<TVersion>;
855
927
  readonly remoteCursorStore: RemoteCursorStore<TVersion>;
@@ -888,6 +960,8 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
888
960
  private writeOnlyDisabledReason?;
889
961
  private joinState?;
890
962
  private opQueue;
963
+ /** Wall-clock ms of the last durably committed local append, if any. */
964
+ private lastLocalAppendAtMs?;
891
965
  /**
892
966
  * Internal catch-up result used when callers need to handle 410 by
893
967
  * preserving local state before rebuilding through bootstrap.
@@ -914,6 +988,15 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
914
988
  * restarts the affected loop(s).
915
989
  */
916
990
  rejoin(): void;
991
+ /**
992
+ * Reports current local upload health for this transport.
993
+ *
994
+ * In join mode this reflects the live room's pending queue; in write-only
995
+ * mode it reflects the write-only queue. Intended for downstream tooling
996
+ * (e.g. a CLI showing "N updates not yet uploaded" plus the blocking
997
+ * error) without needing a `TransportSubscription` reference.
998
+ */
999
+ getLocalSyncStats(): LocalSyncStats;
917
1000
  /**
918
1001
  * Reads the latest remote snapshot offset, if the server exposes one.
919
1002
  */
@@ -1004,18 +1087,101 @@ declare class StreamsCrdt<TVersion extends JsonObject> implements StreamsCrdtLik
1004
1087
  private inferRemoteAndAppendLocal;
1005
1088
  private recoverFromGoneWithLocalBootstrap;
1006
1089
  private runReadLoop;
1090
+ /**
1091
+ * Schedules a background self-restart of the read loop after it exits with
1092
+ * a terminal-class ("error") failure.
1093
+ *
1094
+ * Non-retryable errors (auth failure, protocol error, stream 404) used to
1095
+ * stop the live read loop permanently — the room's remote cursor silently
1096
+ * froze until a process restart. Instead, the room stays in "error" status
1097
+ * (callers still see the failure via `status`/`lastError`) while a capped
1098
+ * exponential backoff (floor 1s, cap = last reconnect-table delay, with
1099
+ * jitter) keeps retrying in the background. Each retry issues fresh
1100
+ * requests, and every request re-runs the auth callback — on a 401/403 the
1101
+ * HTTP layer additionally forces a token refresh (`reason: "unauthorized"`)
1102
+ * and retries that request once — so refreshed credentials are picked up
1103
+ * without any caller intervention.
1104
+ *
1105
+ * "disconnected" (a finite `maxAttempts` budget exhausted on retryable
1106
+ * errors) intentionally stays terminal-until-`rejoin()`: a finite budget is
1107
+ * an explicit opt-in to giving up.
1108
+ */
1109
+ private maybeScheduleReadLoopRestart;
1110
+ /**
1111
+ * Whether an exited read loop still needs a background restart.
1112
+ *
1113
+ * Only ever evaluated after the loop has unwound (from
1114
+ * `maybeScheduleReadLoopRestart`, called at `runReadLoop`'s tail). "error" is
1115
+ * the ordinary terminal-class exit. "reconnecting" here is the anomalous case
1116
+ * where `rejoin()` ran re-entrantly from a status listener *during* the loop's
1117
+ * terminal `setReadSubStatus("error")`: it flipped the sub-status to
1118
+ * "reconnecting" and fire-and-forgot `runReadLoop`, but that no-oped because
1119
+ * the loop was still on the stack — so without this, the loop would exit and
1120
+ * nothing would ever restart it (a permanent stall, the very failure mode this
1121
+ * file removes). "disconnected" (a finite `maxAttempts` budget) stays terminal
1122
+ * by design, and "ok"/"connecting" don't reach this path via an exit.
1123
+ */
1124
+ private readLoopNeedsRestart;
1125
+ private clearReadRestartTimer;
1126
+ private clearWriteRetryTimer;
1127
+ /**
1128
+ * Applies the budgeted backoff for a retryable read-loop failure.
1129
+ *
1130
+ * Returns `true` when the loop should continue with the next attempt, or
1131
+ * `false` (after surfacing "disconnected") when a finite retry budget is
1132
+ * exhausted.
1133
+ */
1134
+ private awaitReadRetryBackoff;
1135
+ private runReadLoopIterations;
1007
1136
  /** Maximum encoded size (bytes) for a single batched append. */
1008
1137
  private static readonly MAX_BATCH_BYTES;
1009
1138
  /**
1010
1139
  * Drain up to `MAX_BATCH_BYTES` worth of queued batches from the front of
1011
1140
  * `pendingLocal` and merge them into a single `CrdtUpdateBatch`.
1012
1141
  *
1142
+ * `maxCount` optionally caps how many queued batches may be merged (used to
1143
+ * isolate the head batch after a 413 rejection).
1144
+ *
1013
1145
  * Returns `undefined` when the queue is empty.
1014
1146
  */
1015
1147
  private drainQueuedBatch;
1016
1148
  private drainPendingBatch;
1017
1149
  private drainPendingWriteOnlyBatch;
1150
+ /**
1151
+ * Drains `pendingLocal` to the server in strict FIFO order.
1152
+ *
1153
+ * FIFO invariant: batches are only popped after a durable append, and a
1154
+ * failing head batch is retried in place — never skipped, dropped, or
1155
+ * reordered. See the `JoinState.pendingLocal` doc for why this strictness
1156
+ * (rather than a version-vector diff upload) is what prevents undetectable
1157
+ * per-author holes.
1158
+ *
1159
+ * Failure handling is self-healing: a terminal-class failure surfaces
1160
+ * `"error"` write status (with `lastWriteError` set) and then retries via
1161
+ * this flush's own backoff timer (`maybeScheduleWriteRetry`) — it does not
1162
+ * depend on a future local op to re-trigger the flush.
1163
+ */
1018
1164
  private flushPendingLocal;
1165
+ /**
1166
+ * Abandons the current frozen "pending" append, fencing its producer
1167
+ * session. Only safe when the server definitively did not append the frozen
1168
+ * body (e.g. a 413 rejection happens before any append). Returns `false`
1169
+ * when there is no matching frozen pending append to abandon.
1170
+ */
1171
+ private abandonPendingFrozenLocalAppend;
1172
+ /**
1173
+ * Schedules a self-driven retry of the pending-local flush after a
1174
+ * terminal-class failure.
1175
+ *
1176
+ * Historically a non-retryable append error parked the queue until the next
1177
+ * local op happened to trigger a flush — an idle session with stranded
1178
+ * updates would never upload them (data stayed local until process
1179
+ * restart). The write queue now owns its retry timer: capped exponential
1180
+ * backoff (floor 1s, cap = last reconnect-table delay, jittered),
1181
+ * independent of new local ops. `rejoin()` and fresh local ops still retry
1182
+ * immediately.
1183
+ */
1184
+ private maybeScheduleWriteRetry;
1019
1185
  /**
1020
1186
  * Decrements all sync waiters by the number of flushed batches and
1021
1187
  * resolves any that have reached zero.
@@ -1086,6 +1252,18 @@ declare class EphemeralStreamCrdt {
1086
1252
  private logError;
1087
1253
  }
1088
1254
  //#endregion
1255
+ //#region src/transport-internal.d.ts
1256
+ /**
1257
+ * Rejection value used for `waitUntilSynced()` when the local append pipeline
1258
+ * fails. Carries the structured {@link TransportError} so downstream callers
1259
+ * (e.g. CLIs surfacing "N updates not yet uploaded") can branch on
1260
+ * `transportError.code` instead of parsing the message.
1261
+ */
1262
+ declare class LocalAppendFailedError extends Error {
1263
+ readonly transportError: TransportError;
1264
+ constructor(transportError: TransportError);
1265
+ }
1266
+ //#endregion
1089
1267
  //#region src/payload-protection.d.ts
1090
1268
  type PayloadProtectionFailureReason = "plaintext_forbidden" | "missing_read_key" | "decrypt_failed" | "invalid_envelope" | "wrong_payload_kind" | "encrypt_failed";
1091
1269
  declare class PayloadProtectionError extends Error {
@@ -1115,5 +1293,5 @@ declare function createStreamUrl(input: {
1115
1293
  baseUrl?: string;
1116
1294
  }): string;
1117
1295
  //#endregion
1118
- export { BeforeRemoteCursorSaveHook as $, PayloadProtectionReadPolicy as A, StreamsCrdtShardUrlsOptions as B, IsolatedCrdtAdapter as C, PayloadProtectionKey as D, PayloadProtectionEncryptionOptions as E, SnapshotTransformHook as F, TransportError as G, TransportCatchupSuccess as H, SnapshotUploadOptions as I, TransportSnapshotUploadSuccess as J, TransportJoinParams as K, StreamsAuthContext as L, PayloadProtectionWritePolicy as M, Result as N, PayloadProtectionKeyProvider as O, SnapshotCodec as P, BeforeRemoteCursorSaveContext as Q, StreamsAuthProvider as R, EphemeralStreamSubscription as S, JsonValue as T, TransportCreateStreamSuccess as U, TransportCatchupParams as V, TransportDeleteStreamSuccess as W, TransportSyncSuccess as X, TransportSubscription as Y, WriteOnlyAppendResult as Z, E2eeScope as _, EphemeralStreamCrdt as a, RemoteCursorStore as at, EphemeralStreamCrdtOptions as b, CrdtApplyOutcome as c, CrdtUpdateBatch as d, InMemoryRemoteCursorStore as et, E2eeEncryptionOptions as f, E2eeReadPolicy as g, E2eeOptions as h, PayloadProtectionError as i, RemoteCursorSaveSource as it, PayloadProtectionScope as j, PayloadProtectionOptions as k, CrdtApplyReturn as l, E2eeKeyProvider as m, isValidBucketId as n, IndexedDbRemoteCursorStoreOptions as nt, StreamsCrdt as o, createInitialRemoteCursor as ot, E2eeKey as p, TransportRoomStatus as q, isValidRillId as r, RemoteCursor as rt, CrdtAdapter as s, createStreamUrl as t, IndexedDbRemoteCursorStore as tt, CrdtUnresolvedSpan as u, E2eeWritePolicy as v, JsonObject as w, EphemeralStreamJoinParams as x, EphemeralStreamAdaptor as y, StreamsCrdtOptions as z };
1119
- //# sourceMappingURL=stream-id-DJYQlTE2.d.ts.map
1296
+ export { WriteOnlyAppendResult as $, PayloadProtectionKeyProvider as A, StreamsAuthProvider as B, EphemeralStreamSubscription as C, LocalSyncStats as D, JsonValue as E, Result as F, TransportCreateStreamSuccess as G, StreamsCrdtShardUrlsOptions as H, SnapshotCodec as I, TransportJoinParams as J, TransportDeleteStreamSuccess as K, SnapshotTransformHook as L, PayloadProtectionReadPolicy as M, PayloadProtectionScope as N, PayloadProtectionEncryptionOptions as O, PayloadProtectionWritePolicy as P, TransportSyncSuccess as Q, SnapshotUploadOptions as R, EphemeralStreamJoinParams as S, JsonObject as T, TransportCatchupParams as U, StreamsCrdtOptions as V, TransportCatchupSuccess as W, TransportSnapshotUploadSuccess as X, TransportRoomStatus as Y, TransportSubscription as Z, E2eeReadPolicy as _, LocalAppendFailedError as a, RemoteCursor as at, EphemeralStreamAdaptor as b, CrdtAdapter as c, createInitialRemoteCursor as ct, CrdtUnresolvedSpan as d, BeforeRemoteCursorSaveContext as et, CrdtUpdateBatch as f, E2eeOptions as g, E2eeKeyProvider as h, PayloadProtectionError as i, IndexedDbRemoteCursorStoreOptions as it, PayloadProtectionOptions as j, PayloadProtectionKey as k, CrdtApplyOutcome as l, E2eeKey as m, isValidBucketId as n, InMemoryRemoteCursorStore as nt, EphemeralStreamCrdt as o, RemoteCursorSaveSource as ot, E2eeEncryptionOptions as p, TransportError as q, isValidRillId as r, IndexedDbRemoteCursorStore as rt, StreamsCrdt as s, RemoteCursorStore as st, createStreamUrl as t, BeforeRemoteCursorSaveHook as tt, CrdtApplyReturn as u, E2eeScope as v, IsolatedCrdtAdapter as w, EphemeralStreamCrdtOptions as x, E2eeWritePolicy as y, StreamsAuthContext as z };
1297
+ //# sourceMappingURL=stream-id-BuXMoCvo.d.ts.map