@loro-dev/streams-crdt 0.15.0 → 0.15.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 +1 -1
- package/dist/flock.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/loro.d.ts +1 -1
- package/dist/loro.js +1 -1
- package/dist/{stream-id-CggbAC6b.js → stream-id-BygPTNvQ.js} +143 -17
- package/dist/stream-id-BygPTNvQ.js.map +1 -0
- package/dist/{stream-id-CO65-58E.d.ts → stream-id-CVCOLgSp.d.ts} +36 -1
- package/package.json +1 -1
- package/dist/stream-id-CggbAC6b.js.map +0 -1
package/dist/flock.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as WriteOnlyAppendResult, A as PayloadProtectionProviderConfig, B as StreamsAuthProvider, C as LocalSyncStats, D as PayloadProtectionOpenInput, E as PayloadProtectionKind, F as Result, G as TransportCreateStreamSuccess, H as StreamsCrdtShardUrlsOptions, I as SnapshotCodec, J as TransportJoinParams, K as TransportDeleteStreamSuccess, L as SnapshotTransformHook, M as PayloadProtectionSealInput, N as PayloadProtectionSealResult, O as PayloadProtectionOptions, P as PayloadProtectionWritePolicy, Q as TransportSyncSuccess, R as SnapshotUploadOptions, S as JsonValue, T as PayloadProtectionFailureReason, U as TransportCatchupParams, V as StreamsCrdtOptions, W as TransportCatchupSuccess, X as TransportSnapshotUploadSuccess, Y as TransportRoomStatus, Z as TransportSubscription, _ as EphemeralStreamCrdtOptions, a as StreamsCrdt, at as RemoteCursor, b as IsolatedCrdtAdapter, c as CrdtApplyReturn, ct as createInitialRemoteCursor, d as E2eeOptions, et as BeforeRemoteCursorSaveContext, f as E2eeProvider, g as EphemeralStreamAdaptor, h as E2eeWritePolicy, i as EphemeralStreamCrdt, it as IndexedDbRemoteCursorStoreOptions, j as PayloadProtectionReadPolicy, k as PayloadProtectionProvider, l as CrdtUnresolvedSpan, m as E2eeReadPolicy, n as PayloadProtectionError, nt as InMemoryRemoteCursorStore, o as CrdtAdapter, ot as RemoteCursorSaveSource, p as E2eeProviderConfig, q as TransportError, r as LocalAppendFailedError, rt as IndexedDbRemoteCursorStore, s as CrdtApplyOutcome, st as RemoteCursorStore, t as isValidRillId, tt as BeforeRemoteCursorSaveHook, u as CrdtUpdateBatch, v as EphemeralStreamJoinParams, w as PayloadProtectionContext, x as JsonObject, y as EphemeralStreamSubscription, z as StreamsAuthContext } from "./stream-id-
|
|
1
|
+
import { $ as WriteOnlyAppendResult, A as PayloadProtectionProviderConfig, B as StreamsAuthProvider, C as LocalSyncStats, D as PayloadProtectionOpenInput, E as PayloadProtectionKind, F as Result, G as TransportCreateStreamSuccess, H as StreamsCrdtShardUrlsOptions, I as SnapshotCodec, J as TransportJoinParams, K as TransportDeleteStreamSuccess, L as SnapshotTransformHook, M as PayloadProtectionSealInput, N as PayloadProtectionSealResult, O as PayloadProtectionOptions, P as PayloadProtectionWritePolicy, Q as TransportSyncSuccess, R as SnapshotUploadOptions, S as JsonValue, T as PayloadProtectionFailureReason, U as TransportCatchupParams, V as StreamsCrdtOptions, W as TransportCatchupSuccess, X as TransportSnapshotUploadSuccess, Y as TransportRoomStatus, Z as TransportSubscription, _ as EphemeralStreamCrdtOptions, a as StreamsCrdt, at as RemoteCursor, b as IsolatedCrdtAdapter, c as CrdtApplyReturn, ct as createInitialRemoteCursor, d as E2eeOptions, et as BeforeRemoteCursorSaveContext, f as E2eeProvider, g as EphemeralStreamAdaptor, h as E2eeWritePolicy, i as EphemeralStreamCrdt, it as IndexedDbRemoteCursorStoreOptions, j as PayloadProtectionReadPolicy, k as PayloadProtectionProvider, l as CrdtUnresolvedSpan, m as E2eeReadPolicy, n as PayloadProtectionError, nt as InMemoryRemoteCursorStore, o as CrdtAdapter, ot as RemoteCursorSaveSource, p as E2eeProviderConfig, q as TransportError, r as LocalAppendFailedError, rt as IndexedDbRemoteCursorStore, s as CrdtApplyOutcome, st as RemoteCursorStore, t as isValidRillId, tt as BeforeRemoteCursorSaveHook, u as CrdtUpdateBatch, v as EphemeralStreamJoinParams, w as PayloadProtectionContext, x as JsonObject, y as EphemeralStreamSubscription, z as StreamsAuthContext } from "./stream-id-CVCOLgSp.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
|
package/dist/flock.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as createInitialRemoteCursor, c as PayloadProtectionError, i as IndexedDbRemoteCursorStore, n as StreamsCrdt, o as EphemeralStreamCrdt, r as InMemoryRemoteCursorStore, s as LocalAppendFailedError, t as isValidRillId } from "./stream-id-
|
|
1
|
+
import { a as createInitialRemoteCursor, c as PayloadProtectionError, i as IndexedDbRemoteCursorStore, n as StreamsCrdt, o as EphemeralStreamCrdt, r as InMemoryRemoteCursorStore, s as LocalAppendFailedError, t as isValidRillId } from "./stream-id-BygPTNvQ.js";
|
|
2
2
|
import { Flock } from "@loro-dev/flock-wasm";
|
|
3
3
|
//#region src/flock.ts
|
|
4
4
|
function isAfter(entry, current) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as WriteOnlyAppendResult, A as PayloadProtectionProviderConfig, B as StreamsAuthProvider, C as LocalSyncStats, D as PayloadProtectionOpenInput, E as PayloadProtectionKind, F as Result, G as TransportCreateStreamSuccess, H as StreamsCrdtShardUrlsOptions, I as SnapshotCodec, J as TransportJoinParams, K as TransportDeleteStreamSuccess, L as SnapshotTransformHook, M as PayloadProtectionSealInput, N as PayloadProtectionSealResult, O as PayloadProtectionOptions, P as PayloadProtectionWritePolicy, Q as TransportSyncSuccess, R as SnapshotUploadOptions, S as JsonValue, T as PayloadProtectionFailureReason, U as TransportCatchupParams, V as StreamsCrdtOptions, W as TransportCatchupSuccess, X as TransportSnapshotUploadSuccess, Y as TransportRoomStatus, Z as TransportSubscription, _ as EphemeralStreamCrdtOptions, a as StreamsCrdt, at as RemoteCursor, b as IsolatedCrdtAdapter, c as CrdtApplyReturn, ct as createInitialRemoteCursor, d as E2eeOptions, et as BeforeRemoteCursorSaveContext, f as E2eeProvider, g as EphemeralStreamAdaptor, h as E2eeWritePolicy, i as EphemeralStreamCrdt, it as IndexedDbRemoteCursorStoreOptions, j as PayloadProtectionReadPolicy, k as PayloadProtectionProvider, l as CrdtUnresolvedSpan, m as E2eeReadPolicy, n as PayloadProtectionError, nt as InMemoryRemoteCursorStore, o as CrdtAdapter, ot as RemoteCursorSaveSource, p as E2eeProviderConfig, q as TransportError, r as LocalAppendFailedError, rt as IndexedDbRemoteCursorStore, s as CrdtApplyOutcome, st as RemoteCursorStore, t as isValidRillId, tt as BeforeRemoteCursorSaveHook, u as CrdtUpdateBatch, v as EphemeralStreamJoinParams, w as PayloadProtectionContext, x as JsonObject, y as EphemeralStreamSubscription, z as StreamsAuthContext } from "./stream-id-
|
|
1
|
+
import { $ as WriteOnlyAppendResult, A as PayloadProtectionProviderConfig, B as StreamsAuthProvider, C as LocalSyncStats, D as PayloadProtectionOpenInput, E as PayloadProtectionKind, F as Result, G as TransportCreateStreamSuccess, H as StreamsCrdtShardUrlsOptions, I as SnapshotCodec, J as TransportJoinParams, K as TransportDeleteStreamSuccess, L as SnapshotTransformHook, M as PayloadProtectionSealInput, N as PayloadProtectionSealResult, O as PayloadProtectionOptions, P as PayloadProtectionWritePolicy, Q as TransportSyncSuccess, R as SnapshotUploadOptions, S as JsonValue, T as PayloadProtectionFailureReason, U as TransportCatchupParams, V as StreamsCrdtOptions, W as TransportCatchupSuccess, X as TransportSnapshotUploadSuccess, Y as TransportRoomStatus, Z as TransportSubscription, _ as EphemeralStreamCrdtOptions, a as StreamsCrdt, at as RemoteCursor, b as IsolatedCrdtAdapter, c as CrdtApplyReturn, ct as createInitialRemoteCursor, d as E2eeOptions, et as BeforeRemoteCursorSaveContext, f as E2eeProvider, g as EphemeralStreamAdaptor, h as E2eeWritePolicy, i as EphemeralStreamCrdt, it as IndexedDbRemoteCursorStoreOptions, j as PayloadProtectionReadPolicy, k as PayloadProtectionProvider, l as CrdtUnresolvedSpan, m as E2eeReadPolicy, n as PayloadProtectionError, nt as InMemoryRemoteCursorStore, o as CrdtAdapter, ot as RemoteCursorSaveSource, p as E2eeProviderConfig, q as TransportError, r as LocalAppendFailedError, rt as IndexedDbRemoteCursorStore, s as CrdtApplyOutcome, st as RemoteCursorStore, t as isValidRillId, tt as BeforeRemoteCursorSaveHook, u as CrdtUpdateBatch, v as EphemeralStreamJoinParams, w as PayloadProtectionContext, x as JsonObject, y as EphemeralStreamSubscription, z as StreamsAuthContext } from "./stream-id-CVCOLgSp.js";
|
|
2
2
|
export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtApplyOutcome, CrdtApplyReturn, CrdtUnresolvedSpan, CrdtUpdateBatch, PayloadProtectionError as E2eeError, PayloadProtectionError, E2eeOptions, E2eeProvider, E2eeProviderConfig, E2eeReadPolicy, E2eeWritePolicy, EphemeralStreamAdaptor, EphemeralStreamCrdt, EphemeralStreamCrdtOptions, EphemeralStreamJoinParams, EphemeralStreamSubscription, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, LocalAppendFailedError, LocalSyncStats, PayloadProtectionContext, PayloadProtectionFailureReason, PayloadProtectionKind, PayloadProtectionOpenInput, PayloadProtectionOptions, PayloadProtectionProvider, PayloadProtectionProviderConfig, PayloadProtectionReadPolicy, PayloadProtectionSealInput, PayloadProtectionSealResult, 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, isValidRillId };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as createInitialRemoteCursor, c as PayloadProtectionError, i as IndexedDbRemoteCursorStore, n as StreamsCrdt, o as EphemeralStreamCrdt, r as InMemoryRemoteCursorStore, s as LocalAppendFailedError, t as isValidRillId } from "./stream-id-
|
|
1
|
+
import { a as createInitialRemoteCursor, c as PayloadProtectionError, i as IndexedDbRemoteCursorStore, n as StreamsCrdt, o as EphemeralStreamCrdt, r as InMemoryRemoteCursorStore, s as LocalAppendFailedError, t as isValidRillId } from "./stream-id-BygPTNvQ.js";
|
|
2
2
|
export { PayloadProtectionError as E2eeError, PayloadProtectionError, EphemeralStreamCrdt, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, LocalAppendFailedError, StreamsCrdt, createInitialRemoteCursor, isValidRillId };
|
package/dist/loro.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as WriteOnlyAppendResult, A as PayloadProtectionProviderConfig, B as StreamsAuthProvider, C as LocalSyncStats, D as PayloadProtectionOpenInput, E as PayloadProtectionKind, F as Result, G as TransportCreateStreamSuccess, H as StreamsCrdtShardUrlsOptions, I as SnapshotCodec, J as TransportJoinParams, K as TransportDeleteStreamSuccess, L as SnapshotTransformHook, M as PayloadProtectionSealInput, N as PayloadProtectionSealResult, O as PayloadProtectionOptions, P as PayloadProtectionWritePolicy, Q as TransportSyncSuccess, R as SnapshotUploadOptions, S as JsonValue, T as PayloadProtectionFailureReason, U as TransportCatchupParams, V as StreamsCrdtOptions, W as TransportCatchupSuccess, X as TransportSnapshotUploadSuccess, Y as TransportRoomStatus, Z as TransportSubscription, _ as EphemeralStreamCrdtOptions, a as StreamsCrdt, at as RemoteCursor, b as IsolatedCrdtAdapter, c as CrdtApplyReturn, ct as createInitialRemoteCursor, d as E2eeOptions, et as BeforeRemoteCursorSaveContext, f as E2eeProvider, g as EphemeralStreamAdaptor, h as E2eeWritePolicy, i as EphemeralStreamCrdt, it as IndexedDbRemoteCursorStoreOptions, j as PayloadProtectionReadPolicy, k as PayloadProtectionProvider, l as CrdtUnresolvedSpan, m as E2eeReadPolicy, n as PayloadProtectionError, nt as InMemoryRemoteCursorStore, o as CrdtAdapter, ot as RemoteCursorSaveSource, p as E2eeProviderConfig, q as TransportError, r as LocalAppendFailedError, rt as IndexedDbRemoteCursorStore, s as CrdtApplyOutcome, st as RemoteCursorStore, t as isValidRillId, tt as BeforeRemoteCursorSaveHook, u as CrdtUpdateBatch, v as EphemeralStreamJoinParams, w as PayloadProtectionContext, x as JsonObject, y as EphemeralStreamSubscription, z as StreamsAuthContext } from "./stream-id-
|
|
1
|
+
import { $ as WriteOnlyAppendResult, A as PayloadProtectionProviderConfig, B as StreamsAuthProvider, C as LocalSyncStats, D as PayloadProtectionOpenInput, E as PayloadProtectionKind, F as Result, G as TransportCreateStreamSuccess, H as StreamsCrdtShardUrlsOptions, I as SnapshotCodec, J as TransportJoinParams, K as TransportDeleteStreamSuccess, L as SnapshotTransformHook, M as PayloadProtectionSealInput, N as PayloadProtectionSealResult, O as PayloadProtectionOptions, P as PayloadProtectionWritePolicy, Q as TransportSyncSuccess, R as SnapshotUploadOptions, S as JsonValue, T as PayloadProtectionFailureReason, U as TransportCatchupParams, V as StreamsCrdtOptions, W as TransportCatchupSuccess, X as TransportSnapshotUploadSuccess, Y as TransportRoomStatus, Z as TransportSubscription, _ as EphemeralStreamCrdtOptions, a as StreamsCrdt, at as RemoteCursor, b as IsolatedCrdtAdapter, c as CrdtApplyReturn, ct as createInitialRemoteCursor, d as E2eeOptions, et as BeforeRemoteCursorSaveContext, f as E2eeProvider, g as EphemeralStreamAdaptor, h as E2eeWritePolicy, i as EphemeralStreamCrdt, it as IndexedDbRemoteCursorStoreOptions, j as PayloadProtectionReadPolicy, k as PayloadProtectionProvider, l as CrdtUnresolvedSpan, m as E2eeReadPolicy, n as PayloadProtectionError, nt as InMemoryRemoteCursorStore, o as CrdtAdapter, ot as RemoteCursorSaveSource, p as E2eeProviderConfig, q as TransportError, r as LocalAppendFailedError, rt as IndexedDbRemoteCursorStore, s as CrdtApplyOutcome, st as RemoteCursorStore, t as isValidRillId, tt as BeforeRemoteCursorSaveHook, u as CrdtUpdateBatch, v as EphemeralStreamJoinParams, w as PayloadProtectionContext, x as JsonObject, y as EphemeralStreamSubscription, z as StreamsAuthContext } from "./stream-id-CVCOLgSp.js";
|
|
2
2
|
import { LoroDoc } from "loro-crdt";
|
|
3
3
|
|
|
4
4
|
//#region src/loro.d.ts
|
package/dist/loro.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as createInitialRemoteCursor, c as PayloadProtectionError, i as IndexedDbRemoteCursorStore, n as StreamsCrdt, o as EphemeralStreamCrdt, r as InMemoryRemoteCursorStore, s as LocalAppendFailedError, t as isValidRillId } from "./stream-id-
|
|
1
|
+
import { a as createInitialRemoteCursor, c as PayloadProtectionError, i as IndexedDbRemoteCursorStore, n as StreamsCrdt, o as EphemeralStreamCrdt, r as InMemoryRemoteCursorStore, s as LocalAppendFailedError, t as isValidRillId } from "./stream-id-BygPTNvQ.js";
|
|
2
2
|
import { LoroDoc, VersionVector, decodeImportBlobMeta } from "loro-crdt";
|
|
3
3
|
//#region src/loro.ts
|
|
4
4
|
function batchImportUpdates(doc, updates) {
|
|
@@ -2066,11 +2066,32 @@ function createInitialRemoteCursor(input) {
|
|
|
2066
2066
|
}
|
|
2067
2067
|
//#endregion
|
|
2068
2068
|
//#region src/transport-cursor.ts
|
|
2069
|
+
const MAX_SPANS_IN_MESSAGE = 8;
|
|
2070
|
+
/**
|
|
2071
|
+
* Renders a bounded summary of unresolved spans for the error message, so a
|
|
2072
|
+
* caller's error log names the author(s) whose ops are missing from the
|
|
2073
|
+
* stream without the caller having to unwrap `error.unresolved` itself.
|
|
2074
|
+
*/
|
|
2075
|
+
function formatUnresolvedSpans(unresolved) {
|
|
2076
|
+
if (unresolved.length === 0) return "";
|
|
2077
|
+
const shown = unresolved.slice(0, MAX_SPANS_IN_MESSAGE).map((span) => `${span.peer}@[${span.start}..${span.end})`).join(", ");
|
|
2078
|
+
const suffix = unresolved.length > MAX_SPANS_IN_MESSAGE ? `, +${unresolved.length - MAX_SPANS_IN_MESSAGE} more` : "";
|
|
2079
|
+
return ` (missing deps for ${unresolved.length} span(s): ${shown}${suffix})`;
|
|
2080
|
+
}
|
|
2069
2081
|
var CrdtApplyIncompleteError = class extends Error {
|
|
2070
2082
|
unresolved;
|
|
2071
|
-
|
|
2072
|
-
|
|
2083
|
+
/**
|
|
2084
|
+
* The volatile server version at the moment apply failed: everything the
|
|
2085
|
+
* server delivered that the local doc actually applied (success spans
|
|
2086
|
+
* only — pending spans never advance it). `exportUpdates(appliedVersion)`
|
|
2087
|
+
* is therefore exactly "the ops this replica holds that the server does
|
|
2088
|
+
* not", which is what the join-time salvage append publishes.
|
|
2089
|
+
*/
|
|
2090
|
+
appliedVersion;
|
|
2091
|
+
constructor(unresolved, appliedVersion) {
|
|
2092
|
+
super(`CRDT apply incomplete: unresolved remote updates remain${formatUnresolvedSpans(unresolved)}`);
|
|
2073
2093
|
this.unresolved = unresolved;
|
|
2094
|
+
this.appliedVersion = appliedVersion;
|
|
2074
2095
|
}
|
|
2075
2096
|
};
|
|
2076
2097
|
function isApplyOutcome(value) {
|
|
@@ -2098,6 +2119,22 @@ var TransportCursorManager = class {
|
|
|
2098
2119
|
isCursorComplete(cursor) {
|
|
2099
2120
|
return !this.incompleteCursors.has(cursor);
|
|
2100
2121
|
}
|
|
2122
|
+
/**
|
|
2123
|
+
* Marks `cursor` incomplete without an apply.
|
|
2124
|
+
*
|
|
2125
|
+
* The `CrdtApplyIncompleteError` throw path discards its candidate cursor
|
|
2126
|
+
* BEFORE the usual WeakMap marking, so a caller's retained cursor keeps
|
|
2127
|
+
* whatever completeness it had. For a caught-up live room the first
|
|
2128
|
+
* poisoned event typically arrives with `up_to_date=true`, so the retained
|
|
2129
|
+
* cursor still reads "complete" for the whole wedge — leaving the degraded
|
|
2130
|
+
* local flush unengaged and the snapshot-upload clean gate open, exactly
|
|
2131
|
+
* the two consumers keyed on this marking. The read loop calls this when
|
|
2132
|
+
* it catches the error so the room's incomplete state is visible on the
|
|
2133
|
+
* cursor it actually holds.
|
|
2134
|
+
*/
|
|
2135
|
+
markCursorIncomplete(cursor, unresolved) {
|
|
2136
|
+
this.incompleteCursors.set(cursor, unresolved);
|
|
2137
|
+
}
|
|
2101
2138
|
async applyBootstrapPayload(cursor, snapshot, updates, nextOffset, upToDate = false) {
|
|
2102
2139
|
let nextVersion = cursor.serverLowerBoundVersion;
|
|
2103
2140
|
let complete = true;
|
|
@@ -2171,7 +2208,7 @@ var TransportCursorManager = class {
|
|
|
2171
2208
|
}
|
|
2172
2209
|
async finalizeCursor(cursor, source, complete, unresolved, upToDate) {
|
|
2173
2210
|
if (!complete) {
|
|
2174
|
-
if (upToDate) throw new CrdtApplyIncompleteError(unresolved);
|
|
2211
|
+
if (upToDate) throw new CrdtApplyIncompleteError(unresolved, cursor.serverLowerBoundVersion);
|
|
2175
2212
|
this.incompleteCursors.set(cursor, unresolved);
|
|
2176
2213
|
return cursor;
|
|
2177
2214
|
}
|
|
@@ -2326,6 +2363,7 @@ function createJoinState(cursor, liveMode = "sse") {
|
|
|
2326
2363
|
pendingLocal: new Deque(),
|
|
2327
2364
|
flushingLocal: false,
|
|
2328
2365
|
retryAttempt: 0,
|
|
2366
|
+
incompleteRecoveryAttempt: 0,
|
|
2329
2367
|
retrySleepController: new AbortController(),
|
|
2330
2368
|
readLoopRunning: false,
|
|
2331
2369
|
readErrorRetryAttempt: 0,
|
|
@@ -2909,7 +2947,13 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
2909
2947
|
async startJoin(state) {
|
|
2910
2948
|
try {
|
|
2911
2949
|
state.requestController ??= new AbortController();
|
|
2912
|
-
|
|
2950
|
+
let joined;
|
|
2951
|
+
try {
|
|
2952
|
+
joined = await this.performInitialJoinSync(state.liveMode, state.requestController?.signal);
|
|
2953
|
+
} catch (error) {
|
|
2954
|
+
if (!(error instanceof CrdtApplyIncompleteError) || state.closed || !await this.salvageLocalBacklogOnIncompleteInitialSync(state, error)) throw error;
|
|
2955
|
+
joined = await this.performInitialJoinSync(state.liveMode, state.requestController?.signal);
|
|
2956
|
+
}
|
|
2913
2957
|
state.cursor = joined.cursor;
|
|
2914
2958
|
state.streamCursor = joined.streamCursor;
|
|
2915
2959
|
state.liveMode = joined.liveMode;
|
|
@@ -2941,6 +2985,65 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
2941
2985
|
throw error;
|
|
2942
2986
|
}
|
|
2943
2987
|
}
|
|
2988
|
+
/**
|
|
2989
|
+
* Join-time salvage for a stream whose head cannot be fully applied
|
|
2990
|
+
* (apply_incomplete at `up_to_date`, even after bootstrap): before the
|
|
2991
|
+
* join surfaces the error, durably append everything this replica holds
|
|
2992
|
+
* that the server does not.
|
|
2993
|
+
*
|
|
2994
|
+
* Why this exists: the dual-author wedge is usually two-sided — the stream
|
|
2995
|
+
* is missing some author's ops, and a replica that HOLDS those ops (e.g.
|
|
2996
|
+
* received out-of-band over a local data plane) may itself be unable to
|
|
2997
|
+
* complete the join because of a second gap. The regular join-time export
|
|
2998
|
+
* runs only AFTER a successful initial sync, so without this salvage such
|
|
2999
|
+
* a replica can never publish the healing ops, and its own backlog from a
|
|
3000
|
+
* previous process run stays local for as long as the stream stays
|
|
3001
|
+
* poisoned.
|
|
3002
|
+
*
|
|
3003
|
+
* `error.appliedVersion` advances only from success spans, so
|
|
3004
|
+
* `exportUpdates(appliedVersion)` is exactly "ops this replica holds that
|
|
3005
|
+
* the server does not" — the failed initial sync already imported
|
|
3006
|
+
* everything the server DOES have, so the delta cannot re-append
|
|
3007
|
+
* server-known data. Cursor safety is untouched: the append is write-only
|
|
3008
|
+
* style (producer-acked, no read catch-up, no durable-cursor save), and
|
|
3009
|
+
* the retried initial sync re-derives its cursor from the server.
|
|
3010
|
+
*
|
|
3011
|
+
* Returns `true` when anything was durably appended; the caller then
|
|
3012
|
+
* retries the initial sync once. Best-effort: a salvage failure logs and
|
|
3013
|
+
* returns `false` so the original apply-incomplete error is what callers
|
|
3014
|
+
* observe.
|
|
3015
|
+
*/
|
|
3016
|
+
async salvageLocalBacklogOnIncompleteInitialSync(state, error) {
|
|
3017
|
+
const appliedVersion = error.appliedVersion;
|
|
3018
|
+
if (appliedVersion == null) return false;
|
|
3019
|
+
try {
|
|
3020
|
+
const appended = await this.enqueueExclusive(async () => {
|
|
3021
|
+
let didAppend = false;
|
|
3022
|
+
const frozen = this.frozenLocalAppend;
|
|
3023
|
+
if (frozen != null) {
|
|
3024
|
+
const ack = await this.appendFrozenLocalBatchRemote(frozen);
|
|
3025
|
+
this.commitProducerAck(ack.producerAck);
|
|
3026
|
+
didAppend = true;
|
|
3027
|
+
}
|
|
3028
|
+
await this.adapter.flushLocalExports?.();
|
|
3029
|
+
const localBatch = this.exportUpdates(appliedVersion);
|
|
3030
|
+
if (localBatch.batch != null) {
|
|
3031
|
+
const appendedBatch = await this.appendLocalBatchRemote({
|
|
3032
|
+
...state.cursor,
|
|
3033
|
+
serverLowerBoundVersion: appliedVersion
|
|
3034
|
+
}, localBatch.batch, void 0, "direct");
|
|
3035
|
+
this.commitProducerAck(appendedBatch.producerAck);
|
|
3036
|
+
didAppend = true;
|
|
3037
|
+
}
|
|
3038
|
+
return didAppend;
|
|
3039
|
+
});
|
|
3040
|
+
if (appended) this.logDebug("join salvage appended local backlog", { streamUrl: this.streamUrl });
|
|
3041
|
+
return appended;
|
|
3042
|
+
} catch (salvageError) {
|
|
3043
|
+
this.logError("join salvage append failed", salvageError, { streamUrl: this.streamUrl });
|
|
3044
|
+
return false;
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
2944
3047
|
async syncOnce(operation, parentSignal) {
|
|
2945
3048
|
let cursor = (await this.finalizeDirectLocalAppendIfNeeded(await this.resolveInitialRemoteState(operation, parentSignal))).cursor;
|
|
2946
3049
|
const localBatch = this.exportUpdates(cursor.serverLowerBoundVersion);
|
|
@@ -2955,8 +3058,11 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
2955
3058
|
let liveMode = preferredLiveMode;
|
|
2956
3059
|
const exportedLocal = this.exportUpdates(localExportRefVersion);
|
|
2957
3060
|
localExportRefVersion = exportedLocal.nextRefVersion;
|
|
2958
|
-
|
|
2959
|
-
|
|
3061
|
+
const exportedBatch = exportedLocal.batch;
|
|
3062
|
+
if (exportedBatch != null) {
|
|
3063
|
+
const appendCursor = cursor;
|
|
3064
|
+
const appendStreamCursor = streamCursor;
|
|
3065
|
+
const committed = await this.enqueueExclusive(() => this.appendLocalBatchDurably(appendCursor, exportedBatch, void 0, "direct", appendStreamCursor));
|
|
2960
3066
|
cursor = committed.cursor;
|
|
2961
3067
|
streamCursor = committed.streamCursor;
|
|
2962
3068
|
}
|
|
@@ -3116,7 +3222,8 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
3116
3222
|
};
|
|
3117
3223
|
}
|
|
3118
3224
|
}
|
|
3119
|
-
async recoverLiveIncompleteRemoteApply(state) {
|
|
3225
|
+
async recoverLiveIncompleteRemoteApply(state, error) {
|
|
3226
|
+
this.cursorManager.markCursorIncomplete(state.cursor, error.unresolved);
|
|
3120
3227
|
try {
|
|
3121
3228
|
const bootstrapped = await this.bootstrapState({
|
|
3122
3229
|
...state.cursor,
|
|
@@ -3126,6 +3233,7 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
3126
3233
|
state.cursor = bootstrapped.cursor;
|
|
3127
3234
|
state.streamCursor = bootstrapped.streamCursor;
|
|
3128
3235
|
state.retryAttempt = 0;
|
|
3236
|
+
state.incompleteRecoveryAttempt = 0;
|
|
3129
3237
|
state.lastError = void 0;
|
|
3130
3238
|
this.setReadSubStatus(state, "ok");
|
|
3131
3239
|
if (state.pendingLocal.length > 0) this.flushPendingLocal(state);
|
|
@@ -3137,7 +3245,10 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
3137
3245
|
code: classified.code
|
|
3138
3246
|
});
|
|
3139
3247
|
state.lastError = toTransportError(error);
|
|
3248
|
+
if (state.pendingLocal.length > 0) this.flushPendingLocal(state);
|
|
3140
3249
|
if (classified.retriable && !isAuthOrProtocolError(error)) {
|
|
3250
|
+
state.incompleteRecoveryAttempt += 1;
|
|
3251
|
+
state.retryAttempt = Math.max(state.retryAttempt, state.incompleteRecoveryAttempt);
|
|
3141
3252
|
this.setReadSubStatus(state, "reconnecting");
|
|
3142
3253
|
return "retry";
|
|
3143
3254
|
}
|
|
@@ -3613,7 +3724,7 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
3613
3724
|
if (state.closed) return;
|
|
3614
3725
|
if (error instanceof CrdtApplyIncompleteError) {
|
|
3615
3726
|
if (capturedController?.signal.aborted && state.requestController === capturedController) state.requestController = new AbortController();
|
|
3616
|
-
const recovery = await this.recoverLiveIncompleteRemoteApply(state);
|
|
3727
|
+
const recovery = await this.recoverLiveIncompleteRemoteApply(state, error);
|
|
3617
3728
|
if (recovery === "recovered") continue;
|
|
3618
3729
|
if (recovery === "terminal") return;
|
|
3619
3730
|
if (await this.awaitReadRetryBackoff(state, error)) continue;
|
|
@@ -3647,7 +3758,7 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
3647
3758
|
const flushError = abortReason instanceof Error ? abortReason : error;
|
|
3648
3759
|
if (flushError instanceof CrdtApplyIncompleteError) {
|
|
3649
3760
|
if (state.requestController === capturedController) state.requestController = new AbortController();
|
|
3650
|
-
const recovery = await this.recoverLiveIncompleteRemoteApply(state);
|
|
3761
|
+
const recovery = await this.recoverLiveIncompleteRemoteApply(state, flushError);
|
|
3651
3762
|
if (recovery === "recovered") continue;
|
|
3652
3763
|
if (recovery === "terminal") return;
|
|
3653
3764
|
if (await this.awaitReadRetryBackoff(state, flushError)) continue;
|
|
@@ -3742,10 +3853,15 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
3742
3853
|
* `"error"` write status (with `lastWriteError` set) and then retries via
|
|
3743
3854
|
* this flush's own backoff timer (`maybeScheduleWriteRetry`) — it does not
|
|
3744
3855
|
* depend on a future local op to re-trigger the flush.
|
|
3856
|
+
*
|
|
3857
|
+
* While the cursor is incomplete (unresolved remote spans), the flush
|
|
3858
|
+
* degrades to an append-only mode instead of pausing: batches still reach
|
|
3859
|
+
* the server, but read progress, durable-cursor saves, and snapshot
|
|
3860
|
+
* scheduling stay untouched. See the inline comment in the incomplete
|
|
3861
|
+
* branch and specs/loro-pending.md "Local Append Rules".
|
|
3745
3862
|
*/
|
|
3746
3863
|
async flushPendingLocal(state) {
|
|
3747
3864
|
if (state.flushingLocal || state.closed) return;
|
|
3748
|
-
if (!this.cursorManager.isCursorComplete(state.cursor)) return;
|
|
3749
3865
|
this.clearWriteRetryTimer(state);
|
|
3750
3866
|
state.flushingLocal = true;
|
|
3751
3867
|
try {
|
|
@@ -3754,16 +3870,23 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
3754
3870
|
if (drained == null) return;
|
|
3755
3871
|
let attempts = 0;
|
|
3756
3872
|
while (!state.closed) try {
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3873
|
+
let committedCount;
|
|
3874
|
+
if (this.cursorManager.isCursorComplete(state.cursor)) {
|
|
3875
|
+
state.cursor = await this.enqueueExclusive(async () => this.appendLocalBatch(state.cursor, drained.batch, drained.count, state.streamCursor));
|
|
3876
|
+
const committedStreamCursor = this.consumeCommittedLocalStreamCursor();
|
|
3877
|
+
if (committedStreamCursor != null) state.streamCursor = committedStreamCursor;
|
|
3878
|
+
committedCount = this.consumeCommittedPendingCount() ?? drained.count;
|
|
3879
|
+
this.snapshotManager.schedule(state);
|
|
3880
|
+
} else {
|
|
3881
|
+
const appended = await this.enqueueExclusive(async () => this.appendLocalBatchRemote(state.cursor, drained.batch, drained.count, "pending"));
|
|
3882
|
+
this.commitProducerAck(appended.producerAck);
|
|
3883
|
+
committedCount = appended.pendingCount ?? drained.count;
|
|
3884
|
+
}
|
|
3761
3885
|
for (let i = 0; i < committedCount; i += 1) state.pendingLocal.popFront();
|
|
3762
3886
|
this.notifySyncWaiters(state, committedCount);
|
|
3763
3887
|
state.flushHeadBatchOnly = false;
|
|
3764
3888
|
state.lastWriteError = void 0;
|
|
3765
3889
|
this.setWriteSubStatus(state, "ok");
|
|
3766
|
-
this.snapshotManager.schedule(state);
|
|
3767
3890
|
break;
|
|
3768
3891
|
} catch (error) {
|
|
3769
3892
|
if (isPayloadTooLargeError(error) && drained.count > 1 && this.abandonPendingFrozenLocalAppend()) {
|
|
@@ -3857,12 +3980,15 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
3857
3980
|
for (const waiter of state.syncWaiters.splice(0)) waiter.reject(error);
|
|
3858
3981
|
}
|
|
3859
3982
|
async saveRemoteCursor(cursor, source) {
|
|
3860
|
-
|
|
3983
|
+
const saved = await persistRemoteCursor({
|
|
3861
3984
|
remoteCursorStore: this.remoteCursorStore,
|
|
3862
3985
|
cursor,
|
|
3863
3986
|
source,
|
|
3864
3987
|
beforeRemoteCursorSave: this.beforeRemoteCursorSave
|
|
3865
3988
|
});
|
|
3989
|
+
const state = this.joinState;
|
|
3990
|
+
if (state != null && !state.closed) state.incompleteRecoveryAttempt = 0;
|
|
3991
|
+
return saved;
|
|
3866
3992
|
}
|
|
3867
3993
|
setReadSubStatus(state, sub) {
|
|
3868
3994
|
if (sub === "ok") state.readErrorRetryAttempt = 0;
|
|
@@ -3945,4 +4071,4 @@ function isValidRillId(id) {
|
|
|
3945
4071
|
//#endregion
|
|
3946
4072
|
export { createInitialRemoteCursor as a, PayloadProtectionError as c, IndexedDbRemoteCursorStore as i, StreamsCrdt as n, EphemeralStreamCrdt as o, InMemoryRemoteCursorStore as r, LocalAppendFailedError as s, isValidRillId as t };
|
|
3947
4073
|
|
|
3948
|
-
//# sourceMappingURL=stream-id-
|
|
4074
|
+
//# sourceMappingURL=stream-id-BygPTNvQ.js.map
|