@loro-dev/streams-crdt 0.8.3 → 0.9.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.
@@ -1 +0,0 @@
1
- {"version":3,"file":"stream-id-BjmGvObf.js","names":["textEncoder","createTransportJoinState","disposeTransportJoinState"],"sources":["../src/deque.ts","../src/reconnect.ts","../src/items.ts","../src/payload-protection.ts","../src/transport-internal.ts","../src/transport-client.ts","../src/ephemeral-runtime.ts","../src/checkpoint.ts","../src/transport-cursor.ts","../src/transport-snapshot.ts","../src/transport-room.ts","../src/transport-runtime.ts","../src/stream-id.ts"],"sourcesContent":["const DEFAULT_COMPACT_THRESHOLD = 64;\nconst DEFAULT_MIN_CAPACITY = 16;\n\nexport interface DequeOptions {\n readonly compactThreshold?: number;\n readonly minCapacity?: number;\n}\n\nexport class Deque<T> implements Iterable<T> {\n private buffer: Array<T | null | undefined>;\n private head = 0;\n private tail = 0;\n private readonly compactThreshold: number;\n private readonly minCapacity: number;\n\n constructor(values?: Iterable<T>, options: DequeOptions = {}) {\n this.compactThreshold = Math.max(\n 1,\n options.compactThreshold ?? DEFAULT_COMPACT_THRESHOLD\n );\n this.minCapacity = Math.max(1, options.minCapacity ?? DEFAULT_MIN_CAPACITY);\n const seed = values == null ? [] : Array.from(values);\n this.buffer = seed.slice();\n this.tail = seed.length;\n }\n\n get length(): number {\n return this.tail - this.head;\n }\n\n isEmpty(): boolean {\n return this.length === 0;\n }\n\n clear(): void {\n this.buffer = [];\n this.head = 0;\n this.tail = 0;\n }\n\n peekFront(): T | undefined {\n if (this.length === 0) {\n return undefined;\n }\n const value = this.buffer[this.head];\n return value == null ? undefined : value;\n }\n\n peekBack(): T | undefined {\n if (this.length === 0) {\n return undefined;\n }\n const value = this.buffer[this.tail - 1];\n return value == null ? undefined : value;\n }\n\n at(index: number): T | undefined {\n const length = this.length;\n if (length === 0) {\n return undefined;\n }\n let offset = index;\n if (offset < 0) {\n offset += length;\n }\n if (offset < 0 || offset >= length) {\n return undefined;\n }\n const value = this.buffer[this.head + offset];\n return value == null ? undefined : value;\n }\n\n pushBack(value: T): number {\n this.ensureBackCapacity();\n this.buffer[this.tail] = value;\n this.tail += 1;\n return this.length;\n }\n\n pushFront(value: T): number {\n if (this.head === 0) {\n this.recenter(1, 0);\n }\n this.head -= 1;\n this.buffer[this.head] = value;\n return this.length;\n }\n\n popFront(): T | undefined {\n if (this.length === 0) {\n return undefined;\n }\n const value = this.buffer[this.head];\n // Explicitly null out removed slot to release references for GC.\n this.buffer[this.head] = null;\n this.head += 1;\n this.compactIfNeeded();\n return value == null ? undefined : value;\n }\n\n popBack(): T | undefined {\n if (this.length === 0) {\n return undefined;\n }\n this.tail -= 1;\n const value = this.buffer[this.tail];\n // Explicitly null out removed slot to release references for GC.\n this.buffer[this.tail] = null;\n this.compactIfNeeded();\n return value == null ? undefined : value;\n }\n\n toArray(): T[] {\n const out: T[] = [];\n for (let i = this.head; i < this.tail; i += 1) {\n out.push(this.buffer[i] as T);\n }\n return out;\n }\n\n *[Symbol.iterator](): IterableIterator<T> {\n for (let i = this.head; i < this.tail; i += 1) {\n yield this.buffer[i] as T;\n }\n }\n\n private ensureBackCapacity(): void {\n if (this.tail < this.buffer.length) {\n return;\n }\n if (this.head > 0) {\n this.compactToFront();\n if (this.tail < this.buffer.length) {\n return;\n }\n }\n this.recenter(0, 1);\n }\n\n private recenter(extraFront: number, extraBack: number): void {\n const length = this.length;\n const needed = length + extraFront + extraBack;\n const currentCapacity = this.buffer.length;\n let nextCapacity = Math.max(currentCapacity, this.minCapacity);\n if (nextCapacity < needed) {\n nextCapacity = Math.max(nextCapacity * 2, needed, this.minCapacity);\n }\n const free = nextCapacity - length;\n const nextHead = Math.max(extraFront, Math.floor(free / 2));\n const next = new Array<T | null | undefined>(nextCapacity);\n for (let i = 0; i < length; i += 1) {\n next[nextHead + i] = this.buffer[this.head + i];\n }\n this.buffer = next;\n this.head = nextHead;\n this.tail = nextHead + length;\n }\n\n private compactIfNeeded(): void {\n const length = this.length;\n if (length === 0) {\n this.clear();\n return;\n }\n if (this.head < this.compactThreshold || this.head < length) {\n return;\n }\n this.compactToFront();\n }\n\n private compactToFront(): void {\n const length = this.length;\n this.buffer = this.buffer.slice(this.head, this.tail);\n this.head = 0;\n this.tail = length;\n }\n}\n","/**\n * Retry and timeout settings used by `StreamsCrdt`.\n */\nexport interface ReconnectConfig {\n readonly delays: readonly number[];\n readonly jitterFraction: number;\n readonly maxAttempts: number;\n readonly connectTimeoutMs: number;\n readonly pollTimeoutMs: number;\n /** SSE inactivity timeout. Aborts and reconnects if no event arrives within this period. */\n readonly liveInactivityTimeoutMs?: number;\n}\n\nexport const DEFAULT_RECONNECT_CONFIG: ReconnectConfig = {\n delays: [0, 500, 1000, 2000, 4000, 8000, 15000],\n jitterFraction: 0.2,\n maxAttempts: Infinity,\n connectTimeoutMs: 10000,\n pollTimeoutMs: 30000,\n liveInactivityTimeoutMs: 45_000,\n};\n\n/**\n * Apply jitter of +/- fraction to a base delay.\n * E.g. applyJitter(1000, 0.2) returns a value in [800, 1200].\n */\nexport function applyJitter(baseMs: number, fraction: number): number {\n if (baseMs === 0) return 0;\n const jitter = baseMs * fraction * (2 * Math.random() - 1);\n return Math.max(0, Math.round(baseMs + jitter));\n}\n\n/**\n * Compute the retry delay for a given attempt number.\n * Uses the delays table from config, clamping at the last entry.\n */\nexport function computeRetryDelay(attempt: number, config: ReconnectConfig = DEFAULT_RECONNECT_CONFIG): number {\n const { delays, jitterFraction } = config;\n const index = Math.min(attempt, delays.length - 1);\n const base = delays[index];\n return applyJitter(base, jitterFraction);\n}\n","export const BINARY_CONTENT_TYPE = \"application/octet-stream\";\n\nexport function encodeItems(items: ReadonlyArray<Uint8Array>): Uint8Array {\n const total = items.reduce((sum, item) => sum + 4 + item.byteLength, 0);\n const out = new Uint8Array(total);\n const view = new DataView(out.buffer, out.byteOffset, out.byteLength);\n let cursor = 0;\n for (const item of items) {\n view.setUint32(cursor, item.byteLength, false);\n cursor += 4;\n out.set(item, cursor);\n cursor += item.byteLength;\n }\n return out;\n}\n\nexport function decodeItems(payload: Uint8Array): Uint8Array[] {\n const out: Uint8Array[] = [];\n const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);\n let cursor = 0;\n while (cursor < payload.byteLength) {\n if (cursor + 4 > payload.byteLength) {\n throw new Error(\"corrupted items payload: truncated length prefix\");\n }\n const len = view.getUint32(cursor, false);\n cursor += 4;\n if (cursor + len > payload.byteLength) {\n throw new Error(\"corrupted items payload: truncated item payload\");\n }\n out.push(payload.slice(cursor, cursor + len));\n cursor += len;\n }\n return out;\n}\n","import { decodeItems, encodeItems } from \"./items\";\nimport type {\n MaybePromise,\n PayloadProtectionKey,\n PayloadProtectionOptions,\n PayloadProtectionReadPolicy,\n PayloadProtectionScope,\n PayloadProtectionWritePolicy,\n} from \"./types\";\n\ntype PayloadKind = \"update_batch\" | \"snapshot\";\n\ntype PayloadProtectionFailureReason =\n | \"plaintext_forbidden\"\n | \"missing_read_key\"\n | \"decrypt_failed\"\n | \"invalid_envelope\"\n | \"wrong_payload_kind\"\n | \"encrypt_failed\";\n\ntype PayloadProtectionRuntimeOptions = {\n readonly readPolicy: PayloadProtectionReadPolicy;\n readonly writePolicy: PayloadProtectionWritePolicy;\n readonly scope?: PayloadProtectionScope;\n readonly writeKey?: PayloadProtectionKey;\n readonly readKeys?: readonly PayloadProtectionKey[] | (() => MaybePromise<readonly PayloadProtectionKey[]>);\n};\n\nconst MAGIC = new Uint8Array([0x4c, 0x53, 0x43, 0x45]); // \"LSCE\"\nconst ENVELOPE_VERSION = 1;\nconst SUITE_AES_256_GCM = 1;\nconst UPDATE_BATCH_KIND = 1;\nconst SNAPSHOT_KIND = 2;\nconst AES_GCM_NONCE_BYTES = 12;\nconst HEADER_BYTES = 10;\nconst AES_GCM_TAG_BITS = 128;\nconst RAW_AES_256_KEY_BYTES = 32;\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\nexport class PayloadProtectionError extends Error {\n readonly reason: PayloadProtectionFailureReason;\n readonly keyId?: string;\n\n constructor(\n reason: PayloadProtectionFailureReason,\n message: string,\n options: { readonly keyId?: string; readonly cause?: unknown } = {},\n ) {\n super(message, options.cause == null ? undefined : { cause: options.cause });\n this.name = \"PayloadProtectionError\";\n this.reason = reason;\n this.keyId = options.keyId;\n }\n}\n\nexport class PayloadProtectionRuntime {\n readonly readPolicy: PayloadProtectionReadPolicy;\n readonly writePolicy: PayloadProtectionWritePolicy;\n\n private readonly scope?: PayloadProtectionScope;\n private readonly writeKey?: PayloadProtectionKey;\n private readonly readKeys?: readonly PayloadProtectionKey[] | (() => MaybePromise<readonly PayloadProtectionKey[]>);\n private importedWriteKey?: Promise<CryptoKey>;\n private readonly importedReadKeys = new Map<string, Promise<CryptoKey>>();\n\n constructor(options: PayloadProtectionRuntimeOptions) {\n this.readPolicy = options.readPolicy;\n this.writePolicy = options.writePolicy;\n this.scope = options.scope;\n this.writeKey = options.writeKey;\n this.readKeys = options.readKeys;\n }\n\n async encodeUpdateItems(\n clearUpdates: readonly Uint8Array[],\n ): Promise<readonly Uint8Array[]> {\n if (this.writePolicy === \"plaintext\") {\n return clearUpdates;\n }\n return [\n await this.encryptPayload(\n \"update_batch\",\n encodeItems(clearUpdates),\n ),\n ];\n }\n\n measureEncodedUpdateItemsByteLength(\n clearUpdates: readonly Uint8Array[],\n ): number {\n if (this.writePolicy === \"plaintext\") {\n return encodedItemsByteLength(clearUpdates);\n }\n\n const writeKey = this.writeKey;\n if (writeKey == null || this.scope == null) {\n throw new PayloadProtectionError(\n \"encrypt_failed\",\n \"E2EE write encryption requires encryption.writeKey and encryption.scope\",\n );\n }\n\n const clearBatchBytes = encodedItemsByteLength(clearUpdates);\n const keyIdBytes = textEncoder.encode(writeKey.id);\n if (keyIdBytes.byteLength === 0 || keyIdBytes.byteLength > 255) {\n throw new PayloadProtectionError(\n \"encrypt_failed\",\n \"E2EE key id must be 1..255 UTF-8 bytes\",\n { keyId: writeKey.id },\n );\n }\n\n const envelopeBytes =\n HEADER_BYTES +\n keyIdBytes.byteLength +\n AES_GCM_NONCE_BYTES +\n clearBatchBytes +\n AES_GCM_TAG_BITS / 8;\n return 4 + envelopeBytes;\n }\n\n async decodeUpdateItems(\n wireItems: readonly Uint8Array[],\n ): Promise<Uint8Array[]> {\n const out: Uint8Array[] = [];\n for (const item of wireItems) {\n if (isEncryptedEnvelope(item)) {\n const clearBatch = await this.decryptPayload(\"update_batch\", item);\n out.push(...decodeItems(clearBatch));\n continue;\n }\n this.assertPlaintextAllowed(\"update batch\");\n out.push(item);\n }\n return out;\n }\n\n async encodeSnapshot(snapshot: Uint8Array): Promise<Uint8Array> {\n if (this.writePolicy === \"plaintext\") {\n return snapshot;\n }\n return await this.encryptPayload(\"snapshot\", snapshot);\n }\n\n async decodeSnapshot(snapshot: Uint8Array): Promise<Uint8Array> {\n if (isEncryptedEnvelope(snapshot)) {\n return await this.decryptPayload(\"snapshot\", snapshot);\n }\n this.assertPlaintextAllowed(\"snapshot\");\n return snapshot;\n }\n\n private async encryptPayload(\n kind: PayloadKind,\n plaintext: Uint8Array,\n ): Promise<Uint8Array> {\n const writeKey = this.writeKey;\n if (writeKey == null || this.scope == null) {\n throw new PayloadProtectionError(\n \"encrypt_failed\",\n \"E2EE write encryption requires encryption.writeKey and encryption.scope\",\n );\n }\n\n try {\n const key = await (this.importedWriteKey ??= importAesGcmKey(writeKey.key));\n const nonce = createNonce();\n const header = createEnvelopeHeader(kind, writeKey.id, nonce);\n const ciphertext = new Uint8Array(\n await getSubtleCrypto().encrypt(\n {\n name: \"AES-GCM\",\n iv: toArrayBuffer(nonce),\n tagLength: AES_GCM_TAG_BITS,\n additionalData: toArrayBuffer(createAad({\n kind,\n keyId: writeKey.id,\n scope: this.scope,\n })),\n },\n key,\n toArrayBuffer(plaintext),\n ),\n );\n return concatBytes(header, ciphertext);\n } catch (error) {\n if (error instanceof PayloadProtectionError) {\n throw error;\n }\n throw new PayloadProtectionError(\n \"encrypt_failed\",\n \"failed to encrypt streams-crdt payload\",\n { keyId: writeKey.id, cause: error },\n );\n }\n }\n\n private async decryptPayload(\n expectedKind: PayloadKind,\n envelope: Uint8Array,\n ): Promise<Uint8Array> {\n const parsed = parseEnvelope(envelope);\n if (parsed.kind !== expectedKind) {\n throw new PayloadProtectionError(\n \"wrong_payload_kind\",\n `expected ${expectedKind} payload, got ${parsed.kind}`,\n { keyId: parsed.keyId },\n );\n }\n if (this.scope == null) {\n throw new PayloadProtectionError(\n \"missing_read_key\",\n \"E2EE read decryption requires encryption.scope\",\n { keyId: parsed.keyId },\n );\n }\n\n const key = await this.getReadKey(parsed.keyId);\n try {\n return new Uint8Array(\n await getSubtleCrypto().decrypt(\n {\n name: \"AES-GCM\",\n iv: toArrayBuffer(parsed.nonce),\n tagLength: AES_GCM_TAG_BITS,\n additionalData: toArrayBuffer(createAad({\n kind: parsed.kind,\n keyId: parsed.keyId,\n scope: this.scope,\n })),\n },\n key,\n toArrayBuffer(parsed.ciphertext),\n ),\n );\n } catch (error) {\n throw new PayloadProtectionError(\n \"decrypt_failed\",\n \"failed to decrypt streams-crdt payload\",\n { keyId: parsed.keyId, cause: error },\n );\n }\n }\n\n private async getReadKey(keyId: string): Promise<CryptoKey> {\n const cached = this.importedReadKeys.get(keyId);\n if (cached != null) {\n return await cached;\n }\n\n const key = await this.resolveReadKey(keyId);\n const imported = importAesGcmKey(key.key);\n this.importedReadKeys.set(keyId, imported);\n return await imported;\n }\n\n private async resolveReadKey(keyId: string): Promise<PayloadProtectionKey> {\n for (const key of await this.resolveReadKeys()) {\n if (key.id === keyId) {\n return key;\n }\n }\n throw new PayloadProtectionError(\n \"missing_read_key\",\n `missing read key for protected streams-crdt payload '${keyId}'`,\n { keyId },\n );\n }\n\n private async resolveReadKeys(): Promise<readonly PayloadProtectionKey[]> {\n const readKeys =\n typeof this.readKeys === \"function\"\n ? await this.readKeys()\n : this.readKeys;\n if (readKeys != null) {\n return readKeys;\n }\n return this.writeKey == null ? [] : [this.writeKey];\n }\n\n private assertPlaintextAllowed(label: string): void {\n if (this.readPolicy === \"allow-plaintext\") {\n return;\n }\n throw new PayloadProtectionError(\n \"plaintext_forbidden\",\n `received plaintext streams-crdt ${label} while readPolicy is encrypted-only`,\n );\n }\n}\n\nexport function normalizePayloadProtection(\n options: PayloadProtectionOptions | undefined,\n label = \"payloadProtection\",\n): PayloadProtectionRuntime | undefined {\n if (options == null) {\n return undefined;\n }\n\n const readPolicy = options.readPolicy ?? \"encrypted-only\";\n const writePolicy = options.writePolicy ?? \"encrypt\";\n if (readPolicy !== \"encrypted-only\" && readPolicy !== \"allow-plaintext\") {\n throw new Error(`${label}.readPolicy must be encrypted-only or allow-plaintext`);\n }\n if (writePolicy !== \"encrypt\" && writePolicy !== \"plaintext\") {\n throw new Error(`${label}.writePolicy must be encrypt or plaintext`);\n }\n\n const encryption = options.encryption;\n if (encryption != null && encryption.scope == null) {\n throw new Error(\n `${label}.encryption.scope is required when encryption is configured`,\n );\n }\n if (writePolicy === \"encrypt\" && encryption?.writeKey == null) {\n throw new Error(\n `${label}.encryption.writeKey is required when writePolicy is encrypt`,\n );\n }\n if (writePolicy === \"encrypt\" && encryption?.scope == null) {\n throw new Error(\n `${label}.encryption.scope is required when writePolicy is encrypt`,\n );\n }\n validateKey(encryption?.writeKey, `${label}.encryption.writeKey`);\n const readKeys = normalizeReadKeys(\n encryption?.readKeys,\n `${label}.encryption.readKeys`,\n );\n return new PayloadProtectionRuntime({\n readPolicy,\n writePolicy,\n scope: encryption?.scope,\n writeKey: encryption?.writeKey,\n readKeys,\n });\n}\n\nexport function isPayloadProtectionError(\n error: unknown,\n): error is PayloadProtectionError {\n return error instanceof PayloadProtectionError;\n}\n\nfunction isEncryptedEnvelope(payload: Uint8Array): boolean {\n return (\n payload.byteLength >= MAGIC.byteLength &&\n MAGIC.every((byte, index) => payload[index] === byte)\n );\n}\n\nfunction createEnvelopeHeader(\n kind: PayloadKind,\n keyId: string,\n nonce: Uint8Array,\n): Uint8Array {\n const keyIdBytes = textEncoder.encode(keyId);\n if (keyIdBytes.byteLength === 0 || keyIdBytes.byteLength > 255) {\n throw new PayloadProtectionError(\n \"encrypt_failed\",\n \"E2EE key id must be 1..255 UTF-8 bytes\",\n { keyId },\n );\n }\n if (nonce.byteLength !== AES_GCM_NONCE_BYTES) {\n throw new PayloadProtectionError(\n \"encrypt_failed\",\n `AES-GCM nonce must be ${AES_GCM_NONCE_BYTES} bytes`,\n { keyId },\n );\n }\n\n const header = new Uint8Array(HEADER_BYTES + keyIdBytes.byteLength + nonce.byteLength);\n header.set(MAGIC, 0);\n header[4] = ENVELOPE_VERSION;\n header[5] = encodeKind(kind);\n header[6] = SUITE_AES_256_GCM;\n header[7] = keyIdBytes.byteLength;\n header[8] = nonce.byteLength;\n header[9] = 0;\n header.set(keyIdBytes, HEADER_BYTES);\n header.set(nonce, HEADER_BYTES + keyIdBytes.byteLength);\n return header;\n}\n\nfunction parseEnvelope(payload: Uint8Array): {\n readonly kind: PayloadKind;\n readonly keyId: string;\n readonly nonce: Uint8Array;\n readonly ciphertext: Uint8Array;\n} {\n if (!isEncryptedEnvelope(payload) || payload.byteLength < HEADER_BYTES) {\n throw new PayloadProtectionError(\n \"invalid_envelope\",\n \"invalid streams-crdt encrypted payload envelope\",\n );\n }\n const version = payload[4];\n const suite = payload[6];\n const keyIdLen = payload[7] ?? 0;\n const nonceLen = payload[8] ?? 0;\n const reserved = payload[9];\n if (\n version !== ENVELOPE_VERSION ||\n suite !== SUITE_AES_256_GCM ||\n nonceLen !== AES_GCM_NONCE_BYTES ||\n reserved !== 0\n ) {\n throw new PayloadProtectionError(\n \"invalid_envelope\",\n \"unsupported streams-crdt encrypted payload envelope\",\n );\n }\n\n const kind = decodeKind(payload[5]);\n const keyIdStart = HEADER_BYTES;\n const nonceStart = keyIdStart + keyIdLen;\n const ciphertextStart = nonceStart + nonceLen;\n if (\n keyIdLen === 0 ||\n payload.byteLength <= ciphertextStart\n ) {\n throw new PayloadProtectionError(\n \"invalid_envelope\",\n \"truncated streams-crdt encrypted payload envelope\",\n );\n }\n const keyId = textDecoder.decode(payload.slice(keyIdStart, nonceStart));\n return {\n kind,\n keyId,\n nonce: payload.slice(nonceStart, ciphertextStart),\n ciphertext: payload.slice(ciphertextStart),\n };\n}\n\nfunction encodeKind(kind: PayloadKind): number {\n return kind === \"update_batch\" ? UPDATE_BATCH_KIND : SNAPSHOT_KIND;\n}\n\nfunction decodeKind(encoded: number | undefined): PayloadKind {\n if (encoded === UPDATE_BATCH_KIND) {\n return \"update_batch\";\n }\n if (encoded === SNAPSHOT_KIND) {\n return \"snapshot\";\n }\n throw new PayloadProtectionError(\n \"invalid_envelope\",\n \"unsupported streams-crdt encrypted payload kind\",\n );\n}\n\nfunction createNonce(): Uint8Array {\n const nonce = new Uint8Array(AES_GCM_NONCE_BYTES);\n getCrypto().getRandomValues(nonce);\n return nonce;\n}\n\nfunction createAad(input: {\n readonly kind: PayloadKind;\n readonly keyId: string;\n readonly scope: PayloadProtectionScope;\n}): Uint8Array {\n return textEncoder.encode(JSON.stringify({\n keyId: input.keyId,\n kind: input.kind,\n protocol: \"loro-streams-crdt-payload-protection\",\n scope: normalizeScope(input.scope),\n suite: \"aes_256_gcm\",\n version: ENVELOPE_VERSION,\n }));\n}\n\nfunction normalizeScope(scope: PayloadProtectionScope): string {\n return typeof scope === \"string\"\n ? scope\n : `bucket:${scope.bucketId}\\nstream:${scope.streamId}`;\n}\n\nfunction concatBytes(left: Uint8Array, right: Uint8Array): Uint8Array {\n const out = new Uint8Array(left.byteLength + right.byteLength);\n out.set(left);\n out.set(right, left.byteLength);\n return out;\n}\n\nfunction encodedItemsByteLength(items: readonly Uint8Array[]): number {\n return items.reduce((sum, item) => sum + 4 + item.byteLength, 0);\n}\n\nasync function importAesGcmKey(key: CryptoKey | Uint8Array): Promise<CryptoKey> {\n if (isCryptoKey(key)) {\n return key;\n }\n validateRawKey(key);\n return await getSubtleCrypto().importKey(\n \"raw\",\n toArrayBuffer(key),\n \"AES-GCM\",\n false,\n [\"encrypt\", \"decrypt\"],\n );\n}\n\nfunction toArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n return bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength,\n ) as ArrayBuffer;\n}\n\nfunction validateKey(\n key: PayloadProtectionKey | undefined,\n label: string,\n): void {\n if (key == null) {\n return;\n }\n if (key.id.trim().length === 0) {\n throw new Error(`${label}.id must be non-empty`);\n }\n if (key.key instanceof Uint8Array) {\n validateRawKey(key.key);\n }\n}\n\nfunction normalizeReadKeys(\n readKeys:\n | readonly PayloadProtectionKey[]\n | (() => MaybePromise<readonly PayloadProtectionKey[]>)\n | undefined,\n label: string,\n):\n | readonly PayloadProtectionKey[]\n | (() => MaybePromise<readonly PayloadProtectionKey[]>)\n | undefined {\n if (readKeys == null) {\n return undefined;\n }\n if (typeof readKeys === \"function\") {\n return async () => {\n const resolved = await readKeys();\n validateKeys(resolved, label);\n return resolved;\n };\n }\n validateKeys(readKeys, label);\n return readKeys;\n}\n\nfunction validateKeys(\n keys: readonly PayloadProtectionKey[],\n label: string,\n): void {\n for (const [index, key] of keys.entries()) {\n validateKey(key, `${label}[${index}]`);\n }\n}\n\nfunction isCryptoKey(key: CryptoKey | Uint8Array): key is CryptoKey {\n return typeof CryptoKey !== \"undefined\" && key instanceof CryptoKey;\n}\n\nfunction validateRawKey(key: Uint8Array): void {\n if (key.byteLength !== RAW_AES_256_KEY_BYTES) {\n throw new Error(\n `E2EE raw AES-GCM keys must be ${RAW_AES_256_KEY_BYTES} bytes`,\n );\n }\n}\n\nfunction getCrypto(): Crypto {\n if (globalThis.crypto == null) {\n throw new PayloadProtectionError(\n \"encrypt_failed\",\n \"Web Crypto is unavailable in this runtime\",\n );\n }\n return globalThis.crypto;\n}\n\nfunction getSubtleCrypto(): SubtleCrypto {\n const subtle = getCrypto().subtle;\n if (subtle == null) {\n throw new PayloadProtectionError(\n \"encrypt_failed\",\n \"SubtleCrypto is unavailable in this runtime\",\n );\n }\n return subtle;\n}\n","import type { RemoteCursor } from \"./checkpoint\";\nimport { Deque } from \"./deque\";\nimport { decodeItems } from \"./items\";\nimport { isPayloadProtectionError } from \"./payload-protection\";\nimport type { TransportSnapshotState } from \"./transport-snapshot\";\nimport type {\n CrdtUpdateBatch,\n JsonObject,\n JsonValue,\n MaybePromise,\n SnapshotCodec,\n SnapshotUploadOptions,\n StreamsAuthContext,\n StreamsAuthProvider,\n TransportError,\n TransportRoomStatus,\n} from \"./types\";\n\nexport type FetchLike = (\n input: string,\n init?: RequestInit,\n) => Promise<Response>;\n\nexport type ReadResult = {\n readonly nextOffset: string;\n readonly upToDate: boolean;\n readonly updates: Uint8Array[];\n readonly streamCursor?: string;\n};\n\nexport type BootstrapResult = {\n readonly nextOffset: string;\n readonly upToDate: boolean;\n readonly snapshot: Uint8Array;\n readonly updates: Uint8Array[];\n readonly streamCursor?: string;\n};\n\nexport type SseControlData = {\n readonly nextOffset: string;\n readonly upToDate: boolean;\n readonly streamCursor?: string;\n};\n\nexport type SseControlHandlerResult = void | \"pause\" | \"stop\";\n\nexport type SseItemsContinueResult = {\n readonly kind: \"ok\";\n readonly latestOffset: string;\n readonly latestCursor?: string;\n readonly paused: boolean;\n};\n\nexport type SseItemsReadResult =\n | {\n readonly kind: \"ok\";\n readonly latestOffset: string;\n readonly latestCursor?: string;\n }\n | { readonly kind: \"gone\" }\n | { readonly kind: \"unsupported\" }\n | { readonly kind: \"not-found\" };\n\nexport type SseItemsSession = {\n continue: (\n onControl: (\n updates: Uint8Array[],\n nextOffset: string,\n upToDate: boolean,\n streamCursor?: string,\n ) => MaybePromise<SseControlHandlerResult>,\n ) => Promise<SseItemsContinueResult>;\n cancel: () => Promise<void>;\n};\n\nexport type SseItemsOpenResult =\n | { readonly kind: \"ok\"; readonly session: SseItemsSession }\n | { readonly kind: \"gone\" }\n | { readonly kind: \"unsupported\" }\n | { readonly kind: \"not-found\" };\n\nexport type AppendResult =\n | {\n readonly kind: \"ok\";\n readonly ackEpoch: number;\n readonly ackSeq: number;\n readonly nextOffset: string;\n }\n | {\n readonly kind: \"stale-epoch\";\n readonly serverEpoch: number;\n }\n | {\n readonly kind: \"seq-gap\";\n readonly expectedSeq: number;\n };\n\nexport type ProducerSession = {\n readonly producerId: string;\n readonly producerEpoch: number;\n readonly nextProducerSeq: number;\n};\n\nexport type LiveReadMode = \"sse\" | \"long-poll\";\n\nexport type ReadSubStatus = \"ok\" | \"connecting\" | \"reconnecting\" | \"error\" | \"disconnected\";\nexport type WriteSubStatus = \"ok\" | \"reconnecting\" | \"error\";\n\nexport type CursorSyncResult<TVersion extends JsonObject> = {\n readonly cursor: RemoteCursor<TVersion>;\n readonly streamCursor?: string;\n};\n\nexport type LocalExportResult<TVersion extends JsonObject> = {\n readonly batch?: CrdtUpdateBatch<TVersion>;\n readonly nextRefVersion: TVersion;\n};\n\nexport type SnapshotUploadRuntimeConfig = {\n readonly canUpload: () => Promise<boolean>;\n readonly debounceMs: number;\n readonly minBytesSinceRemoteSnapshot: bigint;\n};\n\nexport type SnapshotCodecRuntimeConfig = {\n readonly compress: (snapshot: Uint8Array) => Promise<Uint8Array>;\n readonly decompress: (snapshot: Uint8Array) => Promise<Uint8Array>;\n};\n\n/**\n * A pending waiter created by `waitUntilSynced()`.\n *\n * `remaining` tracks how many batches still need to be flushed before\n * this waiter resolves. It is decremented each time a batch is\n * successfully appended.\n */\nexport type SyncWaiter = {\n remaining: number;\n resolve: () => void;\n reject: (error: Error) => void;\n};\n\nexport type JoinState<TVersion extends JsonObject> = {\n refCount: number;\n closed: boolean;\n status: \"connecting\" | TransportRoomStatus;\n readSubStatus: ReadSubStatus;\n writeSubStatus: WriteSubStatus;\n readonly statusListeners: Set<\n (status: \"connecting\" | TransportRoomStatus) => void\n >;\n readonly pendingLocal: Deque<CrdtUpdateBatch<TVersion>>;\n flushingLocal: boolean;\n retryAttempt: number;\n retrySleepController: AbortController;\n requestController?: AbortController;\n unsubscribeLocal?: () => void;\n cursor: RemoteCursor<TVersion>;\n liveMode: LiveReadMode;\n streamCursor?: string;\n snapshot: TransportSnapshotState;\n started: Promise<void>;\n readonly syncWaiters: SyncWaiter[];\n /**\n * Set to `true` when the initial `startJoin()` fails with a retryable error.\n * The join state is kept alive so `rejoin()` can restart the initial sync.\n */\n initialSyncFailed: boolean;\n /** The last transport error observed during initial sync or live loops. */\n lastError?: TransportError;\n};\n\nexport type JoinSyncResult<TVersion extends JsonObject> =\n CursorSyncResult<TVersion> & {\n readonly liveMode: LiveReadMode;\n readonly localExportRefVersion: TVersion;\n };\n\nexport type LastError = {\n code:\n | \"network\"\n | \"timeout\"\n | \"auth_failed\"\n | \"gone_410\"\n | \"protocol\"\n | \"apply_incomplete\"\n | \"internal\";\n message: string;\n retriable: boolean;\n atMs: number;\n};\n\nexport function createProducerId(): string {\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n return `${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;\n}\n\nexport class HttpError extends Error {\n readonly status: number;\n\n constructor(message: string, status: number) {\n super(message);\n this.status = status;\n }\n}\n\nexport class TimeoutError extends Error {\n readonly timeoutType: \"connect\" | \"poll\";\n readonly timeoutMs: number;\n\n constructor(timeoutType: \"connect\" | \"poll\", timeoutMs: number) {\n super(`TIMEOUT: ${timeoutType} request exceeded ${timeoutMs}ms`);\n this.timeoutType = timeoutType;\n this.timeoutMs = timeoutMs;\n }\n}\n\nexport class ProtocolError extends Error {\n readonly status?: number;\n\n constructor(message: string, status?: number) {\n super(message);\n this.status = status;\n }\n}\n\nexport function getHeader(\n headers: Headers,\n lowerCaseName: string,\n titleCaseName: string,\n): string | null {\n return headers.get(lowerCaseName) ?? headers.get(titleCaseName);\n}\n\nfunction visibleHeaderNames(headers: Headers): string {\n const names: string[] = [];\n headers.forEach((_value, name) => {\n names.push(name);\n });\n if (names.length === 0) {\n return \"(none)\";\n }\n names.sort();\n return names.join(\", \");\n}\n\nexport function missingRequiredHeaderError(\n context: \"read\" | \"bootstrap\" | \"append\",\n requiredHeader: string,\n headers: Headers,\n): ProtocolError {\n return new ProtocolError(\n `${context} response missing ${requiredHeader}. ` +\n `If this is cross-origin, expose it via Access-Control-Expose-Headers (${requiredHeader.toLowerCase()}). ` +\n `Visible headers: ${visibleHeaderNames(headers)}`,\n );\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value != null;\n}\n\nexport function parseHeaderU64(value: string | null): number | null {\n if (value == null) {\n return null;\n }\n const parsed = Number.parseInt(value, 10);\n if (!Number.isFinite(parsed) || parsed < 0) {\n return null;\n }\n return parsed;\n}\n\nfunction decodeBase64Bytes(encoded: string): Uint8Array {\n // specs/ds-protocol.md allows servers to split base64 payloads across\n // multiple SSE data lines, and clients must remove the inserted CR/LF\n // characters before decoding.\n const normalized = encoded.replace(/[\\r\\n]/g, \"\");\n if (typeof Buffer !== \"undefined\") {\n return new Uint8Array(Buffer.from(normalized, \"base64\"));\n }\n if (typeof atob === \"function\") {\n const binary = atob(normalized);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) {\n out[i] = binary.charCodeAt(i);\n }\n return out;\n }\n throw new Error(\"base64 decoding is unavailable\");\n}\n\nfunction normalizeSseDataEncoding(\n dataEncoding: string | null | undefined,\n): \"base64\" | undefined {\n const normalized = dataEncoding?.trim().toLowerCase();\n if (normalized == null || normalized.length === 0) {\n return undefined;\n }\n if (normalized === \"base64\") {\n return \"base64\";\n }\n throw new ProtocolError(\n `unsupported stream-sse-data-encoding '${dataEncoding}'`,\n );\n}\n\nfunction decodeSseRawPayload(\n data: string,\n dataEncoding: \"base64\" | undefined,\n): Uint8Array {\n if (dataEncoding === \"base64\") {\n return decodeBase64Bytes(data);\n }\n return new TextEncoder().encode(data);\n}\n\nexport function decodeSseItemPayloads(\n data: string,\n dataEncoding?: string | null,\n): Uint8Array[] {\n const payload = decodeSseRawPayload(\n data,\n normalizeSseDataEncoding(dataEncoding),\n );\n if (payload.byteLength === 0) {\n return [payload];\n }\n try {\n return decodeItems(payload);\n } catch {\n return [payload];\n }\n}\n\n/**\n * Decode a single multipart part body. If the payload contains\n * length-prefixed binary framed items, decode them. Otherwise\n * return the raw bytes as a single-element array. This mirrors the\n * try/fallback approach used by {@link decodeSseItemPayloads}.\n */\nexport function decodeMultipartPartItems(payload: Uint8Array): Uint8Array[] {\n if (payload.byteLength === 0) {\n return [payload];\n }\n try {\n return decodeItems(payload);\n } catch {\n return [payload];\n }\n}\n\nexport function parseSseControlData(data: string): SseControlData {\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch {\n throw new ProtocolError(\"invalid sse control event JSON\");\n }\n if (!isObject(parsed) || typeof parsed.streamNextOffset !== \"string\") {\n throw new ProtocolError(\"sse control event missing streamNextOffset\");\n }\n const streamCursor =\n typeof parsed.streamCursor === \"string\" ? parsed.streamCursor : undefined;\n return {\n nextOffset: parsed.streamNextOffset,\n upToDate: parsed.upToDate === true,\n streamCursor,\n };\n}\n\nexport function isSseUnsupportedErrorText(message: string): boolean {\n const normalized = message.trim().toLowerCase();\n if (normalized.length === 0) {\n return false;\n }\n const mentionsSse =\n normalized.includes(\"sse\") || normalized.includes(\"text/event-stream\");\n const mentionsCompatibility =\n normalized.includes(\"unsupported\") ||\n normalized.includes(\"not support\") ||\n normalized.includes(\"incompatible\") ||\n normalized.includes(\"content type\");\n return mentionsSse && mentionsCompatibility;\n}\n\nexport function withCursorQuery(url: string, cursor?: string): string {\n if (cursor == null || cursor.length === 0) {\n return url;\n }\n const target = new URL(url);\n target.searchParams.set(\"cursor\", cursor);\n return target.toString();\n}\n\nexport function bytesToArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n return bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength,\n ) as ArrayBuffer;\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.resolve();\n }\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n signal?.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timer);\n resolve();\n },\n { once: true },\n );\n });\n}\n\nexport function mergeAbortSignals(\n ...signals: ReadonlyArray<AbortSignal | null | undefined>\n): AbortSignal | undefined {\n const active = signals.filter(\n (signal): signal is AbortSignal => signal != null,\n );\n if (active.length === 0) {\n return undefined;\n }\n if (active.length === 1) {\n return active[0];\n }\n if (typeof AbortSignal.any === \"function\") {\n return AbortSignal.any(active);\n }\n\n const controller = new AbortController();\n for (const signal of active) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n break;\n }\n signal.addEventListener(\n \"abort\",\n () => {\n controller.abort(signal.reason);\n },\n { once: true },\n );\n }\n return controller.signal;\n}\n\nexport function classifyFetchError(err: unknown): LastError {\n const message = err instanceof Error ? err.message : String(err);\n const status = err instanceof HttpError ? err.status : undefined;\n if (isCrdtApplyIncompleteError(err)) {\n return {\n code: \"apply_incomplete\",\n message,\n retriable: true,\n atMs: Date.now(),\n };\n }\n if (status === 401 || status === 403) {\n return { code: \"auth_failed\", message, retriable: false, atMs: Date.now() };\n }\n if (status === 410) {\n return { code: \"gone_410\", message, retriable: true, atMs: Date.now() };\n }\n if (status === 400 || status === 405 || status === 409) {\n return { code: \"protocol\", message, retriable: false, atMs: Date.now() };\n }\n if (err instanceof ProtocolError) {\n return { code: \"protocol\", message, retriable: false, atMs: Date.now() };\n }\n if (err instanceof TimeoutError) {\n return { code: \"timeout\", message, retriable: true, atMs: Date.now() };\n }\n if (err instanceof TypeError) {\n return { code: \"network\", message, retriable: true, atMs: Date.now() };\n }\n if (isPayloadProtectionError(err)) {\n return {\n code: \"protocol\",\n message: err.message,\n retriable: false,\n atMs: Date.now(),\n };\n }\n return { code: \"internal\", message, retriable: false, atMs: Date.now() };\n}\n\nexport function isStreamNotFoundError(error: unknown): boolean {\n return error instanceof HttpError && error.status === 404;\n}\n\nexport function toTransportError(error: unknown): TransportError {\n if (isCrdtApplyIncompleteError(error)) {\n return {\n code: \"apply_incomplete\",\n retryable: true,\n reason: \"loro_pending_updates\",\n message: error instanceof Error ? error.message : String(error),\n };\n }\n if (isStreamNotFoundError(error)) {\n return { code: \"stream_not_found\", retryable: false };\n }\n if (\n error instanceof HttpError &&\n (error.status === 401 || error.status === 403)\n ) {\n return {\n code: \"auth_failed\",\n retryable: false,\n status: error.status as 401 | 403,\n message: error.message,\n };\n }\n if (error instanceof HttpError && [400, 405, 409].includes(error.status)) {\n return {\n code: \"protocol_error\",\n retryable: false,\n status: error.status,\n message: error.message,\n };\n }\n if (error instanceof ProtocolError) {\n return {\n code: \"protocol_error\",\n retryable: false,\n status: error.status,\n message: error.message,\n };\n }\n if (error instanceof TimeoutError) {\n return {\n code: \"timeout\",\n retryable: true,\n phase: error.timeoutType,\n message: error.message,\n };\n }\n if (error instanceof HttpError && error.status === 410) {\n return { code: \"gone\", retryable: true, message: error.message };\n }\n if (isPayloadProtectionError(error)) {\n return {\n code: \"payload_protection_error\",\n retryable: false,\n reason: error.reason,\n keyId: error.keyId,\n message: error.message,\n };\n }\n if (error instanceof TypeError) {\n return {\n code: \"network_error\",\n retryable: true,\n message: error.message,\n };\n }\n if (error instanceof Error) {\n return {\n code: \"internal_error\",\n retryable: false,\n message: error.message,\n };\n }\n return {\n code: \"unknown\",\n retryable: false,\n message: String(error),\n };\n}\n\nfunction isCrdtApplyIncompleteError(error: unknown): boolean {\n return (\n error instanceof Error &&\n Array.isArray(\n (error as { readonly unresolved?: unknown }).unresolved,\n ) &&\n error.message.includes(\"CRDT apply incomplete\")\n );\n}\n\nexport function isReadNoProgress(\n requestOffset: string,\n result: ReadResult,\n): boolean {\n return (\n !result.upToDate &&\n result.updates.length === 0 &&\n result.nextOffset === requestOffset\n );\n}\n\nexport function isAuthOrProtocolError(error: unknown): boolean {\n return (\n error instanceof ProtocolError ||\n (error instanceof HttpError &&\n [400, 401, 403, 405, 409].includes(error.status))\n );\n}\n\nexport function normalizeAuthProvider(\n auth: StreamsAuthProvider | undefined,\n): ((context?: StreamsAuthContext) => Promise<string | undefined>) | undefined {\n if (auth == null) {\n return undefined;\n }\n if (typeof auth === \"string\") {\n const token = auth.trim();\n return async () => (token.length === 0 ? undefined : token);\n }\n return async (context) => {\n const value = await auth(context);\n const token = value?.trim() ?? \"\";\n return token.length === 0 ? undefined : token;\n };\n}\n\nexport function normalizeSnapshotUploadConfig(\n config: SnapshotUploadOptions | undefined,\n): SnapshotUploadRuntimeConfig | undefined {\n if (config == null || config.enabled === false) {\n return undefined;\n }\n if (!Number.isFinite(config.debounceMs ?? 10_000)) {\n throw new Error(\"snapshotUpload.debounceMs must be a finite number\");\n }\n if (!Number.isFinite(config.minBytesSinceRemoteSnapshot ?? 100 * 1024)) {\n throw new Error(\n \"snapshotUpload.minBytesSinceRemoteSnapshot must be a finite number\",\n );\n }\n const debounceMs = Math.max(0, Math.floor(config.debounceMs ?? 10_000));\n const minBytes = config.minBytesSinceRemoteSnapshot ?? 100 * 1024;\n if (minBytes < 0) {\n throw new Error(\n \"snapshotUpload.minBytesSinceRemoteSnapshot must be non-negative\",\n );\n }\n return {\n canUpload: async () => Boolean(await config.canUpload()),\n debounceMs,\n minBytesSinceRemoteSnapshot: BigInt(Math.floor(minBytes)),\n };\n}\n\nfunction assertSnapshotBytes(\n value: unknown,\n label: \"snapshotCodec.compress\" | \"snapshotCodec.decompress\",\n): Uint8Array {\n if (value instanceof Uint8Array) {\n return value;\n }\n throw new Error(`${label} must return Uint8Array`);\n}\n\nexport function normalizeSnapshotCodec(\n codec: SnapshotCodec | undefined,\n): SnapshotCodecRuntimeConfig | undefined {\n if (codec == null) {\n return undefined;\n }\n if (\n typeof codec.compress !== \"function\" ||\n typeof codec.decompress !== \"function\"\n ) {\n throw new Error(\n \"snapshotCodec.compress and snapshotCodec.decompress must be provided together\",\n );\n }\n return {\n compress: async (snapshot) =>\n assertSnapshotBytes(\n await codec.compress(snapshot),\n \"snapshotCodec.compress\",\n ),\n decompress: async (snapshot) =>\n assertSnapshotBytes(\n await codec.decompress(snapshot),\n \"snapshotCodec.decompress\",\n ),\n };\n}\n\nfunction jsonValueEquals(a: JsonValue, b: JsonValue): boolean {\n if (a === b) {\n return true;\n }\n if (typeof a !== typeof b || a == null || b == null) {\n return false;\n }\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i += 1) {\n if (!jsonValueEquals(a[i] as JsonValue, b[i] as JsonValue)) {\n return false;\n }\n }\n return true;\n }\n if (typeof a !== \"object\" || typeof b !== \"object\") {\n return false;\n }\n const aEntries = Object.entries(a).sort(([left], [right]) =>\n left.localeCompare(right),\n );\n const bEntries = Object.entries(b).sort(([left], [right]) =>\n left.localeCompare(right),\n );\n if (aEntries.length !== bEntries.length) {\n return false;\n }\n for (let i = 0; i < aEntries.length; i += 1) {\n const [aKey, aValue] = aEntries[i];\n const [bKey, bValue] = bEntries[i];\n if (aKey !== bKey || !jsonValueEquals(aValue as JsonValue, bValue as JsonValue)) {\n return false;\n }\n }\n return true;\n}\n\nexport function jsonObjectEquals(a: JsonObject, b: JsonObject): boolean {\n return jsonValueEquals(a, b);\n}\n\nexport function parseSnapshotByteOffset(\n offset: string | null | undefined,\n): bigint | null {\n if (offset == null || offset.length === 0 || offset === \"-1\") {\n return 0n;\n }\n if (!/^\\d+$/.test(offset)) {\n return null;\n }\n return BigInt(offset);\n}\n\nexport function resolveNextOffsetAfterConcurrentAdvance(\n requestOffset: string,\n currentOffset: string,\n responseNextOffset: string,\n): string {\n if (currentOffset === requestOffset) {\n return responseNextOffset;\n }\n if (currentOffset === responseNextOffset) {\n return currentOffset;\n }\n const current = parseSnapshotByteOffset(currentOffset);\n const incoming = parseSnapshotByteOffset(responseNextOffset);\n if (current != null && incoming != null) {\n return incoming > current ? responseNextOffset : currentOffset;\n }\n return currentOffset;\n}\n","import {\n decodeSsePayload,\n readSseEvents,\n StreamsClient,\n type StreamError,\n} from \"@loro-dev/streams-client\";\n\nimport { BINARY_CONTENT_TYPE, decodeItems, encodeItems } from \"./items\";\nimport type { ReconnectConfig } from \"./reconnect\";\nimport {\n AppendResult,\n BootstrapResult,\n decodeMultipartPartItems,\n decodeSseItemPayloads,\n FetchLike,\n HttpError,\n isSseUnsupportedErrorText,\n mergeAbortSignals,\n ProtocolError,\n parseSseControlData,\n type ProducerSession,\n ReadResult,\n type SseControlHandlerResult,\n type SseItemsOpenResult,\n type SseItemsReadResult,\n TimeoutError,\n} from \"./transport-internal\";\nimport type {\n CrdtAdapter,\n JsonObject,\n MaybePromise,\n StreamsAuthContext,\n} from \"./types\";\n\ntype StreamsTransportClientOptions<TVersion extends JsonObject> = {\n readonly streamUrl: string;\n readonly adapter: CrdtAdapter<TVersion>;\n readonly authProvider?: (context?: StreamsAuthContext) => Promise<string | undefined>;\n readonly fetchImpl: FetchLike;\n readonly reconnectConfig: ReconnectConfig;\n};\n\n/**\n * Low-level HTTP and SSE client for one stream.\n *\n * This layer owns request headers, timeouts, and response parsing. Higher-level\n * join/sync state transitions stay in `transport-runtime.ts`.\n */\nexport class StreamsTransportClient<TVersion extends JsonObject> {\n readonly streamUrl: string;\n\n private readonly adapter: CrdtAdapter<TVersion>;\n private readonly client: StreamsClient;\n private readonly fetchImpl: FetchLike;\n private readonly authProvider?: (\n context?: StreamsAuthContext,\n ) => Promise<string | undefined>;\n private readonly reconnectConfig: ReconnectConfig;\n private authRefreshInFlight?: Promise<string | undefined>;\n\n constructor(options: StreamsTransportClientOptions<TVersion>) {\n this.streamUrl = options.streamUrl;\n this.adapter = options.adapter;\n this.client = new StreamsClient({\n url: options.streamUrl,\n auth: options.authProvider,\n fetch: options.fetchImpl as typeof globalThis.fetch,\n retry: {\n maxAttempts: 0,\n },\n timeout: {\n connectTimeoutMs: options.reconnectConfig.connectTimeoutMs,\n pollTimeoutMs: options.reconnectConfig.pollTimeoutMs,\n },\n });\n this.fetchImpl = options.fetchImpl;\n this.authProvider = options.authProvider;\n this.reconnectConfig = options.reconnectConfig;\n }\n\n async createStreamRequest(): Promise<boolean> {\n const adapterHeaders = this.adapter.getCreateStreamHeaders\n ? await this.adapter.getCreateStreamHeaders()\n : undefined;\n const result = await this.client.create({\n headers: adapterHeaders,\n });\n if (result.ok) {\n return result.result.created;\n }\n throw this.toTransportFailure(result.result);\n }\n\n async deleteStreamRequest(): Promise<boolean> {\n const result = await this.client.delete();\n if (result.ok) {\n return result.result.deleted;\n }\n throw this.toTransportFailure(result.result);\n }\n\n async readFromOffsetSseItems(\n offset: string,\n cursor: string | undefined,\n onControl: (\n updates: Uint8Array[],\n nextOffset: string,\n upToDate: boolean,\n streamCursor?: string,\n ) => MaybePromise<SseControlHandlerResult>,\n onOpen?: () => void,\n signal?: AbortSignal,\n onActivity?: () => void,\n ): Promise<SseItemsReadResult> {\n return this.readItemsSse(\n offset,\n cursor,\n onControl,\n onOpen,\n signal,\n onActivity,\n );\n }\n\n async readFromOffset(\n offset: string,\n cursor?: string,\n live?: \"long-poll\",\n signal?: AbortSignal,\n ): Promise<{ kind: \"gone\" } | { kind: \"ok\"; value: ReadResult }> {\n const result = await this.client.readOnce({\n offset,\n cursor,\n live,\n signal,\n });\n if (!result.ok) {\n if (result.result.code === \"gone\") {\n return { kind: \"gone\" };\n }\n throw this.toTransportFailure(result.result);\n }\n\n const payload = result.result.payload?.body ?? new Uint8Array();\n return {\n kind: \"ok\",\n value: {\n nextOffset: result.result.nextOffset,\n upToDate: result.result.upToDate,\n updates: payload.byteLength === 0 ? [] : decodeItems(payload),\n streamCursor: result.result.cursor,\n },\n };\n }\n\n async bootstrap(): Promise<BootstrapResult> {\n const result = await this.client.bootstrap();\n if (!result.ok) {\n throw this.toTransportFailure(result.result);\n }\n\n return {\n nextOffset: result.result.nextOffset,\n upToDate: result.result.upToDate,\n snapshot: result.result.snapshot?.body ?? new Uint8Array(),\n updates: result.result.updates.flatMap((part) =>\n decodeMultipartPartItems(part.body)\n ),\n streamCursor: result.result.cursor,\n };\n }\n\n async readSnapshotOffsetValue(): Promise<string | null> {\n const result = await this.client.head();\n if (!result.ok) {\n if (result.result.code === \"not_found\") {\n return null;\n }\n throw this.toTransportFailure(result.result);\n }\n\n const snapshotOffset = result.result.snapshotOffset;\n return snapshotOffset == null || snapshotOffset.length === 0\n ? null\n : snapshotOffset;\n }\n\n async putSnapshotAt(\n snapshotOffset: string,\n snapshot: Uint8Array,\n ): Promise<void> {\n const result = await this.client.putSnapshot({\n offset: snapshotOffset,\n part: {\n contentType: \"application/octet-stream\",\n body: snapshot,\n },\n });\n if (!result.ok) {\n throw this.toTransportFailure(result.result);\n }\n }\n\n async appendUpdates(\n updates: readonly Uint8Array[],\n producerSession: ProducerSession,\n ): Promise<AppendResult> {\n const result = await this.client.append({\n part: {\n contentType: BINARY_CONTENT_TYPE,\n body: encodeItems(updates),\n },\n producer: {\n producerId: producerSession.producerId,\n epoch: producerSession.producerEpoch,\n seq: producerSession.nextProducerSeq,\n },\n });\n if (result.ok) {\n return {\n kind: \"ok\",\n ackEpoch: result.result.producer?.epoch ?? producerSession.producerEpoch,\n ackSeq: result.result.producer?.seq ?? producerSession.nextProducerSeq,\n nextOffset: result.result.nextOffset,\n };\n }\n\n const error = result.result;\n if (\n error.code === \"conflict\" &&\n error.detail?.kind === \"producer_seq_gap\"\n ) {\n return { kind: \"seq-gap\", expectedSeq: error.detail.expectedSeq };\n }\n if (\n error.code === \"forbidden\" &&\n error.detail?.kind === \"stale_producer_epoch\"\n ) {\n return {\n kind: \"stale-epoch\",\n serverEpoch: error.detail.serverEpoch,\n };\n }\n throw this.toTransportFailure(error);\n }\n\n async appendItems(updates: readonly Uint8Array[]): Promise<string> {\n const result = await this.client.append({\n part: {\n contentType: BINARY_CONTENT_TYPE,\n body: encodeItems(updates),\n },\n });\n if (result.ok) {\n return result.result.nextOffset;\n }\n throw this.toTransportFailure(result.result);\n }\n\n async appendEphemeralUpdate(\n update: Uint8Array,\n signal?: AbortSignal,\n ): Promise<void> {\n if (update.byteLength === 0) {\n return;\n }\n const response = await this.fetchAuthorized(\n this.streamUrl,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": BINARY_CONTENT_TYPE,\n },\n body: update as unknown as BodyInit,\n signal,\n },\n this.reconnectConfig.connectTimeoutMs,\n \"connect\",\n );\n if (response.status === 200 || response.status === 204) {\n return;\n }\n throw await this.responseToEphemeralFailure(response, \"ephemeral append\");\n }\n\n /**\n * Opens one bootstrap-first ephemeral SSE session.\n *\n * Ephemeral channels intentionally do not expose offsets or control events:\n * the first event must be `bootstrap`, and all following events must be live\n * `data` updates.\n */\n async readEphemeralSse(\n onBootstrap: (bootstrap: Uint8Array) => MaybePromise<void>,\n onData: (update: Uint8Array) => MaybePromise<void>,\n signal?: AbortSignal,\n ): Promise<\n | { kind: \"ok\" }\n | { kind: \"unsupported\" }\n | { kind: \"not-found\" }\n > {\n const requestUrl = this.ephemeralReadEndpoint();\n const response = await this.fetchAuthorized(\n requestUrl,\n {\n method: \"GET\",\n headers: {\n Accept: \"text/event-stream\",\n },\n signal,\n },\n this.reconnectConfig.connectTimeoutMs,\n \"connect\",\n );\n if (response.status === 404) {\n return { kind: \"not-found\" };\n }\n if (!response.ok) {\n if (response.status === 400) {\n const message = await response.text();\n if (isSseUnsupportedErrorText(message)) {\n return { kind: \"unsupported\" };\n }\n throw new HttpError(\n message.length > 0\n ? `ephemeral live read failed: ${message}`\n : \"ephemeral live read failed\",\n response.status,\n );\n }\n throw await this.responseToEphemeralFailure(response, \"ephemeral live read\");\n }\n if (response.body == null) {\n throw new ProtocolError(\"ephemeral sse response missing body\");\n }\n const responseContentType = response.headers.get(\"Content-Type\") ?? \"\";\n if (!responseContentType.toLowerCase().includes(\"text/event-stream\")) {\n throw new ProtocolError(\n \"ephemeral sse response must use text/event-stream content type\",\n );\n }\n\n const dataEncoding =\n response.headers.get(\"Stream-SSE-Data-Encoding\") ?? undefined;\n const consume = async (): Promise<{ kind: \"ok\" }> => {\n let sawBootstrap = false;\n for await (const event of readSseEvents(response.body!)) {\n if (!sawBootstrap) {\n if (event.event !== \"bootstrap\") {\n throw new ProtocolError(\n `ephemeral sse must start with bootstrap, got '${event.event}'`,\n );\n }\n await onBootstrap(decodeSsePayload(event.data, dataEncoding));\n sawBootstrap = true;\n continue;\n }\n if (event.event === \"data\") {\n await onData(decodeSsePayload(event.data, dataEncoding));\n continue;\n }\n throw new ProtocolError(\n `unexpected ephemeral sse event '${event.event}'`,\n );\n }\n if (!sawBootstrap) {\n throw new ProtocolError(\"ephemeral sse ended before bootstrap\");\n }\n return { kind: \"ok\" };\n };\n\n let abortHandler: (() => void) | undefined;\n const abortRace =\n signal != null && !signal.aborted\n ? new Promise<never>((_, reject) => {\n abortHandler = () => {\n void response.body?.cancel().catch(() => {});\n reject(\n signal.reason ??\n new DOMException(\"The operation was aborted.\", \"AbortError\"),\n );\n };\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n })\n : null;\n try {\n if (signal?.aborted) {\n await response.body.cancel().catch(() => {});\n throw (\n signal.reason ??\n new DOMException(\"The operation was aborted.\", \"AbortError\")\n );\n }\n return abortRace == null\n ? await consume()\n : await Promise.race([consume(), abortRace]);\n } finally {\n if (abortHandler != null && signal != null) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n }\n }\n\n private async fetchAuthorized(\n input: string,\n init: RequestInit,\n timeoutMs: number,\n phase: \"connect\" | \"poll\",\n ): Promise<Response> {\n const firstToken = await this.resolveToken({ reason: \"request\" });\n const firstResponse = await this.fetchWithTimeout(\n input,\n {\n ...init,\n headers: this.attachAuthorization(init.headers, firstToken),\n },\n timeoutMs,\n phase,\n );\n if (\n this.authProvider == null ||\n (firstResponse.status !== 401 && firstResponse.status !== 403)\n ) {\n return firstResponse;\n }\n await firstResponse.body?.cancel().catch(() => {});\n const refreshedToken = await this.refreshTokenAfterFailure(\n firstResponse.status as 401 | 403,\n firstToken,\n );\n return this.fetchWithTimeout(\n input,\n {\n ...init,\n headers: this.attachAuthorization(init.headers, refreshedToken),\n },\n timeoutMs,\n phase,\n );\n }\n\n private async fetchWithTimeout(\n input: string,\n init: RequestInit,\n timeoutMs: number,\n phase: \"connect\" | \"poll\",\n ): Promise<Response> {\n const controller = new AbortController();\n const signal = mergeAbortSignals(init.signal, controller.signal);\n const timer = setTimeout(() => {\n controller.abort(new TimeoutError(phase, timeoutMs));\n }, timeoutMs);\n try {\n return await this.fetchImpl(input, {\n ...init,\n signal,\n });\n } catch (error) {\n if (\n controller.signal.aborted &&\n controller.signal.reason instanceof TimeoutError\n ) {\n throw controller.signal.reason;\n }\n throw error;\n } finally {\n clearTimeout(timer);\n }\n }\n\n private async resolveToken(\n context?: StreamsAuthContext,\n ): Promise<string | undefined> {\n return await this.authProvider?.(context);\n }\n\n private async refreshTokenAfterFailure(\n status: 401 | 403,\n previousToken?: string,\n ): Promise<string | undefined> {\n if (this.authProvider == null) {\n return undefined;\n }\n if (this.authRefreshInFlight == null) {\n this.authRefreshInFlight = this.resolveToken({\n reason: \"unauthorized\",\n status,\n previousToken,\n }).finally(() => {\n this.authRefreshInFlight = undefined;\n });\n }\n return await this.authRefreshInFlight;\n }\n\n private attachAuthorization(\n base: HeadersInit | undefined,\n token: string | undefined,\n ): Headers {\n const headers = new Headers(base);\n if (token == null || token.length === 0) {\n headers.delete(\"Authorization\");\n return headers;\n }\n headers.set(\"Authorization\", `Bearer ${token}`);\n return headers;\n }\n\n private async responseToEphemeralFailure(\n response: Response,\n context: string,\n ): Promise<Error> {\n const message = await response.text().catch(() => \"\");\n return new HttpError(\n message.length > 0\n ? `${context} failed: ${message}`\n : `${context} failed with status ${response.status}`,\n response.status,\n );\n }\n\n private ephemeralReadEndpoint(): string {\n const target = new URL(this.streamUrl);\n target.searchParams.set(\"live\", \"sse\");\n return target.toString();\n }\n\n private toTransportFailure(error: StreamError): Error {\n switch (error.code) {\n case \"bad_request\":\n case \"unauthorized\":\n case \"forbidden\":\n case \"not_found\":\n case \"conflict\":\n case \"gone\":\n case \"rate_limited\":\n return new HttpError(error.message, error.status);\n case \"timeout\":\n return new TimeoutError(\n error.phase,\n error.phase === \"poll\"\n ? this.reconnectConfig.pollTimeoutMs\n : this.reconnectConfig.connectTimeoutMs,\n );\n case \"network_error\":\n return error.cause instanceof TypeError\n ? error.cause\n : new TypeError(error.message);\n case \"protocol_error\":\n return new ProtocolError(error.message, error.status);\n case \"unknown\":\n if (typeof error.status === \"number\") {\n return new HttpError(error.message, error.status);\n }\n if (error.cause instanceof TypeError) {\n return error.cause;\n }\n if (error.cause instanceof Error) {\n return error.cause;\n }\n return new Error(error.message);\n default: {\n const exhaustive: never = error;\n return new Error(String(exhaustive));\n }\n }\n }\n\n private async readItemsSse(\n initialOffset: string,\n cursor: string | undefined,\n onControl: (\n updates: Uint8Array[],\n nextOffset: string,\n upToDate: boolean,\n streamCursor?: string,\n ) => MaybePromise<SseControlHandlerResult>,\n onOpen?: () => void,\n signal?: AbortSignal,\n onActivity?: () => void,\n ): Promise<SseItemsReadResult> {\n const opened = await this.openItemsSseSession(\n initialOffset,\n cursor,\n onOpen,\n signal,\n onActivity,\n );\n if (opened.kind !== \"ok\") {\n return opened;\n }\n // Race continue() against the abort signal so that callers (e.g.\n // the inactivity timeout) can interrupt an in-progress SSE read\n // even when the fetch implementation does not propagate abort to\n // the response body stream.\n let abortHandler: (() => void) | undefined;\n const abortRace =\n signal != null && !signal.aborted\n ? new Promise<never>((_, reject) => {\n abortHandler = () => {\n void opened.session.cancel();\n reject(\n signal.reason ??\n new DOMException(\"The operation was aborted.\", \"AbortError\"),\n );\n };\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n })\n : null;\n try {\n if (signal?.aborted) {\n await opened.session.cancel();\n throw (\n signal.reason ??\n new DOMException(\"The operation was aborted.\", \"AbortError\")\n );\n }\n const result =\n abortRace != null\n ? await Promise.race([\n opened.session.continue(onControl),\n abortRace,\n ])\n : await opened.session.continue(onControl);\n if (result.paused) {\n await opened.session.cancel();\n }\n return {\n kind: \"ok\",\n latestOffset: result.latestOffset,\n latestCursor: result.latestCursor,\n };\n } finally {\n if (abortHandler != null && signal != null) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n }\n }\n\n private async openItemsSseSession(\n initialOffset: string,\n cursor: string | undefined,\n onOpen?: () => void,\n signal?: AbortSignal,\n onActivity?: () => void,\n ): Promise<SseItemsOpenResult> {\n const result = await this.client.openSseSession({\n offset: initialOffset,\n cursor,\n signal,\n onActivity,\n });\n if (!result.ok) {\n if (result.result.code === \"gone\") {\n return { kind: \"gone\" };\n }\n if (result.result.code === \"not_found\") {\n return { kind: \"not-found\" };\n }\n if (\n result.result.code === \"bad_request\" &&\n isSseUnsupportedErrorText(result.result.message)\n ) {\n return { kind: \"unsupported\" };\n }\n throw this.toTransportFailure(result.result);\n }\n\n const session = result.result;\n onOpen?.();\n let pendingDataEvents: string[] = [];\n let latestOffset = session.nextOffset ?? initialOffset;\n let latestCursor = session.cursor;\n let exhausted = false;\n\n return {\n kind: \"ok\",\n session: {\n continue: async (nextOnControl) => {\n if (exhausted) {\n return {\n kind: \"ok\",\n latestOffset,\n latestCursor,\n paused: false,\n };\n }\n for await (const event of session.events) {\n if (event.event === \"data\") {\n pendingDataEvents.push(event.data);\n continue;\n }\n if (event.event !== \"control\") {\n continue;\n }\n const control = parseSseControlData(event.data);\n const updates: Uint8Array[] = [];\n for (const eventData of pendingDataEvents) {\n updates.push(\n ...decodeSseItemPayloads(eventData, session.dataEncoding),\n );\n }\n pendingDataEvents = [];\n const handlerResult = await nextOnControl(\n updates,\n control.nextOffset,\n control.upToDate,\n control.streamCursor,\n );\n latestOffset = control.nextOffset;\n if (control.streamCursor != null) {\n latestCursor = control.streamCursor;\n }\n if (handlerResult === \"stop\") {\n exhausted = true;\n await session.cancel();\n return {\n kind: \"ok\",\n latestOffset,\n latestCursor,\n paused: false,\n };\n }\n if (handlerResult === \"pause\") {\n return {\n kind: \"ok\",\n latestOffset,\n latestCursor,\n paused: true,\n };\n }\n }\n exhausted = true;\n return {\n kind: \"ok\",\n latestOffset,\n latestCursor,\n paused: false,\n };\n },\n cancel: async () => {\n if (exhausted) {\n return;\n }\n exhausted = true;\n await session.cancel();\n },\n },\n };\n }\n}\n","import { Deque } from \"./deque\";\nimport {\n computeRetryDelay,\n DEFAULT_RECONNECT_CONFIG,\n type ReconnectConfig,\n} from \"./reconnect\";\nimport { StreamsTransportClient } from \"./transport-client\";\nimport {\n classifyFetchError,\n HttpError,\n isAuthOrProtocolError,\n normalizeAuthProvider,\n sleep,\n toTransportError,\n} from \"./transport-internal\";\nimport type {\n CrdtAdapter,\n EphemeralStreamAdaptor,\n EphemeralStreamCrdtOptions,\n EphemeralStreamJoinParams,\n EphemeralStreamSubscription,\n JsonObject,\n TransportError,\n TransportRoomStatus,\n} from \"./types\";\n\ntype EphemeralWaiter = {\n /** Number of queued local updates that still need successful POST acknowledgements. */\n remaining: number;\n /** Resolves the caller's `waitUntilSynced()` once the tracked updates flush. */\n resolve: () => void;\n /** Rejects the caller's `waitUntilSynced()` on close or non-recoverable write failure. */\n reject: (error: Error) => void;\n};\n\n/**\n * Shared lifecycle state for all active subscriptions on one ephemeral stream.\n *\n * A single `EphemeralStreamCrdt` instance keeps one SSE read loop and one local\n * write queue. Multiple callers can hold lightweight subscription handles that\n * reference this shared state through `refCount`.\n */\ntype EphemeralJoinState = {\n /** Number of active public subscription handles referencing this join state. */\n refCount: number;\n /** Set after the shared read/write loops have been disposed. */\n closed: boolean;\n /** Public aggregate status derived from read and write sides. */\n status: \"connecting\" | TransportRoomStatus;\n /** Status of the bootstrap-first SSE read loop. */\n readStatus: \"connecting\" | \"ok\" | \"reconnecting\" | \"disconnected\" | \"error\";\n /** Status of the local POST write queue. */\n writeStatus: \"connecting\" | \"ok\" | \"reconnecting\" | \"error\";\n /** Registered public status callbacks. */\n readonly statusListeners: Set<\n (status: \"connecting\" | TransportRoomStatus) => void\n >;\n /** Local ephemeral updates waiting for POST acknowledgement. */\n readonly pendingLocal: Deque<Uint8Array>;\n /** `waitUntilSynced()` waiters bound to the current pending queue length. */\n readonly syncWaiters: EphemeralWaiter[];\n /** Guards against concurrent flush loops for `pendingLocal`. */\n flushingLocal: boolean;\n /** Current reconnect attempt for the read loop. */\n retryAttempt: number;\n /** Abort controller for pending reconnect sleeps. */\n retrySleepController: AbortController;\n /** Abort controller for the active SSE request. */\n requestController?: AbortController;\n /** Unsubscribe callback for local ephemeral store updates. */\n unsubscribeLocal?: () => void;\n /** Resolver for the initial bootstrap event. */\n firstOpenResolve?: () => void;\n /** Rejecter for failures before the initial bootstrap event. */\n firstOpenReject?: (error: Error) => void;\n /** Prevents the initial bootstrap promise from settling twice. */\n firstOpenSettled: boolean;\n /** Join startup promise shared by concurrent `join()` calls. */\n started: Promise<void>;\n};\n\nfunction createNoopAdapter(): CrdtAdapter<JsonObject> {\n return {\n emptyVersion: () => ({}),\n mergeVersions: (base) => base,\n exportSnapshot: () => new Uint8Array(),\n applySnapshot: () => ({}),\n exportUpdates: () => undefined,\n applyRemoteUpdates: () => ({}),\n subscribeLocalUpdates: () => () => undefined,\n };\n}\n\nfunction normalizeEphemeralUpdate(update: Uint8Array | null | undefined) {\n if (update == null || update.byteLength === 0) {\n return null;\n }\n return update;\n}\n\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nfunction closedSubscriptionError(): Error {\n return new Error(\"subscription closed\");\n}\n\n/**\n * Realtime transport for one ephemeral CRDT stream.\n *\n * This is intentionally separate from `StreamsCrdt`: it bootstraps through\n * the first SSE event, then tails live deltas, and never stores a replay\n * offset, snapshot checkpoint, or remote cursor.\n */\nexport class EphemeralStreamCrdt {\n /** Full ephemeral stream URL, including `?ephemeral=` or `?awareness=`. */\n readonly streamUrl: string;\n /** Store-specific adapter used to encode local state and apply remote bytes. */\n readonly adaptor: EphemeralStreamAdaptor;\n\n /** Low-level HTTP/SSE transport. It is reused only for auth, fetch, and parsing helpers. */\n private readonly client: StreamsTransportClient<JsonObject>;\n /** Retry policy shared by the read loop and local write queue. */\n private readonly reconnectConfig: ReconnectConfig;\n /** Enables diagnostic console errors for failed read/write loops. */\n private readonly debug: boolean;\n /** Current shared subscription state, if the stream is joined. */\n private joinState?: EphemeralJoinState;\n\n constructor(options: EphemeralStreamCrdtOptions) {\n this.streamUrl = options.streamUrl;\n this.adaptor = options.adaptor;\n this.reconnectConfig = {\n ...DEFAULT_RECONNECT_CONFIG,\n ...options.reconnectConfig,\n };\n this.debug = options.debug ?? false;\n this.client = new StreamsTransportClient({\n streamUrl: options.streamUrl,\n adapter: createNoopAdapter(),\n authProvider: normalizeAuthProvider(options.auth),\n fetchImpl: options.fetch ?? globalThis.fetch.bind(globalThis),\n reconnectConfig: this.reconnectConfig,\n });\n }\n\n async join(\n params?: EphemeralStreamJoinParams,\n ): Promise<\n | { readonly ok: true; readonly value: EphemeralStreamSubscription }\n | { readonly ok: false; readonly error: TransportError }\n > {\n const existing = this.joinState;\n if (existing != null) {\n try {\n await existing.started;\n return { ok: true, value: this.createSubscription(existing, params) };\n } catch (error) {\n return { ok: false, error: toTransportError(error) };\n }\n }\n\n const state = this.createJoinState();\n state.started = this.startJoin(state);\n this.joinState = state;\n try {\n await state.started;\n return { ok: true, value: this.createSubscription(state, params) };\n } catch (error) {\n await this.disposeJoinState(state);\n return { ok: false, error: toTransportError(error) };\n }\n }\n\n rejoin(): void {\n const state = this.joinState;\n if (state == null || state.closed) {\n return;\n }\n state.retryAttempt = 0;\n state.retrySleepController.abort();\n state.retrySleepController = new AbortController();\n\n const previousController = state.requestController;\n state.requestController = new AbortController();\n previousController?.abort();\n\n if (state.readStatus === \"disconnected\" || state.readStatus === \"error\") {\n this.setReadStatus(state, \"reconnecting\");\n void this.runReadLoop(state);\n }\n if (state.writeStatus === \"error\") {\n this.setWriteStatus(state, \"reconnecting\");\n void this.flushPendingLocal(state);\n }\n }\n\n async close(): Promise<void> {\n const state = this.joinState;\n if (state != null) {\n await this.disposeJoinState(state);\n }\n }\n\n private createJoinState(): EphemeralJoinState {\n return {\n refCount: 0,\n closed: false,\n status: \"connecting\",\n readStatus: \"connecting\",\n writeStatus: \"connecting\",\n statusListeners: new Set(),\n pendingLocal: new Deque(),\n syncWaiters: [],\n flushingLocal: false,\n retryAttempt: 0,\n retrySleepController: new AbortController(),\n firstOpenSettled: false,\n started: Promise.resolve(),\n };\n }\n\n private async startJoin(state: EphemeralJoinState): Promise<void> {\n state.unsubscribeLocal = this.adaptor.subscribeLocalUpdates((update) => {\n this.enqueueLocal(state, normalizeEphemeralUpdate(update));\n });\n state.requestController = new AbortController();\n const firstOpen = this.waitForFirstOpen(state);\n void this.runReadLoop(state);\n await firstOpen;\n\n if (state.closed) {\n return;\n }\n this.setWriteStatus(state, \"ok\");\n await this.flushPendingLocal(state);\n if (state.status === \"error\" || state.status === \"disconnected\") {\n throw new Error(`ephemeral stream failed to join: ${state.status}`);\n }\n }\n\n private waitForFirstOpen(state: EphemeralJoinState): Promise<void> {\n return new Promise((resolve, reject) => {\n state.firstOpenResolve = resolve;\n state.firstOpenReject = reject;\n });\n }\n\n private createSubscription(\n state: EphemeralJoinState,\n params?: EphemeralStreamJoinParams,\n ): EphemeralStreamSubscription {\n state.refCount += 1;\n const statusListener = params?.onStatusChange;\n if (statusListener != null) {\n state.statusListeners.add(statusListener);\n statusListener(state.status);\n }\n let unsubscribed = false;\n return {\n unsubscribe: () => {\n if (unsubscribed) {\n return;\n }\n unsubscribed = true;\n if (statusListener != null) {\n state.statusListeners.delete(statusListener);\n }\n state.refCount = Math.max(0, state.refCount - 1);\n if (state.refCount === 0 && !state.closed) {\n void this.disposeJoinState(state);\n }\n },\n get connected() {\n return state.status === \"joined\";\n },\n get status() {\n return state.status as TransportRoomStatus;\n },\n onStatusChange: (listener) => this.onStatusChange(state, listener),\n waitUntilSynced: async () => {\n if (state.closed) {\n throw closedSubscriptionError();\n }\n const remaining = state.pendingLocal.length;\n if (remaining === 0) {\n return;\n }\n return new Promise<void>((resolve, reject) => {\n state.syncWaiters.push({ remaining, resolve, reject });\n });\n },\n };\n }\n\n private onStatusChange(\n state: EphemeralJoinState,\n listener: (status: \"connecting\" | TransportRoomStatus) => void,\n ): () => void {\n state.statusListeners.add(listener);\n listener(state.status);\n return () => {\n state.statusListeners.delete(listener);\n };\n }\n\n private async disposeJoinState(state: EphemeralJoinState): Promise<void> {\n if (state.closed) {\n return;\n }\n state.closed = true;\n state.unsubscribeLocal?.();\n state.requestController?.abort();\n state.retrySleepController.abort();\n this.rejectFirstOpen(state, closedSubscriptionError());\n this.rejectSyncWaiters(state, closedSubscriptionError());\n if (this.joinState === state) {\n this.joinState = undefined;\n }\n }\n\n private resolveFirstOpen(state: EphemeralJoinState): void {\n if (state.firstOpenSettled) {\n return;\n }\n state.firstOpenSettled = true;\n state.firstOpenResolve?.();\n }\n\n private rejectFirstOpen(\n state: EphemeralJoinState,\n error: Error,\n ): void {\n if (state.firstOpenSettled) {\n return;\n }\n state.firstOpenSettled = true;\n state.firstOpenReject?.(error);\n }\n\n private async runReadLoop(state: EphemeralJoinState): Promise<void> {\n while (!state.closed) {\n state.requestController ??= new AbortController();\n const capturedController = state.requestController;\n try {\n const result = await this.client.readEphemeralSse(\n async (bootstrap) => {\n if (bootstrap.byteLength > 0) {\n await this.adaptor.applyRemoteUpdates([bootstrap]);\n }\n this.setReadStatus(state, \"ok\");\n this.resolveFirstOpen(state);\n },\n async (update) => {\n if (update.byteLength > 0) {\n await this.adaptor.applyRemoteUpdates([update]);\n }\n },\n capturedController.signal,\n );\n if (state.closed) {\n return;\n }\n if (result.kind === \"ok\") {\n state.retryAttempt = 0;\n this.setReadStatus(state, \"reconnecting\");\n await sleep(0);\n continue;\n }\n if (result.kind === \"unsupported\") {\n throw new Error(\"ephemeral stream requires SSE support\");\n }\n if (result.kind === \"not-found\") {\n throw new HttpError(\"ephemeral stream not found\", 404);\n }\n } catch (error) {\n if (state.closed) {\n return;\n }\n if (capturedController.signal.aborted) {\n state.requestController = new AbortController();\n this.setReadStatus(state, \"reconnecting\");\n continue;\n }\n const classified = classifyFetchError(error);\n if (!classified.retriable || isAuthOrProtocolError(error)) {\n this.logError(\"ephemeral live read failed\", error, {\n streamUrl: this.streamUrl,\n code: classified.code,\n });\n this.setReadStatus(state, \"error\");\n this.rejectFirstOpen(state, toError(error));\n return;\n }\n this.setReadStatus(state, \"reconnecting\");\n if (state.retryAttempt >= this.reconnectConfig.maxAttempts) {\n this.logError(\"ephemeral live reconnect exhausted\", error, {\n streamUrl: this.streamUrl,\n attempts: state.retryAttempt,\n });\n this.setReadStatus(state, \"disconnected\");\n this.rejectFirstOpen(state, toError(error));\n return;\n }\n const delay = computeRetryDelay(\n state.retryAttempt,\n this.reconnectConfig,\n );\n state.retryAttempt += 1;\n await sleep(delay, state.retrySleepController.signal);\n }\n }\n }\n\n private enqueueLocal(\n state: EphemeralJoinState,\n update: Uint8Array | null,\n ): void {\n if (state.closed || update == null || update.byteLength === 0) {\n return;\n }\n state.pendingLocal.pushBack(update);\n if (state.writeStatus !== \"connecting\") {\n void this.flushPendingLocal(state);\n }\n }\n\n private async flushPendingLocal(state: EphemeralJoinState): Promise<void> {\n if (state.flushingLocal || state.closed) {\n return;\n }\n state.flushingLocal = true;\n try {\n while (!state.closed) {\n const update = state.pendingLocal.peekFront();\n if (update == null) {\n return;\n }\n let attempt = 0;\n while (!state.closed) {\n try {\n await this.client.appendEphemeralUpdate(update);\n state.pendingLocal.popFront();\n this.notifySyncWaiters(state, 1);\n this.setWriteStatus(state, \"ok\");\n break;\n } catch (error) {\n const classified = classifyFetchError(error);\n if (!classified.retriable || isAuthOrProtocolError(error)) {\n this.rejectSyncWaiters(\n state,\n new Error(`ephemeral append failed: ${classified.code}`),\n );\n this.setWriteStatus(state, \"error\");\n return;\n }\n if (attempt >= this.reconnectConfig.maxAttempts) {\n this.rejectSyncWaiters(\n state,\n new Error(\"ephemeral append retry budget exhausted\"),\n );\n this.setWriteStatus(state, \"error\");\n return;\n }\n this.setWriteStatus(state, \"reconnecting\");\n const delay = computeRetryDelay(attempt, this.reconnectConfig);\n attempt += 1;\n await sleep(delay, state.retrySleepController.signal);\n }\n }\n }\n } finally {\n state.flushingLocal = false;\n }\n }\n\n private notifySyncWaiters(\n state: EphemeralJoinState,\n flushedCount: number,\n ): void {\n let i = 0;\n while (i < state.syncWaiters.length) {\n const waiter = state.syncWaiters[i]!;\n waiter.remaining -= flushedCount;\n if (waiter.remaining <= 0) {\n state.syncWaiters.splice(i, 1);\n waiter.resolve();\n } else {\n i += 1;\n }\n }\n }\n\n private rejectSyncWaiters(state: EphemeralJoinState, error: Error): void {\n for (const waiter of state.syncWaiters.splice(0)) {\n waiter.reject(error);\n }\n }\n\n private setReadStatus(\n state: EphemeralJoinState,\n readStatus: EphemeralJoinState[\"readStatus\"],\n ): void {\n if (state.readStatus === readStatus) {\n return;\n }\n state.readStatus = readStatus;\n this.recomputeStatus(state);\n }\n\n private setWriteStatus(\n state: EphemeralJoinState,\n writeStatus: EphemeralJoinState[\"writeStatus\"],\n ): void {\n if (state.writeStatus === writeStatus) {\n return;\n }\n state.writeStatus = writeStatus;\n this.recomputeStatus(state);\n }\n\n private recomputeStatus(state: EphemeralJoinState): void {\n const status = this.computeStatus(state);\n if (state.status === status) {\n return;\n }\n state.status = status;\n for (const listener of state.statusListeners) {\n listener(status);\n }\n }\n\n private computeStatus(\n state: EphemeralJoinState,\n ): \"connecting\" | TransportRoomStatus {\n if (state.readStatus === \"error\" || state.writeStatus === \"error\") {\n return \"error\";\n }\n if (state.readStatus === \"disconnected\") {\n return \"disconnected\";\n }\n if (\n state.readStatus === \"connecting\" ||\n state.writeStatus === \"connecting\"\n ) {\n return \"connecting\";\n }\n if (\n state.readStatus === \"reconnecting\" ||\n state.writeStatus === \"reconnecting\"\n ) {\n return \"reconnecting\";\n }\n return \"joined\";\n }\n\n private logError(\n message: string,\n error: unknown,\n detail?: Record<string, unknown>,\n ): void {\n if (!this.debug) {\n return;\n }\n console.error(`[streams-crdt] ${message}`, error, detail);\n }\n}\n","import type { JsonObject } from \"./types\";\n\n/**\n * Persisted replay progress for one stream.\n *\n * This is metadata only — it does **not** contain any CRDT update data.\n * A persisted cursor is only valid when the caller also restores the matching\n * local CRDT state before the next `sync()` or `join()`.\n */\nexport interface RemoteCursor<TVersion extends JsonObject = JsonObject> {\n /** Identity of the stream this cursor belongs to. */\n readonly streamUrl: string;\n /** The offset to resume reading from on the next sync/join. */\n readonly nextOffset: string;\n /** The CRDT version that the server data up to `nextOffset` represents. */\n readonly serverLowerBoundVersion: TVersion;\n /** Timestamp (ms) when this cursor was last persisted. */\n readonly updatedAtMs: number;\n}\n\n/**\n * Storage for replay progress metadata.\n *\n * A persisted cursor is only valid when the caller also restores the matching\n * local CRDT state before the next `sync()` or `join()`. Storing only the\n * cursor can make transport resume from `nextOffset` against an empty local\n * document, which is not sufficient for delta-only catchup.\n */\nexport interface RemoteCursorStore<TVersion extends JsonObject = JsonObject> {\n load(streamUrl: string): Promise<RemoteCursor<TVersion> | null>;\n save(cursor: RemoteCursor<TVersion>): Promise<void>;\n delete?(streamUrl: string): Promise<void>;\n}\n\n/**\n * Why the transport is advancing the remote cursor.\n */\nexport type RemoteCursorSaveSource = \"local\" | \"remote\" | \"bootstrap\";\n\n/**\n * Context passed to durability hooks before a remote cursor is persisted.\n *\n * Hooks should make local durable state correspond to `cursor` before this\n * cursor is stored, so a future process can restore the same CRDT state before\n * transport resumes from `cursor.nextOffset`.\n */\nexport type BeforeRemoteCursorSaveContext<\n TVersion extends JsonObject = JsonObject,\n> = {\n readonly cursor: RemoteCursor<TVersion>;\n readonly source: RemoteCursorSaveSource;\n};\n\n/**\n * Hook invoked before the transport persists replay progress.\n *\n * This is the place to persist local CRDT payloads or snapshots. If that local\n * durability step does not happen here, the remote cursor should not advance.\n */\nexport type BeforeRemoteCursorSaveHook<TVersion extends JsonObject = JsonObject> =\n (context: BeforeRemoteCursorSaveContext<TVersion>) => void | Promise<void>;\n\ntype BeforeRemoteCursorSavePredicate<\n TVersion extends JsonObject = JsonObject,\n> = (context: BeforeRemoteCursorSaveContext<TVersion>) => boolean;\n\nexport function composeBeforeRemoteCursorSaveHooks<\n TVersion extends JsonObject = JsonObject,\n>(\n ...hooks: Array<BeforeRemoteCursorSaveHook<TVersion> | null | undefined>\n): BeforeRemoteCursorSaveHook<TVersion> | undefined {\n const active = hooks.filter(\n (hook): hook is BeforeRemoteCursorSaveHook<TVersion> => hook != null,\n );\n if (active.length === 0) {\n return undefined;\n }\n return async (context) => {\n for (const hook of active) {\n await hook(context);\n }\n };\n}\n\nfunction withRemoteCursorSavePredicate<\n TVersion extends JsonObject = JsonObject,\n>(\n predicate: BeforeRemoteCursorSavePredicate<TVersion>,\n hook: BeforeRemoteCursorSaveHook<TVersion>,\n): BeforeRemoteCursorSaveHook<TVersion> {\n return async (context) => {\n if (!predicate(context)) {\n return;\n }\n await hook(context);\n };\n}\n\nexport async function persistRemoteCursor<\n TVersion extends JsonObject = JsonObject,\n>(input: {\n remoteCursorStore: RemoteCursorStore<TVersion>;\n cursor: RemoteCursor<TVersion>;\n source: RemoteCursorSaveSource;\n beforeRemoteCursorSave?: BeforeRemoteCursorSaveHook<TVersion>;\n now?: () => number;\n}): Promise<RemoteCursor<TVersion>> {\n const next: RemoteCursor<TVersion> = {\n ...input.cursor,\n updatedAtMs: (input.now ?? Date.now)(),\n };\n if (input.beforeRemoteCursorSave != null) {\n await input.beforeRemoteCursorSave({\n cursor: next,\n source: input.source,\n });\n }\n await input.remoteCursorStore.save(next);\n return next;\n}\n\n/**\n * Construction options for `IndexedDbRemoteCursorStore`.\n */\nexport interface IndexedDbRemoteCursorStoreOptions {\n readonly dbName?: string;\n readonly storeName?: string;\n readonly dbVersion?: number;\n readonly indexedDb?: IDBFactory;\n}\n\nfunction key(streamUrl: string): string {\n return streamUrl;\n}\n\nfunction isJsonObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value != null && !Array.isArray(value);\n}\n\nfunction cloneJsonValue<T>(value: T): T {\n return structuredClone(value);\n}\n\nfunction cloneRemoteCursor<TVersion extends JsonObject>(\n cursor: RemoteCursor<TVersion>,\n): RemoteCursor<TVersion> {\n return {\n ...cursor,\n serverLowerBoundVersion: cloneJsonValue(cursor.serverLowerBoundVersion),\n };\n}\n\ntype StoredRemoteCursor = {\n readonly streamUrl: string;\n readonly nextOffset: string;\n readonly serverLowerBoundVersion: JsonObject;\n readonly updatedAtMs: number;\n};\n\nfunction fromStoredRemoteCursor<TVersion extends JsonObject>(\n record: StoredRemoteCursor,\n): RemoteCursor<TVersion> {\n if (!isJsonObject(record.serverLowerBoundVersion)) {\n throw new Error(\n \"invalid remote cursor record: serverLowerBoundVersion is not a JSON object\",\n );\n }\n return {\n streamUrl: record.streamUrl,\n nextOffset: record.nextOffset,\n serverLowerBoundVersion: cloneJsonValue(\n record.serverLowerBoundVersion as TVersion,\n ),\n updatedAtMs: record.updatedAtMs,\n };\n}\n\nfunction toStoredRemoteCursor<TVersion extends JsonObject>(\n cursor: RemoteCursor<TVersion>,\n): StoredRemoteCursor {\n return {\n streamUrl: cursor.streamUrl,\n nextOffset: cursor.nextOffset,\n serverLowerBoundVersion: cloneJsonValue(cursor.serverLowerBoundVersion),\n updatedAtMs: cursor.updatedAtMs,\n };\n}\n\n/**\n * In-memory cursor store useful for tests or ephemeral runtimes.\n */\nexport class InMemoryRemoteCursorStore<\n TVersion extends JsonObject = JsonObject,\n> implements RemoteCursorStore<TVersion> {\n private readonly data = new Map<string, RemoteCursor<TVersion>>();\n\n async load(\n streamUrl: string,\n ): Promise<RemoteCursor<TVersion> | null> {\n const value = this.data.get(key(streamUrl));\n return value == null ? null : cloneRemoteCursor(value);\n }\n\n async save(cursor: RemoteCursor<TVersion>): Promise<void> {\n this.data.set(key(cursor.streamUrl), cloneRemoteCursor(cursor));\n }\n\n async delete(streamUrl: string): Promise<void> {\n this.data.delete(key(streamUrl));\n }\n}\n\nconst DEFAULT_DB_NAME = \"streams-crdt\";\nconst DEFAULT_STORE_NAME = \"remote-cursors\";\nconst DEFAULT_DB_VERSION = 1;\n\n/**\n * IndexedDB-backed cursor store for browser clients that need replay progress persistence.\n *\n * This store only persists `RemoteCursor` metadata. It does not persist CRDT\n * payloads. Use it only when the application also persists the corresponding\n * local document state and restores that state before calling `sync()` or\n * `join()` after a reload.\n */\nexport class IndexedDbRemoteCursorStore<\n TVersion extends JsonObject = JsonObject,\n> implements RemoteCursorStore<TVersion> {\n private readonly dbName: string;\n private readonly storeName: string;\n private readonly dbVersion: number;\n private readonly indexedDb: IDBFactory | undefined;\n\n private db: IDBDatabase | null = null;\n private initPromise: Promise<void> | null = null;\n\n constructor(options?: IndexedDbRemoteCursorStoreOptions) {\n this.dbName = options?.dbName ?? DEFAULT_DB_NAME;\n this.storeName = options?.storeName ?? DEFAULT_STORE_NAME;\n this.dbVersion = options?.dbVersion ?? DEFAULT_DB_VERSION;\n this.indexedDb = options?.indexedDb ?? globalThis.indexedDB;\n }\n\n async load(\n streamUrl: string,\n ): Promise<RemoteCursor<TVersion> | null> {\n const db = await this.ensureDb();\n const record = await new Promise<StoredRemoteCursor | null>((resolve, reject) => {\n const tx = db.transaction([this.storeName], \"readonly\");\n const store = tx.objectStore(this.storeName);\n const request = store.get(key(streamUrl));\n request.onsuccess = () => {\n resolve((request.result as StoredRemoteCursor | undefined) ?? null);\n };\n request.onerror = () => {\n reject(request.error ?? new Error(\"indexeddb load failed\"));\n };\n });\n return record == null ? null : fromStoredRemoteCursor<TVersion>(record);\n }\n\n async save(cursor: RemoteCursor<TVersion>): Promise<void> {\n const db = await this.ensureDb();\n const record = toStoredRemoteCursor(cursor);\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction([this.storeName], \"readwrite\");\n const store = tx.objectStore(this.storeName);\n const request = store.put(record, key(cursor.streamUrl));\n tx.oncomplete = () => resolve();\n tx.onerror = () =>\n reject(tx.error ?? request.error ?? new Error(\"indexeddb save failed\"));\n tx.onabort = () =>\n reject(\n tx.error ??\n request.error ??\n new Error(\"indexeddb save transaction aborted\"),\n );\n request.onerror = () =>\n reject(request.error ?? new Error(\"indexeddb save failed\"));\n });\n }\n\n async delete(streamUrl: string): Promise<void> {\n const db = await this.ensureDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction([this.storeName], \"readwrite\");\n const store = tx.objectStore(this.storeName);\n const request = store.delete(key(streamUrl));\n tx.oncomplete = () => resolve();\n tx.onerror = () =>\n reject(\n tx.error ?? request.error ?? new Error(\"indexeddb delete failed\"),\n );\n tx.onabort = () =>\n reject(\n tx.error ??\n request.error ??\n new Error(\"indexeddb delete transaction aborted\"),\n );\n request.onerror = () =>\n reject(request.error ?? new Error(\"indexeddb delete failed\"));\n });\n }\n\n private async ensureDb(): Promise<IDBDatabase> {\n if (this.db != null) {\n return this.db;\n }\n if (this.initPromise != null) {\n await this.initPromise;\n if (this.db == null) {\n throw new Error(\"indexeddb is not available\");\n }\n return this.db;\n }\n if (this.indexedDb == null) {\n throw new Error(\"indexeddb is not available in this environment\");\n }\n\n this.initPromise = new Promise<void>((resolve, reject) => {\n const request = this.indexedDb!.open(this.dbName, this.dbVersion);\n\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(this.storeName)) {\n db.createObjectStore(this.storeName);\n }\n };\n request.onerror = () =>\n reject(request.error ?? new Error(\"indexeddb open failed\"));\n request.onsuccess = () => {\n this.db = request.result;\n this.db.onversionchange = () => {\n this.db?.close();\n this.db = null;\n };\n resolve();\n };\n }).finally(() => {\n this.initPromise = null;\n });\n\n await this.initPromise;\n if (this.db == null) {\n throw new Error(\"indexeddb open succeeded without a database handle\");\n }\n return this.db;\n }\n}\n\n/**\n * Creates a cursor value suitable for seeding a `RemoteCursorStore`.\n */\nexport function createInitialRemoteCursor<\n TVersion extends JsonObject = JsonObject,\n>(input: {\n streamUrl: string;\n nextOffset: string;\n serverLowerBoundVersion: TVersion;\n}): RemoteCursor<TVersion> {\n return {\n streamUrl: input.streamUrl,\n nextOffset: input.nextOffset,\n serverLowerBoundVersion: cloneJsonValue(input.serverLowerBoundVersion),\n updatedAtMs: Date.now(),\n };\n}\n","import type {\n RemoteCursor,\n RemoteCursorSaveSource,\n} from \"./checkpoint\";\nimport {\n resolveNextOffsetAfterConcurrentAdvance,\n type ReadResult,\n} from \"./transport-internal\";\nimport type {\n CrdtAdapter,\n CrdtApplyOutcome,\n CrdtApplyReturn,\n CrdtUnresolvedSpan,\n JsonObject,\n MaybePromise,\n} from \"./types\";\n\nexport class CrdtApplyIncompleteError extends Error {\n readonly unresolved: readonly CrdtUnresolvedSpan[];\n\n constructor(unresolved: readonly CrdtUnresolvedSpan[]) {\n super(\"CRDT apply incomplete: unresolved remote updates remain\");\n this.unresolved = unresolved;\n }\n}\n\nfunction isApplyOutcome<TVersion extends JsonObject>(\n value: CrdtApplyReturn<TVersion>,\n): value is CrdtApplyOutcome<TVersion> {\n return (\n typeof value === \"object\" &&\n value != null &&\n \"version\" in value &&\n \"complete\" in value &&\n typeof (value as { readonly complete?: unknown }).complete === \"boolean\"\n );\n}\n\nfunction normalizeApplyReturn<TVersion extends JsonObject>(\n value: CrdtApplyReturn<TVersion>,\n): CrdtApplyOutcome<TVersion> {\n if (isApplyOutcome(value)) {\n return value;\n }\n return {\n version: value,\n complete: true,\n };\n}\n\ntype TransportCursorManagerOptions<TVersion extends JsonObject> = {\n readonly adapter: Pick<\n CrdtAdapter<TVersion>,\n \"applySnapshot\" | \"applyRemoteUpdates\"\n >;\n readonly decodeSnapshot?: (\n snapshot: Uint8Array,\n ) => MaybePromise<Uint8Array>;\n readonly decodeUpdateItems?: (\n items: readonly Uint8Array[],\n ) => MaybePromise<Uint8Array[]>;\n readonly saveRemoteCursor: (\n cursor: RemoteCursor<TVersion>,\n source: RemoteCursorSaveSource,\n ) => Promise<RemoteCursor<TVersion>>;\n};\n\nexport class TransportCursorManager<TVersion extends JsonObject> {\n private readonly adapter: Pick<\n CrdtAdapter<TVersion>,\n \"applySnapshot\" | \"applyRemoteUpdates\"\n >;\n private readonly decodeSnapshot?: (\n snapshot: Uint8Array,\n ) => MaybePromise<Uint8Array>;\n private readonly decodeUpdateItems?: (\n items: readonly Uint8Array[],\n ) => MaybePromise<Uint8Array[]>;\n private readonly saveRemoteCursor: (\n cursor: RemoteCursor<TVersion>,\n source: RemoteCursorSaveSource,\n ) => Promise<RemoteCursor<TVersion>>;\n private readonly incompleteCursors = new WeakMap<\n RemoteCursor<TVersion>,\n readonly CrdtUnresolvedSpan[]\n >();\n\n constructor(options: TransportCursorManagerOptions<TVersion>) {\n this.adapter = options.adapter;\n this.decodeSnapshot = options.decodeSnapshot;\n this.decodeUpdateItems = options.decodeUpdateItems;\n this.saveRemoteCursor = options.saveRemoteCursor;\n }\n\n isCursorComplete(cursor: RemoteCursor<TVersion>): boolean {\n return !this.incompleteCursors.has(cursor);\n }\n\n async applyBootstrapPayload(\n cursor: RemoteCursor<TVersion>,\n snapshot: Uint8Array,\n updates: readonly Uint8Array[],\n nextOffset: string,\n upToDate = false,\n ): Promise<RemoteCursor<TVersion>> {\n let nextVersion = cursor.serverLowerBoundVersion;\n let complete = true;\n let unresolved: readonly CrdtUnresolvedSpan[] = [];\n if (snapshot.byteLength > 0) {\n const outcome = normalizeApplyReturn(\n await this.adapter.applySnapshot(\n this.decodeSnapshot == null\n ? snapshot\n : await this.decodeSnapshot(snapshot),\n ),\n );\n nextVersion = outcome.version;\n complete = outcome.complete;\n unresolved = outcome.unresolved ?? [];\n }\n const clearUpdates = await this.decodeRemoteUpdateItems(updates);\n if (clearUpdates.length > 0) {\n const outcome = normalizeApplyReturn(\n await this.adapter.applyRemoteUpdates(\n clearUpdates,\n nextVersion,\n ),\n );\n nextVersion = outcome.version;\n complete = outcome.complete;\n unresolved = outcome.unresolved ?? [];\n }\n return await this.finalizeCursor(\n {\n ...cursor,\n nextOffset,\n serverLowerBoundVersion: nextVersion,\n },\n \"bootstrap\",\n complete,\n unresolved,\n upToDate,\n );\n }\n\n async applyBootstrapItems(\n cursor: RemoteCursor<TVersion>,\n items: readonly Uint8Array[],\n nextOffset: string,\n upToDate = false,\n ): Promise<RemoteCursor<TVersion>> {\n const snapshot = items[0] ?? new Uint8Array();\n return await this.applyBootstrapPayload(\n cursor,\n snapshot,\n items.slice(1),\n nextOffset,\n upToDate,\n );\n }\n\n async applyRemoteItems(\n cursor: RemoteCursor<TVersion>,\n items: readonly Uint8Array[],\n nextOffset: string,\n ): Promise<RemoteCursor<TVersion>> {\n return await this.applyRemotePayload(\n cursor,\n [...items],\n nextOffset,\n \"remote\",\n undefined,\n );\n }\n\n async applyCatchupResult(\n cursor: RemoteCursor<TVersion>,\n result: ReadResult,\n onRemoteVersionApplied?: (\n previousVersion: TVersion,\n nextVersion: TVersion,\n ) => MaybePromise<void>,\n ): Promise<RemoteCursor<TVersion>> {\n return await this.applyRemotePayload(\n cursor,\n result.updates,\n result.nextOffset,\n \"remote\",\n onRemoteVersionApplied,\n result.upToDate,\n );\n }\n\n async applyLiveResult(\n cursor: RemoteCursor<TVersion>,\n requestOffset: string,\n result: {\n readonly nextOffset: string;\n readonly updates: readonly Uint8Array[];\n readonly upToDate?: boolean;\n },\n onRemoteVersionApplied?: (\n previousVersion: TVersion,\n nextVersion: TVersion,\n ) => MaybePromise<void>,\n ): Promise<RemoteCursor<TVersion>> {\n const previousVersion = cursor.serverLowerBoundVersion;\n let nextVersion = previousVersion;\n let complete = this.isCursorComplete(cursor);\n let unresolved = this.incompleteCursors.get(cursor) ?? [];\n const clearUpdates = await this.decodeRemoteUpdateItems(result.updates);\n if (clearUpdates.length > 0) {\n const outcome = normalizeApplyReturn(\n await this.adapter.applyRemoteUpdates(\n clearUpdates,\n nextVersion,\n ),\n );\n nextVersion = outcome.version;\n complete = outcome.complete;\n unresolved = outcome.unresolved ?? [];\n await onRemoteVersionApplied?.(previousVersion, nextVersion);\n }\n return await this.finalizeCursor(\n {\n ...cursor,\n nextOffset: resolveNextOffsetAfterConcurrentAdvance(\n requestOffset,\n cursor.nextOffset,\n result.nextOffset,\n ),\n serverLowerBoundVersion: nextVersion,\n },\n \"remote\",\n complete,\n unresolved,\n result.upToDate === true,\n );\n }\n\n private async applyRemotePayload(\n cursor: RemoteCursor<TVersion>,\n updates: readonly Uint8Array[],\n nextOffset: string,\n source: RemoteCursorSaveSource,\n onRemoteVersionApplied?: (\n previousVersion: TVersion,\n nextVersion: TVersion,\n ) => MaybePromise<void>,\n upToDate = false,\n ): Promise<RemoteCursor<TVersion>> {\n const previousVersion = cursor.serverLowerBoundVersion;\n let nextVersion = previousVersion;\n let complete = this.isCursorComplete(cursor);\n let unresolved = this.incompleteCursors.get(cursor) ?? [];\n const clearUpdates = await this.decodeRemoteUpdateItems(updates);\n if (clearUpdates.length > 0) {\n const outcome = normalizeApplyReturn(\n await this.adapter.applyRemoteUpdates(\n clearUpdates,\n nextVersion,\n ),\n );\n nextVersion = outcome.version;\n complete = outcome.complete;\n unresolved = outcome.unresolved ?? [];\n await onRemoteVersionApplied?.(previousVersion, nextVersion);\n }\n return await this.finalizeCursor(\n {\n ...cursor,\n nextOffset,\n serverLowerBoundVersion: nextVersion,\n },\n source,\n complete,\n unresolved,\n upToDate,\n );\n }\n\n private async finalizeCursor(\n cursor: RemoteCursor<TVersion>,\n source: RemoteCursorSaveSource,\n complete: boolean,\n unresolved: readonly CrdtUnresolvedSpan[],\n upToDate: boolean,\n ): Promise<RemoteCursor<TVersion>> {\n if (!complete) {\n if (upToDate) {\n throw new CrdtApplyIncompleteError(unresolved);\n }\n this.incompleteCursors.set(cursor, unresolved);\n return cursor;\n }\n return await this.saveRemoteCursor(cursor, source);\n }\n\n private async decodeRemoteUpdateItems(\n items: readonly Uint8Array[],\n ): Promise<Uint8Array[]> {\n if (items.length === 0) {\n return [];\n }\n return this.decodeUpdateItems == null\n ? [...items]\n : await this.decodeUpdateItems(items);\n }\n}\n","import {\n jsonObjectEquals,\n parseSnapshotByteOffset,\n type JoinState,\n type SnapshotUploadRuntimeConfig,\n} from \"./transport-internal\";\nimport type { RemoteCursor } from \"./checkpoint\";\nimport type { CrdtAdapter, JsonObject, MaybePromise } from \"./types\";\n\nexport type TransportSnapshotState = {\n observedForeignWriter: boolean;\n taskScheduled: boolean;\n taskNeedsRerun: boolean;\n debounceTimer?: ReturnType<typeof setTimeout>;\n};\n\nexport function createTransportSnapshotState(): TransportSnapshotState {\n return {\n observedForeignWriter: false,\n taskScheduled: false,\n taskNeedsRerun: false,\n };\n}\n\ntype TransportSnapshotManagerOptions<TVersion extends JsonObject> = {\n readonly adapter: Pick<CrdtAdapter<TVersion>, \"exportSnapshot\">;\n readonly config?: SnapshotUploadRuntimeConfig;\n readonly encodeSnapshot?: (\n snapshot: Uint8Array,\n ) => MaybePromise<Uint8Array>;\n readonly readSnapshotOffsetValue: () => Promise<string | null>;\n readonly putSnapshotAt: (\n snapshotOffset: string,\n snapshot: Uint8Array,\n ) => Promise<void>;\n readonly enqueueExclusive: <T>(task: () => MaybePromise<T>) => Promise<T>;\n readonly logDebug: (\n message: string,\n detail?: Record<string, unknown>,\n ) => void;\n readonly logError: (\n message: string,\n error?: unknown,\n detail?: Record<string, unknown>,\n ) => void;\n readonly isCurrentState: (state: JoinState<TVersion>) => boolean;\n readonly isCursorComplete: (cursor: RemoteCursor<TVersion>) => boolean;\n};\n\n/**\n * Manages debounce and upload gating for remote snapshots.\n *\n * All \"is this state still current / closed / reconnecting / blocked by a\n * foreign writer?\" checks live here so the runtime does not duplicate them.\n */\nexport class TransportSnapshotManager<TVersion extends JsonObject> {\n private readonly adapter: Pick<CrdtAdapter<TVersion>, \"exportSnapshot\">;\n private readonly config?: SnapshotUploadRuntimeConfig;\n private readonly encodeSnapshot?: (\n snapshot: Uint8Array,\n ) => MaybePromise<Uint8Array>;\n private readonly readSnapshotOffsetValue: () => Promise<string | null>;\n private readonly putSnapshotAt: (\n snapshotOffset: string,\n snapshot: Uint8Array,\n ) => Promise<void>;\n private readonly enqueueExclusive: <T>(\n task: () => MaybePromise<T>,\n ) => Promise<T>;\n private readonly logDebug: (\n message: string,\n detail?: Record<string, unknown>,\n ) => void;\n private readonly logError: (\n message: string,\n error?: unknown,\n detail?: Record<string, unknown>,\n ) => void;\n private readonly isCurrentState: (state: JoinState<TVersion>) => boolean;\n private readonly isCursorComplete: (\n cursor: RemoteCursor<TVersion>,\n ) => boolean;\n\n constructor(options: TransportSnapshotManagerOptions<TVersion>) {\n this.adapter = options.adapter;\n this.config = options.config;\n this.encodeSnapshot = options.encodeSnapshot;\n this.readSnapshotOffsetValue = options.readSnapshotOffsetValue;\n this.putSnapshotAt = options.putSnapshotAt;\n this.enqueueExclusive = options.enqueueExclusive;\n this.logDebug = options.logDebug;\n this.logError = options.logError;\n this.isCurrentState = options.isCurrentState;\n this.isCursorComplete = options.isCursorComplete;\n }\n\n dispose(state: JoinState<TVersion>): void {\n this.clearDebounceTimer(state.snapshot);\n }\n\n observeRemoteVersion(\n state: JoinState<TVersion>,\n previousVersion: TVersion,\n nextVersion: TVersion,\n ): void {\n const snapshot = state.snapshot;\n if (snapshot.debounceTimer == null) {\n return;\n }\n if (!jsonObjectEquals(previousVersion, nextVersion)) {\n snapshot.observedForeignWriter = true;\n }\n }\n\n schedule(state: JoinState<TVersion>): void {\n const config = this.config;\n if (config == null || state.closed) {\n return;\n }\n const snapshot = state.snapshot;\n this.clearDebounceTimer(snapshot);\n snapshot.observedForeignWriter = false;\n snapshot.debounceTimer = setTimeout(() => {\n void this.handleDebounceExpiry(state);\n }, config.debounceMs);\n }\n\n private clearDebounceTimer(snapshot: TransportSnapshotState): void {\n if (snapshot.debounceTimer != null) {\n clearTimeout(snapshot.debounceTimer);\n snapshot.debounceTimer = undefined;\n }\n }\n\n private async handleDebounceExpiry(state: JoinState<TVersion>): Promise<void> {\n const snapshot = state.snapshot;\n snapshot.debounceTimer = undefined;\n if (this.getRunnableConfig(state) == null) {\n return;\n }\n if (snapshot.taskScheduled) {\n snapshot.taskNeedsRerun = true;\n return;\n }\n snapshot.taskScheduled = true;\n void this.enqueueExclusive(async () => {\n await this.maybeUpload(state);\n })\n .catch((error) => {\n this.logError(\"snapshot upload task failed\", error);\n })\n .finally(() => {\n snapshot.taskScheduled = false;\n if (snapshot.taskNeedsRerun && !state.closed) {\n snapshot.taskNeedsRerun = false;\n void this.handleDebounceExpiry(state);\n }\n });\n }\n\n private async maybeUpload(state: JoinState<TVersion>): Promise<void> {\n const config = await this.resolveUploadConfig(state);\n if (config == null) {\n return;\n }\n if (!this.isCursorComplete(state.cursor)) {\n this.logDebug(\"skip snapshot upload: current cursor is incomplete\", {\n offset: state.cursor.nextOffset,\n });\n return;\n }\n const tailBytes = parseSnapshotByteOffset(state.cursor.nextOffset);\n if (tailBytes == null) {\n this.logDebug(\"skip snapshot upload: non-numeric offset token\", {\n offset: state.cursor.nextOffset,\n });\n return;\n }\n try {\n const remoteSnapshotOffset = await this.readSnapshotOffsetValue();\n const snapshotBytes = parseSnapshotByteOffset(remoteSnapshotOffset);\n if (snapshotBytes == null) {\n this.logDebug(\"skip snapshot upload: non-numeric snapshot offset\", {\n offset: remoteSnapshotOffset,\n });\n return;\n }\n if (tailBytes <= snapshotBytes) {\n return;\n }\n if (tailBytes - snapshotBytes < config.minBytesSinceRemoteSnapshot) {\n return;\n }\n const snapshot = this.adapter.exportSnapshot();\n await this.putSnapshotAt(\n state.cursor.nextOffset,\n this.encodeSnapshot == null\n ? snapshot\n : await this.encodeSnapshot(snapshot),\n );\n } catch (error) {\n this.logError(\"snapshot upload failed\", error);\n }\n }\n\n private getRunnableConfig(\n state: JoinState<TVersion>,\n ): SnapshotUploadRuntimeConfig | undefined {\n const config = this.config;\n if (config == null || state.closed || !this.isCurrentState(state)) {\n return undefined;\n }\n if (\n state.snapshot.observedForeignWriter ||\n state.status === \"reconnecting\" ||\n state.status === \"disconnected\" ||\n state.status === \"error\"\n ) {\n return undefined;\n }\n return config;\n }\n\n private async resolveUploadConfig(\n state: JoinState<TVersion>,\n ): Promise<SnapshotUploadRuntimeConfig | undefined> {\n const config = this.getRunnableConfig(state);\n if (config == null) {\n return undefined;\n }\n if (!(await this.canUpload(config))) {\n return undefined;\n }\n return config;\n }\n\n private async canUpload(\n config: SnapshotUploadRuntimeConfig,\n ): Promise<boolean> {\n try {\n return Boolean(await config.canUpload());\n } catch (error) {\n this.logError(\"snapshot upload permission check failed\", error);\n return false;\n }\n }\n}\n","import { Deque } from \"./deque\";\nimport type { RemoteCursor } from \"./checkpoint\";\nimport type { JoinState, LiveReadMode, ReadSubStatus, WriteSubStatus } from \"./transport-internal\";\nimport {\n createTransportSnapshotState,\n type TransportSnapshotManager,\n} from \"./transport-snapshot\";\nimport type { JsonObject } from \"./types\";\n\nexport function createJoinState<TVersion extends JsonObject>(\n cursor: RemoteCursor<TVersion>,\n liveMode: LiveReadMode = \"sse\",\n): JoinState<TVersion> {\n return {\n refCount: 0,\n closed: false,\n status: \"connecting\",\n readSubStatus: \"connecting\" as ReadSubStatus,\n writeSubStatus: \"ok\" as WriteSubStatus,\n statusListeners: new Set(),\n pendingLocal: new Deque(),\n flushingLocal: false,\n retryAttempt: 0,\n retrySleepController: new AbortController(),\n cursor,\n liveMode,\n snapshot: createTransportSnapshotState(),\n started: Promise.resolve(),\n syncWaiters: [],\n initialSyncFailed: false,\n };\n}\n\ntype DisposeJoinStateOptions<TVersion extends JsonObject> = {\n readonly snapshotManager: Pick<TransportSnapshotManager<TVersion>, \"dispose\">;\n readonly onDisposed?: () => void;\n};\n\nexport async function disposeJoinState<TVersion extends JsonObject>(\n state: JoinState<TVersion>,\n options: DisposeJoinStateOptions<TVersion>,\n): Promise<void> {\n state.closed = true;\n options.snapshotManager.dispose(state);\n const requestController = state.requestController;\n state.requestController = undefined;\n const unsubscribeLocal = state.unsubscribeLocal;\n state.unsubscribeLocal = undefined;\n requestController?.abort();\n state.retrySleepController.abort();\n unsubscribeLocal?.();\n const disposedError = new Error(\"subscription closed\");\n for (const waiter of state.syncWaiters.splice(0)) {\n waiter.reject(disposedError);\n }\n options.onDisposed?.();\n}\n","import {\n composeBeforeRemoteCursorSaveHooks,\n createInitialRemoteCursor,\n InMemoryRemoteCursorStore,\n persistRemoteCursor,\n type BeforeRemoteCursorSaveHook,\n type RemoteCursor,\n type RemoteCursorSaveSource,\n type RemoteCursorStore,\n} from \"./checkpoint\";\nimport {\n computeRetryDelay,\n DEFAULT_RECONNECT_CONFIG,\n type ReconnectConfig,\n} from \"./reconnect\";\nimport { Deque } from \"./deque\";\nimport {\n normalizePayloadProtection,\n type PayloadProtectionRuntime,\n} from \"./payload-protection\";\nimport { StreamsTransportClient } from \"./transport-client\";\nimport {\n CrdtApplyIncompleteError,\n TransportCursorManager,\n} from \"./transport-cursor\";\nimport {\n createJoinState as createTransportJoinState,\n disposeJoinState as disposeTransportJoinState,\n} from \"./transport-room\";\nimport { TransportSnapshotManager } from \"./transport-snapshot\";\nimport {\n classifyFetchError,\n createProducerId,\n HttpError,\n isAuthOrProtocolError,\n isReadNoProgress,\n isStreamNotFoundError,\n normalizeAuthProvider,\n normalizeSnapshotCodec,\n normalizeSnapshotUploadConfig,\n sleep,\n toTransportError,\n type CursorSyncResult,\n type JoinState,\n type JoinSyncResult,\n type LiveReadMode,\n type LocalExportResult,\n type ProducerSession,\n type ReadSubStatus,\n type WriteSubStatus,\n} from \"./transport-internal\";\nimport type {\n CrdtAdapter,\n CrdtUpdateBatch,\n JsonObject,\n MaybePromise,\n Result,\n StreamsCrdtLike,\n StreamsCrdtOptions,\n TransportCreateStreamSuccess,\n TransportDeleteStreamSuccess,\n TransportError,\n TransportJoinParams,\n TransportRoomStatus,\n TransportSnapshotUploadSuccess,\n TransportSubscription,\n TransportSyncSuccess,\n WriteOnlyAppendResult,\n} from \"./types\";\n\nconst MIN_RECONNECT_DELAY_MS = 1_000;\nconst MIN_SUCCESSFUL_SSE_DURATION_MS = 5_000;\n\ntype ProducerAck = {\n readonly epoch: number;\n readonly seq: number;\n};\n\ntype TentativeLocalAppend<TVersion extends JsonObject> = {\n readonly cursor: RemoteCursor<TVersion>;\n readonly producerAck: ProducerAck;\n};\n\ntype FrozenLocalAppendSource = \"direct\" | \"pending\" | \"write-only\";\n\ntype FrozenLocalAppend<TVersion extends JsonObject> = {\n readonly source: FrozenLocalAppendSource;\n readonly baseCursor: RemoteCursor<TVersion>;\n readonly batch: CrdtUpdateBatch<TVersion>;\n readonly pendingCount?: number;\n readonly wireUpdates: readonly Uint8Array[];\n producerSession: ProducerSession;\n acknowledged?: TentativeLocalAppend<TVersion>;\n};\n\ntype DurableLocalAppend<TVersion extends JsonObject> = {\n readonly cursor: RemoteCursor<TVersion>;\n readonly pendingCount?: number;\n readonly streamCursor?: string;\n};\n\nfunction reconnectDelayWithFloor(\n attempt: number,\n config: ReconnectConfig,\n): number {\n return Math.max(computeRetryDelay(attempt, config), MIN_RECONNECT_DELAY_MS);\n}\n\nfunction fastSseCloseDelay(startedAtMs: number): number {\n const elapsedMs = Math.max(0, Date.now() - startedAtMs);\n if (elapsedMs >= MIN_SUCCESSFUL_SSE_DURATION_MS) {\n return 0;\n }\n return Math.max(\n MIN_RECONNECT_DELAY_MS,\n MIN_SUCCESSFUL_SSE_DURATION_MS - elapsedMs,\n );\n}\n\n/**\n * Durable Streams transport runtime bound to one stream URL and one local CRDT adapter.\n */\nexport class StreamsCrdt<\n TVersion extends JsonObject,\n> implements StreamsCrdtLike<TVersion> {\n readonly streamUrl: string;\n readonly adapter: CrdtAdapter<TVersion>;\n readonly remoteCursorStore: RemoteCursorStore<TVersion>;\n\n private readonly createStreamIfMissing: boolean;\n private readonly client: StreamsTransportClient<TVersion>;\n private readonly cursorManager: TransportCursorManager<TVersion>;\n private readonly volatileCursorManager: TransportCursorManager<TVersion>;\n private readonly decodeSnapshot?: (\n snapshot: Uint8Array,\n ) => MaybePromise<Uint8Array>;\n private readonly encodeSnapshot?: (\n snapshot: Uint8Array,\n ) => MaybePromise<Uint8Array>;\n private readonly decodeUpdateItems?: (\n items: readonly Uint8Array[],\n ) => MaybePromise<Uint8Array[]>;\n private readonly encodeUpdateItems?: (\n updates: readonly Uint8Array[],\n ) => Promise<readonly Uint8Array[]>;\n private readonly measureUpdateItemsByteLength?: (\n updates: readonly Uint8Array[],\n ) => MaybePromise<number>;\n private readonly reconnectConfig: ReconnectConfig;\n private readonly debug: boolean;\n private readonly beforeRemoteCursorSave?: BeforeRemoteCursorSaveHook<TVersion>;\n private readonly snapshotManager: TransportSnapshotManager<TVersion>;\n private readonly producerId = createProducerId();\n private producerEpoch = 0;\n private nextProducerSeq = 0;\n // Local append invariant:\n // - Before an append request can leave the client, freeze the exact logical\n // batch, encoded wire items, pending queue count, and producer session.\n // - Retry that frozen batch until the server explicitly rejects the session\n // as not appended (seq-gap/stale-epoch), or until the append is locally\n // finalized. Never merge newer pending updates into the same producer seq.\n // - Advance producer seq only after the local durability boundary has\n // completed. In read-capable mode that boundary includes catching up from\n // the pre-append offset and saving that cursor, not saving the append tail.\n // An acknowledged-but-not-finalized append is replayed by finalizing this\n // frozen record, not by sending a new batch.\n // - If a write-only append is abandoned while entering read-capable mode,\n // fence that maybe-sent producer session by moving to the next epoch.\n private frozenLocalAppend?: FrozenLocalAppend<TVersion>;\n private committedPendingCount?: number;\n private committedLocalStreamCursor?: string;\n private readonly pendingWriteOnly = new Deque<CrdtUpdateBatch<TVersion>>();\n private writeOnlyCursor: RemoteCursor<TVersion>;\n private unsubscribeWriteOnlyLocal?: () => void;\n private writeOnlyDisabledReason?: string;\n private joinState?: JoinState<TVersion>;\n private opQueue: Promise<void> = Promise.resolve();\n\n /**\n * Internal catch-up result used when callers need to handle 410 by\n * preserving local state before rebuilding through bootstrap.\n */\n private static readonly GONE_CATCHUP = { kind: \"gone\" as const };\n\n /**\n * Creates a transport for one stream URL and one local adapter instance.\n */\n constructor(options: StreamsCrdtOptions<TVersion>) {\n this.streamUrl = options.streamUrl;\n this.adapter = options.adapter;\n const authProvider = normalizeAuthProvider(options.auth);\n this.createStreamIfMissing = options.createStreamIfMissing ?? false;\n const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.remoteCursorStore =\n options.remoteCursorStore ?? new InMemoryRemoteCursorStore<TVersion>();\n this.reconnectConfig = {\n ...DEFAULT_RECONNECT_CONFIG,\n ...options.reconnectConfig,\n };\n this.debug = options.debug ?? false;\n this.beforeRemoteCursorSave = composeBeforeRemoteCursorSaveHooks(\n options.beforeRemoteCursorSave,\n ...(options.beforeRemoteCursorSaveHooks ?? []),\n );\n const snapshotCodec = normalizeSnapshotCodec(options.snapshotCodec);\n if (options.e2ee != null && options.payloadProtection != null) {\n throw new Error(\n \"Specify only one of e2ee or payloadProtection\",\n );\n }\n const payloadProtectionOption = options.e2ee ?? options.payloadProtection;\n const payloadProtectionLabel =\n options.e2ee != null ? \"e2ee\" : \"payloadProtection\";\n const payloadProtection = normalizePayloadProtection(\n payloadProtectionOption,\n payloadProtectionLabel,\n );\n if (\n payloadProtection?.writePolicy === \"plaintext\" &&\n options.snapshotUpload != null &&\n options.snapshotUpload.enabled !== false\n ) {\n throw new Error(\n `snapshotUpload is not supported when ${payloadProtectionLabel}.writePolicy is plaintext`,\n );\n }\n const snapshotUploadConfig = normalizeSnapshotUploadConfig(\n options.snapshotUpload,\n );\n this.decodeSnapshot = this.composeSnapshotDecoder(\n payloadProtection,\n snapshotCodec?.decompress,\n );\n this.encodeSnapshot = this.composeSnapshotEncoder(\n payloadProtection,\n snapshotCodec?.compress,\n );\n this.decodeUpdateItems =\n payloadProtection == null\n ? undefined\n : (items) => payloadProtection.decodeUpdateItems(items);\n this.encodeUpdateItems =\n payloadProtection == null\n ? undefined\n : (updates) => payloadProtection.encodeUpdateItems(updates);\n this.measureUpdateItemsByteLength =\n payloadProtection == null\n ? undefined\n : (updates) => payloadProtection.measureEncodedUpdateItemsByteLength(updates);\n this.client = new StreamsTransportClient({\n streamUrl: this.streamUrl,\n adapter: this.adapter,\n authProvider,\n fetchImpl,\n reconnectConfig: this.reconnectConfig,\n });\n this.cursorManager = this.createCursorManager(\n this.adapter,\n (cursor, source) => this.saveRemoteCursor(cursor, source),\n );\n this.volatileCursorManager = this.createCursorManager(\n this.adapter,\n async (cursor) => cursor,\n );\n this.snapshotManager = new TransportSnapshotManager({\n adapter: this.adapter,\n config: snapshotUploadConfig,\n encodeSnapshot: this.encodeSnapshot,\n readSnapshotOffsetValue: () => this.client.readSnapshotOffsetValue(),\n putSnapshotAt: (snapshotOffset, snapshot) =>\n this.client.putSnapshotAt(snapshotOffset, snapshot),\n enqueueExclusive: (task) => this.enqueueExclusive(task),\n logDebug: (message, detail) => this.logDebug(message, detail),\n logError: (message, error, detail) =>\n this.logError(message, error, detail),\n isCurrentState: (state) => this.joinState === state,\n isCursorComplete: (cursor) => this.cursorManager.isCursorComplete(cursor),\n });\n this.writeOnlyCursor = this.createInitialCursor();\n this.unsubscribeWriteOnlyLocal = this.adapter.subscribeLocalUpdates((batch) => {\n if (this.writeOnlyDisabledReason != null) {\n return;\n }\n this.pendingWriteOnly.pushBack(batch);\n });\n }\n\n private composeSnapshotDecoder(\n payloadProtection: PayloadProtectionRuntime | undefined,\n decompressSnapshot: ((snapshot: Uint8Array) => MaybePromise<Uint8Array>) | undefined,\n ): ((snapshot: Uint8Array) => MaybePromise<Uint8Array>) | undefined {\n if (payloadProtection == null && decompressSnapshot == null) {\n return undefined;\n }\n return async (snapshot) => {\n const clearSnapshot =\n payloadProtection == null\n ? snapshot\n : await payloadProtection.decodeSnapshot(snapshot);\n return decompressSnapshot == null\n ? clearSnapshot\n : await decompressSnapshot(clearSnapshot);\n };\n }\n\n private composeSnapshotEncoder(\n payloadProtection: PayloadProtectionRuntime | undefined,\n compressSnapshot: ((snapshot: Uint8Array) => MaybePromise<Uint8Array>) | undefined,\n ): ((snapshot: Uint8Array) => MaybePromise<Uint8Array>) | undefined {\n if (payloadProtection == null && compressSnapshot == null) {\n return undefined;\n }\n return async (snapshot) => {\n const compressedSnapshot =\n compressSnapshot == null\n ? snapshot\n : await compressSnapshot(snapshot);\n return payloadProtection == null\n ? compressedSnapshot\n : await payloadProtection.encodeSnapshot(compressedSnapshot);\n };\n }\n\n /**\n * Stops any active live join and releases transport-owned state.\n */\n async close(): Promise<void> {\n this.disableWriteOnlyMode(\"transport closed\");\n const state = this.joinState;\n if (state != null) {\n await this.disposeJoinState(state);\n }\n }\n\n /**\n * Resets the retry counter and immediately attempts to reconnect.\n *\n * Use this after device wake or network recovery to skip any pending\n * backoff delay. No-op when the room is already fully healthy (both\n * read and write sub-statuses are \"ok\") or no join is active.\n *\n * If the room reached `\"disconnected\"` or `\"error\"` status, this\n * restarts the affected loop(s).\n */\n rejoin(): void {\n const state = this.joinState;\n if (state == null || state.closed) {\n return;\n }\n\n // Handle failed initial sync recovery: restart the entire startJoin flow.\n if (state.initialSyncFailed) {\n state.initialSyncFailed = false;\n state.lastError = undefined;\n state.retryAttempt = 0;\n state.retrySleepController.abort();\n state.retrySleepController = new AbortController();\n state.readSubStatus = \"connecting\";\n state.writeSubStatus = \"ok\";\n this.recomputeJoinStatus(state);\n state.started = this.startJoin(state);\n // If startJoin fails again, decide based on retryability:\n // retryable → mark for another rejoin(); non-retryable → dispose.\n state.started.catch((error) => {\n if (state.closed) {\n return;\n }\n const transportError = toTransportError(error);\n if (transportError.retryable) {\n state.initialSyncFailed = true;\n state.lastError = transportError;\n } else {\n void this.disposeJoinState(state);\n }\n });\n return;\n }\n\n // Both loops are healthy — nothing to do.\n // Also no-op when initial sync is in progress (\"connecting\") to avoid\n // aborting an in-flight startJoin() from a prior rejoin() call.\n if (\n (state.readSubStatus === \"ok\" || state.readSubStatus === \"connecting\") &&\n state.writeSubStatus === \"ok\"\n ) {\n return;\n }\n state.retryAttempt = 0;\n state.retrySleepController.abort();\n state.retrySleepController = new AbortController();\n\n // Abort the current read request so the loop restarts immediately.\n const previousController = state.requestController;\n state.requestController = new AbortController();\n previousController?.abort();\n\n const needsReadRestart =\n state.readSubStatus === \"disconnected\" ||\n state.readSubStatus === \"error\";\n if (needsReadRestart) {\n this.setReadSubStatus(state, \"reconnecting\");\n void this.runReadLoop(state);\n }\n // For \"ok\" and \"reconnecting\", aborting the request controller\n // causes the current read loop iteration to restart.\n\n if (state.writeSubStatus === \"error\") {\n state.writeSubStatus =\n state.pendingLocal.length > 0 ? \"reconnecting\" : \"ok\";\n this.recomputeJoinStatus(state);\n if (state.pendingLocal.length > 0) {\n void this.flushPendingLocal(state);\n }\n }\n }\n\n /**\n * Reads the latest remote snapshot offset, if the server exposes one.\n */\n async getSnapshotOffset(): Promise<Result<string | null, TransportError>> {\n try {\n return {\n ok: true,\n value: await this.client.readSnapshotOffsetValue(),\n };\n } catch (error) {\n return { ok: false, error: toTransportError(error) };\n }\n }\n\n /**\n * Uploads a full adapter snapshot at the current remote tail.\n *\n * @internal Intended for end-to-end tests that must deterministically create\n * a remote snapshot without waiting for the best-effort join() debounce.\n */\n async uploadSnapshotForTesting(): Promise<\n Result<TransportSnapshotUploadSuccess, TransportError>\n > {\n try {\n await this.adapter.flushLocalExports?.();\n const cursor = await this.syncOnce();\n const rawSnapshot = this.adapter.exportSnapshot();\n const snapshot =\n this.encodeSnapshot == null\n ? rawSnapshot\n : await this.encodeSnapshot(rawSnapshot);\n await this.client.putSnapshotAt(cursor.nextOffset, snapshot);\n return {\n ok: true,\n value: {\n snapshotOffset: cursor.nextOffset,\n snapshotByteLength: snapshot.byteLength,\n },\n };\n } catch (error) {\n return { ok: false, error: toTransportError(error) };\n }\n }\n\n /**\n * Explicitly creates the target stream.\n */\n async createStream(): Promise<\n Result<TransportCreateStreamSuccess, TransportError>\n > {\n try {\n const created = await this.client.createStreamRequest();\n return { ok: true, value: { created } };\n } catch (error) {\n return { ok: false, error: toTransportError(error) };\n }\n }\n\n /**\n * Deletes the target stream and clears persisted replay state when supported by the cursor store.\n */\n async deleteStream(): Promise<\n Result<TransportDeleteStreamSuccess, TransportError>\n > {\n try {\n await this.close();\n const deleted = await this.enqueueExclusive(async () => {\n const deleted = await this.client.deleteStreamRequest();\n await this.remoteCursorStore.delete?.(this.streamUrl);\n this.producerEpoch = 0;\n this.nextProducerSeq = 0;\n this.frozenLocalAppend = undefined;\n this.committedPendingCount = undefined;\n this.committedLocalStreamCursor = undefined;\n return deleted;\n });\n return { ok: true, value: { deleted } };\n } catch (error) {\n return { ok: false, error: toTransportError(error) };\n }\n }\n\n /**\n * Runs one bootstrap/catch-up cycle without entering live mode.\n */\n async sync(): Promise<\n Result<TransportSyncSuccess<TVersion>, TransportError>\n > {\n try {\n this.disableWriteOnlyMode(\n \"appendWriteOnly cannot be used after sync() on the same transport\",\n );\n const cursor = await this.syncOnce();\n return { ok: true, value: { cursor } };\n } catch (error) {\n return { ok: false, error: toTransportError(error) };\n }\n }\n\n /**\n * Appends local CRDT updates without reading the stream first.\n *\n * Use this for write-only actors whose auth token may POST to the stream but\n * may not bootstrap, catch up, or join live reads. The method does not save a\n * RemoteCursorStore cursor and does not apply remote updates.\n */\n async appendWriteOnly(): Promise<\n Result<WriteOnlyAppendResult<TVersion>, TransportError>\n > {\n return await this.enqueueExclusive(async () => {\n try {\n if (this.writeOnlyDisabledReason != null) {\n throw new Error(this.writeOnlyDisabledReason);\n }\n await this.adapter.flushLocalExports?.();\n const drained = await this.drainPendingWriteOnlyBatch();\n if (drained == null) {\n return {\n ok: true,\n value: {\n appended: false,\n localVersion: this.writeOnlyCursor.serverLowerBoundVersion,\n },\n };\n }\n\n const appended = await this.appendLocalBatchRemote(\n this.writeOnlyCursor,\n drained.batch,\n drained.count,\n \"write-only\",\n );\n const committedCount = appended.pendingCount ?? drained.count;\n for (let i = 0; i < committedCount; i += 1) {\n this.pendingWriteOnly.popFront();\n }\n this.writeOnlyCursor = appended.cursor;\n this.commitProducerAck(appended.producerAck);\n return {\n ok: true,\n value: {\n appended: true,\n nextOffset: appended.cursor.nextOffset,\n localVersion: appended.cursor.serverLowerBoundVersion,\n },\n };\n } catch (error) {\n return { ok: false, error: toTransportError(error) };\n }\n });\n }\n\n /**\n * Replays remote state, flushes queued local updates, then enters live read mode.\n */\n async join(\n params?: TransportJoinParams,\n ): Promise<Result<TransportSubscription, TransportError>> {\n this.disableWriteOnlyMode(\n \"appendWriteOnly cannot be used after join() on the same transport\",\n );\n const existing = this.joinState;\n if (existing != null) {\n // If initial sync failed but state was kept alive, return a subscription\n // immediately so callers can observe the error status and use rejoin().\n if (existing.initialSyncFailed && !existing.closed) {\n return { ok: true, value: this.createSubscription(existing, params) };\n }\n try {\n await existing.started;\n return { ok: true, value: this.createSubscription(existing, params) };\n } catch (error) {\n return { ok: false, error: toTransportError(error) };\n }\n }\n\n const initialCursor = createInitialRemoteCursor<TVersion>({\n streamUrl: this.streamUrl,\n nextOffset: \"-1\",\n serverLowerBoundVersion: this.adapter.emptyVersion(),\n });\n const state = createTransportJoinState(initialCursor);\n // Register the status listener early so it observes the \"connecting\" →\n // \"error\" transition if initial sync fails.\n const statusListener = params?.onStatusChange;\n if (statusListener != null) {\n state.statusListeners.add(statusListener);\n }\n state.started = this.startJoin(state);\n this.joinState = state;\n try {\n await state.started;\n return { ok: true, value: this.createSubscription(state, params) };\n } catch (error) {\n const transportError = toTransportError(error);\n // Keep state alive on retryable failure so rejoin() can recover.\n if (transportError.retryable && !state.closed) {\n state.initialSyncFailed = true;\n state.lastError = transportError;\n // Clean up the early status listener since no subscription is\n // returned. Without this, repeated failed join() calls would\n // accumulate stale callbacks.\n if (statusListener != null) {\n state.statusListeners.delete(statusListener);\n }\n return { ok: false, error: transportError };\n }\n await this.disposeJoinState(state);\n return { ok: false, error: transportError };\n }\n }\n\n private createSubscription(\n state: JoinState<TVersion>,\n params?: TransportJoinParams,\n ): TransportSubscription {\n state.refCount += 1;\n const statusListener = params?.onStatusChange;\n if (statusListener != null) {\n // The listener may already have been registered in join() before\n // startJoin() was awaited. Only emit the initial status if this\n // is a fresh registration to avoid a duplicate \"joined\" callback.\n const alreadyRegistered = state.statusListeners.has(statusListener);\n if (!alreadyRegistered) {\n state.statusListeners.add(statusListener);\n statusListener(state.status as TransportRoomStatus);\n }\n }\n let unsubscribed = false;\n return {\n unsubscribe: () => {\n if (unsubscribed) {\n return;\n }\n unsubscribed = true;\n if (statusListener != null) {\n state.statusListeners.delete(statusListener);\n }\n state.refCount = Math.max(0, state.refCount - 1);\n if (state.refCount !== 0 || state.closed) {\n return;\n }\n void this.disposeJoinState(state);\n },\n get connected() {\n return state.status === \"joined\";\n },\n get status(): TransportRoomStatus {\n // Map internal \"connecting\" to \"reconnecting\" for the public API.\n // The internal distinction exists for computeOverallStatus, but\n // callers should see a valid TransportRoomStatus value.\n return state.status === \"connecting\"\n ? \"reconnecting\"\n : state.status;\n },\n get lastError() {\n return state.lastError;\n },\n onStatusChange: (listener) => {\n state.statusListeners.add(listener);\n listener(state.status as TransportRoomStatus);\n return () => {\n state.statusListeners.delete(listener);\n };\n },\n waitUntilSynced: async () => {\n // Reject immediately if initial sync failed and has not recovered.\n if (state.initialSyncFailed) {\n const msg = state.lastError?.code\n ? `initial sync failed: ${state.lastError.code}`\n : \"initial sync failed\";\n throw new Error(msg);\n }\n // If initial sync is still in progress (e.g. rejoin() just\n // restarted startJoin()), wait for it before checking the\n // pending queue. Local edits are not moved into pendingLocal\n // until startJoin() exports them. Wrap to produce a structured\n // error consistent with the initialSyncFailed path above.\n if (state.readSubStatus === \"connecting\") {\n try {\n await state.started;\n } catch (error) {\n const te = toTransportError(error);\n throw new Error(\n te.code\n ? `initial sync failed: ${te.code}`\n : \"initial sync failed\",\n );\n }\n }\n // Reject immediately if the write loop has permanently failed.\n // Items remain in pendingLocal but nobody will flush them.\n if (state.writeSubStatus === \"error\") {\n const msg = state.lastError?.code\n ? `local append failed: ${state.lastError.code}`\n : \"local append failed\";\n throw new Error(msg);\n }\n // Let adapters with async export queues (e.g. Flock) drain\n // before we snapshot the pending queue length.\n await this.adapter.flushLocalExports?.();\n if (state.closed) {\n throw new Error(\"subscription closed\");\n }\n // drainPendingBatch reads from pendingLocal without popping;\n // items are only removed after a successful append. So\n // pendingLocal.length includes any in-flight batch.\n const remaining = state.pendingLocal.length;\n if (remaining === 0) {\n return;\n }\n return new Promise<void>((resolve, reject) => {\n state.syncWaiters.push({ remaining, resolve, reject });\n });\n },\n };\n }\n\n private async disposeJoinState(state: JoinState<TVersion>): Promise<void> {\n await disposeTransportJoinState(state, {\n snapshotManager: this.snapshotManager,\n onDisposed: () => {\n if (this.joinState === state) {\n this.joinState = undefined;\n }\n },\n });\n }\n\n private async startJoin(state: JoinState<TVersion>): Promise<void> {\n try {\n state.requestController ??= new AbortController();\n const joined = await this.performInitialJoinSync(state.liveMode);\n state.cursor = joined.cursor;\n state.streamCursor = joined.streamCursor;\n state.liveMode = joined.liveMode;\n const pendingLocal = this.exportUpdates(\n joined.localExportRefVersion,\n );\n const hasQueuedPreSubscribeBatch = pendingLocal.batch != null;\n if (pendingLocal.batch != null) {\n state.pendingLocal.pushBack(pendingLocal.batch);\n }\n if (state.closed) {\n return;\n }\n state.unsubscribeLocal = this.adapter.subscribeLocalUpdates((batch) => {\n state.pendingLocal.pushBack(batch);\n void this.flushPendingLocal(state);\n });\n if (hasQueuedPreSubscribeBatch) {\n await this.flushPendingLocal(state);\n if (state.status === \"error\" || state.status === \"disconnected\") {\n throw new Error(\"join local sync failed before subscription became ready\");\n }\n }\n if (state.closed) {\n return;\n }\n state.lastError = undefined;\n this.setReadSubStatus(state, \"ok\");\n this.setWriteSubStatus(state, \"ok\");\n void this.runReadLoop(state);\n } catch (error) {\n if (state.closed) {\n return;\n }\n this.snapshotManager.dispose(state);\n state.lastError = toTransportError(error);\n this.logError(\"join first sync failed\", error, {\n streamUrl: this.streamUrl,\n });\n state.readSubStatus = \"error\";\n state.writeSubStatus = \"error\";\n this.recomputeJoinStatus(state);\n throw error;\n }\n }\n\n private async syncOnce(): Promise<RemoteCursor<TVersion>> {\n let cursor = (await this.finalizeDirectLocalAppendIfNeeded(\n await this.resolveInitialRemoteState(),\n )).cursor;\n const localBatch = this.exportUpdates(\n cursor.serverLowerBoundVersion,\n );\n if (localBatch.batch != null) {\n cursor = (await this.appendLocalBatchDurably(\n cursor,\n localBatch.batch,\n )).cursor;\n }\n return cursor;\n }\n\n private async performInitialJoinSync(\n preferredLiveMode: LiveReadMode,\n ): Promise<JoinSyncResult<TVersion>> {\n const initial = await this.finalizeDirectLocalAppendIfNeeded(\n await this.resolveInitialRemoteState(),\n );\n let cursor = initial.cursor;\n let localExportRefVersion = cursor.serverLowerBoundVersion;\n let streamCursor = initial.streamCursor;\n let liveMode = preferredLiveMode;\n\n const exportedLocal = this.exportUpdates(localExportRefVersion);\n localExportRefVersion = exportedLocal.nextRefVersion;\n if (exportedLocal.batch != null) {\n const committed = await this.appendLocalBatchDurably(\n cursor,\n exportedLocal.batch,\n undefined,\n \"direct\",\n streamCursor,\n );\n cursor = committed.cursor;\n streamCursor = committed.streamCursor;\n }\n localExportRefVersion = cursor.serverLowerBoundVersion;\n\n return {\n cursor,\n streamCursor,\n liveMode,\n localExportRefVersion,\n };\n }\n\n private createInitialCursor(): RemoteCursor<TVersion> {\n return createInitialRemoteCursor<TVersion>({\n streamUrl: this.streamUrl,\n nextOffset: \"-1\",\n serverLowerBoundVersion: this.adapter.emptyVersion(),\n });\n }\n\n private async resolveInitialRemoteState(): Promise<CursorSyncResult<TVersion>> {\n const loaded = await this.remoteCursorStore.load(this.streamUrl);\n const cursor = loaded ?? this.createInitialCursor();\n\n try {\n // When a cursor already exists, transport resumes from `nextOffset`\n // instead of bootstrapping. That requires the caller to have already\n // restored the matching local CRDT state for `serverLowerBoundVersion`.\n if (loaded == null) {\n return await this.bootstrapState(cursor);\n }\n const caughtUp = await this.catchupOrGone(cursor, {\n goneStrategy: \"return-gone\",\n });\n if (\"kind\" in caughtUp) {\n return await this.recoverFromGoneWithLocalBootstrap(cursor);\n }\n return caughtUp;\n } catch (error) {\n if (!isStreamNotFoundError(error) || !this.createStreamIfMissing) {\n throw error;\n }\n await this.ensureStreamExists();\n return await this.bootstrapState(this.createInitialCursor());\n }\n }\n\n private async bootstrapState(\n cursor: RemoteCursor<TVersion>,\n options: {\n readonly cursorManager?: TransportCursorManager<TVersion>;\n } = {},\n ): Promise<CursorSyncResult<TVersion>> {\n const cursorManager = options.cursorManager ?? this.cursorManager;\n const result = await this.client.bootstrap();\n let saved = await cursorManager.applyBootstrapPayload(\n cursor,\n result.snapshot,\n result.updates,\n result.nextOffset,\n result.upToDate,\n );\n let bootstrapCursor = result.streamCursor;\n if (!result.upToDate) {\n const caughtUp = await this.catchupOrGone(saved, {\n streamCursor: bootstrapCursor,\n cursorManager,\n });\n if (\"kind\" in caughtUp) {\n throw new Error(\"bootstrap catch-up unexpectedly returned gone\");\n }\n saved = caughtUp.cursor;\n bootstrapCursor = caughtUp.streamCursor;\n }\n return { cursor: saved, streamCursor: bootstrapCursor };\n }\n\n private async catchup(\n cursor: RemoteCursor<TVersion>,\n live?: \"long-poll\",\n streamCursor?: string,\n onRemoteVersionApplied?: (\n previousVersion: TVersion,\n nextVersion: TVersion,\n ) => MaybePromise<void>,\n ): Promise<CursorSyncResult<TVersion>> {\n const result = await this.catchupOrGone(cursor, {\n live,\n streamCursor,\n onRemoteVersionApplied,\n });\n if (\"kind\" in result) {\n throw new Error(\"catchup unexpectedly returned gone\");\n }\n return result;\n }\n\n private async catchupOrGone(\n cursor: RemoteCursor<TVersion>,\n options: {\n readonly live?: \"long-poll\";\n readonly streamCursor?: string;\n readonly onRemoteVersionApplied?: (\n previousVersion: TVersion,\n nextVersion: TVersion,\n ) => MaybePromise<void>;\n readonly goneStrategy?: \"bootstrap\" | \"return-gone\";\n readonly cursorManager?: TransportCursorManager<TVersion>;\n } = {},\n ): Promise<CursorSyncResult<TVersion> | typeof StreamsCrdt.GONE_CATCHUP> {\n let current = cursor;\n let currentStreamCursor = options.streamCursor;\n const goneStrategy = options.goneStrategy ?? \"bootstrap\";\n const cursorManager = options.cursorManager ?? this.cursorManager;\n while (true) {\n const requestOffset = current.nextOffset;\n const response = await this.client.readFromOffset(\n requestOffset,\n currentStreamCursor,\n options.live,\n );\n if (response.kind === \"gone\") {\n if (goneStrategy === \"return-gone\") {\n return StreamsCrdt.GONE_CATCHUP;\n }\n return await this.bootstrapState(\n {\n ...current,\n nextOffset: \"-1\",\n serverLowerBoundVersion: this.adapter.emptyVersion(),\n },\n { cursorManager },\n );\n }\n const result = response.value;\n if (isReadNoProgress(requestOffset, result)) {\n throw new Error(\n `catch-up made no progress at offset ${requestOffset}; server returned empty updates and unchanged next offset with up_to_date=false`,\n );\n }\n try {\n current = await cursorManager.applyCatchupResult(\n current,\n result,\n options.onRemoteVersionApplied,\n );\n } catch (error) {\n if (error instanceof CrdtApplyIncompleteError && result.upToDate) {\n return await this.bootstrapState(\n {\n ...current,\n nextOffset: \"-1\",\n serverLowerBoundVersion: this.adapter.emptyVersion(),\n },\n { cursorManager },\n );\n }\n throw error;\n }\n currentStreamCursor = result.streamCursor;\n if (result.upToDate) {\n return { cursor: current, streamCursor: currentStreamCursor };\n }\n }\n }\n\n private async recoverLiveIncompleteRemoteApply(\n state: JoinState<TVersion>,\n ): Promise<boolean> {\n try {\n const bootstrapped = await this.bootstrapState({\n ...state.cursor,\n nextOffset: \"-1\",\n serverLowerBoundVersion: this.adapter.emptyVersion(),\n });\n state.cursor = bootstrapped.cursor;\n state.streamCursor = bootstrapped.streamCursor;\n state.retryAttempt = 0;\n state.lastError = undefined;\n this.setReadSubStatus(state, \"ok\");\n if (state.pendingLocal.length > 0) {\n void this.flushPendingLocal(state);\n }\n return true;\n } catch (error) {\n const classified = classifyFetchError(error);\n this.logError(\"live incomplete apply recovery failed\", error, {\n streamUrl: this.streamUrl,\n code: classified.code,\n });\n state.lastError = toTransportError(error);\n this.setReadSubStatus(\n state,\n classified.retriable ? \"disconnected\" : \"error\",\n );\n return false;\n }\n }\n\n private async appendLocalBatch(\n cursor: RemoteCursor<TVersion>,\n batch: CrdtUpdateBatch<TVersion>,\n pendingCount?: number,\n streamCursor?: string,\n ): Promise<RemoteCursor<TVersion>> {\n const source: FrozenLocalAppendSource =\n pendingCount == null ? \"direct\" : \"pending\";\n const committed = await this.appendLocalBatchDurably(\n cursor,\n batch,\n pendingCount,\n source,\n streamCursor,\n );\n this.committedPendingCount = committed.pendingCount;\n this.committedLocalStreamCursor = committed.streamCursor;\n return committed.cursor;\n }\n\n private async appendLocalBatchDurably(\n cursor: RemoteCursor<TVersion>,\n batch: CrdtUpdateBatch<TVersion>,\n pendingCount?: number,\n source: FrozenLocalAppendSource = pendingCount == null ? \"direct\" : \"pending\",\n streamCursor?: string,\n ): Promise<DurableLocalAppend<TVersion>> {\n const frozen = await this.getOrCreateFrozenLocalAppend(\n cursor,\n batch,\n pendingCount,\n source,\n );\n const appended = await this.appendFrozenLocalBatchRemote(frozen);\n const caughtUp = await this.catchupLocalAppend(frozen, streamCursor);\n this.commitProducerAck(appended.producerAck);\n return {\n cursor: caughtUp.cursor,\n pendingCount: frozen.pendingCount,\n streamCursor: caughtUp.streamCursor,\n };\n }\n\n private localAppendReadCursor(\n frozen: FrozenLocalAppend<TVersion>,\n ): RemoteCursor<TVersion> {\n return {\n ...frozen.baseCursor,\n serverLowerBoundVersion: this.adapter.mergeVersions(\n frozen.baseCursor.serverLowerBoundVersion,\n frozen.batch.version,\n ),\n };\n }\n\n private async catchupLocalAppend(\n frozen: FrozenLocalAppend<TVersion>,\n streamCursor?: string,\n ): Promise<CursorSyncResult<TVersion>> {\n const caughtUp = await this.catchupOrGone(\n this.localAppendReadCursor(frozen),\n {\n streamCursor,\n cursorManager: this.volatileCursorManager,\n },\n );\n if (\"kind\" in caughtUp) {\n throw new Error(\"local append catch-up unexpectedly returned gone\");\n }\n const saved = await this.saveRemoteCursor(caughtUp.cursor, \"local\");\n return {\n cursor: saved,\n streamCursor: caughtUp.streamCursor,\n };\n }\n\n private async appendLocalBatchRemote(\n cursor: RemoteCursor<TVersion>,\n batch: CrdtUpdateBatch<TVersion>,\n pendingCount?: number,\n source: FrozenLocalAppendSource = pendingCount == null ? \"direct\" : \"pending\",\n ): Promise<TentativeLocalAppend<TVersion> & { readonly pendingCount?: number }> {\n const frozen = await this.getOrCreateFrozenLocalAppend(\n cursor,\n batch,\n pendingCount,\n source,\n );\n const appended = await this.appendFrozenLocalBatchRemote(frozen);\n return {\n ...appended,\n pendingCount: frozen.pendingCount,\n };\n }\n\n private async getOrCreateFrozenLocalAppend(\n cursor: RemoteCursor<TVersion>,\n batch: CrdtUpdateBatch<TVersion>,\n pendingCount?: number,\n source: FrozenLocalAppendSource = pendingCount == null ? \"direct\" : \"pending\",\n ): Promise<FrozenLocalAppend<TVersion>> {\n const existing = this.frozenLocalAppend;\n if (existing != null) {\n if (existing.source !== source) {\n throw new Error(\n `${existing.source} local append must be finalized before ${source} append`,\n );\n }\n return existing;\n }\n\n const wireUpdates =\n this.encodeUpdateItems == null\n ? batch.updates\n : await this.encodeUpdateItems(batch.updates);\n const frozen: FrozenLocalAppend<TVersion> = {\n source,\n baseCursor: cursor,\n batch,\n pendingCount,\n wireUpdates,\n producerSession: this.createCurrentProducerSession(),\n };\n this.frozenLocalAppend = frozen;\n return frozen;\n }\n\n private async appendFrozenLocalBatchRemote(\n frozen: FrozenLocalAppend<TVersion>,\n ): Promise<TentativeLocalAppend<TVersion>> {\n if (frozen.acknowledged != null) {\n return frozen.acknowledged;\n }\n\n for (let attempt = 0; attempt < 3; attempt += 1) {\n let result: {\n readonly kind: \"ok\";\n readonly ackEpoch: number;\n readonly ackSeq: number;\n readonly nextOffset: string;\n } | {\n readonly kind: \"stale-epoch\";\n readonly serverEpoch: number;\n } | {\n readonly kind: \"seq-gap\";\n readonly expectedSeq: number;\n };\n try {\n result = await this.client.appendUpdates(\n frozen.wireUpdates,\n frozen.producerSession,\n );\n } catch (error) {\n // specs/ds-protocol.md: In a multi-node cluster the stream creation\n // may not have replicated to the node that handles this append yet.\n // When `createStreamIfMissing` is enabled, re-create the stream\n // (tolerating 409 Conflict) and retry the same frozen append.\n if (isStreamNotFoundError(error) && this.createStreamIfMissing) {\n await this.ensureStreamExists();\n continue;\n }\n throw error;\n }\n if (result.kind === \"ok\") {\n // This cursor is only a producer-ack fact. Read-capable paths must not\n // persist it because there may be unread remote updates between\n // `frozen.baseCursor.nextOffset` and the server append tail.\n const next: RemoteCursor<TVersion> = {\n ...frozen.baseCursor,\n nextOffset: result.nextOffset,\n serverLowerBoundVersion: this.adapter.mergeVersions(\n frozen.baseCursor.serverLowerBoundVersion,\n frozen.batch.version,\n ),\n };\n const appended: TentativeLocalAppend<TVersion> = {\n cursor: next,\n producerAck: {\n epoch: result.ackEpoch,\n seq: result.ackSeq,\n },\n };\n frozen.acknowledged = appended;\n return appended;\n }\n if (result.kind === \"seq-gap\") {\n frozen.producerSession = {\n ...frozen.producerSession,\n nextProducerSeq: result.expectedSeq,\n };\n continue;\n }\n frozen.producerSession = {\n ...frozen.producerSession,\n producerEpoch: result.serverEpoch + 1,\n nextProducerSeq: 0,\n };\n }\n throw new Error(\"append retry budget exhausted\");\n }\n\n private async finalizeDirectLocalAppendIfNeeded(\n state: CursorSyncResult<TVersion>,\n ): Promise<CursorSyncResult<TVersion>> {\n const frozen = this.frozenLocalAppend;\n if (frozen == null || frozen.pendingCount != null) {\n return state;\n }\n const appended = await this.appendFrozenLocalBatchRemote(frozen);\n const caughtUp = await this.catchupLocalAppend(frozen, state.streamCursor);\n this.commitProducerAck(appended.producerAck);\n return caughtUp;\n }\n\n private createCurrentProducerSession(): ProducerSession {\n return {\n producerId: this.producerId,\n producerEpoch: this.producerEpoch,\n nextProducerSeq: this.nextProducerSeq,\n };\n }\n\n private commitProducerAck(ack: ProducerAck): void {\n this.producerEpoch = ack.epoch;\n this.nextProducerSeq = ack.seq + 1;\n this.frozenLocalAppend = undefined;\n }\n\n private abandonFrozenLocalAppend(\n frozen: FrozenLocalAppend<TVersion>,\n ): void {\n if (this.frozenLocalAppend !== frozen) {\n return;\n }\n this.frozenLocalAppend = undefined;\n this.producerEpoch = Math.max(\n this.producerEpoch,\n frozen.producerSession.producerEpoch + 1,\n );\n this.nextProducerSeq = 0;\n }\n\n private consumeCommittedPendingCount(): number | undefined {\n const count = this.committedPendingCount;\n this.committedPendingCount = undefined;\n return count;\n }\n\n private consumeCommittedLocalStreamCursor(): string | undefined {\n const streamCursor = this.committedLocalStreamCursor;\n this.committedLocalStreamCursor = undefined;\n return streamCursor;\n }\n\n /**\n * Ensures the target stream exists, creating it if necessary.\n * Tolerates 409 Conflict (stream already created concurrently).\n */\n private async ensureStreamExists(): Promise<void> {\n try {\n await this.client.createStreamRequest();\n } catch (error) {\n if (!(error instanceof HttpError && error.status === 409)) {\n throw error;\n }\n }\n }\n\n private exportUpdates(fromVersion: TVersion): LocalExportResult<TVersion> {\n const exported = this.adapter.exportUpdates(fromVersion);\n if (exported == null) {\n return { nextRefVersion: fromVersion };\n }\n const nextRefVersion = this.adapter.mergeVersions(\n fromVersion,\n exported.version,\n );\n if (exported.updates.length === 0) {\n return { nextRefVersion };\n }\n return {\n batch: exported,\n nextRefVersion,\n };\n }\n\n private async applyBootstrapItems(\n cursor: RemoteCursor<TVersion>,\n items: readonly Uint8Array[],\n nextOffset: string,\n ): Promise<RemoteCursor<TVersion>> {\n return await this.cursorManager.applyBootstrapItems(\n cursor,\n items,\n nextOffset,\n );\n }\n\n private async applyRemoteItems(\n cursor: RemoteCursor<TVersion>,\n items: readonly Uint8Array[],\n nextOffset: string,\n ): Promise<RemoteCursor<TVersion>> {\n return await this.cursorManager.applyRemoteItems(cursor, items, nextOffset);\n }\n\n private createCursorManager(\n adapter: Pick<CrdtAdapter<TVersion>, \"applySnapshot\" | \"applyRemoteUpdates\">,\n saveRemoteCursor: (\n cursor: RemoteCursor<TVersion>,\n source: RemoteCursorSaveSource,\n ) => Promise<RemoteCursor<TVersion>>,\n ): TransportCursorManager<TVersion> {\n return new TransportCursorManager({\n adapter,\n decodeSnapshot: this.decodeSnapshot,\n decodeUpdateItems: this.decodeUpdateItems,\n saveRemoteCursor,\n });\n }\n\n private async createIsolatedCursorManager(): Promise<\n TransportCursorManager<TVersion> | null\n > {\n const isolated = await this.adapter.createIsolatedAdapter?.();\n if (isolated == null) {\n return null;\n }\n return this.createCursorManager(isolated, async (cursor) => cursor);\n }\n\n /**\n * Infers remote state via bootstrap with the given cursor manager,\n * exports and appends any local delta, then re-bootstraps to converge\n * persisted cursor and local CRDT state on server truth.\n */\n private async inferRemoteAndAppendLocal(\n bootstrapCursor: RemoteCursor<TVersion>,\n cursorManager: TransportCursorManager<TVersion>,\n ): Promise<CursorSyncResult<TVersion>> {\n const inferredRemote = await this.bootstrapState(bootstrapCursor, {\n cursorManager,\n });\n let pendingAppend: TentativeLocalAppend<TVersion> | undefined;\n if (this.frozenLocalAppend != null) {\n if (this.frozenLocalAppend.pendingCount != null) {\n throw new Error(\"pending local append must be finalized by pending flush\");\n }\n pendingAppend = await this.appendFrozenLocalBatchRemote(\n this.frozenLocalAppend,\n );\n } else {\n const localBatch = this.exportUpdates(\n inferredRemote.cursor.serverLowerBoundVersion,\n );\n if (localBatch.batch != null) {\n pendingAppend = await this.appendLocalBatchRemote(\n inferredRemote.cursor,\n localBatch.batch,\n );\n }\n }\n // The recovery append is not durable until the second bootstrap has saved\n // the cursor and restored local state to server truth. Keep the producer ack\n // tentative until then, otherwise a failed final bootstrap can replay the\n // same local recovery delta under a changed producer seq.\n const result = await this.bootstrapState(bootstrapCursor);\n if (pendingAppend != null) {\n this.commitProducerAck(pendingAppend.producerAck);\n }\n return result;\n }\n\n private async recoverFromGoneWithLocalBootstrap(\n cursor: RemoteCursor<TVersion>,\n ): Promise<CursorSyncResult<TVersion>> {\n const isolatedCursorManager = await this.createIsolatedCursorManager();\n const bootstrapCursor = {\n ...cursor,\n nextOffset: \"-1\",\n serverLowerBoundVersion: this.adapter.emptyVersion(),\n };\n\n if (isolatedCursorManager != null) {\n return await this.inferRemoteAndAppendLocal(\n bootstrapCursor,\n isolatedCursorManager,\n );\n }\n\n const preservedLocalSnapshot = this.adapter.exportSnapshot();\n let restoredLocalSnapshot = false;\n\n try {\n // specs/ds-client-state.md §4.2 / §6 and specs/ds-extensions.md\n // require 410 recovery to rebuild through bootstrap. Preserve local\n // cached state long enough to export a first-sync delta against the\n // inferred remote version, then bootstrap again so persisted cursor and\n // visible local CRDT state converge on the same server truth.\n const inferredRemote = await this.bootstrapState(bootstrapCursor, {\n cursorManager: this.volatileCursorManager,\n });\n await this.adapter.applySnapshot(preservedLocalSnapshot);\n restoredLocalSnapshot = true;\n\n let pendingAppend: TentativeLocalAppend<TVersion> | undefined;\n if (this.frozenLocalAppend != null) {\n if (this.frozenLocalAppend.pendingCount != null) {\n throw new Error(\"pending local append must be finalized by pending flush\");\n }\n pendingAppend = await this.appendFrozenLocalBatchRemote(\n this.frozenLocalAppend,\n );\n } else {\n const localBatch = this.exportUpdates(\n inferredRemote.cursor.serverLowerBoundVersion,\n );\n if (localBatch.batch != null) {\n pendingAppend = await this.appendLocalBatchRemote(\n inferredRemote.cursor,\n localBatch.batch,\n );\n }\n }\n // Same durability boundary as the isolated-adapter path above: the append\n // only becomes locally committed after the final bootstrap converges and\n // saves the cursor.\n const result = await this.bootstrapState(bootstrapCursor);\n if (pendingAppend != null) {\n this.commitProducerAck(pendingAppend.producerAck);\n }\n return result;\n } catch (error) {\n if (!restoredLocalSnapshot) {\n await this.adapter.applySnapshot(preservedLocalSnapshot);\n }\n throw error;\n }\n }\n\n private async runReadLoop(state: JoinState<TVersion>): Promise<void> {\n state.requestController ??= new AbortController();\n while (!state.closed) {\n const capturedController: AbortController | undefined = state.requestController;\n let inactivityTimer: ReturnType<typeof setTimeout> | null = null;\n let inactivityTriggered = false;\n\n const clearInactivity = (): void => {\n if (inactivityTimer != null) {\n clearTimeout(inactivityTimer);\n inactivityTimer = null;\n }\n };\n\n const resetInactivity = (): void => {\n clearInactivity();\n const timeoutMs = this.reconnectConfig.liveInactivityTimeoutMs;\n if (timeoutMs != null && timeoutMs > 0 && !state.closed) {\n inactivityTimer = setTimeout(() => {\n inactivityTriggered = true;\n capturedController?.abort();\n }, timeoutMs);\n }\n };\n\n try {\n if (state.liveMode === \"long-poll\") {\n const requestOffset = state.cursor.nextOffset;\n const response = await this.client.readFromOffset(\n requestOffset,\n state.streamCursor,\n \"long-poll\",\n capturedController?.signal,\n );\n if (response.kind === \"gone\") {\n const bootstrapped = await this.bootstrapState({\n ...state.cursor,\n nextOffset: \"-1\",\n serverLowerBoundVersion: this.adapter.emptyVersion(),\n });\n state.cursor = bootstrapped.cursor;\n state.streamCursor = bootstrapped.streamCursor;\n state.retryAttempt = 0;\n continue;\n }\n\n const result = response.value;\n if (isReadNoProgress(requestOffset, result)) {\n throw new Error(\n `catch-up made no progress at offset ${requestOffset}; server returned empty updates and unchanged next offset with up_to_date=false`,\n );\n }\n\n await this.enqueueExclusive(async () => {\n if (state.closed) {\n return;\n }\n state.cursor = await this.cursorManager.applyLiveResult(\n state.cursor,\n requestOffset,\n result,\n (previousVersion, nextVersion) => {\n this.snapshotManager.observeRemoteVersion(\n state,\n previousVersion,\n nextVersion,\n );\n },\n );\n if (result.streamCursor != null) {\n state.streamCursor = result.streamCursor;\n }\n });\n if (state.closed) {\n return;\n }\n state.retryAttempt = 0;\n this.setReadSubStatus(state, \"ok\");\n if (state.pendingLocal.length > 0) {\n void this.flushPendingLocal(state);\n }\n await sleep(0);\n continue;\n }\n\n const sseRequestOffset = state.cursor.nextOffset;\n const pendingRemote: Array<{\n updates: Uint8Array[];\n nextOffset: string;\n upToDate: boolean;\n streamCursor?: string;\n }> = [];\n let flushChain = Promise.resolve();\n\n resetInactivity();\n\n const sseStartedAtMs = Date.now();\n const sseResult = await this.client.readFromOffsetSseItems(\n sseRequestOffset,\n state.streamCursor,\n (updates, nextOffset, upToDate, streamCursor) => {\n if (capturedController?.signal.aborted) {\n return \"stop\";\n }\n resetInactivity();\n pendingRemote.push({ updates, nextOffset, upToDate, streamCursor });\n flushChain = flushChain.then(async () => {\n const events = pendingRemote.splice(0);\n if (\n events.length === 0 ||\n state.closed ||\n capturedController?.signal.aborted\n ) {\n return;\n }\n const allUpdates = events.flatMap((e) => e.updates);\n const lastEvent = events[events.length - 1]!;\n await this.enqueueExclusive(async () => {\n if (state.closed) {\n return;\n }\n state.cursor = await this.cursorManager.applyLiveResult(\n state.cursor,\n sseRequestOffset,\n {\n nextOffset: lastEvent.nextOffset,\n updates: allUpdates,\n upToDate: lastEvent.upToDate,\n },\n (previousVersion, nextVersion) => {\n this.snapshotManager.observeRemoteVersion(\n state,\n previousVersion,\n nextVersion,\n );\n },\n );\n for (let i = events.length - 1; i >= 0; i--) {\n if (events[i]!.streamCursor != null) {\n state.streamCursor = events[i]!.streamCursor!;\n break;\n }\n }\n });\n this.setReadSubStatus(state, \"ok\");\n if (state.pendingLocal.length > 0) {\n void this.flushPendingLocal(state);\n }\n }).catch((flushError) => {\n // If applyLiveResult or cursor save throws (e.g. CRDT import\n // error, E2EE decryption failure), abort the SSE connection so\n // the read loop can reconnect cleanly. Without this, the chain\n // stays rejected and all subsequent SSE events are silently\n // dropped until the connection closes naturally.\n //\n // Pass the original error as the abort reason so the outer\n // catch path can classify it properly (auth/protocol → \"error\",\n // network/timeout → reconnect) instead of treating it as a\n // benign rejoin/inactivity abort.\n this.logError(\"sse flush chain error\", flushError, {\n streamUrl: this.streamUrl,\n });\n capturedController?.abort(flushError);\n });\n },\n () => {\n resetInactivity();\n this.setReadSubStatus(state, \"ok\");\n },\n capturedController?.signal,\n resetInactivity,\n );\n clearInactivity();\n await flushChain;\n if (state.closed) {\n return;\n }\n if (sseResult.kind === \"ok\") {\n state.retryAttempt = 0;\n state.streamCursor = sseResult.latestCursor;\n const delay = fastSseCloseDelay(sseStartedAtMs);\n await sleep(delay, state.retrySleepController.signal);\n continue;\n }\n if (sseResult.kind === \"unsupported\") {\n state.liveMode = \"long-poll\";\n await sleep(0);\n continue;\n }\n if (sseResult.kind === \"not-found\") {\n const notFoundError = new HttpError(\"stream not found\", 404);\n this.logError(\n \"live read failed\",\n notFoundError,\n {\n streamUrl: this.streamUrl,\n code: \"stream_not_found\",\n },\n );\n state.lastError = toTransportError(notFoundError);\n this.setReadSubStatus(state, \"error\");\n return;\n }\n const bootstrapped = await this.bootstrapState({\n ...state.cursor,\n nextOffset: \"-1\",\n serverLowerBoundVersion: this.adapter.emptyVersion(),\n });\n state.cursor = bootstrapped.cursor;\n state.streamCursor = bootstrapped.streamCursor;\n state.retryAttempt = 0;\n } catch (error) {\n clearInactivity();\n if (state.closed) {\n return;\n }\n if (error instanceof CrdtApplyIncompleteError) {\n if (\n capturedController?.signal.aborted &&\n state.requestController === capturedController\n ) {\n state.requestController = new AbortController();\n }\n if (await this.recoverLiveIncompleteRemoteApply(state)) {\n continue;\n }\n return;\n }\n\n // Deliberate abort: rejoin(), SSE inactivity timeout, or\n // flush-chain processing error.\n if (capturedController?.signal.aborted) {\n if (inactivityTriggered) {\n // Inactivity timeout consumes the retry budget so that a\n // persistently-silent SSE endpoint eventually reaches\n // \"disconnected\" when maxAttempts is finite.\n if (state.requestController === capturedController) {\n state.requestController = new AbortController();\n }\n this.logDebug(\"sse inactivity timeout\", {\n streamUrl: this.streamUrl,\n });\n this.setReadSubStatus(state, \"reconnecting\");\n const maxAttempts = this.reconnectConfig.maxAttempts;\n if (state.retryAttempt >= maxAttempts) {\n this.logError(\"live read reconnect exhausted (inactivity)\", error, {\n streamUrl: this.streamUrl,\n attempts: state.retryAttempt,\n });\n state.lastError = {\n code: \"timeout\",\n retryable: true,\n phase: \"poll\",\n message: \"SSE inactivity timeout exhausted retry budget\",\n };\n this.setReadSubStatus(state, \"disconnected\");\n return;\n }\n state.retryAttempt += 1;\n const delay = reconnectDelayWithFloor(\n state.retryAttempt - 1,\n this.reconnectConfig,\n );\n await sleep(delay, state.retrySleepController.signal);\n continue;\n }\n // Flush-chain error: signal.reason carries the original error\n // from applyLiveResult / cursor save / E2EE decrypt. The abort\n // race in transport-client resolves with signal.reason, so\n // `error` and `abortReason` are often the same object. Classify\n // the reason (or fall back to error) so auth/protocol failures\n // surface as terminal \"error\" status instead of a silent\n // reconnect loop.\n const abortReason = capturedController.signal.reason;\n const flushError =\n abortReason instanceof Error ? abortReason : error;\n if (flushError instanceof CrdtApplyIncompleteError) {\n if (state.requestController === capturedController) {\n state.requestController = new AbortController();\n }\n if (await this.recoverLiveIncompleteRemoteApply(state)) {\n continue;\n }\n return;\n }\n if (\n flushError instanceof Error &&\n !(flushError instanceof DOMException &&\n flushError.name === \"AbortError\")\n ) {\n const flushClassified = classifyFetchError(flushError);\n if (\n !flushClassified.retriable ||\n isAuthOrProtocolError(flushError)\n ) {\n this.logError(\"live read failed (flush chain)\", flushError, {\n streamUrl: this.streamUrl,\n code: flushClassified.code,\n });\n state.lastError = toTransportError(flushError);\n this.setReadSubStatus(state, \"error\");\n return;\n }\n // Retriable flush error — fall through to reconnect.\n }\n\n // rejoin() abort or retriable flush — reset budget and restart.\n state.retryAttempt = 0;\n this.setReadSubStatus(state, \"reconnecting\");\n continue;\n }\n\n const classified = classifyFetchError(error);\n if (!classified.retriable || isAuthOrProtocolError(error)) {\n this.logError(\"live read failed\", error, {\n streamUrl: this.streamUrl,\n code: classified.code,\n });\n state.lastError = toTransportError(error);\n this.setReadSubStatus(state, \"error\");\n return;\n }\n this.setReadSubStatus(state, \"reconnecting\");\n const maxAttempts = this.reconnectConfig.maxAttempts;\n if (state.retryAttempt >= maxAttempts) {\n this.logError(\"live read reconnect exhausted\", error, {\n streamUrl: this.streamUrl,\n attempts: state.retryAttempt,\n });\n state.lastError = toTransportError(error);\n this.setReadSubStatus(state, \"disconnected\");\n return;\n }\n const delay = computeRetryDelay(\n state.retryAttempt,\n this.reconnectConfig,\n );\n state.retryAttempt += 1;\n await sleep(delay, state.retrySleepController.signal);\n }\n }\n }\n\n /** Maximum encoded size (bytes) for a single batched append. */\n private static readonly MAX_BATCH_BYTES = 256 * 1024;\n\n /**\n * Drain up to `MAX_BATCH_BYTES` worth of queued batches from the front of\n * `pendingLocal` and merge them into a single `CrdtUpdateBatch`.\n *\n * Returns `undefined` when the queue is empty.\n */\n private async drainQueuedBatch(\n pendingLocal:\n Pick<Deque<CrdtUpdateBatch<TVersion>>, \"peekFront\" | \"at\"> &\n { readonly length: number },\n ): Promise<{ batch: CrdtUpdateBatch<TVersion>; count: number } | undefined> {\n const first = pendingLocal.peekFront();\n if (first == null) {\n return undefined;\n }\n\n const maxBytes = StreamsCrdt.MAX_BATCH_BYTES;\n let mergedVersion = first.version;\n const allUpdates: Uint8Array[] = Array.from(first.updates);\n let count = 1;\n\n while (count < pendingLocal.length) {\n const next = pendingLocal.at(count)!;\n const candidateUpdates = [...allUpdates, ...next.updates];\n const candidateBytes = await this.measureAppendBodyByteLength(\n candidateUpdates,\n );\n if (candidateBytes > maxBytes) {\n break;\n }\n allUpdates.push(...next.updates);\n mergedVersion = this.adapter.mergeVersions(mergedVersion, next.version);\n count += 1;\n }\n\n if (count === 1) {\n return { batch: first, count: 1 };\n }\n return { batch: { updates: allUpdates, version: mergedVersion }, count };\n }\n\n private drainPendingBatch(\n state: JoinState<TVersion>,\n ): Promise<{ batch: CrdtUpdateBatch<TVersion>; count: number } | undefined> {\n return this.drainQueuedBatch(state.pendingLocal);\n }\n\n private drainPendingWriteOnlyBatch(): Promise<\n { batch: CrdtUpdateBatch<TVersion>; count: number } | undefined\n > {\n return this.drainQueuedBatch(this.pendingWriteOnly);\n }\n\n private async flushPendingLocal(state: JoinState<TVersion>): Promise<void> {\n if (state.flushingLocal || state.closed) {\n return;\n }\n if (!this.cursorManager.isCursorComplete(state.cursor)) {\n return;\n }\n state.flushingLocal = true;\n try {\n while (!state.closed) {\n const drained = await this.drainPendingBatch(state);\n if (drained == null) {\n return;\n }\n let attempts = 0;\n while (!state.closed) {\n try {\n state.cursor = await this.enqueueExclusive(async () =>\n this.appendLocalBatch(\n state.cursor,\n drained.batch,\n drained.count,\n state.streamCursor,\n )\n );\n const committedStreamCursor =\n this.consumeCommittedLocalStreamCursor();\n if (committedStreamCursor != null) {\n state.streamCursor = committedStreamCursor;\n }\n const committedCount =\n this.consumeCommittedPendingCount() ?? drained.count;\n for (let i = 0; i < committedCount; i += 1) {\n state.pendingLocal.popFront();\n }\n this.notifySyncWaiters(state, committedCount);\n this.setWriteSubStatus(state, \"ok\");\n this.snapshotManager.schedule(state);\n break;\n } catch (error) {\n const classified = classifyFetchError(error);\n if (!classified.retriable || isAuthOrProtocolError(error)) {\n this.logError(\"local append failed\", error, {\n streamUrl: this.streamUrl,\n code: classified.code,\n });\n state.lastError = toTransportError(error);\n this.rejectSyncWaiters(\n state,\n new Error(`local append failed: ${classified.code}`),\n );\n this.setWriteSubStatus(state, \"error\");\n return;\n }\n const maxAttempts = this.reconnectConfig.maxAttempts;\n if (attempts >= maxAttempts) {\n state.lastError = toTransportError(error);\n this.rejectSyncWaiters(\n state,\n new Error(\"local append retry budget exhausted\"),\n );\n this.setWriteSubStatus(state, \"error\");\n return;\n }\n this.setWriteSubStatus(state, \"reconnecting\");\n const delay = computeRetryDelay(attempts, this.reconnectConfig);\n attempts += 1;\n await sleep(delay, state.retrySleepController.signal);\n }\n }\n }\n } finally {\n state.flushingLocal = false;\n }\n }\n\n /**\n * Decrements all sync waiters by the number of flushed batches and\n * resolves any that have reached zero.\n */\n private notifySyncWaiters(\n state: JoinState<TVersion>,\n flushedCount: number,\n ): void {\n let i = 0;\n while (i < state.syncWaiters.length) {\n const waiter = state.syncWaiters[i]!;\n waiter.remaining -= flushedCount;\n if (waiter.remaining <= 0) {\n state.syncWaiters.splice(i, 1);\n waiter.resolve();\n } else {\n i += 1;\n }\n }\n }\n\n /** Rejects and removes all sync waiters. */\n private rejectSyncWaiters(\n state: JoinState<TVersion>,\n error: Error,\n ): void {\n for (const waiter of state.syncWaiters.splice(0)) {\n waiter.reject(error);\n }\n }\n\n private async saveRemoteCursor(\n cursor: RemoteCursor<TVersion>,\n source: RemoteCursorSaveSource,\n ): Promise<RemoteCursor<TVersion>> {\n return await persistRemoteCursor({\n remoteCursorStore: this.remoteCursorStore,\n cursor,\n source,\n beforeRemoteCursorSave: this.beforeRemoteCursorSave,\n });\n }\n\n private setReadSubStatus(\n state: JoinState<TVersion>,\n sub: ReadSubStatus,\n ): void {\n if (state.readSubStatus === sub) {\n return;\n }\n state.readSubStatus = sub;\n this.recomputeJoinStatus(state);\n }\n\n private setWriteSubStatus(\n state: JoinState<TVersion>,\n sub: WriteSubStatus,\n ): void {\n if (state.writeSubStatus === sub) {\n return;\n }\n state.writeSubStatus = sub;\n this.recomputeJoinStatus(state);\n }\n\n private recomputeJoinStatus(state: JoinState<TVersion>): void {\n const status = this.computeOverallStatus(state);\n if (state.status === status) {\n return;\n }\n state.status = status;\n // Clear stale error when the room fully recovers.\n if (status === \"joined\") {\n state.lastError = undefined;\n }\n for (const listener of state.statusListeners) {\n listener(status as TransportRoomStatus);\n }\n }\n\n private computeOverallStatus(\n state: JoinState<TVersion>,\n ): \"connecting\" | TransportRoomStatus {\n const { readSubStatus, writeSubStatus } = state;\n if (readSubStatus === \"error\" || writeSubStatus === \"error\") {\n return \"error\";\n }\n if (readSubStatus === \"disconnected\") {\n return \"disconnected\";\n }\n if (readSubStatus === \"connecting\") {\n return \"connecting\";\n }\n if (\n readSubStatus === \"reconnecting\" ||\n writeSubStatus === \"reconnecting\"\n ) {\n return \"reconnecting\";\n }\n return \"joined\";\n }\n\n private enqueueExclusive<T>(task: () => MaybePromise<T>): Promise<T> {\n const run = this.opQueue.then(task, task);\n this.opQueue = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n }\n\n private async measureAppendBodyByteLength(\n updates: readonly Uint8Array[],\n ): Promise<number> {\n if (this.measureUpdateItemsByteLength != null) {\n return await this.measureUpdateItemsByteLength(updates);\n }\n return updates.reduce((sum, update) => sum + 4 + update.byteLength, 0);\n }\n\n private disableWriteOnlyMode(reason: string): void {\n const frozen = this.frozenLocalAppend;\n if (frozen?.source === \"write-only\") {\n this.abandonFrozenLocalAppend(frozen);\n }\n if (this.writeOnlyDisabledReason != null) {\n return;\n }\n this.writeOnlyDisabledReason = reason;\n this.pendingWriteOnly.clear();\n this.unsubscribeWriteOnlyLocal?.();\n this.unsubscribeWriteOnlyLocal = undefined;\n }\n\n private logDebug(message: string, detail?: Record<string, unknown>): void {\n if (!this.debug) {\n return;\n }\n if (detail == null) {\n console.debug(`[streams-crdt] ${message}`);\n return;\n }\n console.debug(`[streams-crdt] ${message}`, detail);\n }\n\n private logError(\n message: string,\n error?: unknown,\n detail?: Record<string, unknown>,\n ): void {\n const payload =\n error == null\n ? detail\n : {\n ...detail,\n error: error instanceof Error ? error.message : String(error),\n };\n if (payload == null) {\n console.error(`[streams-crdt] ${message}`);\n return;\n }\n console.error(`[streams-crdt] ${message}`, payload);\n }\n}\n","const MAX_STREAM_ID_BYTES = 512;\nconst textEncoder = new TextEncoder();\nconst DEFAULT_BASE_URL = \"https://streams-api.loro.dev\";\n\nfunction utf8ByteLength(value: string): number {\n return textEncoder.encode(value).byteLength;\n}\n\n/**\n * Validates a stream id accepted by `createStreamUrl()`.\n */\nexport function isValidRillId(id: string): boolean {\n return (\n id.length > 0 &&\n utf8ByteLength(id) <= MAX_STREAM_ID_BYTES &&\n !id.includes(\"/\") &&\n !id.includes(\"\\0\") &&\n !id.includes(\"..\")\n );\n}\n\n/**\n * Validates a bucket id accepted by `createStreamUrl()`.\n */\nexport function isValidBucketId(id: string): boolean {\n return isValidRillId(id);\n}\n\n/**\n * Builds a Durable Streams HTTP URL for one `(bucketId, streamId)` pair.\n */\nexport function createStreamUrl(input: {\n bucketId: string;\n streamId: string;\n baseUrl?: string;\n}): string {\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/g, \"\");\n if (!isValidBucketId(input.bucketId)) {\n throw new Error(\n `invalid bucketId: ${JSON.stringify(input.bucketId)} ` +\n `(must be non-empty UTF-8, <= ${MAX_STREAM_ID_BYTES} bytes, and exclude \"/\", \"\\\\0\", \"..\")`,\n );\n }\n if (!isValidRillId(input.streamId)) {\n throw new Error(\n `invalid streamId: ${JSON.stringify(input.streamId)} ` +\n `(must be non-empty UTF-8, <= ${MAX_STREAM_ID_BYTES} bytes, and exclude \"/\", \"\\\\0\", \"..\")`,\n );\n }\n const streamId = input.streamId;\n return `${baseUrl}/ds/${encodeURIComponent(input.bucketId)}/${encodeURIComponent(streamId)}`;\n}\n"],"mappings":"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"}