@korajs/sync 1.0.0-beta.13 → 1.0.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-5S6U7EVU.js → chunk-YMPQ53EI.js} +21 -2
- package/dist/chunk-YMPQ53EI.js.map +1 -0
- package/dist/index.cjs +51 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +20 -1
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +2 -2
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +1 -1
- package/dist/{transport-Bemtqwep.d.cts → transport-CoPFFarn.d.cts} +6 -0
- package/dist/{transport-Bemtqwep.d.ts → transport-CoPFFarn.d.ts} +6 -0
- package/package.json +3 -3
- package/dist/chunk-5S6U7EVU.js.map +0 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/scopes/scope-snapshot.ts","../src/scopes/scope-filter.ts","../src/scopes/query-subset.ts","../src/delta/delta-cursor.ts","../src/protocol/schema-version.ts","../src/protocol/protocol-version.ts","../src/encryption/key-messages.ts","../src/protocol/messages.ts","../src/protocol/serializer.ts","../src/transport/websocket-transport.ts","../src/transport/http-long-polling-transport.ts","../src/transport/chaos-transport.ts","../src/engine/sync-engine.ts","../src/awareness/awareness-manager.ts","../src/blob/blob-chunk-channel.ts","../src/diagnostics/bandwidth-estimator.ts","../src/diagnostics/percentile.ts","../src/diagnostics/metrics-collector.ts","../src/encryption/keyring.ts","../src/encryption/key-cache.ts","../src/encryption/key-derivation.ts","../src/encryption/key-record.ts","../src/encryption/keyring-crypto.ts","../src/encryption/sync-encryptor.ts","../src/richtext/richtext-doc-channel.ts","../src/richtext/doc-channel-wire.ts","../src/scopes/scope-view-key.ts","../src/engine/memory-queue-storage.ts","../src/engine/outbound-queue.ts","../src/engine/verify-inbound.ts","../src/engine/connection-monitor.ts","../src/engine/reconnection-manager.ts","../src/encryption/encrypted-relations.ts","../src/reactivity/sync-status-controller.ts","../src/reactivity/collaborators-snapshot.ts"],"sourcesContent":["// @korajs/sync — public API\n// Every export here is a public API commitment. Be explicit.\n\n// === Types ===\nexport type {\n\tQueueStorage,\n\tQuarantinedOperation,\n\tRejectedOperation,\n\tRejectedOperationStorage,\n\tSyncStatePersistence,\n\tSyncAuthRequest,\n\tSyncConfig,\n\tSyncEncryptionConfig,\n\tSyncScopeContext,\n\tSyncScopeMap,\n\tSyncState,\n\tSyncStatus,\n\tSyncStatusInfo,\n\tSyncPhase,\n\tSyncSettlementOptions,\n\tSyncSettlementResult,\n\tActiveApplyFailure,\n\tDeltaCursor,\n\tLocalNodeInfo,\n\tHeldNodeInfo,\n\tHeldReason,\n\tAdoptionScheduleInfo,\n\tTerminalRejectionRecord,\n} from './types'\n\nexport {\n\tInvalidScopeError,\n\tMemoryRejectedOperationStorage,\n\tScopeViolationError,\n\tSYNC_STATES,\n\tSYNC_STATUSES,\n} from './types'\n\n// === Scope Filtering ===\nexport { filterOperationsByScope, operationMatchesScope } from './scopes/scope-filter'\nexport type { ScopeSnapshotOptions } from './scopes/scope-snapshot'\nexport {\n\tdedupeQuerySubsets,\n\tquerySubsetContains,\n\toperationMatchesQuerySubsets,\n\ttype SyncQuerySubset,\n} from './scopes/query-subset'\n\n// === Delta Cursor ===\nexport {\n\tcreateDeltaCursorFromBatch,\n\tdecodeDeltaCursor,\n\tencodeDeltaCursor,\n\tsliceOperationsAfterCursor,\n} from './delta/delta-cursor'\n\n// === SyncStore Interface ===\nexport type { ApplyFailureReason, ApplyResult, SyncStore } from './engine/sync-store'\n\n// === Protocol Messages ===\nexport type {\n\tAcknowledgmentMessage,\n\tErrorMessage,\n\tHandshakeMessage,\n\tHeartbeatMessage,\n\tHandshakeResponseMessage,\n\tOperationBatchMessage,\n\tOperationRejectedMessage,\n\tSerializedOperation,\n\tSyncMessage,\n\tWireFormat,\n} from './protocol/messages'\n\nexport {\n\tisClientSchemaVersionSupported,\n\tisSchemaMismatchReject,\n\tSCHEMA_MISMATCH_PREFIX,\n} from './protocol/schema-version'\n\nexport {\n\tdeclaredProtocolVersion,\n\tINVALID_OPERATION_ID,\n\tLEGACY_SYNC_PROTOCOL_VERSION,\n\tPLAINTEXT_REJECTED,\n\tPROTOCOL_V1_DEPRECATED,\n\tSYNC_PROTOCOL_VERSION,\n} from './protocol/protocol-version'\n\nexport {\n\tisAcknowledgmentMessage,\n\tisErrorMessage,\n\tisHandshakeMessage,\n\tisHandshakeResponseMessage,\n\tisOperationBatchMessage,\n\tisOperationRejectedMessage,\n\tisSyncMessage,\n} from './protocol/messages'\n\n// === Serialization ===\nexport type { MessageSerializer } from './protocol/serializer'\n\nexport {\n\tJsonMessageSerializer,\n\tNegotiatedMessageSerializer,\n\tProtobufMessageSerializer,\n\tversionVectorToWire,\n\twireToVersionVector,\n} from './protocol/serializer'\n\n// === Transport ===\nexport type {\n\tSyncTransport,\n\tTransportCloseHandler,\n\tTransportErrorHandler,\n\tTransportMessageHandler,\n\tTransportOptions,\n} from './transport/transport'\n\nexport type {\n\tWebSocketConstructor,\n\tWebSocketLike,\n\tWebSocketTransportOptions,\n} from './transport/websocket-transport'\n\nexport type { HttpLongPollingTransportOptions } from './transport/http-long-polling-transport'\n\nexport { WebSocketTransport } from './transport/websocket-transport'\nexport {\n\tHTTP_SYNC_SESSION_HEADER,\n\tHttpLongPollingTransport,\n} from './transport/http-long-polling-transport'\n\nexport type { ChaosConfig } from './transport/chaos-transport'\n\nexport { ChaosTransport } from './transport/chaos-transport'\n\n// === Engine ===\nexport type { SyncDiagnostics, SyncEngineOptions } from './engine/sync-engine'\n\nexport { SyncEngine } from './engine/sync-engine'\n\nexport type { OutboundBatch } from './engine/outbound-queue'\n\nexport { OutboundQueue } from './engine/outbound-queue'\n\nexport type { ConnectionMonitorConfig } from './engine/connection-monitor'\n\nexport { ConnectionMonitor } from './engine/connection-monitor'\n\nexport type { ReconnectionConfig } from './engine/reconnection-manager'\n\nexport { ReconnectionManager } from './engine/reconnection-manager'\n\n// === Awareness ===\nexport type {\n\tAwarenessChange,\n\tAwarenessCursor,\n\tAwarenessMessage,\n\tAwarenessState,\n\tAwarenessUser,\n\tCursorInfo,\n} from './awareness/types'\n\nexport { AwarenessManager } from './awareness/awareness-manager'\n\n// === Awareness Protocol Messages ===\nexport type {\n\tAwarenessStateWire,\n\tAwarenessUpdateMessage,\n} from './protocol/messages'\n\nexport { isAwarenessUpdateMessage } from './protocol/messages'\n\n// === Richtext doc channel (large documents) ===\nexport type { YjsDocUpdateMessage } from './protocol/messages'\n\nexport { isYjsDocUpdateMessage } from './protocol/messages'\n\nexport {\n\tDEFAULT_RICHTEXT_DOC_CHANNEL_THRESHOLD,\n\tRichtextDocChannel,\n} from './richtext/richtext-doc-channel'\n\nexport type {\n\tRichtextDocChannelOptions,\n\tRichtextDocListener,\n} from './richtext/richtext-doc-channel'\n\nexport { decodeYjsUpdate, encodeYjsUpdate, richtextDocKey } from './richtext/doc-channel-wire'\n\n// === Blob chunk channel (out-of-band blob transfer) ===\nexport type {\n\tBlobChunkPushMessage,\n\tBlobChunkRequestMessage,\n\tBlobChunkResponseMessage,\n} from './protocol/messages'\n\nexport {\n\tisBlobChunkPushMessage,\n\tisBlobChunkRequestMessage,\n\tisBlobChunkResponseMessage,\n} from './protocol/messages'\n\nexport {\n\tBlobChunkChannel,\n\tdecodeBlobChunkBytes,\n\tencodeBlobChunkBytes,\n} from './blob/blob-chunk-channel'\n\nexport type {\n\tBlobChunkChannelMessage,\n\tBlobChunkChannelOptions,\n} from './blob/blob-chunk-channel'\n\n// === Encryption ===\nexport type {\n\tEncryptedPayload,\n\tSyncEncryptionAlgorithm,\n\tVersionedKey,\n} from './encryption/types'\n\nexport {\n\tSyncEncryptor,\n\tEncryptionError,\n\tDecryptionError,\n\tisEncryptedPayload,\n} from './encryption/sync-encryptor'\n\nexport {\n\tSealedRelationFieldError,\n\tvalidateEncryptedRelations,\n} from './encryption/encrypted-relations'\nexport type { EncryptedRelationConfig } from './encryption/encrypted-relations'\n\nexport {\n\tKeyDerivationError,\n\tderiveKey,\n\tderiveVersionedKey,\n\tgenerateSalt,\n} from './encryption/key-derivation'\n\n// Shared key material (ENC-1): the server-stored, passphrase-wrapped keyring.\nexport { EncryptionKeyring, EncryptionKeyError } from './encryption/keyring'\nexport type {\n\tEncryptionKeyringOptions,\n\tEncryptionLockState,\n\tEncryptionStatus,\n\tEncryptionStatusCode,\n\tKeyServiceChannel,\n\tKeyServiceReply,\n} from './encryption/keyring'\nexport {\n\tIndexedDbKeyCache,\n\tMemoryKeyCache,\n\tNoKeyCache,\n\tcreateKeyCache,\n} from './encryption/key-cache'\nexport type { CachedKeyring, KeyCache } from './encryption/key-cache'\nexport {\n\tDEFAULT_KEYRING,\n\tKEY_RECORD_FORMAT,\n\tMAX_KDF_ITERATIONS,\n\tMAX_KEY_RECORD_BYTES,\n\tisKeyId,\n\tisKeyRecordSuccessor,\n\tisValidKeyringName,\n\tvalidateKeyRecord,\n} from './encryption/key-record'\nexport type {\n\tKeyRecordKdf,\n\tKeyRecordRecovery,\n\tKeyRecordValidation,\n\tWrappedDataKey,\n\tWrappedKeyRecord,\n\tWrappedMasterKey,\n} from './encryption/key-record'\nexport { KeyUnwrapError } from './encryption/keyring-crypto'\nexport { isEncryptionKeyMessage } from './encryption/key-messages'\nexport type {\n\tEncryptionKeyMessage,\n\tEncryptionKeyPutMessage,\n\tEncryptionKeyRequestMessage,\n\tEncryptionKeyResponseMessage,\n\tEncryptionKeyResponseStatus,\n} from './encryption/key-messages'\n\nexport {\n\tcreateSyncStatusController,\n\tOFFLINE_SYNC_STATUS,\n} from './reactivity/sync-status-controller'\nexport type {\n\tSyncStatusController,\n\tSyncStatusControllerOptions,\n} from './reactivity/sync-status-controller'\n\nexport {\n\tgetRemoteAwarenessStates,\n\tsubscribeRemoteAwarenessStates,\n} from './reactivity/collaborators-snapshot'\n","import type { Operation, VersionVector } from '@korajs/core'\nimport { KoraError } from '@korajs/core'\nimport type { SyncEncryptionConfig } from './encryption/types'\n\n// Re-export for convenience — consumers can import from '@korajs/sync' types\nexport type { SyncEncryptionConfig }\n\n/**\n * Internal sync engine states. Used for state machine transitions.\n */\nexport const SYNC_STATES = [\n\t'disconnected',\n\t'connecting',\n\t'handshaking',\n\t'syncing',\n\t'streaming',\n\t'error',\n] as const\nexport type SyncState = (typeof SYNC_STATES)[number]\n\n/**\n * Developer-facing sync status. Simplified view of the internal state.\n */\nexport const SYNC_STATUSES = [\n\t'connected',\n\t'reconnecting',\n\t'syncing',\n\t'synced',\n\t'offline',\n\t'clock-error',\n\t'error',\n\t'schema-mismatch',\n\t'auth-required',\n\t// End-to-end encryption is on and this device's keyring is locked (ENC-1): sync is\n\t// paused until `app.encryption.unlock(passphrase)`. Local reads and writes go on.\n\t'encryption-locked',\n] as const\nexport type SyncStatus = (typeof SYNC_STATUSES)[number]\n\nexport type SyncPhase =\n\t| 'suspended'\n\t| 'offline'\n\t| 'connecting'\n\t| 'authenticating'\n\t| 'handshaking'\n\t| 'uploading'\n\t| 'receiving'\n\t| 'applying'\n\t| 'streaming'\n\t| 'blocked'\n\nexport interface ActiveApplyFailure {\n\toperationId: string\n\tcollection: string\n\trecordId: string\n\tcode: string\n\tmessage: string\n\tretriable: boolean\n\tfirstSeenAt: number\n\tretryCount: number\n}\n\n/** Why a local node's unsynced writes are held (RT-38, RT-50). */\nexport type HeldReason = 'other-user' | 'unassigned'\n\n/** A local node whose unsynced writes are held (see {@link SyncStatusInfo.heldNodes}). */\nexport interface HeldNodeInfo {\n\tnodeId: string\n\t/** Unsynced writes the node holds. */\n\toperationCount: number\n\treason: HeldReason\n\t/** The user the node is bound to, or null when nobody knows (unassigned). */\n\tprincipal: string | null\n}\n\n/**\n * Sync status information exposed to developers.\n */\nexport interface SyncStatusInfo {\n\t/** Current developer-facing status */\n\tstatus: SyncStatus\n\tphase?: SyncPhase\n\treason?: string\n\t/** True when the engine is actively trying to re-establish a transport session. */\n\treconnecting: boolean\n\t/** Number of operations waiting to be sent */\n\tpendingOperations: number\n\t/** Timestamp of last successful sync (null if never synced) */\n\tlastSyncedAt: number | null\n\t/** Timestamp of last successful push to the server (null if never pushed) */\n\tlastSuccessfulPush: number | null\n\t/** Timestamp of last successful pull from the server (null if never pulled) */\n\tlastSuccessfulPull: number | null\n\t/** Number of merge conflicts encountered during this session */\n\tconflicts: number\n\t/**\n\t * Unsynced writes of another user who shared this local database (RT-38): the server\n\t * refused their node for the signed-in principal, so they wait, not counted in\n\t * `pendingOperations`, until that user signs in again on this device. Use\n\t * `store.namespaceByAuthUser` to give each user their own database instead.\n\t */\n\theldOperations?: number\n\t/**\n\t * The local nodes whose unsynced writes are held (RT-38, RT-50), with how many each\n\t * holds and why: `other-user` (they belong to another user, or the server refused\n\t * the node for the signed-in one: they upload when their user signs in) or\n\t * `unassigned` (written before the app knew who was signed in, on a node that never\n\t * synced: nobody can tell whose they are, so the app decides with\n\t * `app.sync.assignHeld` or `app.sync.discardHeld`). As of the last session start.\n\t */\n\theldNodes?: HeldNodeInfo[]\n\t/**\n\t * `degraded` when this device's local database could not be made durable several times\n\t * in a row (storage quota exceeded, IndexedDB broken; RT-49). Uploads continue so the\n\t * server keeps a durable copy, and a later reload recovers its writes from the server;\n\t * writes made while offline in this state may be lost if the page closes. Show the user\n\t * a warning (free up storage, stay online). `durable` otherwise.\n\t */\n\tlocalDurability?: 'durable' | 'degraded'\n\t/**\n\t * Sync protocol version the server answered with in the current (or last) session,\n\t * or null before any server answered.\n\t */\n\tserverProtocolVersion?: number | null\n\t/**\n\t * True when the server speaks an older sync protocol than this client (a pre-beta.13\n\t * server). It still syncs, but that protocol is deprecated and a later release\n\t * refuses it: upgrade the sync server.\n\t */\n\tprotocolDeprecated?: boolean\n\t/** serverTime - localTime in ms measured at the last handshake, or null before first connect. Negative = this device's clock is fast. */\n\tclockSkewMs: number | null\n\tinFlightUploadOperations?: number\n\thasInFlightDeliveryBatch?: boolean\n\tactiveViewId?: string\n\tactiveViewComplete?: boolean\n\tinitialSync?: {\n\t\tcomplete: boolean\n\t\treceivedBatches: number\n\t\ttotalBatches: number | null\n\t\tprogress: number | null\n\t}\n\tdeliveryWatermark?: number\n\tserverFrontier?: number | null\n\tblockedFailure?: ActiveApplyFailure | null\n}\n\nexport interface SyncSettlementOptions {\n\tupload?: boolean\n\tdownload?: 'active-view' | false\n\ttimeoutMs?: number\n\tsignal?: AbortSignal\n}\n\nexport type SyncSettlementResult =\n\t| { outcome: 'settled'; status: SyncStatusInfo }\n\t| { outcome: 'offline'; status: SyncStatusInfo }\n\t| { outcome: 'suspended'; reason: string; status: SyncStatusInfo }\n\t| { outcome: 'blocked'; failure: ActiveApplyFailure; status: SyncStatusInfo }\n\t| { outcome: 'timeout'; status: SyncStatusInfo }\n\t| { outcome: 'aborted'; status: SyncStatusInfo }\n\n/**\n * Per-collection sync scope map. Maps collection names to field-value filters.\n * Empty filter `{}` means no restriction (all records visible).\n * Missing collection means hidden (no records visible for that collection).\n */\nexport type SyncScopeMap = Record<string, Record<string, unknown>>\n\n/** Options passed to {@link SyncConfig.auth}. */\nexport interface SyncAuthRequest {\n\t/**\n\t * True when the server ended the previous session because its credential\n\t * expired or was revoked. A cached token must not be reused.\n\t */\n\tforceRefresh?: boolean\n}\n\n/**\n * Sync configuration provided by the developer.\n */\nexport interface SyncConfig {\n\t/** WebSocket or HTTP URL for the sync server */\n\turl: string\n\t/** Transport type to use. Defaults to 'websocket'. */\n\ttransport?: 'websocket' | 'http'\n\t/**\n\t * Auth provider function. Called before each connection attempt. After the\n\t * server ends a session with `AUTH_EXPIRED` or `AUTH_REVOKED` it is called\n\t * with `{ forceRefresh: true }`: return a freshly refreshed token, never the\n\t * cached one the server just refused.\n\t */\n\tauth?: (options?: SyncAuthRequest) => Promise<{ token: string }>\n\t/**\n\t * The signed-in user whose writes this device makes (RT-42): a user id, or null /\n\t * undefined when nobody is known to be signed in. Called before each session. With\n\t * it, the engine binds the store's node to that user (the store moves to the user's\n\t * own node when another user's is in use) and never uploads, or adopts, a node that\n\t * belongs to another user: those writes wait for their user (`heldOperations`).\n\t * `createApp` derives it from `sync.authClient`.\n\t */\n\tprincipal?: () => Promise<string | null | undefined>\n\t/** Auth readiness gate. A suspended result prevents transport creation and retries. */\n\tauthState?: () => Promise<{\n\t\tstate: 'loading' | 'signed-out' | 'anonymous' | 'authenticated'\n\t\tmayConnectAnonymously?: boolean\n\t}>\n\tquerySubsets?: { mode?: 'reactive' | 'static' | 'disabled' }\n\t/** Client-local behavior for records leaving the accepted downlink scope. Defaults to `retain`. */\n\tscopeExit?: 'retain' | 'retract'\n\t/** Sync scopes per collection. Limits which records sync to this client. */\n\tscopes?: Record<string, (ctx: SyncScopeContext) => Record<string, unknown>>\n\t/**\n\t * Pre-computed per-collection sync scope map. Sent to the server in the handshake.\n\t * Built automatically by createApp from schema scope declarations + flat scope values.\n\t */\n\tscopeMap?: SyncScopeMap\n\t/**\n\t * Collections the app's schema syncs (all of them, or the sync-scoped ones when the\n\t * schema declares partial sync rules). Set by createApp. When no `scopeMap` is\n\t * configured, a write on one of these that the server's accepted upload scope omits\n\t * is surfaced as `sync:operation-rejected` (`OUT_OF_UPLINK_SCOPE`) instead of being\n\t * treated as local-only (RT-89). Omitted: such writes stay local without a rejection.\n\t */\n\tsyncedCollections?: readonly string[]\n\t/** Number of operations per batch. Defaults to 100. */\n\tbatchSize?: number\n\t/**\n\t * Maximum time to wait for the server to acknowledge an outbound batch before\n\t * treating the connection as stalled. The batch is returned to the durable\n\t * outbound queue before disconnecting so it can be retried on reconnect.\n\t * Defaults to 30000ms. Set to 0 to disable the watchdog.\n\t */\n\toutboundAckTimeoutMs?: number\n\t/** Initial delay before resending a transiently rejected outbound operation. Defaults to 250ms. */\n\toutboundRetryBaseDelayMs?: number\n\t/** Maximum transient-operation retry delay. Defaults to 30000ms. */\n\toutboundRetryMaxDelayMs?: number\n\t/** Initial reconnection delay in ms. Defaults to 1000. */\n\treconnectInterval?: number\n\t/** Maximum reconnection delay in ms. Defaults to 30000. */\n\tmaxReconnectInterval?: number\n\t/** Schema version of this client. */\n\tschemaVersion?: number\n\t/** Start sync automatically when the engine is created. Defaults to false. */\n\tautoConnect?: boolean\n\t/**\n\t * When true, wait for server ACKs on all outbound handshake delta batches before\n\t * entering streaming. Improves backpressure for large initial syncs.\n\t */\n\tstrictHandshake?: boolean\n\t/**\n\t * Optional schema transforms for cross-schema-version sync. They run at fold time\n\t * (RT-84): delivered operations are stored exactly as received and the local store\n\t * folds their transformed view, so the store must be given the same list (`createApp`\n\t * does). An operation with no transform path to this schema version is kept aside\n\t * and replayed after an upgrade.\n\t */\n\toperationTransforms?: import('@korajs/core').OperationTransform[]\n\t/**\n\t * Richtext snapshot size (bytes) at which the optional Yjs doc channel is used.\n\t * Defaults to 4096.\n\t */\n\trichtextDocChannelThreshold?: number\n\t/**\n\t * End-to-end encryption configuration.\n\t * When enabled, `data` and `previousData` fields are encrypted before sending\n\t * over the wire. The server never sees plaintext user data.\n\t */\n\tencryption?: SyncEncryptionConfig\n}\n\n/**\n * Context passed to sync scope functions.\n */\nexport interface SyncScopeContext {\n\tuserId?: string\n\t[key: string]: unknown\n}\n\n/**\n * Persists last-acked server version vector and computes unsynced operations from the op log.\n */\nexport interface DeltaCursor {\n\t/** ID of the last fully applied operation in the previous delta stream */\n\tlastOperationId: string\n\t/** Zero-based batch index where the cursor was recorded */\n\tbatchIndex: number\n}\n\nexport interface SyncStatePersistence {\n\tloadLastAckedServerVector(): Promise<VersionVector>\n\tsaveLastAckedServerVector(vector: VersionVector): Promise<void>\n\tmergeServerVectors(a: VersionVector, b: VersionVector): VersionVector\n\tcountUnsyncedOperations(serverVector: VersionVector): Promise<number>\n\tgetUnsyncedOperations(serverVector: VersionVector): Promise<Operation[]>\n\t/** Resume position for paginated initial sync (optional). */\n\tloadDeltaCursor?(): Promise<DeltaCursor | null>\n\tsaveDeltaCursor?(cursor: DeltaCursor | null): Promise<void>\n\t/**\n\t * The delivery watermark for a sync view, keyed by an opaque view signature (the auth\n\t * scope plus active query subscriptions). Persisted per view so a restarted client\n\t * resumes each view's gap-free stream from the right point, and switching views does not\n\t * re-sync a view already synced. The empty-string signature is the default, unfiltered\n\t * view. Optional: a persistence layer that omits these falls back to re-sync on restart.\n\t */\n\tloadDeliveryWatermark?(signature: string): Promise<number>\n\tsaveDeliveryWatermark?(signature: string, watermark: number): Promise<void>\n\t/** Load every persisted view watermark, keyed by signature. */\n\tloadAllDeliveryWatermarks?(): Promise<Record<string, number>>\n\t/**\n\t * Delete a persisted view watermark. Used to bound the number of stored view watermarks\n\t * (a cold view is evicted from the client's cache and its persisted row removed). Safe:\n\t * an evicted view back-fills from 0 (deduplicated) when next visited.\n\t */\n\tdeleteDeliveryWatermark?(signature: string): Promise<void>\n\t/**\n\t * The per-device node token the server issued at this node id's first anonymous\n\t * claim (RT-12), stored next to the node id so the device can reconnect with it.\n\t * Optional: without it the token lives only as long as the sync engine.\n\t */\n\tloadNodeToken?(nodeId?: string): Promise<string | null>\n\t/**\n\t * `nodeId` names the node the token was issued for: a database that authors under\n\t * several node ids (a rotated identity, per-tab isolation) keeps one token per node.\n\t */\n\tsaveNodeToken?(token: string, nodeId?: string): Promise<void>\n\t/**\n\t * The node ids the server named authoritative in its last accepted handshake response\n\t * (`HandshakeResponseMessage.authoritativeNodeIds`, protocol v2): operations from\n\t * these nodes are server-authored and may carry `fieldVersions` / `foldState`.\n\t * Returns null when no protocol-2 server ever answered. Optional.\n\t */\n\tloadAuthoritativeNodeIds?(): Promise<string[] | null>\n\tsaveAuthoritativeNodeIds?(nodeIds: string[]): Promise<void>\n\t/**\n\t * The contiguous acknowledged prefix of this device's own operations (W3): the highest\n\t * sequence s such that every own operation with sequence <= s is stored on the server,\n\t * terminally rejected and recorded, or was never upload-eligible. Keyed by node id, so\n\t * a rotated node starts from 0. Returns null when nothing was ever recorded under this\n\t * contract (a device upgrading from a release that persisted a max, not a prefix): the\n\t * engine then re-uploads the device's own history once, from 0; the server dedups by id.\n\t * Optional: without it the engine trusts the own entry of the last acked server vector.\n\t */\n\tloadOwnAckedThrough?(nodeId: string): Promise<number | null>\n\tsaveOwnAckedThrough?(nodeId: string, sequence: number): Promise<void>\n\t/**\n\t * Durable inbound quarantine (W4): delivered operations the client deliberately did\n\t * not apply (unknown collection, transform unavailable, deferred or rejected apply,\n\t * far-future timestamp, undecryptable payload). When `watermark` is given, the rows and\n\t * the delivery watermark advance MUST be written in one transaction, so the watermark\n\t * never passes an operation that is neither applied nor recorded here. Optional: a\n\t * persistence layer without it keeps the old behaviour (the watermark stalls instead).\n\t */\n\tsaveQuarantine?(\n\t\tentries: QuarantinedOperation[],\n\t\twatermark?: { signature: string; watermark: number },\n\t): Promise<void>\n\t/** Every quarantined operation, oldest delivery first. */\n\tloadQuarantine?(): Promise<QuarantinedOperation[]>\n\t/** Remove quarantined operations once applied (replay) or reconciled. */\n\tremoveQuarantine?(operationIds: string[]): Promise<void>\n\t/**\n\t * The downlink scope the server last accepted for this device (SYNC-11). The next\n\t * handshake reports its canonical key and that view's delivery watermark next to the\n\t * requested view's, so a server that resolves the same scope again resumes the\n\t * stream instead of restarting it from 0. Never sent as the requested scope.\n\t * Optional; without it every handshake with a server-chosen scope rescans from 0.\n\t */\n\tloadAcceptedDownlinkScope?(): Promise<SyncScopeMap | null>\n\tsaveAcceptedDownlinkScope?(scope: SyncScopeMap | null): Promise<void>\n\t/**\n\t * Durable terminal-rejection markers (RT-36): operations the server refused with a\n\t * non-retriable rejection. Never cleared (unlike the app's rejected list), so a\n\t * rescan of the device's own history never submits a refused operation again.\n\t * Optional: without them the engine remembers refusals for its own lifetime only.\n\t */\n\trecordTerminalRejections?(entries: TerminalRejectionRecord[]): Promise<void>\n\t/** Which of these operation ids carry a terminal-rejection marker. */\n\tfindTerminalRejections?(operationIds: string[]): Promise<Set<string>>\n\t/**\n\t * The node ids this database authored operations under (RT-38, RT-40), with what the\n\t * sync server said about each. Optional: without it the engine tracks only the\n\t * current node and keeps the pre-Phase-2 behaviour for refused nodes.\n\t */\n\tlistLocalNodes?(): Promise<LocalNodeInfo[]>\n\t/** Record an accepted handshake as `nodeId`; starts a new refusal cycle. */\n\tmarkLocalNodeAccepted?(nodeId: string): Promise<void>\n\t/** Record that the server refused `nodeId`; `held` holds its unsynced writes. */\n\tmarkLocalNodeRefused?(nodeId: string, held: boolean): Promise<void>\n\t/**\n\t * A handshake as `nodeId` was accepted for `principal` (RT-50): bind an unbound node\n\t * (or replace a guessed binding) from the server's answer.\n\t */\n\tconfirmLocalNodePrincipal?(nodeId: string, principal: string): Promise<void>\n\t/**\n\t * The server refused `nodeId` for `principal` (RT-50): clear a guessed binding, and\n\t * remember that an unbound node is not theirs.\n\t */\n\trecordLocalNodeRefusedFor?(nodeId: string, principal: string): Promise<void>\n\t/**\n\t * Assign a held, unbound node's writes to `principal` (RT-50, the app's decision).\n\t * @returns false when the node is not unbound, or was refused for `principal`\n\t */\n\tassignLocalNodePrincipal?(nodeId: string, principal: string): Promise<boolean>\n\t/** Forget a non-current local node whatever it holds (its held writes were discarded). */\n\tdropLocalNode?(nodeId: string): Promise<void>\n\t/** The current refusal cycle (count of accepted handshakes). */\n\tloadAcceptedCycle?(): Promise<number>\n\t/** Forget a non-current local node with nothing left to upload (bounds the registry). */\n\tforgetLocalNode?(nodeId: string): Promise<void>\n\t/**\n\t * Which adoptions of other local nodes are parked (RT-46), and an upload-progress\n\t * counter. Optional: without it the schedule lives for the engine's lifetime.\n\t */\n\tloadAdoptionSchedule?(): Promise<AdoptionScheduleInfo>\n\tsaveAdoptionSchedule?(schedule: AdoptionScheduleInfo): Promise<void>\n}\n\n/**\n * Upload scheduling across a database's local nodes (RT-46). A node whose adoption made\n * no progress is parked: it is retried once anything else uploaded (`progress` moved past\n * `progressMark`) or after its backoff (`untilMs`).\n */\nexport interface AdoptionScheduleInfo {\n\tprogress: number\n\tparked: Record<\n\t\tstring,\n\t\t{ progressMark: number; untilMs: number; count: number; parkedAtMs: number }\n\t>\n}\n\n/** A terminally rejected operation, as recorded in the durable markers (RT-36). */\nexport interface TerminalRejectionRecord {\n\toperationId: string\n\tnodeId: string | null\n\tsequenceNumber: number | null\n\tcode: string\n\trejectedAt: number\n}\n\n/** A node id this database authored operations under (RT-38, RT-40). */\nexport interface LocalNodeInfo {\n\tnodeId: string\n\t/** A handshake as this node was accepted at least once. */\n\taccepted: boolean\n\t/** Refused after acceptance: its unsynced writes belong to a principal not signed in. */\n\theld: boolean\n\t/** Refusal cycle in which the server last refused it, or null. */\n\trefusedCycle: number | null\n\t/**\n\t * The user the node's writes belong to (RT-42), or null when unknown. A node bound to\n\t * another user than the signed-in one is never uploaded or adopted.\n\t */\n\tprincipal?: string | null\n\t/**\n\t * How `principal` was learned (RT-50): `fresh` (bound before its first write),\n\t * `server` (an accepted handshake), `app` (assigned by the app: a guess the server can\n\t * overrule), or null (unbound, or a guess by an earlier release).\n\t */\n\tbinding?: 'fresh' | 'server' | 'app' | null\n\t/** Users the server refused this unbound node for (RT-50): never tried for them again. */\n\trefusedPrincipals?: string[]\n}\n\n/**\n * A delivered operation the client deliberately did not apply, kept durably so it is\n * never silently lost and can be replayed (on start, after a schema upgrade, or on demand).\n */\nexport interface QuarantinedOperation {\n\t/** The operation as delivered (still encrypted when decryption failed). */\n\toperation: Operation\n\t/** Delivery sequence of the batch that carried it, or null for a legacy batch. */\n\tdeliverySequence: number | null\n\t/** Machine-readable reason (for example `APPLY_SKIPPED`, `REMOTE_CLOCK_DRIFT`). */\n\tcode: string\n\t/** Human-readable explanation. */\n\tmessage: string\n\t/** Wall-clock time (ms) it was quarantined. Display only. */\n\tquarantinedAt: number\n}\n\n/**\n * Interface for persisting the outbound operation queue.\n * Operations must survive page refreshes and be sent when connection is re-established.\n */\nexport interface QueueStorage {\n\t/** Load all queued operations from persistent storage */\n\tload(): Promise<Operation[]>\n\t/** Persist an operation to the queue */\n\tenqueue(op: Operation): Promise<void>\n\t/** Remove acknowledged operations by their IDs */\n\tdequeue(ids: string[]): Promise<void>\n\t/** Return number of operations in storage */\n\tcount(): Promise<number>\n\t/**\n\t * Record that these queued operations were put on the wire at least once. A sent\n\t * operation may already be stored on the server (its ack can be lost), so it must\n\t * never be re-stamped by a clock rebase (W3 step 4). Optional: without it the flag\n\t * lives for the engine's lifetime only.\n\t */\n\tmarkSent?(ops: Operation[]): Promise<void>\n\t/** Ids of queued operations recorded by {@link markSent}. */\n\tloadSentIds?(): Promise<string[]>\n}\n\n/**\n * A record of one outbound operation the server permanently refused. Preserved so\n * a rejected op is explainable and reconcilable rather than silently lost.\n */\nexport interface RejectedOperation {\n\t/** Content-addressed id of the rejected operation. */\n\toperationId: string\n\t/** Collection the operation targeted. */\n\tcollection: string\n\t/** Record the operation targeted. */\n\trecordId: string\n\t/** Stable, machine-readable reason code from the server. */\n\tcode: string\n\t/** Human-readable explanation. */\n\tmessage: string\n\t/** Whether resubmitting the identical operation may later succeed. */\n\tretriable: boolean\n\t/** Wall-clock time (ms since epoch) the rejection was recorded. Display only. */\n\trejectedAt: number\n}\n\n/**\n * Interface for durably persisting operations the server rejected, so the record\n * survives a page refresh. Keyed by operation id (idempotent on re-rejection).\n */\nexport interface RejectedOperationStorage {\n\t/** Record (or overwrite) a rejected operation. */\n\trecord(rejected: RejectedOperation): Promise<void>\n\t/** List all recorded rejected operations. */\n\tlist(): Promise<RejectedOperation[]>\n\t/** Remove rejected operations by their operation ids (after the app reconciles them). */\n\tremove(operationIds: string[]): Promise<void>\n}\n\n/**\n * In-memory {@link RejectedOperationStorage}. The default when no durable store is\n * provided; suitable for tests and ephemeral sessions.\n */\nexport class MemoryRejectedOperationStorage implements RejectedOperationStorage {\n\tprivate readonly rejected = new Map<string, RejectedOperation>()\n\n\tasync record(rejected: RejectedOperation): Promise<void> {\n\t\tthis.rejected.set(rejected.operationId, rejected)\n\t}\n\n\tasync list(): Promise<RejectedOperation[]> {\n\t\treturn [...this.rejected.values()]\n\t}\n\n\tasync remove(operationIds: string[]): Promise<void> {\n\t\tfor (const id of operationIds) {\n\t\t\tthis.rejected.delete(id)\n\t\t}\n\t}\n}\n\n/**\n * Thrown when an operation violates sync scope constraints.\n * This can happen when:\n * - A client tries to push an operation outside its configured scope\n * - The server rejects an operation because it falls outside the client's scope\n */\nexport class ScopeViolationError extends KoraError {\n\tconstructor(\n\t\tpublic readonly operationId: string,\n\t\tpublic readonly collection: string,\n\t\tpublic readonly scope: Record<string, unknown>,\n\t\tmessage?: string,\n\t) {\n\t\tsuper(\n\t\t\tmessage ?? `Operation \"${operationId}\" in collection \"${collection}\" violates sync scope`,\n\t\t\t'SCOPE_VIOLATION',\n\t\t\t{ operationId, collection, scope },\n\t\t)\n\t\tthis.name = 'ScopeViolationError'\n\t}\n}\n\n/**\n * Thrown when a sync scope configuration is invalid.\n */\nexport class InvalidScopeError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'INVALID_SCOPE', context)\n\t\tthis.name = 'InvalidScopeError'\n\t}\n}\n","import type { Operation } from '@korajs/core'\n\n/** Options for {@link buildScopeSnapshot}. */\nexport interface ScopeSnapshotOptions {\n\t/**\n\t * Layer the writer's `previousData` between the stored record and `op.data`.\n\t * Off by default: `previousData` is supplied by the writer and is never checked\n\t * against the stored row, so a server visibility decision built on it lets one\n\t * tenant steer its operations into another tenant's log (RT-3). Opt in only\n\t * where the writer is trusted for the decision, such as a client filtering its\n\t * own local view.\n\t * @default false\n\t */\n\tincludePreviousData?: boolean\n}\n\n/**\n * Build the record snapshot a scope or query-subset predicate is evaluated against\n * for an operation.\n *\n * Layering is `fullRecord < data` (or `fullRecord < previousData < data` with\n * `includePreviousData`), so the snapshot approximates the record after the\n * operation. The record identity is NOT taken from any of those\n * layers: `id` is always `op.recordId`, assigned last. An operation (or a stale\n * stored row) can therefore never claim to be a different record than the one it\n * actually targets, which is what made `previousData: { id: '<allowed>' }` a write\n * bypass under id-scoped predicates.\n *\n * This is the single snapshot builder shared by the client scope filter, the client\n * query-subset filter and the server scope filter. It is used for visibility\n * decisions only; upload authorization never reads `previousData` (see the server's\n * `authorizeUplinkWrite`).\n *\n * @param op - The operation being judged\n * @param fullRecord - Optional stored record state used to fill fields the op omits\n * @param options - Whether to trust the writer's `previousData` (default: no)\n * @returns The snapshot, always carrying `id === op.recordId`\n */\nexport function buildScopeSnapshot(\n\top: Operation,\n\tfullRecord?: Record<string, unknown> | null,\n\toptions: ScopeSnapshotOptions = {},\n): Record<string, unknown> {\n\treturn {\n\t\t...(fullRecord ?? {}),\n\t\t...(options.includePreviousData ? (asPlainRecord(op.previousData) ?? {}) : {}),\n\t\t...(asPlainRecord(op.data) ?? {}),\n\t\tid: op.recordId,\n\t}\n}\n\n/**\n * True when `actual` satisfies a scope predicate value: either an exact value\n * (compared with `Object.is`) or a bounded `{ $in: [...] }` set. An `undefined` or\n * `null` predicate value (or `$in` member) never matches.\n *\n * @param actual - The record's field value\n * @param expected - The scope predicate for that field\n * @returns Whether the value is inside the predicate\n */\nexport function matchesScopePredicate(actual: unknown, expected: unknown): boolean {\n\t// Fail closed (RT-8): an undefined/null predicate value would otherwise match\n\t// every record that lacks the field. The server refuses such grants outright;\n\t// this keeps any other caller from widening by accident.\n\tif (expected === undefined || expected === null) return false\n\tif (typeof expected === 'object' && !Array.isArray(expected) && '$in' in expected) {\n\t\tconst values = (expected as { $in?: unknown }).$in\n\t\treturn (\n\t\t\tArray.isArray(values) &&\n\t\t\tvalues.some((value) => value !== undefined && value !== null && Object.is(actual, value))\n\t\t)\n\t}\n\treturn Object.is(actual, expected)\n}\n\n/**\n * True when every field predicate in a collection scope matches the record.\n *\n * @param record - The record (or snapshot) to test\n * @param collectionScope - Field predicates for the record's collection\n * @returns Whether the record is inside the scope\n */\nexport function recordMatchesScopePredicates(\n\trecord: Record<string, unknown>,\n\tcollectionScope: Record<string, unknown>,\n): boolean {\n\tfor (const [field, expected] of Object.entries(collectionScope)) {\n\t\tif (!matchesScopePredicate(record[field], expected)) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunction asPlainRecord(value: unknown): Record<string, unknown> | null {\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) {\n\t\treturn null\n\t}\n\treturn value as Record<string, unknown>\n}\n","import type { Operation } from '@korajs/core'\nimport type { SyncScopeMap } from '../types'\nimport {\n\ttype ScopeSnapshotOptions,\n\tbuildScopeSnapshot,\n\trecordMatchesScopePredicates,\n} from './scope-snapshot'\n\n/**\n * Check whether an operation matches the given scope map.\n *\n * Rules:\n * - No scope map configured: operation is always in scope.\n * - Collection not present in scope map: operation is out of scope.\n * - Empty scope for a collection `{}`: no field restrictions, operation is in scope.\n * - Scope has field/value pairs: all must match in the operation's data snapshot.\n *\n * The snapshot is the operation's `data` (layered over `previousData` only with\n * `options.includePreviousData`), with `id` always taken from `op.recordId`,\n * which represents the record's state after the operation is applied. When an optional\n * `fullRecord` is provided (e.g., from the local store), its values fill in scope fields\n * that weren't included in the operation's data (critical for update operations where\n * `data` only contains changed fields).\n *\n * @param op - The operation to check\n * @param scopeMap - Per-collection scope filters, or undefined for no filtering\n * @param fullRecord - Optional full record state from the store, used to fill in scope\n * fields not present in the operation's partial data\n * @param options - Snapshot options; `includePreviousData` trusts the writer's\n * `previousData`, which is only safe for a client judging its own local view\n * @returns true if the operation is within scope\n */\nexport function operationMatchesScope(\n\top: Operation,\n\tscopeMap: SyncScopeMap | undefined,\n\tfullRecord?: Record<string, unknown> | null,\n\toptions: ScopeSnapshotOptions = {},\n): boolean {\n\tif (!scopeMap) return true\n\n\tconst collectionScope = scopeMap[op.collection]\n\t// Collection not present in scope map means it's out of scope\n\tif (!collectionScope) return false\n\n\t// Empty scope means no field restrictions\n\tif (Object.keys(collectionScope).length === 0) return true\n\n\t// The record identity is always op.recordId (assigned last by the shared\n\t// snapshot), so an op cannot claim an in-scope `id` through data/previousData.\n\treturn recordMatchesScopePredicates(buildScopeSnapshot(op, fullRecord, options), collectionScope)\n}\n\n/**\n * Filter operations to only those matching the given scope map.\n *\n * @param operations - Array of operations to filter\n * @param scopeMap - Per-collection scope filters\n * @returns Operations that match the scope\n */\nexport function filterOperationsByScope(\n\toperations: Operation[],\n\tscopeMap: SyncScopeMap | undefined,\n): Operation[] {\n\tif (!scopeMap) return operations\n\treturn operations.filter((op) => operationMatchesScope(op, scopeMap))\n}\n","import type { Operation } from '@korajs/core'\nimport { type ScopeSnapshotOptions, buildScopeSnapshot } from './scope-snapshot'\n\n/**\n * A live query filter that narrows which operations sync for a collection.\n */\nexport interface SyncQuerySubset {\n\tcollection: string\n\twhere: Record<string, unknown>\n}\n\nfunction recordMatchesWhere(\n\tsnapshot: Record<string, unknown>,\n\twhere: Record<string, unknown>,\n): boolean {\n\tfor (const [field, expected] of Object.entries(where)) {\n\t\tif (snapshot[field] !== expected) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n/**\n * Returns true when an operation matches at least one active query subset\n * for its collection. Collections without query subsets pass through.\n *\n * The writer's `previousData` is ignored unless `options.includePreviousData` is\n * set (see {@link ScopeSnapshotOptions}); the server never sets it.\n */\nexport function operationMatchesQuerySubsets(\n\top: Operation,\n\tsubsets: SyncQuerySubset[] | undefined,\n\tfullRecord?: Record<string, unknown> | null,\n\toptions: ScopeSnapshotOptions = {},\n): boolean {\n\tif (!subsets || subsets.length === 0) {\n\t\treturn true\n\t}\n\n\tconst collectionSubsets = subsets.filter((subset) => subset.collection === op.collection)\n\tif (collectionSubsets.length === 0) {\n\t\treturn true\n\t}\n\t// An empty predicate is collection-wide. It must also admit fieldless deletes,\n\t// which intentionally have no data snapshot to evaluate.\n\tif (collectionSubsets.some((subset) => Object.keys(subset.where).length === 0)) {\n\t\treturn true\n\t}\n\n\t// Shared snapshot: identity always comes from op.recordId, never from op fields.\n\tconst snapshot = buildScopeSnapshot(op, fullRecord, options)\n\treturn collectionSubsets.some((subset) => recordMatchesWhere(snapshot, subset.where))\n}\n\nfunction stableValue(value: unknown): unknown {\n\tif (Array.isArray(value)) return value.map(stableValue)\n\tif (value && typeof value === 'object') {\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(value as Record<string, unknown>)\n\t\t\t\t.filter(([, entry]) => entry !== undefined)\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([key, entry]) => [key, stableValue(entry)]),\n\t\t)\n\t}\n\treturn value\n}\n\nfunction whereContains(broad: Record<string, unknown>, narrow: Record<string, unknown>): boolean {\n\treturn Object.entries(broad).every(\n\t\t([field, expected]) =>\n\t\t\tJSON.stringify(stableValue(narrow[field])) === JSON.stringify(stableValue(expected)),\n\t)\n}\n\n/** Canonicalize equality subsets and remove subsets already covered by a broader one. */\nexport function dedupeQuerySubsets(subsets: SyncQuerySubset[]): SyncQuerySubset[] {\n\tconst normalized = subsets.map((subset) => ({\n\t\tcollection: subset.collection,\n\t\twhere: stableValue(subset.where) as Record<string, unknown>,\n\t}))\n\tconst result: SyncQuerySubset[] = []\n\tfor (const candidate of normalized) {\n\t\tif (\n\t\t\tnormalized.some(\n\t\t\t\t(other) =>\n\t\t\t\t\tother !== candidate &&\n\t\t\t\t\tother.collection === candidate.collection &&\n\t\t\t\t\twhereContains(other.where, candidate.where) &&\n\t\t\t\t\t(Object.keys(other.where).length < Object.keys(candidate.where).length ||\n\t\t\t\t\t\tJSON.stringify(other.where) === JSON.stringify(candidate.where)),\n\t\t\t)\n\t\t) {\n\t\t\tconst duplicateAlreadyKept = result.some(\n\t\t\t\t(item) =>\n\t\t\t\t\titem.collection === candidate.collection &&\n\t\t\t\t\tJSON.stringify(item.where) === JSON.stringify(candidate.where),\n\t\t\t)\n\t\t\tif (\n\t\t\t\tduplicateAlreadyKept ||\n\t\t\t\tnormalized.some(\n\t\t\t\t\t(other) =>\n\t\t\t\t\t\tother !== candidate &&\n\t\t\t\t\t\tother.collection === candidate.collection &&\n\t\t\t\t\t\tObject.keys(other.where).length < Object.keys(candidate.where).length &&\n\t\t\t\t\t\twhereContains(other.where, candidate.where),\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tcontinue\n\t\t}\n\t\tif (\n\t\t\t!result.some(\n\t\t\t\t(item) =>\n\t\t\t\t\titem.collection === candidate.collection &&\n\t\t\t\t\tJSON.stringify(item.where) === JSON.stringify(candidate.where),\n\t\t\t)\n\t\t)\n\t\t\tresult.push(candidate)\n\t}\n\treturn result.sort((a, b) =>\n\t\t`${a.collection}:${JSON.stringify(a.where)}`.localeCompare(\n\t\t\t`${b.collection}:${JSON.stringify(b.where)}`,\n\t\t),\n\t)\n}\n\n/** True when every record selected by `narrow` is also selected by `broad`. */\nexport function querySubsetContains(broad: SyncQuerySubset[], narrow: SyncQuerySubset[]): boolean {\n\tconst collections = new Set([\n\t\t...broad.map((subset) => subset.collection),\n\t\t...narrow.map((subset) => subset.collection),\n\t])\n\tfor (const collection of collections) {\n\t\tconst broadForCollection = broad.filter((subset) => subset.collection === collection)\n\t\tconst narrowForCollection = narrow.filter((subset) => subset.collection === collection)\n\t\t// No predicate for a collection means that collection is unrestricted.\n\t\tif (narrowForCollection.length === 0) {\n\t\t\tif (broadForCollection.length > 0) return false\n\t\t\tcontinue\n\t\t}\n\t\tif (broadForCollection.length === 0) continue\n\t\tif (\n\t\t\t!narrowForCollection.every((candidate) =>\n\t\t\t\tbroadForCollection.some((other) => whereContains(other.where, candidate.where)),\n\t\t\t)\n\t\t)\n\t\t\treturn false\n\t}\n\treturn true\n}\n","import type { DeltaCursor } from '../types'\n\nexport type { DeltaCursor }\n\nfunction bytesToBase64Url(bytes: Uint8Array): string {\n\tlet binary = ''\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte)\n\t}\n\treturn btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction base64UrlToBytes(value: string): Uint8Array {\n\tconst base64 = value.replace(/-/g, '+').replace(/_/g, '/')\n\tconst padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4)\n\tconst binary = atob(padded)\n\tconst bytes = new Uint8Array(binary.length)\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tbytes[i] = binary.charCodeAt(i)\n\t}\n\treturn bytes\n}\n\n/**\n * Encode a delta cursor for wire transfer or `_kora_meta` persistence.\n */\nexport function encodeDeltaCursor(cursor: DeltaCursor): string {\n\tconst json = JSON.stringify(cursor)\n\treturn bytesToBase64Url(new TextEncoder().encode(json))\n}\n\n/**\n * Decode a delta cursor string. Returns null when malformed.\n */\nexport function decodeDeltaCursor(value: string | undefined | null): DeltaCursor | null {\n\tif (!value) {\n\t\treturn null\n\t}\n\n\ttry {\n\t\tconst json = new TextDecoder().decode(base64UrlToBytes(value))\n\t\tconst parsed: unknown = JSON.parse(json)\n\t\tif (\n\t\t\ttypeof parsed !== 'object' ||\n\t\t\tparsed === null ||\n\t\t\ttypeof (parsed as DeltaCursor).lastOperationId !== 'string' ||\n\t\t\ttypeof (parsed as DeltaCursor).batchIndex !== 'number'\n\t\t) {\n\t\t\treturn null\n\t\t}\n\t\treturn parsed as DeltaCursor\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Slice a causally sorted operation list to resume after a cursor.\n */\nexport function sliceOperationsAfterCursor(\n\toperations: import('@korajs/core').Operation[],\n\tcursor: DeltaCursor | null,\n): import('@korajs/core').Operation[] {\n\tif (!cursor) {\n\t\treturn operations\n\t}\n\n\tconst index = operations.findIndex((op) => op.id === cursor.lastOperationId)\n\tif (index === -1) {\n\t\treturn operations\n\t}\n\n\treturn operations.slice(index + 1)\n}\n\n/**\n * Build a cursor from the last operation in a delta batch.\n */\nexport function createDeltaCursorFromBatch(\n\toperations: import('@korajs/core').Operation[],\n\tbatchIndex: number,\n): DeltaCursor | null {\n\tconst last = operations[operations.length - 1]\n\tif (!last) {\n\t\treturn null\n\t}\n\n\treturn {\n\t\tlastOperationId: last.id,\n\t\tbatchIndex,\n\t}\n}\n","/** Prefix for handshake rejection when client schema version is unsupported. */\nexport const SCHEMA_MISMATCH_PREFIX = 'SCHEMA_MISMATCH'\n\n/**\n * Returns true when a handshake `rejectReason` indicates unsupported schema version.\n */\nexport function isSchemaMismatchReject(rejectReason: string | undefined): boolean {\n\treturn rejectReason?.startsWith(SCHEMA_MISMATCH_PREFIX) === true\n}\n\n/**\n * Returns true when the client schema version is within the inclusive supported range.\n */\nexport function isClientSchemaVersionSupported(\n\tclientVersion: number,\n\tsupported: { min: number; max: number },\n): boolean {\n\treturn clientVersion >= supported.min && clientVersion <= supported.max\n}\n","/**\n * Sync protocol version this build speaks (decision D2: one bump carrying all wire\n * breaks). Version 2 adds:\n * - operation ids hashed with content-hash version 2 (CORE-1), with `hashVersion`\n * carried on the wire and verified on receive;\n * - the end-to-end encryption envelope v2 (`op.encrypted`, AES-GCM AAD, ENC-3 and\n * NEW-ENC-1);\n * - the sequence-reservation capability on every handshake (RT-37);\n * - `authoritativeNodeIds` and server-authored `foldState` (W7).\n *\n * A handshake without `protocolVersion` is version 1 (Kora <= beta.12). Servers accept\n * version-1 clients for one release (beta.13) with a deprecation warning.\n */\nexport const SYNC_PROTOCOL_VERSION = 2\n\n/** Protocol version assumed when a handshake or response does not declare one. */\nexport const LEGACY_SYNC_PROTOCOL_VERSION = 1\n\n/** Code of the server's deprecation warning for a version-1 client. */\nexport const PROTOCOL_V1_DEPRECATED = 'PROTOCOL_V1_DEPRECATED'\n\n/** Non-retriable rejection code for an operation whose id is not its content hash. */\nexport const INVALID_OPERATION_ID = 'INVALID_OPERATION_ID'\n\n/**\n * Non-retriable rejection code for a plaintext operation uploaded to a server that\n * requires the encryption envelope.\n */\nexport const PLAINTEXT_REJECTED = 'PLAINTEXT_REJECTED'\n\n/**\n * The protocol version a handshake (or handshake response) declares.\n *\n * @param declared - The message's `protocolVersion`\n * @returns The declared version, or 1 when absent or malformed\n */\nexport function declaredProtocolVersion(declared: unknown): number {\n\treturn typeof declared === 'number' && Number.isInteger(declared) && declared >= 1\n\t\t? declared\n\t\t: LEGACY_SYNC_PROTOCOL_VERSION\n}\n","import type { WrappedKeyRecord } from './key-record'\n\n/**\n * Key-distribution side channel of the sync protocol (ENC-1, D4b). Accepted only after\n * a successful handshake. The server answers for the session's own principal: a\n * message never names a user, so no session can read or write another user's record.\n * JSON on every wire format (the records are small and rare).\n */\n\n/** Client -> server: fetch the session principal's record of one keyring. */\nexport interface EncryptionKeyRequestMessage {\n\ttype: 'encryption-key-request'\n\tmessageId: string\n\t/** Correlates the response. */\n\trequestId: string\n\tkeyring: string\n}\n\n/** Client -> server: write the record with compare-and-set on its revision. */\nexport interface EncryptionKeyPutMessage {\n\ttype: 'encryption-key-put'\n\tmessageId: string\n\trequestId: string\n\tkeyring: string\n\t/** The full new record; its revision must be `expectedRevision + 1`. */\n\trecord: WrappedKeyRecord\n\t/** The revision being replaced (0: create). */\n\texpectedRevision: number\n}\n\n/** Status of a key-service response. */\nexport type EncryptionKeyResponseStatus =\n\t| 'ok'\n\t| 'conflict'\n\t| 'forbidden'\n\t| 'invalid'\n\t| 'unsupported'\n\t| 'throttled'\n\n/**\n * Server -> client: the answer to a request or put (with `requestId`), or an\n * unsolicited push of a record another device of the same principal wrote (without).\n */\nexport interface EncryptionKeyResponseMessage {\n\ttype: 'encryption-key-response'\n\tmessageId: string\n\trequestId?: string\n\tkeyring: string\n\tstatus: EncryptionKeyResponseStatus\n\t/** The current record (null when none exists, or on refusal). */\n\trecord: WrappedKeyRecord | null\n\t/** Human-readable reason for a refusal. */\n\tmessage?: string\n\t/**\n\t * With `record: null`: key ids the server's stored operations of this owner are\n\t * encrypted with (a sample, RT-104). Non-empty means encrypted history exists, so\n\t * the record was lost: a new device waits for one that holds the ring.\n\t */\n\tknownKeyIds?: string[]\n}\n\n/** Every key-distribution message. */\nexport type EncryptionKeyMessage =\n\t| EncryptionKeyRequestMessage\n\t| EncryptionKeyPutMessage\n\t| EncryptionKeyResponseMessage\n\nconst STATUSES: ReadonlySet<string> = new Set([\n\t'ok',\n\t'conflict',\n\t'forbidden',\n\t'invalid',\n\t'unsupported',\n\t'throttled',\n])\n\n/** Whether a message type belongs to the key-distribution channel. */\nexport function isEncryptionKeyMessageType(type: unknown): boolean {\n\treturn (\n\t\ttype === 'encryption-key-request' ||\n\t\ttype === 'encryption-key-put' ||\n\t\ttype === 'encryption-key-response'\n\t)\n}\n\n/**\n * Structural guard of the key-distribution messages (records are validated in full\n * by the receiver).\n */\nexport function isEncryptionKeyMessage(value: unknown): value is EncryptionKeyMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\tif (typeof msg.messageId !== 'string' || typeof msg.keyring !== 'string') return false\n\tswitch (msg.type) {\n\t\tcase 'encryption-key-request':\n\t\t\treturn typeof msg.requestId === 'string'\n\t\tcase 'encryption-key-put':\n\t\t\treturn (\n\t\t\t\ttypeof msg.requestId === 'string' &&\n\t\t\t\ttypeof msg.record === 'object' &&\n\t\t\t\tmsg.record !== null &&\n\t\t\t\ttypeof msg.expectedRevision === 'number'\n\t\t\t)\n\t\tcase 'encryption-key-response':\n\t\t\treturn (\n\t\t\t\t(msg.requestId === undefined || typeof msg.requestId === 'string') &&\n\t\t\t\ttypeof msg.status === 'string' &&\n\t\t\t\tSTATUSES.has(msg.status) &&\n\t\t\t\t(msg.knownKeyIds === undefined ||\n\t\t\t\t\t(Array.isArray(msg.knownKeyIds) &&\n\t\t\t\t\t\tmsg.knownKeyIds.every((keyId) => typeof keyId === 'string'))) &&\n\t\t\t\t(msg.record === null || (typeof msg.record === 'object' && msg.record !== undefined))\n\t\t\t)\n\t\tdefault:\n\t\t\treturn false\n\t}\n}\n","import type {\n\tAtomicOp,\n\tEncryptedOperationEnvelope,\n\tHLCTimestamp,\n\tOperationType,\n} from '@korajs/core'\nimport type { EncryptionKeyMessage } from '../encryption/key-messages'\nimport { isEncryptionKeyMessage } from '../encryption/key-messages'\nimport type { SyncQuerySubset } from '../scopes/query-subset'\n\nexport type WireFormat = 'json' | 'protobuf'\n\n/**\n * Wire-format operation. Plain object (no Map) for JSON serialization.\n * Maps 1:1 with Operation, but uses Record instead of Map for version vectors.\n */\nexport interface SerializedOperation {\n\tid: string\n\tnodeId: string\n\ttype: OperationType\n\tcollection: string\n\trecordId: string\n\tdata: Record<string, unknown> | null\n\tpreviousData: Record<string, unknown> | null\n\ttimestamp: HLCTimestamp\n\tsequenceNumber: number\n\tcausalDeps: string[]\n\tschemaVersion: number\n\t/** Atomic operation intents, present only when atomic ops were used. */\n\tatomicOps?: Record<string, AtomicOp>\n\t/** Groups this operation with others in an atomic transaction. */\n\ttransactionId?: string\n\t/** Human-readable name for the mutation group. For DevTools display. */\n\tmutationName?: string\n\t/** Per-field versions of a server scope-entry insert (RT-27). Server-authored only. */\n\tfieldVersions?: Record<string, HLCTimestamp>\n\t/**\n\t * Content-hash version of `id` (CORE-1, protocol v2). Absent means 1. Protobuf\n\t * operation field 14.\n\t */\n\thashVersion?: 1 | 2\n\t/**\n\t * Serialized fold state of a server scope-entry operation (W7). Server-authored\n\t * only: the server strips it from device uploads. Protobuf operation field 15.\n\t */\n\tfoldState?: string\n\t/**\n\t * End-to-end encryption envelope v2 (W9). `data` is then null or holds only the\n\t * documented cleartext scope fields. Protobuf operation field 16 (JSON).\n\t */\n\tencrypted?: EncryptedOperationEnvelope\n}\n\n/**\n * Handshake message sent by client to initiate sync.\n */\nexport interface HandshakeMessage {\n\ttype: 'handshake'\n\tmessageId: string\n\tnodeId: string\n\t/** Version vector as plain object (nodeId -> sequence number) */\n\tversionVector: Record<string, number>\n\tschemaVersion: number\n\tauthToken?: string\n\tsupportedWireFormats?: WireFormat[]\n\t/** Per-collection sync scope filters. Limits which records are synced to this client. */\n\tsyncScope?: Record<string, Record<string, unknown>>\n\t/** Base64url-encoded delta cursor for resuming paginated initial sync */\n\tdeltaCursor?: string\n\t/** Live query filters that further narrow synced data within auth/schema scope */\n\tsyncQueries?: SyncQuerySubset[]\n\t/**\n\t * The client's persisted delivery watermark: the highest server delivery sequence\n\t * up to which it has contiguously applied every in-scope operation. Omitted (or 0)\n\t * on first sync. When present, the server resumes the gap-free server->client\n\t * stream from just after it, superseding the version-vector delta and the resume\n\t * cursor for the server->client direction. Optional, so old servers ignore it.\n\t */\n\tlastDeliverySequence?: number\n\t/**\n\t * SYNC-11: canonical key (`scopeViewKey`) of the downlink scope the server accepted at\n\t * this client's last handshake, when it differs from `syncScope`. A server that\n\t * resolves a scope with the same key resumes the delivery stream from\n\t * `acceptedScopeWatermark` instead of restarting it from 0. Optional; old servers\n\t * ignore it.\n\t */\n\tacceptedScopeKey?: string\n\t/** The client's delivery watermark for the view named by `acceptedScopeKey`. */\n\tacceptedScopeWatermark?: number\n\t/** Opt in to client-local removal when records leave the accepted downlink view. */\n\tscopeExitPolicy?: 'retain' | 'retract'\n\t/**\n\t * The per-device secret the server issued at this node id's first claim\n\t * (`HandshakeResponseMessage.nodeToken`). An anonymous principal must present it\n\t * to reconnect with its node id, so another anonymous client cannot take the node\n\t * over (RT-12). Stored by the client next to its node id; never sent elsewhere.\n\t */\n\tnodeToken?: string\n\t/**\n\t * Set by a client transport that understands `heartbeat` messages (LMS #12). Only\n\t * then does the server send them, so an older client never receives a message type\n\t * it cannot decode. Old servers ignore it.\n\t */\n\tsupportsHeartbeat?: boolean\n\t/**\n\t * Capability (RT-37): this client reserves each operation's sequence number inside\n\t * the same local transaction that writes the operation, so it never puts two\n\t * different operations under one `(nodeId, sequenceNumber)`. Only for such a client\n\t * does the server refuse a second, different operation under a held sequence\n\t * (`SEQUENCE_CONFLICT`). A client that omits it (Kora <= beta.12, which could give\n\t * two concurrent transactions one number) is served as legacy: such a pair is\n\t * stored, both operations are delivered, and a warning is logged. Old servers\n\t * ignore it. Protobuf field 45.\n\t */\n\tsequenceReservation?: boolean\n\t/**\n\t * Sync protocol version the client speaks (`SYNC_PROTOCOL_VERSION`). Absent means 1\n\t * (Kora <= beta.12). A protocol-2 client sends hash-version-2 operation ids, the\n\t * encryption envelope v2 and always `sequenceReservation`. Protobuf field 47.\n\t */\n\tprotocolVersion?: number\n}\n\n/**\n * Application-level liveness message (LMS #12). A server sends it every\n * `HandshakeResponseMessage.heartbeatIntervalMs` to a client that set\n * `HandshakeMessage.supportsHeartbeat`. Browsers cannot see WebSocket ping frames, so\n * this is how a client notices a half-open connection: no inbound traffic for about\n * two intervals means the connection is dead. Carries no data; never acknowledged.\n */\nexport interface HeartbeatMessage {\n\ttype: 'heartbeat'\n\tmessageId: string\n}\n\n/**\n * Server response to a handshake.\n */\nexport interface HandshakeResponseMessage {\n\ttype: 'handshake-response'\n\tmessageId: string\n\tnodeId: string\n\tversionVector: Record<string, number>\n\tschemaVersion: number\n\taccepted: boolean\n\trejectReason?: string\n\t/** Inclusive minimum schema version the server accepts (present when rejected for schema mismatch). */\n\tsupportedSchemaMin?: number\n\t/** Inclusive maximum schema version the server accepts (present when rejected for schema mismatch). */\n\tsupportedSchemaMax?: number\n\tselectedWireFormat?: WireFormat\n\t/** The server-accepted per-collection sync scope. Confirms what data will be synced. */\n\tacceptedScope?: Record<string, Record<string, unknown>>\n\t/** Server-authoritative scope for records delivered to this client. */\n\tacceptedDownlinkScopes?: Record<string, Record<string, unknown>>\n\t/** Server-authoritative scope for operations uploaded by this client. */\n\tacceptedUplinkScopes?: Record<string, Record<string, unknown>>\n\t/** Server wall-clock time (ms since epoch) at response creation. Lets clients measure their own clock skew. */\n\tserverTime?: number\n\t/**\n\t * Whether the server persists blob bytes centrally. When true, a client\n\t * automatically uploads the bytes behind its `blob` fields so they remain\n\t * available to other devices even after the authoring device goes offline.\n\t */\n\tblobStorageEnabled?: boolean\n\t/**\n\t * The server's highest delivery sequence at handshake time. A client whose\n\t * persisted delivery watermark exceeds this (the server's operation log was rolled\n\t * back, for example by a backup restore) resets its watermark to 0 so it resyncs\n\t * from the beginning instead of sitting above a frontier that no longer exists.\n\t */\n\tserverMaxDeliverySequence?: number\n\t/**\n\t * A per-device secret issued when this handshake made the first claim of the node\n\t * id for an anonymous principal (RT-12). The client stores it next to its node id\n\t * and presents it in every later handshake (`HandshakeMessage.nodeToken`).\n\t *\n\t * The claim stays provisional until the client proves it saved the token, by\n\t * presenting it in a later handshake or by echoing it in an acknowledgment of this\n\t * response (`AcknowledgmentMessage.nodeToken`) right after persisting it (RT-21).\n\t */\n\tnodeToken?: string\n\t/**\n\t * Peer-relay servers (no central blob storage) set this to ask the client to push\n\t * the bytes behind its blob references anyway: the server verifies them against\n\t * their hash, records possession, and drops them (RT-23).\n\t */\n\tblobPossessionProof?: boolean\n\t/**\n\t * Interval of the server's `heartbeat` messages, in ms, present when the client set\n\t * `supportsHeartbeat` and the server sends them (LMS #12).\n\t */\n\theartbeatIntervalMs?: number\n\t/**\n\t * Sync protocol version the server speaks. Absent means 1 (a beta.12-era server).\n\t * Protobuf field 47.\n\t */\n\tprotocolVersion?: number\n\t/**\n\t * Explicit authoritative node ids (RT-75): legacy server node ids from before protocol\n\t * 2 and extras configured on the server store. Every `kora:server:` node id is\n\t * authoritative by its prefix and is not listed. Only operations of authoritative\n\t * nodes may carry server-authored metadata (`fieldVersions`, `foldState`); the client\n\t * keeps the union of every id it learns (`SyncStatePersistence.saveAuthoritativeNodeIds`).\n\t * Absent from older servers. Protobuf field 46 (repeated).\n\t */\n\tauthoritativeNodeIds?: string[]\n\t/**\n\t * Explicit authoritative node ids the deployment revoked (RT-81, server config\n\t * `revokedAuthoritativeNodeIds`). A client removes them from the authority union it\n\t * keeps, never learns them again, and re-folds the affected records; the server\n\t * never accepts them as device node ids. Absent when nothing was revoked, and from\n\t * older servers. Protobuf field 48 (repeated).\n\t */\n\trevokedAuthoritativeNodeIds?: string[]\n}\n\n/**\n * Batch of operations sent during delta exchange or streaming.\n */\nexport interface OperationBatchMessage {\n\ttype: 'operation-batch'\n\tmessageId: string\n\toperations: SerializedOperation[]\n\t/** Client-local authorization removals; these are never domain delete operations. */\n\tretractions?: ScopeRetraction[]\n\t/** True if this is the last batch in the delta exchange phase */\n\tisFinal: boolean\n\t/** Index of this batch (0-based) for ordering */\n\tbatchIndex: number\n\t/** Base64url-encoded cursor marking the last operation in this batch */\n\tcursor?: string\n\t/** Total batches in this delta exchange (for progress reporting) */\n\ttotalBatches?: number\n\t/**\n\t * Delivery-watermark chaining (server->client). `baseDeliverySequence` is the\n\t * delivery sequence the client must already hold for this batch to apply in order\n\t * (it equals the previous batch's `maxDeliverySequence`, or the client's reported\n\t * watermark for the first batch after a handshake). `maxDeliverySequence` is the\n\t * highest delivery sequence carried by this batch. The client applies the batch\n\t * only when its watermark equals `baseDeliverySequence`, then advances the\n\t * watermark to `maxDeliverySequence`. A dropped batch breaks the chain and stalls\n\t * the watermark, so the reconnect resend recovers it. Present only on batches the\n\t * server sends as part of the delivery stream; absent on legacy version-vector\n\t * batches.\n\t */\n\tbaseDeliverySequence?: number\n\tmaxDeliverySequence?: number\n}\n\nexport interface ScopeRetraction {\n\tcollection: string\n\trecordId: string\n}\n\n/**\n * Acknowledgment of a received message.\n */\nexport interface AcknowledgmentMessage {\n\ttype: 'acknowledgment'\n\tmessageId: string\n\tacknowledgedMessageId: string\n\tlastSequenceNumber: number\n\t/**\n\t * The acknowledged batch's `maxDeliverySequence`, echoed so the server can advance\n\t * this client's durable delivery watermark. Present only when acking a delivery-\n\t * stream batch. `lastSequenceNumber` stays for existing per-node behavior.\n\t */\n\tdeliverySequence?: number\n\t/**\n\t * Claim confirmation (RT-21): the node token from a handshake response, echoed\n\t * once the client has persisted it, so the server makes the provisional claim of\n\t * the node id permanent. Sent with `acknowledgedMessageId` set to the response id.\n\t */\n\tnodeToken?: string\n}\n\n/**\n * Error message from the server or client.\n */\nexport interface ErrorMessage {\n\ttype: 'error'\n\tmessageId: string\n\tcode: string\n\tmessage: string\n\tretriable: boolean\n}\n\n/**\n * Server-to-client rejection of ONE specific operation the client submitted.\n *\n * Unlike {@link ErrorMessage}, which is connection-level, this is tied to an\n * `operationId` so the client can divert exactly that operation out of its\n * pending outbound queue and into a durable rejected-operations store — kept and\n * explainable rather than silently retried forever or lost on the batch ack. The\n * operation never entered the server's authoritative log, so no other replica\n * ever sees it; the submitter is told so its optimistic local copy can be\n * reconciled (rolled back or resubmitted) by the app.\n */\nexport interface OperationRejectedMessage {\n\ttype: 'operation-rejected'\n\tmessageId: string\n\t/** Content-addressed id of the rejected operation. */\n\toperationId: string\n\t/** Collection the rejected operation targeted (for app routing / display). */\n\tcollection: string\n\t/** Record the rejected operation targeted. */\n\trecordId: string\n\t/** Stable, machine-readable reason code. */\n\tcode: string\n\t/** Human-readable explanation. */\n\tmessage: string\n\t/** Whether resubmitting the identical operation may later succeed. */\n\tretriable: boolean\n}\n\n/**\n * Awareness state for a single client (cursor position, user info).\n * Wire-format representation for JSON transport.\n * Ephemeral -- not persisted, only shared with connected peers.\n */\nexport interface AwarenessStateWire {\n\tuser: {\n\t\tname: string\n\t\tcolor: string\n\t\tavatar?: string\n\t}\n\tcursor?: {\n\t\tcollection: string\n\t\trecordId: string\n\t\tfield: string\n\t\tanchor: number\n\t\thead: number\n\t}\n}\n\n/**\n * Awareness update message. Carries ephemeral presence data (cursors, user info).\n * Processed separately from operation sync -- never persisted.\n */\nexport interface AwarenessUpdateMessage {\n\ttype: 'awareness-update'\n\tmessageId: string\n\t/** Client ID of the sender */\n\tclientId: number\n\t/** Map of clientId -> state (null means removal) */\n\tstates: Record<string, AwarenessStateWire | null>\n}\n\n/**\n * Incremental Yjs update for a richtext field. Ephemeral side channel for large documents.\n * JSON transport only; not persisted on the server operation log.\n */\nexport interface YjsDocUpdateMessage {\n\ttype: 'yjs-doc-update'\n\tmessageId: string\n\tcollection: string\n\trecordId: string\n\tfield: string\n\t/** Base64-encoded Yjs update binary */\n\tupdate: string\n}\n\n/**\n * Request for a single blob chunk by content hash. Ephemeral side channel for\n * out-of-band blob transfer over the sync connection; never persisted in the\n * operation log (durable state is the BlobRef inside a record's fields).\n *\n * Knowing a hash is not a capability: the server serves (from its central store)\n * or forwards a request only when a live record inside the requester's download\n * scope references the hash (as a blob, a manifest, or a chunk of one), forwards it\n * only to peers whose own scope references it, accepts the answer only from a peer\n * it asked and only when the bytes hash to the requested hash. Any other request\n * is answered `bytes: null`, exactly like an unknown hash.\n */\nexport interface BlobChunkRequestMessage {\n\ttype: 'blob-chunk-request'\n\tmessageId: string\n\t/** Correlates the response to this request. */\n\trequestId: string\n\t/** SHA-256 hash of the requested chunk. */\n\thash: string\n}\n\n/**\n * Response to a {@link BlobChunkRequestMessage}. Carries the chunk bytes, or\n * null when the responder does not hold that hash.\n */\nexport interface BlobChunkResponseMessage {\n\ttype: 'blob-chunk-response'\n\tmessageId: string\n\t/** Echoes the request's `requestId` for correlation. */\n\trequestId: string\n\t/** Base64-encoded chunk bytes, or null when the responder does not hold the hash. */\n\tbytes: string | null\n\t/**\n\t * True when the server refused the request for rate (RT-24), not because the\n\t * chunk is missing: retry after `retryAfterMs`. Absent (or false) otherwise.\n\t */\n\tthrottled?: boolean\n\t/** With `throttled`: milliseconds until the request may be retried. */\n\tretryAfterMs?: number\n}\n\n/**\n * Unsolicited upload of a blob chunk (or a blob manifest) from a client to the\n * server, so the server can persist it centrally and serve it to other devices\n * even after the authoring device disconnects. Content-addressed: the receiver\n * verifies the bytes hash to `hash`.\n */\nexport interface BlobChunkPushMessage {\n\ttype: 'blob-chunk-push'\n\tmessageId: string\n\t/** SHA-256 hash of the pushed bytes (a chunk hash or a manifest hash). */\n\thash: string\n\t/** Base64-encoded bytes. */\n\tbytes: string\n}\n\n/**\n * Union of all sync protocol messages.\n */\nexport type SyncMessage =\n\t| HandshakeMessage\n\t| HandshakeResponseMessage\n\t| OperationBatchMessage\n\t| AcknowledgmentMessage\n\t| ErrorMessage\n\t| OperationRejectedMessage\n\t| AwarenessUpdateMessage\n\t| YjsDocUpdateMessage\n\t| BlobChunkRequestMessage\n\t| BlobChunkResponseMessage\n\t| BlobChunkPushMessage\n\t| HeartbeatMessage\n\t| EncryptionKeyMessage\n\n// --- Type Guards ---\n\n/**\n * Check if an unknown value is a valid SyncMessage.\n */\nexport function isSyncMessage(value: unknown): value is SyncMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\tif (typeof msg.type !== 'string' || typeof msg.messageId !== 'string') return false\n\tswitch (msg.type) {\n\t\tcase 'handshake':\n\t\t\treturn isHandshakeMessage(value)\n\t\tcase 'handshake-response':\n\t\t\treturn isHandshakeResponseMessage(value)\n\t\tcase 'operation-batch':\n\t\t\treturn isOperationBatchMessage(value)\n\t\tcase 'acknowledgment':\n\t\t\treturn isAcknowledgmentMessage(value)\n\t\tcase 'error':\n\t\t\treturn isErrorMessage(value)\n\t\tcase 'operation-rejected':\n\t\t\treturn isOperationRejectedMessage(value)\n\t\tcase 'awareness-update':\n\t\t\treturn isAwarenessUpdateMessage(value)\n\t\tcase 'yjs-doc-update':\n\t\t\treturn isYjsDocUpdateMessage(value)\n\t\tcase 'blob-chunk-request':\n\t\t\treturn isBlobChunkRequestMessage(value)\n\t\tcase 'blob-chunk-response':\n\t\t\treturn isBlobChunkResponseMessage(value)\n\t\tcase 'blob-chunk-push':\n\t\t\treturn isBlobChunkPushMessage(value)\n\t\tcase 'heartbeat':\n\t\t\treturn true\n\t\tcase 'encryption-key-request':\n\t\tcase 'encryption-key-put':\n\t\tcase 'encryption-key-response':\n\t\t\treturn isEncryptionKeyMessage(value)\n\t\tdefault:\n\t\t\treturn false\n\t}\n}\n\n/**\n * Check if a value is a HandshakeMessage.\n */\nexport function isHandshakeMessage(value: unknown): value is HandshakeMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'handshake' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.nodeId === 'string' &&\n\t\ttypeof msg.versionVector === 'object' &&\n\t\tmsg.versionVector !== null &&\n\t\t!Array.isArray(msg.versionVector) &&\n\t\ttypeof msg.schemaVersion === 'number'\n\t)\n}\n\n/**\n * Check if a value is a HandshakeResponseMessage.\n */\nexport function isHandshakeResponseMessage(value: unknown): value is HandshakeResponseMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'handshake-response' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.nodeId === 'string' &&\n\t\ttypeof msg.versionVector === 'object' &&\n\t\tmsg.versionVector !== null &&\n\t\t!Array.isArray(msg.versionVector) &&\n\t\ttypeof msg.schemaVersion === 'number' &&\n\t\ttypeof msg.accepted === 'boolean'\n\t)\n}\n\n/**\n * Check if a value is an OperationBatchMessage.\n */\nexport function isOperationBatchMessage(value: unknown): value is OperationBatchMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'operation-batch' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\tArray.isArray(msg.operations) &&\n\t\ttypeof msg.isFinal === 'boolean' &&\n\t\ttypeof msg.batchIndex === 'number'\n\t)\n}\n\n/**\n * Check if a value is an AcknowledgmentMessage.\n */\nexport function isAcknowledgmentMessage(value: unknown): value is AcknowledgmentMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'acknowledgment' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.acknowledgedMessageId === 'string' &&\n\t\ttypeof msg.lastSequenceNumber === 'number'\n\t)\n}\n\n/**\n * Check if a value is an ErrorMessage.\n */\nexport function isErrorMessage(value: unknown): value is ErrorMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'error' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.code === 'string' &&\n\t\ttypeof msg.message === 'string' &&\n\t\ttypeof msg.retriable === 'boolean'\n\t)\n}\n\n/**\n * Check if a value is an OperationRejectedMessage.\n */\nexport function isOperationRejectedMessage(value: unknown): value is OperationRejectedMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'operation-rejected' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.operationId === 'string' &&\n\t\ttypeof msg.collection === 'string' &&\n\t\ttypeof msg.recordId === 'string' &&\n\t\ttypeof msg.code === 'string' &&\n\t\ttypeof msg.message === 'string' &&\n\t\ttypeof msg.retriable === 'boolean'\n\t)\n}\n\n/**\n * Check if a value is an AwarenessUpdateMessage.\n */\nexport function isAwarenessUpdateMessage(value: unknown): value is AwarenessUpdateMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'awareness-update' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.clientId === 'number' &&\n\t\ttypeof msg.states === 'object' &&\n\t\tmsg.states !== null &&\n\t\t!Array.isArray(msg.states)\n\t)\n}\n\n/**\n * Check if a value is a YjsDocUpdateMessage.\n */\nexport function isYjsDocUpdateMessage(value: unknown): value is YjsDocUpdateMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'yjs-doc-update' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.collection === 'string' &&\n\t\ttypeof msg.recordId === 'string' &&\n\t\ttypeof msg.field === 'string' &&\n\t\ttypeof msg.update === 'string'\n\t)\n}\n\n/**\n * Check if a value is a BlobChunkRequestMessage.\n */\nexport function isBlobChunkRequestMessage(value: unknown): value is BlobChunkRequestMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'blob-chunk-request' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.requestId === 'string' &&\n\t\ttypeof msg.hash === 'string'\n\t)\n}\n\n/**\n * Check if a value is a BlobChunkResponseMessage.\n */\nexport function isBlobChunkResponseMessage(value: unknown): value is BlobChunkResponseMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'blob-chunk-response' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.requestId === 'string' &&\n\t\t(typeof msg.bytes === 'string' || msg.bytes === null)\n\t)\n}\n\n/**\n * Check if a value is a BlobChunkPushMessage.\n */\nexport function isBlobChunkPushMessage(value: unknown): value is BlobChunkPushMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'blob-chunk-push' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.hash === 'string' &&\n\t\ttypeof msg.bytes === 'string'\n\t)\n}\n","import { SyncError } from '@korajs/core'\nimport type {\n\tEncryptedOperationEnvelope,\n\tHLCTimestamp,\n\tOperation,\n\tVersionVector,\n} from '@korajs/core'\n// protobufjs/minimal is CJS — named ESM imports fail in some runtimes (tsx, Node ESM).\n// Use a default import for the runtime values and type aliases for annotations.\n// The explicit .js extension is required: protobufjs has no \"exports\" map, so\n// Node ESM cannot resolve the extensionless \"protobufjs/minimal\" subpath\n// (bundlers tolerate it, which is why this only breaks in plain Node).\nimport protobuf from 'protobufjs/minimal.js'\n\ntype Reader = protobuf.Reader\ntype Writer = protobuf.Writer\nconst { Reader, Writer } = protobuf\nimport { isEncryptionKeyMessageType } from '../encryption/key-messages'\nimport type {\n\tAcknowledgmentMessage,\n\tErrorMessage,\n\tHandshakeMessage,\n\tHandshakeResponseMessage,\n\tOperationBatchMessage,\n\tSerializedOperation,\n\tSyncMessage,\n\tWireFormat,\n} from './messages'\nimport { isSyncMessage } from './messages'\n\nexport type EncodedMessage = string | Uint8Array\n\n/**\n * Interface for encoding/decoding sync protocol messages.\n */\nexport interface MessageSerializer {\n\tencode(message: SyncMessage): EncodedMessage\n\tdecode(data: string | Uint8Array | ArrayBuffer): SyncMessage\n\tencodeOperation(op: Operation): SerializedOperation\n\tdecodeOperation(serialized: SerializedOperation): Operation\n\tsetWireFormat?(format: WireFormat): void\n\tgetWireFormat?(): WireFormat\n}\n\n/**\n * Convert a VersionVector (Map) to a plain object for wire transmission.\n */\nexport function versionVectorToWire(vector: VersionVector): Record<string, number> {\n\tconst wire: Record<string, number> = {}\n\tfor (const [nodeId, seq] of vector) {\n\t\twire[nodeId] = seq\n\t}\n\treturn wire\n}\n\n/**\n * Convert a wire-format version vector (plain object) back to a VersionVector (Map).\n */\nexport function wireToVersionVector(wire: Record<string, number>): VersionVector {\n\treturn new Map(Object.entries(wire))\n}\n\nconst WIRE_BYTES_KEY = '__kora_bytes__'\n\nfunction toBase64(bytes: Uint8Array): string {\n\tlet binary = ''\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte)\n\t}\n\treturn btoa(binary)\n}\n\nfunction fromBase64(value: string): Uint8Array {\n\tconst binary = atob(value)\n\tconst bytes = new Uint8Array(binary.length)\n\tfor (let index = 0; index < binary.length; index++) {\n\t\tbytes[index] = binary.charCodeAt(index)\n\t}\n\treturn bytes\n}\n\nfunction serializeWireRecord(\n\trecord: Record<string, unknown> | null,\n): Record<string, unknown> | null {\n\tif (!record) {\n\t\treturn null\n\t}\n\n\tconst out: Record<string, unknown> = {}\n\tfor (const [key, value] of Object.entries(record)) {\n\t\tout[key] = serializeWireValue(value)\n\t}\n\treturn out\n}\n\nfunction serializeWireValue(value: unknown): unknown {\n\tif (value instanceof Uint8Array) {\n\t\treturn { [WIRE_BYTES_KEY]: toBase64(value) }\n\t}\n\tif (value instanceof ArrayBuffer) {\n\t\treturn { [WIRE_BYTES_KEY]: toBase64(new Uint8Array(value)) }\n\t}\n\treturn value\n}\n\nfunction deserializeWireRecord(\n\trecord: Record<string, unknown> | null,\n): Record<string, unknown> | null {\n\tif (!record) {\n\t\treturn null\n\t}\n\n\tconst out: Record<string, unknown> = {}\n\tfor (const [key, value] of Object.entries(record)) {\n\t\tout[key] = deserializeWireValue(value)\n\t}\n\treturn out\n}\n\n/**\n * A top-level op-data value in the wire's binary form: an object whose ONLY member is\n * `__kora_bytes__` (a base64 string), as `serializeWireValue` writes a `Uint8Array`\n * (beta.12 richtext). Any other object is data and is returned as is.\n *\n * Two earlier rules corrupted json and object values and are gone (value domain, RT-86):\n * an object that merely CONTAINED `__kora_bytes__` lost its other members, and an object\n * whose keys were all integers with number values (`{ \"1\": 2 }`, even `{ \" \": 0 }`) was\n * read as the indexed byte record of the 0.5 internal beta and became a `Uint8Array`.\n * The local API refuses a field value that is exactly the binary form, so nothing a\n * device writes can be mistaken for it.\n */\nfunction deserializeWireValue(value: unknown): unknown {\n\tif (isWireBytesValue(value)) {\n\t\treturn fromBase64(value[WIRE_BYTES_KEY])\n\t}\n\treturn value\n}\n\nfunction isWireBytesValue(value: unknown): value is Record<typeof WIRE_BYTES_KEY, string> {\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) return false\n\tconst keys = Object.keys(value)\n\treturn (\n\t\tkeys.length === 1 &&\n\t\tkeys[0] === WIRE_BYTES_KEY &&\n\t\ttypeof (value as Record<string, unknown>)[WIRE_BYTES_KEY] === 'string'\n\t)\n}\n\n/**\n * JSON-based message serializer.\n */\nexport class JsonMessageSerializer implements MessageSerializer {\n\tencode(message: SyncMessage): string {\n\t\treturn JSON.stringify(message)\n\t}\n\n\tdecode(data: string | Uint8Array | ArrayBuffer): SyncMessage {\n\t\tconst text = decodeTextPayload(data)\n\n\t\tlet parsed: unknown\n\t\ttry {\n\t\t\tparsed = JSON.parse(text)\n\t\t} catch {\n\t\t\tthrow new SyncError('Failed to decode sync message: invalid JSON', {\n\t\t\t\tdataLength: text.length,\n\t\t\t})\n\t\t}\n\n\t\tif (!isSyncMessage(parsed)) {\n\t\t\tthrow new SyncError('Failed to decode sync message: invalid message structure', {\n\t\t\t\treceivedType:\n\t\t\t\t\ttypeof parsed === 'object' && parsed !== null\n\t\t\t\t\t\t? (parsed as Record<string, unknown>).type\n\t\t\t\t\t\t: typeof parsed,\n\t\t\t})\n\t\t}\n\n\t\treturn parsed\n\t}\n\n\tencodeOperation(op: Operation): SerializedOperation {\n\t\treturn {\n\t\t\tid: op.id,\n\t\t\tnodeId: op.nodeId,\n\t\t\ttype: op.type,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tdata: serializeWireRecord(op.data),\n\t\t\tpreviousData: serializeWireRecord(op.previousData),\n\t\t\ttimestamp: {\n\t\t\t\twallTime: op.timestamp.wallTime,\n\t\t\t\tlogical: op.timestamp.logical,\n\t\t\t\tnodeId: op.timestamp.nodeId,\n\t\t\t},\n\t\t\tsequenceNumber: op.sequenceNumber,\n\t\t\tcausalDeps: [...op.causalDeps],\n\t\t\tschemaVersion: op.schemaVersion,\n\t\t\t...(op.atomicOps !== undefined ? { atomicOps: op.atomicOps } : {}),\n\t\t\t...(op.transactionId !== undefined ? { transactionId: op.transactionId } : {}),\n\t\t\t...(op.mutationName !== undefined ? { mutationName: op.mutationName } : {}),\n\t\t\t...(op.fieldVersions !== undefined\n\t\t\t\t? { fieldVersions: copyFieldVersions(op.fieldVersions) }\n\t\t\t\t: {}),\n\t\t\t...(op.hashVersion !== undefined ? { hashVersion: op.hashVersion } : {}),\n\t\t\t...(op.foldState !== undefined ? { foldState: op.foldState } : {}),\n\t\t\t...(op.encrypted !== undefined ? { encrypted: copyEnvelope(op.encrypted) } : {}),\n\t\t}\n\t}\n\n\tdecodeOperation(serialized: SerializedOperation): Operation {\n\t\treturn {\n\t\t\tid: serialized.id,\n\t\t\tnodeId: serialized.nodeId,\n\t\t\ttype: serialized.type,\n\t\t\tcollection: serialized.collection,\n\t\t\trecordId: serialized.recordId,\n\t\t\tdata: deserializeWireRecord(serialized.data),\n\t\t\tpreviousData: deserializeWireRecord(serialized.previousData),\n\t\t\ttimestamp: {\n\t\t\t\twallTime: serialized.timestamp.wallTime,\n\t\t\t\tlogical: serialized.timestamp.logical,\n\t\t\t\tnodeId: serialized.timestamp.nodeId,\n\t\t\t},\n\t\t\tsequenceNumber: serialized.sequenceNumber,\n\t\t\tcausalDeps: [...serialized.causalDeps],\n\t\t\tschemaVersion: serialized.schemaVersion,\n\t\t\t...(serialized.atomicOps !== undefined ? { atomicOps: serialized.atomicOps } : {}),\n\t\t\t...(serialized.transactionId !== undefined\n\t\t\t\t? { transactionId: serialized.transactionId }\n\t\t\t\t: {}),\n\t\t\t...(serialized.mutationName !== undefined ? { mutationName: serialized.mutationName } : {}),\n\t\t\t...withFieldVersions(serialized.fieldVersions),\n\t\t\t...withHashVersion(serialized.hashVersion),\n\t\t\t...(typeof serialized.foldState === 'string' ? { foldState: serialized.foldState } : {}),\n\t\t\t...withEnvelope(serialized.encrypted),\n\t\t}\n\t}\n}\n\n/**\n * Carry a wire hash version through. 1 and 2 are the defined versions. Any other\n * declared value (an unknown future version, garbage from an untrusted peer) is kept,\n * so id verification fails closed instead of treating the operation as version 1.\n */\nfunction withHashVersion(raw: unknown): { hashVersion?: 1 | 2 } {\n\tif (raw === undefined || raw === null) return {}\n\treturn { hashVersion: raw as 1 | 2 }\n}\n\n/**\n * Validate a wire encryption envelope (protocol v2). A malformed envelope from an\n * untrusted peer is dropped; the operation then has no envelope and fails decryption\n * (encryption enabled: plaintext is refused) or id verification. It never applies\n * as garbage.\n *\n * @param raw - The decoded `encrypted` value\n * @returns A validated copy, or undefined\n */\nexport function normalizeEnvelope(raw: unknown): EncryptedOperationEnvelope | undefined {\n\tif (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return undefined\n\tconst env = raw as Record<string, unknown>\n\tif (\n\t\tenv.v !== 2 ||\n\t\tenv.alg !== 'aes-256-gcm' ||\n\t\ttypeof env.keyId !== 'string' ||\n\t\ttypeof env.keyVersion !== 'number' ||\n\t\t!Number.isInteger(env.keyVersion)\n\t) {\n\t\treturn undefined\n\t}\n\tconst data = normalizeEnvelopeField(env.data)\n\tconst previousData = normalizeEnvelopeField(env.previousData)\n\tif (data === undefined || previousData === undefined) return undefined\n\tconst atomicOps = env.atomicOps === undefined ? null : normalizeEnvelopeField(env.atomicOps)\n\tif (atomicOps === undefined) return undefined\n\treturn {\n\t\tv: 2,\n\t\talg: 'aes-256-gcm',\n\t\tkeyId: env.keyId,\n\t\tkeyVersion: env.keyVersion,\n\t\tdata,\n\t\tpreviousData,\n\t\t...(atomicOps !== null ? { atomicOps } : {}),\n\t}\n}\n\nfunction normalizeEnvelopeField(raw: unknown): { iv: string; ct: string } | null | undefined {\n\tif (raw === null) return null\n\tif (typeof raw !== 'object' || raw === undefined) return undefined\n\tconst field = raw as Record<string, unknown>\n\tif (typeof field.iv !== 'string' || typeof field.ct !== 'string') return undefined\n\treturn { iv: field.iv, ct: field.ct }\n}\n\nfunction copyEnvelope(envelope: EncryptedOperationEnvelope): EncryptedOperationEnvelope {\n\treturn {\n\t\tv: envelope.v,\n\t\talg: envelope.alg,\n\t\tkeyId: envelope.keyId,\n\t\tkeyVersion: envelope.keyVersion,\n\t\tdata: envelope.data ? { ...envelope.data } : null,\n\t\tpreviousData: envelope.previousData ? { ...envelope.previousData } : null,\n\t\t...(envelope.atomicOps ? { atomicOps: { ...envelope.atomicOps } } : {}),\n\t}\n}\n\nfunction withEnvelope(raw: unknown): { encrypted?: EncryptedOperationEnvelope } {\n\tconst encrypted = raw === undefined ? undefined : normalizeEnvelope(raw)\n\treturn encrypted !== undefined ? { encrypted } : {}\n}\n\n/**\n * Validate wire per-field versions (RT-27): a map of field name to a well-formed HLC\n * timestamp. Anything else (from an untrusted peer) yields undefined: the operation\n * is then applied with its single timestamp, never with a malformed version.\n *\n * @param raw - The decoded `fieldVersions` value\n * @returns A fresh validated copy, or undefined\n */\nexport function normalizeFieldVersions(raw: unknown): Record<string, HLCTimestamp> | undefined {\n\tif (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return undefined\n\tconst result: Record<string, HLCTimestamp> = {}\n\tfor (const [field, value] of Object.entries(raw as Record<string, unknown>)) {\n\t\tif (value === null || typeof value !== 'object') return undefined\n\t\tconst ts = value as Record<string, unknown>\n\t\tif (\n\t\t\ttypeof ts.wallTime !== 'number' ||\n\t\t\t!Number.isFinite(ts.wallTime) ||\n\t\t\ttypeof ts.logical !== 'number' ||\n\t\t\t!Number.isFinite(ts.logical) ||\n\t\t\ttypeof ts.nodeId !== 'string'\n\t\t) {\n\t\t\treturn undefined\n\t\t}\n\t\tresult[field] = { wallTime: ts.wallTime, logical: ts.logical, nodeId: ts.nodeId }\n\t}\n\treturn result\n}\n\nfunction copyFieldVersions(versions: Record<string, HLCTimestamp>): Record<string, HLCTimestamp> {\n\tconst copy: Record<string, HLCTimestamp> = {}\n\tfor (const [field, ts] of Object.entries(versions)) {\n\t\tcopy[field] = { wallTime: ts.wallTime, logical: ts.logical, nodeId: ts.nodeId }\n\t}\n\treturn copy\n}\n\nfunction withFieldVersions(raw: unknown): { fieldVersions?: Record<string, HLCTimestamp> } {\n\tconst fieldVersions = raw === undefined ? undefined : normalizeFieldVersions(raw)\n\treturn fieldVersions !== undefined ? { fieldVersions } : {}\n}\n\n/**\n * Protobuf-based serializer for sync messages.\n */\n/**\n * Protobuf message serializer.\n *\n * Lossless by construction (SYNC-9): fields with a native protobuf slot are written there;\n * every other top-level member of the message (fields added after the native mapping,\n * and the bodies of the JSON-shaped awareness and Yjs messages) travels in the `extJson`\n * envelope field (49). Decoding therefore yields exactly what the JSON wire would, for\n * every message type, which a property test over every message type pins.\n *\n * It is not negotiated: the sync engine advertises `['json']` only and the server\n * reports the format its transport actually frames with. Use it explicitly, on both\n * ends of a transport, or not at all.\n */\nexport class ProtobufMessageSerializer implements MessageSerializer {\n\tencode(message: SyncMessage): Uint8Array {\n\t\tconst envelope = toProtoEnvelope(message)\n\t\tconst native = encodeEnvelope(envelope)\n\t\t// Decode what the native slots carried and ship everything they lost (or invented,\n\t\t// such as a defaulted field the message never had) in the extension field.\n\t\tconst ext = residualFields(message, fromProtoEnvelope(decodeEnvelope(native)))\n\t\tif (ext === undefined) return native\n\t\treturn encodeEnvelope({ ...envelope, extJson: JSON.stringify(ext) })\n\t}\n\n\tdecode(data: string | Uint8Array | ArrayBuffer): SyncMessage {\n\t\tconst bytes = toBytes(data)\n\t\tconst envelope = decodeEnvelope(bytes)\n\t\treturn applyResidualFields(fromProtoEnvelope(envelope), envelope.extJson)\n\t}\n\n\tencodeOperation(op: Operation): SerializedOperation {\n\t\treturn new JsonMessageSerializer().encodeOperation(op)\n\t}\n\n\tdecodeOperation(serialized: SerializedOperation): Operation {\n\t\treturn new JsonMessageSerializer().decodeOperation(serialized)\n\t}\n}\n\n/**\n * Negotiated serializer that supports runtime wire-format switching.\n */\nexport class NegotiatedMessageSerializer implements MessageSerializer {\n\tprivate readonly json = new JsonMessageSerializer()\n\tprivate readonly protobuf = new ProtobufMessageSerializer()\n\tprivate wireFormat: WireFormat\n\n\tconstructor(initialWireFormat: WireFormat = 'json') {\n\t\tthis.wireFormat = initialWireFormat\n\t}\n\n\tencode(message: SyncMessage): EncodedMessage {\n\t\tif (this.wireFormat === 'protobuf' && !isEncryptionKeyMessageType(message.type)) {\n\t\t\treturn this.protobuf.encode(message)\n\t\t}\n\n\t\treturn this.json.encode(message)\n\t}\n\n\tdecode(data: string | Uint8Array | ArrayBuffer): SyncMessage {\n\t\tif (typeof data === 'string') {\n\t\t\treturn this.json.decode(data)\n\t\t}\n\n\t\ttry {\n\t\t\treturn this.protobuf.decode(data)\n\t\t} catch {\n\t\t\treturn this.json.decode(data)\n\t\t}\n\t}\n\n\tencodeOperation(op: Operation): SerializedOperation {\n\t\treturn this.json.encodeOperation(op)\n\t}\n\n\tdecodeOperation(serialized: SerializedOperation): Operation {\n\t\treturn this.json.decodeOperation(serialized)\n\t}\n\n\tsetWireFormat(format: WireFormat): void {\n\t\tthis.wireFormat = format\n\t}\n\n\tgetWireFormat(): WireFormat {\n\t\treturn this.wireFormat\n\t}\n}\n\ninterface ProtoVectorEntry {\n\tkey: string\n\tvalue: number\n}\n\ninterface ProtoTimestamp {\n\twallTime: number\n\tlogical: number\n\tnodeId: string\n}\n\ninterface ProtoOperation {\n\tid: string\n\tnodeId: string\n\ttype: string\n\tcollection: string\n\trecordId: string\n\tdataJson: string\n\tpreviousDataJson: string\n\ttimestamp: ProtoTimestamp\n\tsequenceNumber: number\n\tcausalDeps: string[]\n\tschemaVersion: number\n\thasData: boolean\n\thasPreviousData: boolean\n\t/** Field 14: content-hash version (protocol v2). 0/absent = 1. */\n\thashVersion?: number\n\t/** Field 15: serialized fold state of a server scope-entry operation. */\n\tfoldState?: string\n\t/** Field 16: JSON of the encryption envelope v2. */\n\tencryptedJson?: string\n}\n\ninterface ProtoEnvelope {\n\ttype: SyncMessage['type']\n\tmessageId: string\n\tnodeId?: string\n\tversionVector?: ProtoVectorEntry[]\n\tschemaVersion?: number\n\tauthToken?: string\n\tsupportedWireFormats?: string[]\n\taccepted?: boolean\n\trejectReason?: string\n\tselectedWireFormat?: string\n\tserverTime?: number\n\toperations?: ProtoOperation[]\n\tisFinal?: boolean\n\tbatchIndex?: number\n\tacknowledgedMessageId?: string\n\tlastSequenceNumber?: number\n\terrorCode?: string\n\terrorMessage?: string\n\tretriable?: boolean\n\trequestId?: string\n\thash?: string\n\t/** Base64-encoded chunk bytes for a blob-chunk-response or blob-chunk-push. */\n\tchunkBytes?: string\n\t/** Distinguishes a held chunk (true) from \"not held\" (false) when bytes is empty/absent. */\n\thasBytes?: boolean\n\t/** Whether the server persists blob bytes centrally (handshake-response). */\n\tblobStorageEnabled?: boolean\n\t/** Fields 26-28: per-operation rejection (operation-rejected). code/message/retriable reuse the error fields. */\n\toperationId?: string\n\tcollection?: string\n\trecordId?: string\n\t/** Field 29: client's delivery watermark on a handshake. */\n\tlastDeliverySequence?: number\n\t/** Fields 30-31: delivery-stream chaining on an operation-batch. */\n\tbaseDeliverySequence?: number\n\tmaxDeliverySequence?: number\n\t/** Field 32: acked delivery sequence on an acknowledgment. */\n\tdeliverySequence?: number\n\t/** Field 33: server's max delivery sequence on a handshake-response. */\n\tserverMaxDeliverySequence?: number\n\t/** Fields 34-37: directional scope/retraction protocol JSON and policy. */\n\tacceptedScopeJson?: string\n\tacceptedDownlinkScopesJson?: string\n\tacceptedUplinkScopesJson?: string\n\tretractionsJson?: string\n\tscopeExitPolicy?: string\n\t/** Field 39: per-device node token (handshake, handshake-response, RT-12; acknowledgment, RT-21). */\n\tnodeToken?: string\n\t/** Field 40: peer-relay server asks for blob possession proofs (handshake-response, RT-23). */\n\tblobPossessionProof?: boolean\n\t/** Fields 41-42: a throttled blob-chunk-response and its retry delay (RT-24). */\n\tthrottled?: boolean\n\tretryAfterMs?: number\n\t/** Fields 43-44: the accepted view a handshake resumes (SYNC-11). */\n\tacceptedScopeKey?: string\n\tacceptedScopeWatermark?: number\n\t/** Field 45: the handshake's sequence-reservation capability (RT-37). */\n\tsequenceReservation?: boolean\n\t/** Field 46 (repeated): server-authoritative node ids (handshake-response, protocol v2). */\n\tauthoritativeNodeIds?: string[]\n\t/** Field 47: sync protocol version (handshake and handshake-response, protocol v2). */\n\tprotocolVersion?: number\n\t/** Field 48 (repeated): revoked explicit authoritative node ids (handshake-response, RT-81). */\n\trevokedAuthoritativeNodeIds?: string[]\n\t/**\n\t * Field 49: JSON `{ set?: {...}, unset?: [...] }` of the top-level message members the\n\t * native fields do not carry losslessly (SYNC-9). Absent when the native fields suffice.\n\t */\n\textJson?: string\n}\n\ninterface ResidualFields {\n\tset?: Record<string, unknown>\n\tunset?: string[]\n}\n\n/**\n * The difference between a message as the JSON wire carries it and what the native\n * protobuf fields decoded to: members to set, and members the decoder invented.\n */\nfunction residualFields(message: SyncMessage, decoded: SyncMessage): ResidualFields | undefined {\n\tconst want = JSON.parse(JSON.stringify(message)) as Record<string, unknown>\n\tconst got = JSON.parse(JSON.stringify(decoded)) as Record<string, unknown>\n\tconst set: Record<string, unknown> = {}\n\tconst unset: string[] = []\n\tfor (const [key, value] of Object.entries(want)) {\n\t\tif (!(key in got) || stableStringify(got[key]) !== stableStringify(value)) set[key] = value\n\t}\n\tfor (const key of Object.keys(got)) {\n\t\tif (!(key in want)) unset.push(key)\n\t}\n\tconst hasSet = Object.keys(set).length > 0\n\tif (!hasSet && unset.length === 0) return undefined\n\treturn { ...(hasSet ? { set } : {}), ...(unset.length > 0 ? { unset } : {}) }\n}\n\nfunction applyResidualFields(message: SyncMessage, extJson: string | undefined): SyncMessage {\n\tif (extJson === undefined || extJson.length === 0) return message\n\tlet ext: unknown\n\ttry {\n\t\text = JSON.parse(extJson)\n\t} catch {\n\t\tthrow new SyncError('Failed to decode sync message: invalid protobuf extension JSON', {\n\t\t\tlength: extJson.length,\n\t\t})\n\t}\n\tif (typeof ext !== 'object' || ext === null || Array.isArray(ext)) {\n\t\tthrow new SyncError('Failed to decode sync message: protobuf extension is not an object', {\n\t\t\ttype: message.type,\n\t\t})\n\t}\n\tconst out: Record<string, unknown> = { ...message }\n\tconst { set, unset } = ext as { set?: unknown; unset?: unknown }\n\tif (Array.isArray(unset)) {\n\t\tfor (const key of unset) if (typeof key === 'string') delete out[key]\n\t}\n\tif (typeof set === 'object' && set !== null && !Array.isArray(set)) {\n\t\tfor (const [key, value] of Object.entries(set)) {\n\t\t\t// The extension may never change what kind of message this is.\n\t\t\tif (key === 'type') continue\n\t\t\tout[key] = value\n\t\t}\n\t}\n\tif (!isSyncMessage(out)) {\n\t\tthrow new SyncError('Failed to decode sync message: invalid message structure', {\n\t\t\treceivedType: message.type,\n\t\t})\n\t}\n\treturn out\n}\n\n/** JSON with object keys sorted, so equal values compare equal whatever their key order. */\nfunction stableStringify(value: unknown): string {\n\tif (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`\n\tif (typeof value === 'object' && value !== null) {\n\t\tconst entries = Object.keys(value)\n\t\t\t.sort()\n\t\t\t.map(\n\t\t\t\t(key) =>\n\t\t\t\t\t`${JSON.stringify(key)}:${stableStringify((value as Record<string, unknown>)[key])}`,\n\t\t\t)\n\t\treturn `{${entries.join(',')}}`\n\t}\n\treturn JSON.stringify(value) ?? 'null'\n}\n\nfunction toProtoEnvelope(message: SyncMessage): ProtoEnvelope {\n\tswitch (message.type) {\n\t\tcase 'handshake':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\tnodeId: message.nodeId,\n\t\t\t\tversionVector: Object.entries(message.versionVector).map(([key, value]) => ({\n\t\t\t\t\tkey,\n\t\t\t\t\tvalue,\n\t\t\t\t})),\n\t\t\t\tschemaVersion: message.schemaVersion,\n\t\t\t\tauthToken: message.authToken,\n\t\t\t\tsupportedWireFormats: message.supportedWireFormats,\n\t\t\t\t...(message.lastDeliverySequence !== undefined\n\t\t\t\t\t? { lastDeliverySequence: message.lastDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.acceptedScopeKey !== undefined\n\t\t\t\t\t? { acceptedScopeKey: message.acceptedScopeKey }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.acceptedScopeWatermark !== undefined\n\t\t\t\t\t? { acceptedScopeWatermark: message.acceptedScopeWatermark }\n\t\t\t\t\t: {}),\n\t\t\t\tscopeExitPolicy: message.scopeExitPolicy,\n\t\t\t\t...(message.nodeToken !== undefined ? { nodeToken: message.nodeToken } : {}),\n\t\t\t\t...(message.sequenceReservation !== undefined\n\t\t\t\t\t? { sequenceReservation: message.sequenceReservation }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.protocolVersion !== undefined\n\t\t\t\t\t? { protocolVersion: message.protocolVersion }\n\t\t\t\t\t: {}),\n\t\t\t}\n\t\tcase 'handshake-response':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\tnodeId: message.nodeId,\n\t\t\t\tversionVector: Object.entries(message.versionVector).map(([key, value]) => ({\n\t\t\t\t\tkey,\n\t\t\t\t\tvalue,\n\t\t\t\t})),\n\t\t\t\tschemaVersion: message.schemaVersion,\n\t\t\t\taccepted: message.accepted,\n\t\t\t\trejectReason: message.rejectReason,\n\t\t\t\tselectedWireFormat: message.selectedWireFormat,\n\t\t\t\tserverTime: message.serverTime,\n\t\t\t\tblobStorageEnabled: message.blobStorageEnabled,\n\t\t\t\t...(message.serverMaxDeliverySequence !== undefined\n\t\t\t\t\t? { serverMaxDeliverySequence: message.serverMaxDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\tacceptedScopeJson: message.acceptedScope\n\t\t\t\t\t? JSON.stringify(message.acceptedScope)\n\t\t\t\t\t: undefined,\n\t\t\t\tacceptedDownlinkScopesJson: message.acceptedDownlinkScopes\n\t\t\t\t\t? JSON.stringify(message.acceptedDownlinkScopes)\n\t\t\t\t\t: undefined,\n\t\t\t\tacceptedUplinkScopesJson: message.acceptedUplinkScopes\n\t\t\t\t\t? JSON.stringify(message.acceptedUplinkScopes)\n\t\t\t\t\t: undefined,\n\t\t\t\t...(message.nodeToken !== undefined ? { nodeToken: message.nodeToken } : {}),\n\t\t\t\t...(message.blobPossessionProof !== undefined\n\t\t\t\t\t? { blobPossessionProof: message.blobPossessionProof }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.protocolVersion !== undefined\n\t\t\t\t\t? { protocolVersion: message.protocolVersion }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.authoritativeNodeIds !== undefined\n\t\t\t\t\t? { authoritativeNodeIds: [...message.authoritativeNodeIds] }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.revokedAuthoritativeNodeIds !== undefined\n\t\t\t\t\t? { revokedAuthoritativeNodeIds: [...message.revokedAuthoritativeNodeIds] }\n\t\t\t\t\t: {}),\n\t\t\t}\n\t\tcase 'operation-batch':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\toperations: message.operations.map(serializeProtoOperation),\n\t\t\t\tisFinal: message.isFinal,\n\t\t\t\tbatchIndex: message.batchIndex,\n\t\t\t\t...(message.baseDeliverySequence !== undefined\n\t\t\t\t\t? { baseDeliverySequence: message.baseDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.maxDeliverySequence !== undefined\n\t\t\t\t\t? { maxDeliverySequence: message.maxDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\tretractionsJson: message.retractions ? JSON.stringify(message.retractions) : undefined,\n\t\t\t}\n\t\tcase 'acknowledgment':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\tacknowledgedMessageId: message.acknowledgedMessageId,\n\t\t\t\tlastSequenceNumber: message.lastSequenceNumber,\n\t\t\t\t...(message.deliverySequence !== undefined\n\t\t\t\t\t? { deliverySequence: message.deliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.nodeToken !== undefined ? { nodeToken: message.nodeToken } : {}),\n\t\t\t}\n\t\tcase 'error':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\terrorCode: message.code,\n\t\t\t\terrorMessage: message.message,\n\t\t\t\tretriable: message.retriable,\n\t\t\t}\n\t\tcase 'operation-rejected':\n\t\t\t// Reuse errorCode/errorMessage/retriable (fields 16-18) for the shared\n\t\t\t// code/message/retriable; fields 26-28 carry the operation identity.\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\toperationId: message.operationId,\n\t\t\t\tcollection: message.collection,\n\t\t\t\trecordId: message.recordId,\n\t\t\t\terrorCode: message.code,\n\t\t\t\terrorMessage: message.message,\n\t\t\t\tretriable: message.retriable,\n\t\t\t}\n\t\tcase 'awareness-update':\n\t\tcase 'yjs-doc-update':\n\t\t\t// Ephemeral presence/doc messages use JSON serialization only. Return a minimal envelope.\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t}\n\t\tcase 'blob-chunk-request':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\trequestId: message.requestId,\n\t\t\t\thash: message.hash,\n\t\t\t}\n\t\tcase 'blob-chunk-response':\n\t\t\t// Blob chunks carry durable user data, so they are fully represented on the\n\t\t\t// protobuf wire (not JSON-only). hasBytes distinguishes a held chunk from\n\t\t\t// \"not held\" (bytes === null), which an empty string alone could not.\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\trequestId: message.requestId,\n\t\t\t\thasBytes: message.bytes !== null,\n\t\t\t\t...(message.bytes !== null ? { chunkBytes: message.bytes } : {}),\n\t\t\t\t...(message.throttled ? { throttled: true } : {}),\n\t\t\t\t...(message.retryAfterMs !== undefined ? { retryAfterMs: message.retryAfterMs } : {}),\n\t\t\t}\n\t\tcase 'blob-chunk-push':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\thash: message.hash,\n\t\t\t\tchunkBytes: message.bytes,\n\t\t\t}\n\t\tcase 'heartbeat':\n\t\t\treturn { type: message.type, messageId: message.messageId }\n\t\tcase 'encryption-key-request':\n\t\tcase 'encryption-key-put':\n\t\tcase 'encryption-key-response':\n\t\t\t// NegotiatedMessageSerializer sends key distribution as a JSON text frame on every\n\t\t\t// wire format (ENC-1). A transport that uses this serializer explicitly still gets\n\t\t\t// it losslessly: the body travels in the extension field (49), like awareness.\n\t\t\treturn { type: message.type, messageId: message.messageId }\n\t}\n}\n\nfunction fromProtoEnvelope(envelope: ProtoEnvelope): SyncMessage {\n\tswitch (envelope.type) {\n\t\tcase 'handshake':\n\t\t\treturn {\n\t\t\t\ttype: 'handshake',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\tnodeId: envelope.nodeId ?? '',\n\t\t\t\tversionVector: Object.fromEntries(\n\t\t\t\t\t(envelope.versionVector ?? []).map((entry) => [entry.key, entry.value]),\n\t\t\t\t),\n\t\t\t\tschemaVersion: envelope.schemaVersion ?? 0,\n\t\t\t\tauthToken: envelope.authToken,\n\t\t\t\tsupportedWireFormats: envelope.supportedWireFormats?.filter(\n\t\t\t\t\t(format): format is WireFormat => format === 'json' || format === 'protobuf',\n\t\t\t\t),\n\t\t\t\t...(envelope.lastDeliverySequence !== undefined\n\t\t\t\t\t? { lastDeliverySequence: envelope.lastDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.acceptedScopeKey !== undefined\n\t\t\t\t\t? { acceptedScopeKey: envelope.acceptedScopeKey }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.acceptedScopeWatermark !== undefined\n\t\t\t\t\t? { acceptedScopeWatermark: envelope.acceptedScopeWatermark }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.scopeExitPolicy === 'retain' || envelope.scopeExitPolicy === 'retract'\n\t\t\t\t\t? { scopeExitPolicy: envelope.scopeExitPolicy }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.nodeToken ? { nodeToken: envelope.nodeToken } : {}),\n\t\t\t\t...(envelope.sequenceReservation !== undefined\n\t\t\t\t\t? { sequenceReservation: envelope.sequenceReservation }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.protocolVersion !== undefined\n\t\t\t\t\t? { protocolVersion: envelope.protocolVersion }\n\t\t\t\t\t: {}),\n\t\t\t}\n\t\tcase 'handshake-response':\n\t\t\treturn {\n\t\t\t\ttype: 'handshake-response',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\tnodeId: envelope.nodeId ?? '',\n\t\t\t\tversionVector: Object.fromEntries(\n\t\t\t\t\t(envelope.versionVector ?? []).map((entry) => [entry.key, entry.value]),\n\t\t\t\t),\n\t\t\t\tschemaVersion: envelope.schemaVersion ?? 0,\n\t\t\t\taccepted: envelope.accepted ?? false,\n\t\t\t\trejectReason: envelope.rejectReason,\n\t\t\t\tselectedWireFormat:\n\t\t\t\t\tenvelope.selectedWireFormat === 'json' || envelope.selectedWireFormat === 'protobuf'\n\t\t\t\t\t\t? envelope.selectedWireFormat\n\t\t\t\t\t\t: undefined,\n\t\t\t\t// Preserve the server's wall-clock time so clock-skew detection and\n\t\t\t\t// automatic timestamp rebase work over the protobuf wire, not just JSON.\n\t\t\t\t...(envelope.serverTime !== undefined ? { serverTime: envelope.serverTime } : {}),\n\t\t\t\t...(envelope.blobStorageEnabled !== undefined\n\t\t\t\t\t? { blobStorageEnabled: envelope.blobStorageEnabled }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.serverMaxDeliverySequence !== undefined\n\t\t\t\t\t? { serverMaxDeliverySequence: envelope.serverMaxDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.acceptedScopeJson\n\t\t\t\t\t? { acceptedScope: JSON.parse(envelope.acceptedScopeJson) }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.acceptedDownlinkScopesJson\n\t\t\t\t\t? { acceptedDownlinkScopes: JSON.parse(envelope.acceptedDownlinkScopesJson) }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.acceptedUplinkScopesJson\n\t\t\t\t\t? { acceptedUplinkScopes: JSON.parse(envelope.acceptedUplinkScopesJson) }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.nodeToken ? { nodeToken: envelope.nodeToken } : {}),\n\t\t\t\t...(envelope.blobPossessionProof !== undefined\n\t\t\t\t\t? { blobPossessionProof: envelope.blobPossessionProof }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.protocolVersion !== undefined\n\t\t\t\t\t? { protocolVersion: envelope.protocolVersion }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.authoritativeNodeIds !== undefined\n\t\t\t\t\t? { authoritativeNodeIds: envelope.authoritativeNodeIds }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.revokedAuthoritativeNodeIds !== undefined\n\t\t\t\t\t? { revokedAuthoritativeNodeIds: envelope.revokedAuthoritativeNodeIds }\n\t\t\t\t\t: {}),\n\t\t\t}\n\t\tcase 'operation-batch':\n\t\t\treturn {\n\t\t\t\ttype: 'operation-batch',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\toperations: (envelope.operations ?? []).map(deserializeProtoOperation),\n\t\t\t\tisFinal: envelope.isFinal ?? false,\n\t\t\t\tbatchIndex: envelope.batchIndex ?? 0,\n\t\t\t\t...(envelope.baseDeliverySequence !== undefined\n\t\t\t\t\t? { baseDeliverySequence: envelope.baseDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.maxDeliverySequence !== undefined\n\t\t\t\t\t? { maxDeliverySequence: envelope.maxDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.retractionsJson ? { retractions: JSON.parse(envelope.retractionsJson) } : {}),\n\t\t\t}\n\t\tcase 'acknowledgment':\n\t\t\treturn {\n\t\t\t\ttype: 'acknowledgment',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\tacknowledgedMessageId: envelope.acknowledgedMessageId ?? '',\n\t\t\t\tlastSequenceNumber: envelope.lastSequenceNumber ?? 0,\n\t\t\t\t...(envelope.deliverySequence !== undefined\n\t\t\t\t\t? { deliverySequence: envelope.deliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.nodeToken ? { nodeToken: envelope.nodeToken } : {}),\n\t\t\t}\n\t\tcase 'error':\n\t\t\treturn {\n\t\t\t\ttype: 'error',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\tcode: envelope.errorCode ?? 'UNKNOWN',\n\t\t\t\tmessage: envelope.errorMessage ?? 'Unknown error',\n\t\t\t\tretriable: envelope.retriable ?? false,\n\t\t\t}\n\t\tcase 'operation-rejected':\n\t\t\treturn {\n\t\t\t\ttype: 'operation-rejected',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\toperationId: envelope.operationId ?? '',\n\t\t\t\tcollection: envelope.collection ?? '',\n\t\t\t\trecordId: envelope.recordId ?? '',\n\t\t\t\tcode: envelope.errorCode ?? 'REJECTED',\n\t\t\t\tmessage: envelope.errorMessage ?? '',\n\t\t\t\tretriable: envelope.retriable ?? false,\n\t\t\t}\n\t\tcase 'blob-chunk-request':\n\t\t\treturn {\n\t\t\t\ttype: 'blob-chunk-request',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\trequestId: envelope.requestId ?? '',\n\t\t\t\thash: envelope.hash ?? '',\n\t\t\t}\n\t\tcase 'blob-chunk-response':\n\t\t\treturn {\n\t\t\t\ttype: 'blob-chunk-response',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\trequestId: envelope.requestId ?? '',\n\t\t\t\tbytes: envelope.hasBytes ? (envelope.chunkBytes ?? '') : null,\n\t\t\t\t...(envelope.throttled ? { throttled: true } : {}),\n\t\t\t\t...(envelope.retryAfterMs !== undefined ? { retryAfterMs: envelope.retryAfterMs } : {}),\n\t\t\t}\n\t\tcase 'blob-chunk-push':\n\t\t\treturn {\n\t\t\t\ttype: 'blob-chunk-push',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\thash: envelope.hash ?? '',\n\t\t\t\tbytes: envelope.chunkBytes ?? '',\n\t\t\t}\n\t\tcase 'heartbeat':\n\t\t\treturn { type: 'heartbeat', messageId: envelope.messageId }\n\t\tcase 'awareness-update':\n\t\t\t// The body travels in the extension field (49); these are its empty defaults.\n\t\t\treturn { type: 'awareness-update', messageId: envelope.messageId, clientId: 0, states: {} }\n\t\tcase 'yjs-doc-update':\n\t\t\treturn {\n\t\t\t\ttype: 'yjs-doc-update',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\tcollection: '',\n\t\t\t\trecordId: '',\n\t\t\t\tfield: '',\n\t\t\t\tupdate: '',\n\t\t\t}\n\t\t// Key-distribution bodies travel in the extension field (49); these are skeletons the\n\t\t// extension completes (applyResidualFields validates the result).\n\t\tcase 'encryption-key-request':\n\t\t\treturn {\n\t\t\t\ttype: 'encryption-key-request',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\trequestId: '',\n\t\t\t\tkeyring: '',\n\t\t\t}\n\t\tcase 'encryption-key-put':\n\t\t\t// `record` has no meaningful default; the extension always sets it, and a put\n\t\t\t// without one fails the structural check in applyResidualFields.\n\t\t\treturn {\n\t\t\t\ttype: 'encryption-key-put',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\trequestId: '',\n\t\t\t\tkeyring: '',\n\t\t\t\texpectedRevision: 0,\n\t\t\t} as SyncMessage\n\t\tcase 'encryption-key-response':\n\t\t\treturn {\n\t\t\t\ttype: 'encryption-key-response',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\tkeyring: '',\n\t\t\t\tstatus: 'invalid',\n\t\t\t\trecord: null,\n\t\t\t}\n\t\tdefault:\n\t\t\tthrow new SyncError('Failed to decode sync message: unknown protobuf type', {\n\t\t\t\ttype: envelope.type,\n\t\t\t})\n\t}\n}\n\nfunction serializeProtoOperation(operation: SerializedOperation): ProtoOperation {\n\t// Embed metadata in the data JSON when present (piggyback on existing field)\n\tconst hasMetadata =\n\t\toperation.transactionId !== undefined ||\n\t\toperation.mutationName !== undefined ||\n\t\t(operation.atomicOps !== undefined && Object.keys(operation.atomicOps).length > 0)\n\tlet dataJson = ''\n\tif (operation.data !== null) {\n\t\tconst dataPayload: Record<string, unknown> = { ...operation.data }\n\t\tif (operation.atomicOps !== undefined && Object.keys(operation.atomicOps).length > 0) {\n\t\t\tdataPayload.__kora_atomic_ops__ = operation.atomicOps\n\t\t}\n\t\tif (operation.transactionId !== undefined) {\n\t\t\tdataPayload.__kora_tx_id__ = operation.transactionId\n\t\t}\n\t\tif (operation.mutationName !== undefined) {\n\t\t\tdataPayload.__kora_mutation__ = operation.mutationName\n\t\t}\n\t\tif (operation.fieldVersions !== undefined) {\n\t\t\tdataPayload.__kora_field_versions__ = operation.fieldVersions\n\t\t}\n\t\tdataJson = JSON.stringify(dataPayload)\n\t} else if (hasMetadata) {\n\t\t// For operations without data (deletes), still carry metadata. hasData stays\n\t\t// false, so the decoder keeps data null.\n\t\tconst meta: Record<string, unknown> = {}\n\t\tif (operation.atomicOps !== undefined && Object.keys(operation.atomicOps).length > 0) {\n\t\t\tmeta.__kora_atomic_ops__ = operation.atomicOps\n\t\t}\n\t\tif (operation.transactionId !== undefined) meta.__kora_tx_id__ = operation.transactionId\n\t\tif (operation.mutationName !== undefined) meta.__kora_mutation__ = operation.mutationName\n\t\tdataJson = JSON.stringify(meta)\n\t}\n\n\treturn {\n\t\tid: operation.id,\n\t\tnodeId: operation.nodeId,\n\t\ttype: operation.type,\n\t\tcollection: operation.collection,\n\t\trecordId: operation.recordId,\n\t\tdataJson,\n\t\tpreviousDataJson: operation.previousData === null ? '' : JSON.stringify(operation.previousData),\n\t\ttimestamp: {\n\t\t\twallTime: operation.timestamp.wallTime,\n\t\t\tlogical: operation.timestamp.logical,\n\t\t\tnodeId: operation.timestamp.nodeId,\n\t\t},\n\t\tsequenceNumber: operation.sequenceNumber,\n\t\tcausalDeps: [...operation.causalDeps],\n\t\tschemaVersion: operation.schemaVersion,\n\t\thasData: operation.data !== null,\n\t\thasPreviousData: operation.previousData !== null,\n\t\t...(operation.hashVersion !== undefined ? { hashVersion: operation.hashVersion } : {}),\n\t\t...(operation.foldState !== undefined ? { foldState: operation.foldState } : {}),\n\t\t...(operation.encrypted !== undefined\n\t\t\t? { encryptedJson: JSON.stringify(operation.encrypted) }\n\t\t\t: {}),\n\t}\n}\n\nfunction deserializeProtoOperation(operation: ProtoOperation): SerializedOperation {\n\tlet data: Record<string, unknown> | null = null\n\tlet atomicOps: Record<string, unknown> | undefined\n\tlet transactionId: string | undefined\n\tlet mutationName: string | undefined\n\tlet fieldVersions: Record<string, HLCTimestamp> | undefined\n\n\tif (operation.hasData || operation.dataJson.length > 0) {\n\t\tconst parsed = JSON.parse(operation.dataJson) as Record<string, unknown>\n\t\tif ('__kora_atomic_ops__' in parsed) {\n\t\t\tatomicOps = parsed.__kora_atomic_ops__ as Record<string, unknown>\n\t\t}\n\t\tif ('__kora_tx_id__' in parsed) {\n\t\t\ttransactionId = parsed.__kora_tx_id__ as string\n\t\t}\n\t\tif ('__kora_mutation__' in parsed) {\n\t\t\tmutationName = parsed.__kora_mutation__ as string\n\t\t}\n\t\tif ('__kora_field_versions__' in parsed) {\n\t\t\tfieldVersions = normalizeFieldVersions(parsed.__kora_field_versions__)\n\t\t}\n\t\tconst {\n\t\t\t__kora_atomic_ops__: _a,\n\t\t\t__kora_tx_id__: _t,\n\t\t\t__kora_mutation__: _m,\n\t\t\t__kora_field_versions__: _f,\n\t\t\t...rest\n\t\t} = parsed\n\t\t// `hasData` says whether the op carried data: `{}` stays `{}` (its id covers it).\n\t\tdata = operation.hasData ? rest : null\n\t}\n\n\treturn {\n\t\tid: operation.id,\n\t\tnodeId: operation.nodeId,\n\t\ttype: operation.type as SerializedOperation['type'],\n\t\tcollection: operation.collection,\n\t\trecordId: operation.recordId,\n\t\tdata,\n\t\tpreviousData: operation.hasPreviousData\n\t\t\t? (JSON.parse(operation.previousDataJson) as Record<string, unknown>)\n\t\t\t: null,\n\t\ttimestamp: {\n\t\t\twallTime: operation.timestamp.wallTime,\n\t\t\tlogical: operation.timestamp.logical,\n\t\t\tnodeId: operation.timestamp.nodeId,\n\t\t},\n\t\tsequenceNumber: operation.sequenceNumber,\n\t\tcausalDeps: [...operation.causalDeps],\n\t\tschemaVersion: operation.schemaVersion,\n\t\t...(atomicOps !== undefined\n\t\t\t? { atomicOps: atomicOps as SerializedOperation['atomicOps'] }\n\t\t\t: {}),\n\t\t...(transactionId !== undefined ? { transactionId } : {}),\n\t\t...(mutationName !== undefined ? { mutationName } : {}),\n\t\t...(fieldVersions !== undefined ? { fieldVersions } : {}),\n\t\t...(operation.hashVersion !== undefined && operation.hashVersion !== 0\n\t\t\t? { hashVersion: operation.hashVersion as 1 | 2 }\n\t\t\t: {}),\n\t\t...(operation.foldState !== undefined ? { foldState: operation.foldState } : {}),\n\t\t...decodeEnvelopeJson(operation.encryptedJson),\n\t}\n}\n\nfunction decodeEnvelopeJson(json: string | undefined): {\n\tencrypted?: EncryptedOperationEnvelope\n} {\n\tif (json === undefined || json.length === 0) return {}\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(json)\n\t} catch {\n\t\tthrow new SyncError('Failed to decode sync message: invalid encryption envelope JSON', {\n\t\t\tlength: json.length,\n\t\t})\n\t}\n\tconst encrypted = normalizeEnvelope(parsed)\n\treturn encrypted !== undefined ? { encrypted } : {}\n}\n\nfunction decodeTextPayload(data: string | Uint8Array | ArrayBuffer): string {\n\tif (typeof data === 'string') return data\n\treturn new TextDecoder().decode(toBytes(data))\n}\n\nfunction toBytes(data: string | Uint8Array | ArrayBuffer): Uint8Array {\n\tif (typeof data === 'string') {\n\t\treturn new TextEncoder().encode(data)\n\t}\n\n\tif (data instanceof Uint8Array) {\n\t\treturn data\n\t}\n\n\tif (data instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(data)\n\t}\n\n\tthrow new SyncError('Unsupported sync payload type', { receivedType: typeof data })\n}\n\nfunction encodeEnvelope(envelope: ProtoEnvelope): Uint8Array {\n\tconst writer = Writer.create()\n\tif (envelope.type.length > 0) writer.uint32(10).string(envelope.type)\n\tif (envelope.messageId.length > 0) writer.uint32(18).string(envelope.messageId)\n\tif (envelope.nodeId && envelope.nodeId.length > 0) writer.uint32(26).string(envelope.nodeId)\n\tfor (const entry of envelope.versionVector ?? []) {\n\t\twriter.uint32(34).fork()\n\t\twriter.uint32(10).string(entry.key)\n\t\twriter.uint32(16).int64(entry.value)\n\t\twriter.ldelim()\n\t}\n\tif (envelope.schemaVersion !== undefined) writer.uint32(40).int32(envelope.schemaVersion)\n\tif (envelope.authToken && envelope.authToken.length > 0)\n\t\twriter.uint32(50).string(envelope.authToken)\n\tfor (const format of envelope.supportedWireFormats ?? []) {\n\t\twriter.uint32(58).string(format)\n\t}\n\tif (envelope.accepted !== undefined) writer.uint32(64).bool(envelope.accepted)\n\tif (envelope.rejectReason && envelope.rejectReason.length > 0)\n\t\twriter.uint32(74).string(envelope.rejectReason)\n\tif (envelope.selectedWireFormat && envelope.selectedWireFormat.length > 0) {\n\t\twriter.uint32(82).string(envelope.selectedWireFormat)\n\t}\n\tfor (const operation of envelope.operations ?? []) {\n\t\twriter.uint32(90).fork()\n\t\tencodeProtoOperation(writer, operation)\n\t\twriter.ldelim()\n\t}\n\tif (envelope.isFinal !== undefined) writer.uint32(96).bool(envelope.isFinal)\n\tif (envelope.batchIndex !== undefined) writer.uint32(104).uint32(envelope.batchIndex)\n\tif (envelope.acknowledgedMessageId && envelope.acknowledgedMessageId.length > 0) {\n\t\twriter.uint32(114).string(envelope.acknowledgedMessageId)\n\t}\n\tif (envelope.lastSequenceNumber !== undefined)\n\t\twriter.uint32(120).int64(envelope.lastSequenceNumber)\n\tif (envelope.errorCode && envelope.errorCode.length > 0)\n\t\twriter.uint32(130).string(envelope.errorCode)\n\tif (envelope.errorMessage && envelope.errorMessage.length > 0) {\n\t\twriter.uint32(138).string(envelope.errorMessage)\n\t}\n\tif (envelope.retriable !== undefined) writer.uint32(144).bool(envelope.retriable)\n\t// Field 19: server wall-clock time (ms epoch). Optional; only handshake-response sets it.\n\tif (envelope.serverTime !== undefined) writer.uint32(152).int64(envelope.serverTime)\n\t// Fields 20-23: blob chunk side channel (out-of-band blob transfer).\n\tif (envelope.requestId && envelope.requestId.length > 0)\n\t\twriter.uint32(162).string(envelope.requestId)\n\tif (envelope.hash && envelope.hash.length > 0) writer.uint32(170).string(envelope.hash)\n\tif (envelope.chunkBytes && envelope.chunkBytes.length > 0)\n\t\twriter.uint32(178).string(envelope.chunkBytes)\n\tif (envelope.hasBytes !== undefined) writer.uint32(184).bool(envelope.hasBytes)\n\t// Field 25: server advertises central blob storage (handshake-response).\n\tif (envelope.blobStorageEnabled !== undefined)\n\t\twriter.uint32(200).bool(envelope.blobStorageEnabled)\n\t// Fields 26-28: per-operation rejection identity (operation-rejected).\n\tif (envelope.operationId && envelope.operationId.length > 0)\n\t\twriter.uint32(210).string(envelope.operationId)\n\tif (envelope.collection && envelope.collection.length > 0)\n\t\twriter.uint32(218).string(envelope.collection)\n\tif (envelope.recordId && envelope.recordId.length > 0)\n\t\twriter.uint32(226).string(envelope.recordId)\n\t// Fields 29-32: delivery-watermark protocol. int64 (field << 3 | wiretype 0).\n\tif (envelope.lastDeliverySequence !== undefined)\n\t\twriter.uint32(232).int64(envelope.lastDeliverySequence)\n\tif (envelope.baseDeliverySequence !== undefined)\n\t\twriter.uint32(240).int64(envelope.baseDeliverySequence)\n\tif (envelope.maxDeliverySequence !== undefined)\n\t\twriter.uint32(248).int64(envelope.maxDeliverySequence)\n\tif (envelope.deliverySequence !== undefined) writer.uint32(256).int64(envelope.deliverySequence)\n\tif (envelope.serverMaxDeliverySequence !== undefined)\n\t\twriter.uint32(264).int64(envelope.serverMaxDeliverySequence)\n\tif (envelope.acceptedScopeJson) writer.uint32(274).string(envelope.acceptedScopeJson)\n\tif (envelope.acceptedDownlinkScopesJson)\n\t\twriter.uint32(282).string(envelope.acceptedDownlinkScopesJson)\n\tif (envelope.acceptedUplinkScopesJson)\n\t\twriter.uint32(290).string(envelope.acceptedUplinkScopesJson)\n\tif (envelope.retractionsJson) writer.uint32(298).string(envelope.retractionsJson)\n\tif (envelope.scopeExitPolicy) writer.uint32(306).string(envelope.scopeExitPolicy)\n\tif (envelope.nodeToken) writer.uint32(314).string(envelope.nodeToken)\n\t// Field 40 (bool, wiretype 0): 40 << 3 = 320. Fields 41-42: 328, 336.\n\tif (envelope.blobPossessionProof !== undefined)\n\t\twriter.uint32(320).bool(envelope.blobPossessionProof)\n\tif (envelope.throttled) writer.uint32(328).bool(envelope.throttled)\n\tif (envelope.retryAfterMs !== undefined) writer.uint32(336).int64(envelope.retryAfterMs)\n\tif (envelope.acceptedScopeKey !== undefined) writer.uint32(346).string(envelope.acceptedScopeKey)\n\tif (envelope.acceptedScopeWatermark !== undefined)\n\t\twriter.uint32(352).int64(envelope.acceptedScopeWatermark)\n\t// Field 45 (bool, wiretype 0): 45 << 3 = 360. Written whenever set (false included),\n\t// so a client that explicitly opts out is told apart from one that predates it.\n\tif (envelope.sequenceReservation !== undefined)\n\t\twriter.uint32(360).bool(envelope.sequenceReservation)\n\t// Field 46 (repeated string, wiretype 2): 46 << 3 | 2 = 370.\n\tfor (const nodeId of envelope.authoritativeNodeIds ?? []) writer.uint32(370).string(nodeId)\n\t// Field 47 (uint32, wiretype 0): 47 << 3 = 376.\n\tif (envelope.protocolVersion !== undefined) writer.uint32(376).uint32(envelope.protocolVersion)\n\t// Field 48 (repeated string, wiretype 2): 48 << 3 | 2 = 386.\n\tfor (const nodeId of envelope.revokedAuthoritativeNodeIds ?? []) writer.uint32(386).string(nodeId)\n\t// Field 49 (string, wiretype 2): 49 << 3 | 2 = 394.\n\tif (envelope.extJson) writer.uint32(394).string(envelope.extJson)\n\treturn writer.finish()\n}\n\nfunction decodeEnvelope(bytes: Uint8Array): ProtoEnvelope {\n\tconst reader = Reader.create(bytes)\n\tconst envelope: ProtoEnvelope = { type: 'error', messageId: '' }\n\n\twhile (reader.pos < reader.len) {\n\t\tconst tag = reader.uint32()\n\t\tswitch (tag >>> 3) {\n\t\t\tcase 1:\n\t\t\t\tenvelope.type = reader.string() as SyncMessage['type']\n\t\t\t\tbreak\n\t\t\tcase 2:\n\t\t\t\tenvelope.messageId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 3:\n\t\t\t\tenvelope.nodeId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 4:\n\t\t\t\tenvelope.versionVector = [\n\t\t\t\t\t...(envelope.versionVector ?? []),\n\t\t\t\t\tdecodeVectorEntry(reader, reader.uint32()),\n\t\t\t\t]\n\t\t\t\tbreak\n\t\t\tcase 5:\n\t\t\t\tenvelope.schemaVersion = reader.int32()\n\t\t\t\tbreak\n\t\t\tcase 6:\n\t\t\t\tenvelope.authToken = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 7:\n\t\t\t\tenvelope.supportedWireFormats = [...(envelope.supportedWireFormats ?? []), reader.string()]\n\t\t\t\tbreak\n\t\t\tcase 8:\n\t\t\t\tenvelope.accepted = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 9:\n\t\t\t\tenvelope.rejectReason = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 10:\n\t\t\t\tenvelope.selectedWireFormat = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 11:\n\t\t\t\tenvelope.operations = [\n\t\t\t\t\t...(envelope.operations ?? []),\n\t\t\t\t\tdecodeProtoOperation(reader, reader.uint32()),\n\t\t\t\t]\n\t\t\t\tbreak\n\t\t\tcase 12:\n\t\t\t\tenvelope.isFinal = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 13:\n\t\t\t\tenvelope.batchIndex = reader.uint32()\n\t\t\t\tbreak\n\t\t\tcase 14:\n\t\t\t\tenvelope.acknowledgedMessageId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 15:\n\t\t\t\tenvelope.lastSequenceNumber = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 16:\n\t\t\t\tenvelope.errorCode = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 17:\n\t\t\t\tenvelope.errorMessage = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 18:\n\t\t\t\tenvelope.retriable = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 19:\n\t\t\t\tenvelope.serverTime = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 20:\n\t\t\t\tenvelope.requestId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 21:\n\t\t\t\tenvelope.hash = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 22:\n\t\t\t\tenvelope.chunkBytes = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 23:\n\t\t\t\tenvelope.hasBytes = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 25:\n\t\t\t\tenvelope.blobStorageEnabled = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 26:\n\t\t\t\tenvelope.operationId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 27:\n\t\t\t\tenvelope.collection = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 28:\n\t\t\t\tenvelope.recordId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 29:\n\t\t\t\tenvelope.lastDeliverySequence = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 30:\n\t\t\t\tenvelope.baseDeliverySequence = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 31:\n\t\t\t\tenvelope.maxDeliverySequence = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 32:\n\t\t\t\tenvelope.deliverySequence = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 33:\n\t\t\t\tenvelope.serverMaxDeliverySequence = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 34:\n\t\t\t\tenvelope.acceptedScopeJson = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 35:\n\t\t\t\tenvelope.acceptedDownlinkScopesJson = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 36:\n\t\t\t\tenvelope.acceptedUplinkScopesJson = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 37:\n\t\t\t\tenvelope.retractionsJson = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 38:\n\t\t\t\tenvelope.scopeExitPolicy = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 39:\n\t\t\t\tenvelope.nodeToken = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 40:\n\t\t\t\tenvelope.blobPossessionProof = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 41:\n\t\t\t\tenvelope.throttled = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 42:\n\t\t\t\tenvelope.retryAfterMs = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 43:\n\t\t\t\tenvelope.acceptedScopeKey = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 44:\n\t\t\t\tenvelope.acceptedScopeWatermark = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 45:\n\t\t\t\tenvelope.sequenceReservation = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 46:\n\t\t\t\tenvelope.authoritativeNodeIds = [...(envelope.authoritativeNodeIds ?? []), reader.string()]\n\t\t\t\tbreak\n\t\t\tcase 47:\n\t\t\t\tenvelope.protocolVersion = reader.uint32()\n\t\t\t\tbreak\n\t\t\tcase 48:\n\t\t\t\tenvelope.revokedAuthoritativeNodeIds = [\n\t\t\t\t\t...(envelope.revokedAuthoritativeNodeIds ?? []),\n\t\t\t\t\treader.string(),\n\t\t\t\t]\n\t\t\t\tbreak\n\t\t\tcase 49:\n\t\t\t\tenvelope.extJson = reader.string()\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\treader.skipType(tag & 7)\n\t\t}\n\t}\n\n\treturn envelope\n}\n\nfunction encodeProtoOperation(writer: Writer, operation: ProtoOperation): void {\n\tif (operation.id.length > 0) writer.uint32(10).string(operation.id)\n\tif (operation.nodeId.length > 0) writer.uint32(18).string(operation.nodeId)\n\tif (operation.type.length > 0) writer.uint32(26).string(operation.type)\n\tif (operation.collection.length > 0) writer.uint32(34).string(operation.collection)\n\tif (operation.recordId.length > 0) writer.uint32(42).string(operation.recordId)\n\tif (operation.dataJson.length > 0) writer.uint32(50).string(operation.dataJson)\n\tif (operation.previousDataJson.length > 0) writer.uint32(58).string(operation.previousDataJson)\n\twriter.uint32(66).fork()\n\twriter.uint32(8).int64(operation.timestamp.wallTime)\n\twriter.uint32(16).uint32(operation.timestamp.logical)\n\twriter.uint32(26).string(operation.timestamp.nodeId)\n\twriter.ldelim()\n\twriter.uint32(72).int64(operation.sequenceNumber)\n\tfor (const dep of operation.causalDeps) {\n\t\twriter.uint32(82).string(dep)\n\t}\n\twriter.uint32(88).int32(operation.schemaVersion)\n\twriter.uint32(96).bool(operation.hasData)\n\twriter.uint32(104).bool(operation.hasPreviousData)\n\t// Fields 14-16 (protocol v2). Older decoders skip them as unknown fields.\n\tif (operation.hashVersion !== undefined) writer.uint32(112).uint32(operation.hashVersion)\n\tif (operation.foldState !== undefined) writer.uint32(122).string(operation.foldState)\n\tif (operation.encryptedJson !== undefined) writer.uint32(130).string(operation.encryptedJson)\n}\n\nfunction decodeProtoOperation(reader: Reader, length: number): ProtoOperation {\n\tconst end = reader.pos + length\n\tconst operation: ProtoOperation = {\n\t\tid: '',\n\t\tnodeId: '',\n\t\ttype: 'insert',\n\t\tcollection: '',\n\t\trecordId: '',\n\t\tdataJson: '',\n\t\tpreviousDataJson: '',\n\t\ttimestamp: { wallTime: 0, logical: 0, nodeId: '' },\n\t\tsequenceNumber: 0,\n\t\tcausalDeps: [],\n\t\tschemaVersion: 0,\n\t\thasData: false,\n\t\thasPreviousData: false,\n\t}\n\n\twhile (reader.pos < end) {\n\t\tconst tag = reader.uint32()\n\t\tswitch (tag >>> 3) {\n\t\t\tcase 1:\n\t\t\t\toperation.id = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 2:\n\t\t\t\toperation.nodeId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 3:\n\t\t\t\toperation.type = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 4:\n\t\t\t\toperation.collection = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 5:\n\t\t\t\toperation.recordId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 6:\n\t\t\t\toperation.dataJson = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 7:\n\t\t\t\toperation.previousDataJson = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 8: {\n\t\t\t\tconst timestampEnd = reader.pos + reader.uint32()\n\t\t\t\twhile (reader.pos < timestampEnd) {\n\t\t\t\t\tconst timestampTag = reader.uint32()\n\t\t\t\t\tswitch (timestampTag >>> 3) {\n\t\t\t\t\t\tcase 1:\n\t\t\t\t\t\t\toperation.timestamp.wallTime = longToNumber(reader.int64())\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tcase 2:\n\t\t\t\t\t\t\toperation.timestamp.logical = reader.uint32()\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tcase 3:\n\t\t\t\t\t\t\toperation.timestamp.nodeId = reader.string()\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treader.skipType(timestampTag & 7)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 9:\n\t\t\t\toperation.sequenceNumber = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 10:\n\t\t\t\toperation.causalDeps.push(reader.string())\n\t\t\t\tbreak\n\t\t\tcase 11:\n\t\t\t\toperation.schemaVersion = reader.int32()\n\t\t\t\tbreak\n\t\t\tcase 12:\n\t\t\t\toperation.hasData = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 13:\n\t\t\t\toperation.hasPreviousData = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 14:\n\t\t\t\toperation.hashVersion = reader.uint32()\n\t\t\t\tbreak\n\t\t\tcase 15:\n\t\t\t\toperation.foldState = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 16:\n\t\t\t\toperation.encryptedJson = reader.string()\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\treader.skipType(tag & 7)\n\t\t}\n\t}\n\n\treturn operation\n}\n\nfunction decodeVectorEntry(reader: Reader, length: number): ProtoVectorEntry {\n\tconst end = reader.pos + length\n\tconst entry: ProtoVectorEntry = { key: '', value: 0 }\n\twhile (reader.pos < end) {\n\t\tconst tag = reader.uint32()\n\t\tswitch (tag >>> 3) {\n\t\t\tcase 1:\n\t\t\t\tentry.key = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 2:\n\t\t\t\tentry.value = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\treader.skipType(tag & 7)\n\t\t}\n\t}\n\treturn entry\n}\n\nfunction longToNumber(value: unknown): number {\n\tif (typeof value === 'number') return value\n\tif (typeof value === 'string') return Number.parseInt(value, 10)\n\tif (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\t'toNumber' in value &&\n\t\ttypeof (value as { toNumber: unknown }).toNumber === 'function'\n\t) {\n\t\treturn (value as { toNumber(): number }).toNumber()\n\t}\n\n\tthrow new SyncError('Failed to decode int64 value', {\n\t\treceivedType: typeof value,\n\t})\n}\n","import { SyncError } from '@korajs/core'\nimport type { SyncMessage } from '../protocol/messages'\nimport { JsonMessageSerializer } from '../protocol/serializer'\nimport type { MessageSerializer } from '../protocol/serializer'\nimport type {\n\tSyncTransport,\n\tTransportCloseHandler,\n\tTransportErrorHandler,\n\tTransportMessageHandler,\n\tTransportOptions,\n} from './transport'\n\n/**\n * WebSocket event interface for dependency injection.\n * Matches the subset of the browser WebSocket API that we need.\n */\nexport interface WebSocketLike {\n\treadonly readyState: number\n\tsend(data: string | Uint8Array): void\n\tclose(code?: number, reason?: string): void\n\tonopen: ((event: unknown) => void) | null\n\tonmessage: ((event: { data: unknown }) => void) | null\n\tonclose: ((event: { reason: string; code: number }) => void) | null\n\tonerror: ((event: unknown) => void) | null\n}\n\n/**\n * Constructor for WebSocket-like objects. Allows injection of mock WebSocket for testing.\n */\nexport type WebSocketConstructor = new (url: string, protocols?: string | string[]) => WebSocketLike\n\n/**\n * Options for the WebSocket transport.\n */\nexport interface WebSocketTransportOptions {\n\t/** Custom serializer. Defaults to JSON. */\n\tserializer?: MessageSerializer\n\t/** Injectable WebSocket constructor for testing. Defaults to globalThis.WebSocket. */\n\tWebSocketImpl?: WebSocketConstructor\n\t/** Connection timeout in ms. Defaults to 10000 (10s). */\n\tconnectTimeout?: number\n\t/**\n\t * Also append the auth token to the connection URL as `?token=`. Off by\n\t * default: the token already travels in the handshake message, and URLs are\n\t * recorded by reverse proxies, load balancers and access logs. Enable only for an\n\t * intermediary that authenticates the WebSocket upgrade by query parameter.\n\t */\n\ttokenInUrl?: boolean\n\t/**\n\t * Detect dead connections with the server's application-level heartbeat (LMS #12).\n\t * The transport advertises support in the handshake; a server that sends\n\t * heartbeats states their interval, and a connection that then receives nothing\n\t * for `heartbeatTimeoutFactor` intervals is closed as dead, which starts a\n\t * reconnect. Browsers cannot observe WebSocket pings, so without this a half-open\n\t * connection (a phone that changed networks) looks alive until TCP gives up, often\n\t * minutes later. Defaults to true.\n\t */\n\theartbeat?: boolean\n\t/** Silent intervals tolerated before the connection is declared dead. Defaults to 2.5. */\n\theartbeatTimeoutFactor?: number\n}\n\n// WebSocket readyState constants\nconst WS_OPEN = 1\n\n/**\n * WebSocket-based sync transport implementation.\n *\n * Every socket is bound to a generation (SYNC-5): `connect()` closes and detaches the\n * previous socket first, and callbacks of a superseded socket are ignored, so a stale\n * socket closing late can never sever (or feed messages into) the live session.\n */\nexport class WebSocketTransport implements SyncTransport {\n\tprivate ws: WebSocketLike | null = null\n\t/** Incremented for every socket; callbacks check theirs is still current. */\n\tprivate generation = 0\n\tprivate messageHandler: TransportMessageHandler | null = null\n\tprivate closeHandler: TransportCloseHandler | null = null\n\tprivate errorHandler: TransportErrorHandler | null = null\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly WebSocketImpl: WebSocketConstructor\n\tprivate readonly connectTimeout: number\n\tprivate readonly tokenInUrl: boolean\n\tprivate readonly heartbeat: boolean\n\tprivate readonly heartbeatTimeoutFactor: number\n\t/** Silence (ms) after which the current connection is declared dead; 0 = unarmed. */\n\tprivate livenessTimeoutMs = 0\n\tprivate livenessTimer: ReturnType<typeof setTimeout> | null = null\n\n\tconstructor(options?: WebSocketTransportOptions) {\n\t\tthis.serializer = options?.serializer ?? new JsonMessageSerializer()\n\t\tthis.connectTimeout = options?.connectTimeout ?? 10000\n\t\tthis.tokenInUrl = options?.tokenInUrl ?? false\n\t\tthis.heartbeat = options?.heartbeat ?? true\n\t\tthis.heartbeatTimeoutFactor = Math.max(1.5, options?.heartbeatTimeoutFactor ?? 2.5)\n\n\t\tif (options?.WebSocketImpl) {\n\t\t\tthis.WebSocketImpl = options.WebSocketImpl\n\t\t} else if (typeof globalThis.WebSocket !== 'undefined') {\n\t\t\tthis.WebSocketImpl = globalThis.WebSocket as unknown as WebSocketConstructor\n\t\t} else {\n\t\t\t// Deferred — will throw on connect() if no implementation available\n\t\t\tthis.WebSocketImpl = null as unknown as WebSocketConstructor\n\t\t}\n\t}\n\n\tasync connect(url: string, options?: TransportOptions): Promise<void> {\n\t\tif (!this.WebSocketImpl) {\n\t\t\tthrow new SyncError('WebSocket is not available in this environment', {\n\t\t\t\thint: 'Provide a WebSocketImpl option or use a polyfill',\n\t\t\t})\n\t\t}\n\n\t\t// Never let two sockets feed one engine: close and detach any previous socket\n\t\t// before opening the next (SYNC-5).\n\t\tthis.releaseSocket('Client reconnecting')\n\t\tconst generation = ++this.generation\n\t\tconst isCurrent = (): boolean => generation === this.generation\n\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tlet settled = false\n\n\t\t\tconst settle = (fn: () => void): void => {\n\t\t\t\tif (settled) return\n\t\t\t\tsettled = true\n\t\t\t\tclearTimeout(timer)\n\t\t\t\tfn()\n\t\t\t}\n\n\t\t\t// Connection timeout — prevents hanging for 30+ seconds on mobile when offline\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tsettle(() => {\n\t\t\t\t\tconst err = new SyncError('WebSocket connection timed out', {\n\t\t\t\t\t\turl,\n\t\t\t\t\t\ttimeout: this.connectTimeout,\n\t\t\t\t\t})\n\t\t\t\t\tif (isCurrent()) {\n\t\t\t\t\t\tthis.releaseSocket('Connection timed out')\n\t\t\t\t\t\tthis.errorHandler?.(err)\n\t\t\t\t\t}\n\t\t\t\t\treject(err)\n\t\t\t\t})\n\t\t\t}, this.connectTimeout)\n\n\t\t\ttry {\n\t\t\t\t// The token is NOT put in the URL by default (it is sent in the handshake);\n\t\t\t\t// see WebSocketTransportOptions.tokenInUrl for the explicit opt-in.\n\t\t\t\tconst connectUrl =\n\t\t\t\t\tthis.tokenInUrl && options?.authToken\n\t\t\t\t\t\t? `${url}${url.includes('?') ? '&' : '?'}token=${encodeURIComponent(options.authToken)}`\n\t\t\t\t\t\t: url\n\n\t\t\t\tconst ws = new this.WebSocketImpl(connectUrl)\n\t\t\t\tthis.ws = ws\n\n\t\t\t\tws.onopen = () => {\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tsettle(() => resolve())\n\t\t\t\t}\n\n\t\t\t\tws.onmessage = (event: { data: unknown }) => {\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\t// Any inbound frame proves the connection alive.\n\t\t\t\t\tthis.touchLiveness()\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof event.data !== 'string' &&\n\t\t\t\t\t\t\t!(event.data instanceof Uint8Array) &&\n\t\t\t\t\t\t\t!(event.data instanceof ArrayBuffer)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst message = this.serializer.decode(event.data)\n\t\t\t\t\t\tif (message.type === 'heartbeat') return\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tmessage.type === 'handshake-response' &&\n\t\t\t\t\t\t\ttypeof message.heartbeatIntervalMs === 'number' &&\n\t\t\t\t\t\t\tmessage.heartbeatIntervalMs > 0\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tthis.armLiveness(message.heartbeatIntervalMs)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.messageHandler?.(message)\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthis.errorHandler?.(new SyncError('Failed to decode incoming message'))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tws.onclose = (event: { reason: string; code: number }) => {\n\t\t\t\t\t// A superseded socket closing late must not touch the live one (SYNC-5).\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tthis.ws = null\n\t\t\t\t\tthis.clearLiveness()\n\t\t\t\t\tthis.livenessTimeoutMs = 0\n\t\t\t\t\tthis.closeHandler?.(event.reason || `WebSocket closed with code ${event.code}`)\n\t\t\t\t}\n\n\t\t\t\tws.onerror = (_event: unknown) => {\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tconst err = new SyncError('WebSocket error', {\n\t\t\t\t\t\turl,\n\t\t\t\t\t})\n\t\t\t\t\tthis.errorHandler?.(err)\n\t\t\t\t\t// If we haven't connected yet, reject the connect promise\n\t\t\t\t\tif (!this.isConnected()) {\n\t\t\t\t\t\tthis.ws = null\n\t\t\t\t\t\tsettle(() => reject(err))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tsettle(() =>\n\t\t\t\t\treject(\n\t\t\t\t\t\terr instanceof SyncError\n\t\t\t\t\t\t\t? err\n\t\t\t\t\t\t\t: new SyncError('Failed to create WebSocket', {\n\t\t\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t\t\t\terror: String(err),\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\t// Invalidate the current socket's callbacks: an intentional disconnect fires no\n\t\t// close handler.\n\t\tthis.generation++\n\t\tthis.releaseSocket('Client disconnecting')\n\t}\n\n\tsend(message: SyncMessage): void {\n\t\tif (!this.ws || this.ws.readyState !== WS_OPEN) {\n\t\t\tthrow new SyncError('Cannot send message: WebSocket is not connected', {\n\t\t\t\tmessageType: message.type,\n\t\t\t})\n\t\t}\n\t\t// The heartbeat is a transport capability, so the transport advertises it.\n\t\tconst outgoing: SyncMessage =\n\t\t\tthis.heartbeat && message.type === 'handshake'\n\t\t\t\t? { ...message, supportsHeartbeat: true }\n\t\t\t\t: message\n\t\tconst encoded = this.serializer.encode(outgoing)\n\t\tthis.ws.send(encoded)\n\t}\n\n\tonMessage(handler: TransportMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: TransportCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t}\n\n\tonError(handler: TransportErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.ws !== null && this.ws.readyState === WS_OPEN\n\t}\n\n\t/** Close and detach the current socket, if any, without firing the close handler. */\n\tprivate releaseSocket(reason: string): void {\n\t\tthis.clearLiveness()\n\t\tthis.livenessTimeoutMs = 0\n\t\tconst ws = this.ws\n\t\tthis.ws = null\n\t\tif (!ws) return\n\t\tws.onopen = null\n\t\tws.onmessage = null\n\t\tws.onclose = null\n\t\tws.onerror = null\n\t\ttry {\n\t\t\tws.close(1000, reason)\n\t\t} catch {\n\t\t\t// Closing an already-failed socket can throw; it is detached either way.\n\t\t}\n\t}\n\n\t/** Start watching for silence once the server stated its heartbeat interval. */\n\tprivate armLiveness(heartbeatIntervalMs: number): void {\n\t\tif (!this.heartbeat) return\n\t\tthis.livenessTimeoutMs = Math.round(heartbeatIntervalMs * this.heartbeatTimeoutFactor)\n\t\tthis.touchLiveness()\n\t}\n\n\tprivate touchLiveness(): void {\n\t\tif (this.livenessTimeoutMs <= 0) return\n\t\tthis.clearLiveness()\n\t\tconst generation = this.generation\n\t\tthis.livenessTimer = setTimeout(() => {\n\t\t\tthis.livenessTimer = null\n\t\t\tif (generation !== this.generation || this.ws === null) return\n\t\t\t// Nothing arrived for several heartbeat intervals: the connection is dead even\n\t\t\t// though the browser still reports it open. Drop it and report the close so\n\t\t\t// the engine reconnects.\n\t\t\tconst silenceMs = this.livenessTimeoutMs\n\t\t\tthis.generation++\n\t\t\tthis.releaseSocket('Heartbeat timeout')\n\t\t\tthis.closeHandler?.(`Heartbeat timeout: no message from the server for ${silenceMs} ms`)\n\t\t}, this.livenessTimeoutMs)\n\t}\n\n\tprivate clearLiveness(): void {\n\t\tif (this.livenessTimer !== null) {\n\t\t\tclearTimeout(this.livenessTimer)\n\t\t\tthis.livenessTimer = null\n\t\t}\n\t}\n}\n","import { SyncError } from '@korajs/core'\nimport { NegotiatedMessageSerializer } from '../protocol/serializer'\nimport type { MessageSerializer } from '../protocol/serializer'\nimport type {\n\tSyncTransport,\n\tTransportCloseHandler,\n\tTransportErrorHandler,\n\tTransportMessageHandler,\n\tTransportOptions,\n} from './transport'\nimport { WebSocketTransport } from './websocket-transport'\n\nconst DEFAULT_RETRY_DELAY_MS = 250\n\n/**\n * Header carrying the server-issued HTTP sync session id. The server returns it on\n * the response to the first POST (the handshake); the client sends it on every\n * later request, together with its bearer credential.\n */\nexport const HTTP_SYNC_SESSION_HEADER = 'x-kora-session'\n\n/** Statuses after which the server-side HTTP session is gone: reconnect from scratch. */\nconst SESSION_ENDED_STATUSES = new Set([401, 403, 404, 410])\n\nexport interface HttpLongPollingTransportOptions {\n\tserializer?: MessageSerializer\n\tfetchImpl?: typeof fetch\n\tretryDelayMs?: number\n\tpreferWebSocket?: boolean\n\twebSocketFactory?: () => SyncTransport\n}\n\n/**\n * HTTP long-polling transport with optional WebSocket upgrade.\n *\n * The first POST (the handshake) opens a server-side session; the server answers\n * with a high-entropy session id in the `x-kora-session` header. Every later\n * request carries that id and the bearer credential, and the long-poll loop starts\n * only once the id is known. POSTs are sent one at a time, in order. A 401, 403,\n * 404 or 410 means the server-side session is gone: the transport closes so the\n * sync engine reconnects (and re-authenticates) from scratch.\n */\nexport class HttpLongPollingTransport implements SyncTransport {\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly fetchImpl: typeof fetch\n\tprivate readonly retryDelayMs: number\n\tprivate readonly preferWebSocket: boolean\n\tprivate readonly webSocketFactory: () => SyncTransport\n\n\tprivate messageHandler: TransportMessageHandler | null = null\n\tprivate closeHandler: TransportCloseHandler | null = null\n\tprivate errorHandler: TransportErrorHandler | null = null\n\n\tprivate connected = false\n\tprivate polling = false\n\tprivate url: string | null = null\n\tprivate authToken: string | undefined\n\tprivate pollAbort: AbortController | null = null\n\tprivate upgradedTransport: SyncTransport | null = null\n\t/** Server-issued session id; null until the handshake POST is answered. */\n\tprivate sessionId: string | null = null\n\t/** Serializes POSTs so the server receives messages in send order. */\n\tprivate postChain: Promise<void> = Promise.resolve()\n\n\tconstructor(options?: HttpLongPollingTransportOptions) {\n\t\tthis.serializer = options?.serializer ?? new NegotiatedMessageSerializer('json')\n\t\tthis.fetchImpl = options?.fetchImpl ?? fetch\n\t\tthis.retryDelayMs = options?.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS\n\t\tthis.preferWebSocket = options?.preferWebSocket ?? true\n\t\tthis.webSocketFactory =\n\t\t\toptions?.webSocketFactory ?? (() => new WebSocketTransport({ serializer: this.serializer }))\n\t}\n\n\tasync connect(url: string, options?: TransportOptions): Promise<void> {\n\t\tif (this.connected) {\n\t\t\tthrow new SyncError('HTTP long-poll transport already connected', { url })\n\t\t}\n\n\t\tthis.url = normalizeHttpUrl(url)\n\t\tthis.authToken = options?.authToken\n\n\t\tif (this.preferWebSocket) {\n\t\t\tconst upgraded = await this.tryUpgradeToWebSocket(url, options)\n\t\t\tif (upgraded) {\n\t\t\t\tthis.upgradedTransport = upgraded\n\t\t\t\tthis.connected = true\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.connected = true\n\t\tthis.sessionId = null\n\t\tthis.postChain = Promise.resolve()\n\t\tthis.pollAbort = new AbortController()\n\t\t// Polling starts once the server has issued a session id (first POST).\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\tif (this.upgradedTransport) {\n\t\t\tawait this.upgradedTransport.disconnect()\n\t\t\tthis.upgradedTransport = null\n\t\t}\n\n\t\tthis.connected = false\n\t\tthis.polling = false\n\t\tthis.pollAbort?.abort()\n\t\tthis.pollAbort = null\n\t\tthis.url = null\n\t\tthis.sessionId = null\n\t}\n\n\tsend(message: import('../protocol/messages').SyncMessage): void {\n\t\tif (!this.connected) {\n\t\t\tthrow new SyncError('Cannot send message: HTTP long-poll transport is not connected', {\n\t\t\t\tmessageType: message.type,\n\t\t\t})\n\t\t}\n\n\t\tif (this.upgradedTransport) {\n\t\t\tthis.upgradedTransport.send(message)\n\t\t\treturn\n\t\t}\n\n\t\tthis.postChain = this.postChain.then(() => this.postMessage(message))\n\t}\n\n\tonMessage(handler: TransportMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t\tif (this.upgradedTransport) {\n\t\t\tthis.upgradedTransport.onMessage(handler)\n\t\t}\n\t}\n\n\tonClose(handler: TransportCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t\tif (this.upgradedTransport) {\n\t\t\tthis.upgradedTransport.onClose(handler)\n\t\t}\n\t}\n\n\tonError(handler: TransportErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t\tif (this.upgradedTransport) {\n\t\t\tthis.upgradedTransport.onError(handler)\n\t\t}\n\t}\n\n\tisConnected(): boolean {\n\t\tif (this.upgradedTransport) {\n\t\t\treturn this.upgradedTransport.isConnected()\n\t\t}\n\t\treturn this.connected\n\t}\n\n\tprivate async tryUpgradeToWebSocket(\n\t\turl: string,\n\t\toptions?: TransportOptions,\n\t): Promise<SyncTransport | null> {\n\t\tconst wsTransport = this.webSocketFactory()\n\n\t\tif (this.messageHandler) wsTransport.onMessage(this.messageHandler)\n\t\tif (this.closeHandler) wsTransport.onClose(this.closeHandler)\n\t\tif (this.errorHandler) wsTransport.onError(this.errorHandler)\n\n\t\ttry {\n\t\t\tawait wsTransport.connect(normalizeWebSocketUrl(url), options)\n\t\t\treturn wsTransport\n\t\t} catch {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tprivate async postMessage(message: import('../protocol/messages').SyncMessage): Promise<void> {\n\t\tif (!this.url || !this.connected) return\n\n\t\tconst encoded = this.serializer.encode(message)\n\t\tconst headers = this.makeHeaders()\n\n\t\tconst isBinary = encoded instanceof Uint8Array\n\t\theaders.set('content-type', isBinary ? 'application/x-protobuf' : 'application/json')\n\n\t\ttry {\n\t\t\tconst requestBody = isBinary ? toArrayBuffer(encoded) : encoded\n\n\t\t\tconst response = await this.fetchImpl(this.url, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders,\n\t\t\t\tbody: requestBody,\n\t\t\t})\n\n\t\t\tif (SESSION_ENDED_STATUSES.has(response.status)) {\n\t\t\t\tthis.endSession(response.status)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new SyncError('HTTP transport send failed', {\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\tmessageType: message.type,\n\t\t\t\t})\n\t\t\t}\n\t\t\tconst issued = response.headers.get(HTTP_SYNC_SESSION_HEADER)\n\t\t\tif (this.sessionId === null && issued) {\n\t\t\t\tthis.sessionId = issued\n\t\t\t\tthis.startPolling()\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.errorHandler?.(error instanceof Error ? error : new Error(String(error)))\n\t\t}\n\t}\n\n\tprivate startPolling(): void {\n\t\tif (this.polling || !this.connected) return\n\t\tthis.polling = true\n\t\tvoid this.runPollLoop()\n\t}\n\n\t/** The server-side session is gone: stop and report a close so the engine reconnects. */\n\tprivate endSession(status: number): void {\n\t\tif (!this.connected) return\n\t\tthis.connected = false\n\t\tthis.polling = false\n\t\tthis.pollAbort?.abort()\n\t\tthis.pollAbort = null\n\t\tthis.sessionId = null\n\t\tthis.closeHandler?.(`http sync session ended (${String(status)})`)\n\t}\n\n\tprivate async runPollLoop(): Promise<void> {\n\t\twhile (this.polling && this.connected && this.url) {\n\t\t\ttry {\n\t\t\t\tconst response = await this.fetchImpl(this.url, {\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\theaders: this.makeHeaders(),\n\t\t\t\t\tsignal: this.pollAbort?.signal,\n\t\t\t\t})\n\n\t\t\t\tif (SESSION_ENDED_STATUSES.has(response.status)) {\n\t\t\t\t\tthis.endSession(response.status)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (response.status === 204) {\n\t\t\t\t\tawait sleep(this.retryDelayMs)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new SyncError('HTTP long-poll request failed', {\n\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tconst payload = await readResponsePayload(response)\n\t\t\t\tif (payload === null) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tconst message = this.serializer.decode(payload)\n\t\t\t\tthis.messageHandler?.(message)\n\t\t\t} catch (error) {\n\t\t\t\tif (!this.connected || !this.polling) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tif (isAbortError(error)) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tthis.errorHandler?.(error instanceof Error ? error : new Error(String(error)))\n\t\t\t\tawait sleep(this.retryDelayMs)\n\t\t\t}\n\t\t}\n\n\t\tif (!this.connected) {\n\t\t\tthis.closeHandler?.('http long-polling disconnected')\n\t\t}\n\t}\n\n\t/** Accept, bearer credential and (once issued) the session id: sent on every request. */\n\tprivate makeHeaders(): Headers {\n\t\tconst headers = new Headers()\n\t\theaders.set('accept', 'application/json, application/x-protobuf')\n\t\tif (this.authToken) {\n\t\t\theaders.set('authorization', `Bearer ${this.authToken}`)\n\t\t}\n\t\tif (this.sessionId) {\n\t\t\theaders.set(HTTP_SYNC_SESSION_HEADER, this.sessionId)\n\t\t}\n\t\treturn headers\n\t}\n}\n\nfunction normalizeHttpUrl(url: string): string {\n\tif (url.startsWith('http://') || url.startsWith('https://')) {\n\t\treturn url\n\t}\n\n\tif (url.startsWith('ws://')) {\n\t\treturn `http://${url.slice('ws://'.length)}`\n\t}\n\n\tif (url.startsWith('wss://')) {\n\t\treturn `https://${url.slice('wss://'.length)}`\n\t}\n\n\treturn url\n}\n\nfunction normalizeWebSocketUrl(url: string): string {\n\tif (url.startsWith('ws://') || url.startsWith('wss://')) {\n\t\treturn url\n\t}\n\n\tif (url.startsWith('http://')) {\n\t\treturn `ws://${url.slice('http://'.length)}`\n\t}\n\n\tif (url.startsWith('https://')) {\n\t\treturn `wss://${url.slice('https://'.length)}`\n\t}\n\n\treturn url\n}\n\nasync function readResponsePayload(response: Response): Promise<string | Uint8Array | null> {\n\tconst contentType = response.headers.get('content-type') ?? ''\n\tif (contentType.includes('application/x-protobuf')) {\n\t\tconst buffer = await response.arrayBuffer()\n\t\tif (buffer.byteLength === 0) return null\n\t\treturn new Uint8Array(buffer)\n\t}\n\n\tconst text = await response.text()\n\tif (text.length === 0) return null\n\treturn text\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === 'object' &&\n\t\terror !== null &&\n\t\t'name' in error &&\n\t\t(error as { name?: string }).name === 'AbortError'\n\t)\n}\n\nfunction toArrayBuffer(data: Uint8Array): ArrayBuffer {\n\tconst copied = new Uint8Array(data.byteLength)\n\tcopied.set(data)\n\treturn copied.buffer\n}\n","import type { SyncMessage } from '../protocol/messages'\nimport type {\n\tSyncTransport,\n\tTransportCloseHandler,\n\tTransportErrorHandler,\n\tTransportMessageHandler,\n\tTransportOptions,\n} from './transport'\n\n/**\n * Configuration for the chaos transport.\n */\nexport interface ChaosConfig {\n\t/** Probability of dropping a message (0-1). Defaults to 0. */\n\tdropRate?: number\n\t/** Probability of duplicating a message (0-1). Defaults to 0. */\n\tduplicateRate?: number\n\t/** Probability of reordering messages (0-1). Defaults to 0. */\n\treorderRate?: number\n\t/** Maximum latency in ms for delayed messages. Defaults to 0. */\n\tmaxLatency?: number\n\t/** Injectable random source for deterministic testing. Returns value in [0, 1). */\n\trandomSource?: () => number\n\t/**\n\t * Deterministic targeted drop. When it returns true for a message and direction,\n\t * that message is dropped unconditionally (independent of `dropRate`). Enables\n\t * reproducing an exact gap (e.g. drop one specific operation) instead of relying\n\t * on probabilistic drops. `direction` is 'outgoing' for `send` and 'incoming' for\n\t * messages arriving from the inner transport.\n\t */\n\tdropPredicate?: (message: SyncMessage, direction: 'outgoing' | 'incoming') => boolean\n}\n\n/**\n * Chaos transport that wraps another transport and injects faults.\n * Used for testing sync convergence under unreliable network conditions.\n *\n * Supports message dropping, duplication, reordering, and latency injection.\n * All random behavior is injectable for deterministic, reproducible tests.\n */\nexport class ChaosTransport implements SyncTransport {\n\tprivate readonly inner: SyncTransport\n\tprivate readonly dropRate: number\n\tprivate readonly duplicateRate: number\n\tprivate readonly reorderRate: number\n\tprivate readonly maxLatency: number\n\tprivate readonly random: () => number\n\tprivate readonly dropPredicate:\n\t\t| ((message: SyncMessage, direction: 'outgoing' | 'incoming') => boolean)\n\t\t| null\n\n\tprivate messageHandler: TransportMessageHandler | null = null\n\tprivate reorderBuffer: SyncMessage[] = []\n\tprivate pending: Array<{ timer: ReturnType<typeof setTimeout>; message: SyncMessage }> = []\n\n\tconstructor(inner: SyncTransport, config?: ChaosConfig) {\n\t\tthis.inner = inner\n\t\tthis.dropRate = config?.dropRate ?? 0\n\t\tthis.duplicateRate = config?.duplicateRate ?? 0\n\t\tthis.reorderRate = config?.reorderRate ?? 0\n\t\tthis.maxLatency = config?.maxLatency ?? 0\n\t\tthis.random = config?.randomSource ?? Math.random\n\t\tthis.dropPredicate = config?.dropPredicate ?? null\n\t}\n\n\tasync connect(url: string, options?: TransportOptions): Promise<void> {\n\t\t// Intercept incoming messages from the inner transport\n\t\tthis.inner.onMessage((msg) => this.handleIncoming(msg))\n\t\treturn this.inner.connect(url, options)\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\t// Flush reorder buffer\n\t\tthis.flushReorderBuffer()\n\t\t// Deliver any incoming messages still sitting in their simulated-latency\n\t\t// window rather than discarding them. `maxLatency` models delay already\n\t\t// incurred on the wire before we hung up: the peer already sent these,\n\t\t// so a local disconnect() shouldn't retroactively erase them. Cancelling\n\t\t// the timer without delivering silently inflates the effective drop\n\t\t// rate past what `dropRate` configures, and makes convergence depend on\n\t\t// the real wall-clock race between each message's random delay and\n\t\t// whenever a test happens to call disconnect() next, exactly the kind\n\t\t// of timing dependency that makes \"seeded\" chaos tests flaky anyway.\n\t\tconst flushed = this.pending\n\t\tthis.pending = []\n\t\tfor (const { timer } of flushed) {\n\t\t\tclearTimeout(timer)\n\t\t}\n\t\tfor (const { message } of flushed) {\n\t\t\tthis.deliverIncoming(message)\n\t\t}\n\t\treturn this.inner.disconnect()\n\t}\n\n\tsend(message: SyncMessage): void {\n\t\t// Deterministic targeted drop takes precedence over probabilistic drop.\n\t\tif (this.dropPredicate?.(message, 'outgoing')) {\n\t\t\treturn // Dropped\n\t\t}\n\t\t// Apply chaos to outgoing messages\n\t\tif (this.random() < this.dropRate) {\n\t\t\treturn // Dropped\n\t\t}\n\n\t\tif (this.random() < this.reorderRate) {\n\t\t\tthis.reorderBuffer.push(message)\n\t\t\t// Flush buffer on next non-reordered send\n\t\t\treturn\n\t\t}\n\n\t\t// Flush any buffered messages first\n\t\tthis.flushReorderBuffer()\n\n\t\tthis.inner.send(message)\n\n\t\t// Duplicate?\n\t\tif (this.random() < this.duplicateRate) {\n\t\t\tthis.inner.send(message)\n\t\t}\n\t}\n\n\tonMessage(handler: TransportMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: TransportCloseHandler): void {\n\t\tthis.inner.onClose(handler)\n\t}\n\n\tonError(handler: TransportErrorHandler): void {\n\t\tthis.inner.onError(handler)\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.inner.isConnected()\n\t}\n\n\tprivate handleIncoming(message: SyncMessage): void {\n\t\tif (!this.messageHandler) return\n\n\t\t// Deterministic targeted drop takes precedence over probabilistic drop.\n\t\tif (this.dropPredicate?.(message, 'incoming')) {\n\t\t\treturn // Dropped\n\t\t}\n\t\t// Apply chaos to incoming messages\n\t\tif (this.random() < this.dropRate) {\n\t\t\treturn // Dropped\n\t\t}\n\n\t\tif (this.maxLatency > 0) {\n\t\t\tconst delay = Math.floor(this.random() * this.maxLatency)\n\t\t\t// `timer` is assigned before the callback can possibly run (setTimeout\n\t\t\t// callbacks never fire synchronously), so the closure below always\n\t\t\t// sees the real handle, letting the entry remove itself from\n\t\t\t// `pending` once its delivery actually happens.\n\t\t\tconst timer: ReturnType<typeof setTimeout> = setTimeout(() => {\n\t\t\t\tthis.pending = this.pending.filter((p) => p.timer !== timer)\n\t\t\t\tthis.deliverIncoming(message)\n\t\t\t}, delay)\n\t\t\tthis.pending.push({ timer, message })\n\t\t\treturn\n\t\t}\n\n\t\tthis.deliverIncoming(message)\n\t}\n\n\tprivate deliverIncoming(message: SyncMessage): void {\n\t\tif (!this.messageHandler) return\n\n\t\tthis.messageHandler(message)\n\n\t\t// Duplicate incoming?\n\t\tif (this.random() < this.duplicateRate) {\n\t\t\tthis.messageHandler(message)\n\t\t}\n\t}\n\n\tprivate flushReorderBuffer(): void {\n\t\t// Send buffered messages in random order\n\t\tconst buffer = [...this.reorderBuffer]\n\t\tthis.reorderBuffer = []\n\n\t\t// Fisher-Yates shuffle with injectable random\n\t\tfor (let i = buffer.length - 1; i > 0; i--) {\n\t\t\tconst j = Math.floor(this.random() * (i + 1))\n\t\t\tconst temp = buffer[i] as SyncMessage\n\t\t\tbuffer[i] = buffer[j] as SyncMessage\n\t\t\tbuffer[j] = temp\n\t\t}\n\n\t\tfor (const msg of buffer) {\n\t\t\tthis.inner.send(msg)\n\t\t}\n\t}\n}\n","import type {\n\tApplyFailureReason,\n\tApplyResult,\n\tKoraEventEmitter,\n\tOperation,\n\tOperationTransform,\n\tSyncDiagnosticsSnapshot,\n\tVersionVector,\n} from '@korajs/core'\nimport {\n\tAPPLY_FAILURE_CODES,\n\tClockDriftError,\n\tHybridLogicalClock,\n\tInvalidTimestampError,\n\tKoraError,\n\tRemoteClockDriftError,\n\tSyncError,\n\tcanonicalizeProvenLegacyClear,\n\tdefaultApplyFailureReason,\n\tisAuthoritativeNodeId,\n\tisServerNodeId,\n\toperationSchemaView,\n\toperationTransformsFingerprint,\n} from '@korajs/core'\nimport { AwarenessManager } from '../awareness/awareness-manager'\nimport type { AwarenessMessage, AwarenessState } from '../awareness/types'\nimport { BlobChunkChannel } from '../blob/blob-chunk-channel'\nimport {\n\tcreateDeltaCursorFromBatch,\n\tdecodeDeltaCursor,\n\tencodeDeltaCursor,\n\tsliceOperationsAfterCursor,\n} from '../delta/delta-cursor'\nimport { SyncMetricsCollector } from '../diagnostics/metrics-collector'\nimport type { MetricsCollectorConfig } from '../diagnostics/metrics-collector'\nimport type {\n\tEncryptionKeyResponseMessage,\n\tEncryptionKeyResponseStatus,\n} from '../encryption/key-messages'\nimport type { WrappedKeyRecord } from '../encryption/key-record'\nimport type { EncryptionKeyring, KeyServiceChannel, KeyServiceReply } from '../encryption/keyring'\nimport { EncryptionKeyError } from '../encryption/keyring'\nimport type { SyncEncryptor } from '../encryption/sync-encryptor'\nimport type {\n\tAcknowledgmentMessage,\n\tAwarenessStateWire,\n\tAwarenessUpdateMessage,\n\tBlobChunkPushMessage,\n\tBlobChunkRequestMessage,\n\tBlobChunkResponseMessage,\n\tHandshakeResponseMessage,\n\tOperationBatchMessage,\n\tOperationRejectedMessage,\n\tSyncMessage,\n\tWireFormat,\n\tYjsDocUpdateMessage,\n} from '../protocol/messages'\nimport { SYNC_PROTOCOL_VERSION, declaredProtocolVersion } from '../protocol/protocol-version'\nimport { isSchemaMismatchReject } from '../protocol/schema-version'\nimport {\n\tNegotiatedMessageSerializer,\n\tversionVectorToWire,\n\twireToVersionVector,\n} from '../protocol/serializer'\nimport type { MessageSerializer } from '../protocol/serializer'\nimport { RichtextDocChannel } from '../richtext/richtext-doc-channel'\nimport {\n\ttype SyncQuerySubset,\n\tdedupeQuerySubsets,\n\tquerySubsetContains,\n} from '../scopes/query-subset'\nimport { operationMatchesScope } from '../scopes/scope-filter'\nimport { scopeViewKey } from '../scopes/scope-view-key'\nimport type { SyncTransport } from '../transport/transport'\nimport type { DeltaCursor } from '../types'\nimport {\n\ttype AdoptionScheduleInfo,\n\ttype HeldNodeInfo,\n\ttype HeldReason,\n\ttype LocalNodeInfo,\n\tMemoryRejectedOperationStorage,\n\ttype QuarantinedOperation,\n\ttype QueueStorage,\n\ttype RejectedOperation,\n\ttype RejectedOperationStorage,\n\ttype SyncConfig,\n\ttype SyncScopeMap,\n\ttype SyncState,\n\ttype SyncStatePersistence,\n\ttype SyncStatusInfo,\n} from '../types'\nimport { MemoryQueueStorage } from './memory-queue-storage'\nimport type { OutboundBatch } from './outbound-queue'\nimport { OutboundQueue } from './outbound-queue'\nimport type { SyncStore } from './sync-store'\nimport { verifyInboundOperation } from './verify-inbound'\n\nconst DEFAULT_BATCH_SIZE = 100\nconst DEFAULT_SCHEMA_VERSION = 1\nconst DEFAULT_OUTBOUND_ACK_TIMEOUT_MS = 30000\nconst DEFAULT_OUTBOUND_RETRY_BASE_DELAY_MS = 250\nconst DEFAULT_OUTBOUND_RETRY_MAX_DELAY_MS = 30000\n/**\n * How many own sequence numbers the engine reads from the op log at a time when it\n * (re)builds the upload set. Bounds memory during the one-time upgrade re-upload of a\n * device's whole history: the next chunk is read only once the previous one is resolved.\n * Large on purpose: an engine without sync-state persistence re-reads its history on\n * every start, and against a legacy (version-vector) server a relay of a later chunk\n * that overtakes a dropped earlier one leaves a vector gap on peers.\n */\nconst OWN_LOG_SCAN_CHUNK = 2000\n/**\n * A delivered operation stamped further than this ahead of the trusted reference time\n * is quarantined instead of applied (SYNC-7). Matches the HLC's own refusal threshold.\n */\nconst MAX_REMOTE_FUTURE_MS = 5 * 60_000\n/**\n * Consecutive failed durability barriers before uploads stop waiting for local durability\n * (RT-49). A transient failure (a busy IndexedDB transaction) postpones the upload and\n * retries; a persistent one (quota exceeded, a broken store) would otherwise withhold every\n * write from the reachable server while the app keeps writing into memory. Once the device\n * may lose its local tail anyway, the server is the better place for it: RT-35 recovery\n * (handshake raise, full resync, renumber on SEQUENCE_CONFLICT) restores it after a reload.\n */\nconst DURABILITY_FAILURES_BEFORE_DEGRADED = 3\n/**\n * Backoff of a parked adoption (RT-46): an adopted node whose uploads the server keeps\n * deferring is retried once anything else from this database uploaded, or after this\n * delay (doubling per consecutive park, capped).\n */\nconst ADOPTION_PARK_BASE_MS = 30_000\nconst ADOPTION_PARK_MAX_MS = 60 * 60_000\n/** Terminal marker of held writes the app discarded from sync (RT-50). */\nconst HELD_DISCARDED_CODE = 'HELD_DISCARDED'\n/** `FoldStateError.code` (`@korajs/core`): a record state the fold cannot merge into. */\nconst FOLD_STATE_INVALID_CODE = 'FOLD_STATE_INVALID'\n/** Times one start() begins again because the signed-in user changed while connecting (RT-52). */\nconst MAX_PRINCIPAL_RESTARTS = 2\n/**\n * A live session of this tab's own node ends to retry a parked adoption (that other\n * uploads may have unblocked) at most this soon after it was parked, so a node the\n * server defers for good costs at most one reconnect per interval (RT-46).\n */\nconst ADOPTION_YIELD_MIN_MS = 30_000\n/** Minimum time between two full resyncs triggered by SEQUENCE_CONFLICT recoveries (RT-44). */\nconst FULL_RESYNC_MIN_INTERVAL_MS = 60_000\n/** Quarantine reason codes for inbound operations the client did not apply (W4). */\nconst QUARANTINE_CODES = {\n\tDECRYPT_FAILED: 'DECRYPT_FAILED',\n\tREMOTE_CLOCK_DRIFT: 'REMOTE_CLOCK_DRIFT',\n\tTRANSFORM_UNAVAILABLE: 'SCHEMA_TRANSFORM_UNAVAILABLE',\n\tTRANSFORM_INVALID: 'SCHEMA_TRANSFORM_INVALID',\n} as const\n\n/** A taken outbound batch, from the moment it leaves the queue until it is resolved. */\ninterface InFlightUpload {\n\tbatch: OutboundBatch\n\t/** Wire message id once sent; acks are matched to batches by it (SYNC-4). */\n\tmessageId: string | null\n\t/** Session the batch was taken in; a batch from an older session is never sent. */\n\tepoch: number\n\t/** A retriable rejection hit this batch: back off before flushing again. */\n\tretryBackoff: boolean\n}\n\n/** A local node's unsynced operations (see SyncEngine.countUnsyncedOfNode). */\ninterface UnsyncedOfNode {\n\tcount: number\n\tnotQueued: number\n\tids: ReadonlySet<string>\n\tdeps: ReadonlySet<string>\n}\n\n/**\n * Valid state transitions for the sync engine state machine.\n */\n/** Server error codes that end a session because its credential expired or was revoked. */\nfunction isCredentialEndingCode(code: string): boolean {\n\treturn code === 'AUTH_EXPIRED' || code === 'AUTH_REVOKED'\n}\n\nconst VALID_TRANSITIONS: Record<SyncState, SyncState[]> = {\n\tdisconnected: ['connecting'],\n\tconnecting: ['handshaking', 'error', 'disconnected'],\n\thandshaking: ['syncing', 'error', 'disconnected'],\n\tsyncing: ['streaming', 'error', 'disconnected'],\n\tstreaming: ['disconnected', 'error'],\n\terror: ['disconnected'],\n}\n\n/**\n * Options for creating a SyncEngine.\n */\nexport interface SyncEngineOptions {\n\t/** Transport implementation (WebSocket, memory, etc.) */\n\ttransport: SyncTransport\n\t/** Local store implementing SyncStore */\n\tstore: SyncStore\n\t/** Sync configuration */\n\tconfig: SyncConfig\n\t/** Message serializer. Defaults to JSON. */\n\tserializer?: MessageSerializer\n\t/** Event emitter for DevTools integration */\n\temitter?: KoraEventEmitter\n\t/** Queue storage for persistent outbound queue. Defaults to in-memory. */\n\tqueueStorage?: QueueStorage\n\t/**\n\t * Durable storage for operations the server rejected. Defaults to in-memory.\n\t * Provide a store-backed implementation so rejections survive a page refresh.\n\t */\n\trejectedStorage?: RejectedOperationStorage\n\t/**\n\t * Optional encryptor for end-to-end encryption.\n\t * When provided, `data` and `previousData` fields of operations are encrypted\n\t * before sending and decrypted after receiving. The server never sees plaintext data.\n\t */\n\tencryptor?: SyncEncryptor\n\t/**\n\t * End-to-end encryption keyring (ENC-1): the server-stored, passphrase-wrapped data\n\t * keys, opened during each handshake before any operation is exchanged. Takes the\n\t * place of `encryptor`. While it is locked, sync does not connect.\n\t */\n\tkeyring?: EncryptionKeyring\n\t/** How long a key-service request may take before the session is retried (ms). */\n\tkeyServiceTimeoutMs?: number\n\t/** Optional configuration for the metrics collector. */\n\tmetricsConfig?: MetricsCollectorConfig\n\t/** Op-log backed sync state (last acked server vector, unsynced counts). */\n\tsyncState?: SyncStatePersistence\n}\n\n/**\n * Diagnostics snapshot for debugging and support.\n */\nexport interface SyncDiagnostics {\n\tstate: SyncState\n\tstatus: SyncStatusInfo\n\tnodeId: string\n\turl: string\n\tschemaVersion: number\n\tlastSyncedAt: number | null\n\tlastSuccessfulPush: number | null\n\tlastSuccessfulPull: number | null\n\tconflicts: number\n\tpendingOperations: number\n\thasInFlightBatch: boolean\n\treconnecting: boolean\n\tdeliveryWatermark: number\n\tdeliveryGapRepeatCount: number\n\ttimestamp: number\n}\n\nlet nextMessageId = 0\nlet nextQuerySubsetId = 0\n\n/** Rejection code for a local operation the uplink scope refuses (recorded client-side). */\nconst OUT_OF_UPLINK_SCOPE = 'OUT_OF_UPLINK_SCOPE'\n/** The server holds another operation of this node under the sequence number (RT-35). */\nconst SEQUENCE_CONFLICT = 'SEQUENCE_CONFLICT'\n/**\n * Non-retriable rejection codes that never become a durable terminal marker (RT-36): the\n * operation was not refused for its content, so uploading it later may succeed.\n */\nconst NON_TERMINAL_REJECTION_CODES: ReadonlySet<string> = new Set([\n\tSEQUENCE_CONFLICT,\n\t'NODE_ID_MISMATCH',\n\tOUT_OF_UPLINK_SCOPE,\n])\n/** Default deadline of one key-service request. */\nconst DEFAULT_KEY_SERVICE_TIMEOUT_MS = 15_000\n\nfunction generateMessageId(): string {\n\treturn `msg-${Date.now()}-${nextMessageId++}`\n}\n\n/**\n * Upper bound on the number of per-view delivery watermarks retained (in memory and in\n * persistence). A client that churns through many distinct views (for example a search that\n * registers a fresh query subscription per keystroke) would otherwise accumulate an\n * unbounded number of `_kora_meta` rows. Cold views are evicted least-recently-used; the\n * default view and the live view are never evicted. Eviction is a storage/performance\n * tradeoff only, never a correctness one: an evicted view back-fills from 0 (deduplicated)\n * when next visited.\n */\nconst MAX_DELIVERY_VIEW_WATERMARKS = 64\n\n/**\n * Deterministic JSON stringify with sorted object keys, so two equal values always\n * produce the identical string. Used to build a stable sync-view signature for keying the\n * per-view delivery watermark.\n */\nfunction stableStringify(value: unknown): string {\n\tif (value === null || typeof value !== 'object') {\n\t\treturn JSON.stringify(value) ?? 'null'\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn `[${value.map(stableStringify).join(',')}]`\n\t}\n\tconst entries = Object.entries(value as Record<string, unknown>)\n\t\t.filter(([, v]) => v !== undefined)\n\t\t.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n\t\t.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`)\n\treturn `{${entries.join(',')}}`\n}\n\n/**\n * Core sync orchestrator. Manages the sync lifecycle:\n * disconnected → connecting → handshaking → syncing → streaming\n *\n * Coordinates handshake, delta exchange, and real-time streaming\n * between a local store and a remote sync server.\n */\nexport class SyncEngine {\n\tprivate state: SyncState = 'disconnected'\n\t/** Listeners registered through {@link onStateChange}. */\n\tprivate readonly stateListeners = new Set<(state: SyncState) => void>()\n\tprivate readonly transport: SyncTransport\n\tprivate readonly store: SyncStore\n\tprivate readonly config: SyncConfig\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly emitter: KoraEventEmitter | null\n\tprivate readonly outboundQueue: OutboundQueue\n\tprivate readonly rejectedStorage: RejectedOperationStorage\n\tprivate readonly batchSize: number\n\tprivate readonly outboundAckTimeoutMs: number\n\tprivate readonly outboundRetryBaseDelayMs: number\n\tprivate readonly outboundRetryMaxDelayMs: number\n\tprivate readonly encryptor: SyncEncryptor | null\n\tprivate readonly keyring: EncryptionKeyring | null\n\tprivate readonly keyServiceTimeoutMs: number\n\t/** Key-service requests awaiting their response, by request id. */\n\tprivate readonly keyRequests = new Map<\n\t\tstring,\n\t\t{\n\t\t\tresolve: (message: EncryptionKeyResponseMessage) => void\n\t\t\treject: (error: Error) => void\n\t\t\ttimer: ReturnType<typeof setTimeout>\n\t\t}\n\t>()\n\t/** Sync is paused because the encryption keyring is locked (ENC-1). */\n\tprivate encryptionLocked = false\n\t/** A key-record refresh for an unknown key version is running. */\n\tprivate keyRefreshInFlight = false\n\tprivate readonly awarenessManager: AwarenessManager\n\tprivate readonly richtextDocChannel: RichtextDocChannel\n\tprivate readonly blobChunkChannel: BlobChunkChannel\n\tprivate readonly metricsCollector: SyncMetricsCollector\n\tprivate readonly syncState: SyncStatePersistence | null\n\n\tprivate remoteVector: VersionVector = new Map()\n\t/** Runs before each outbound operation batch is sent (see setOutboundPreparer). */\n\tprivate outboundPreparer: ((operations: Operation[]) => Promise<void>) | null = null\n\t/**\n\t * Per-device node token the server issued at this node id's first anonymous claim\n\t * (RT-12). Presented in every handshake; persisted next to the node id when the\n\t * sync-state persistence supports it, otherwise kept for this engine's lifetime.\n\t */\n\tprivate nodeToken: string | null = null\n\t/**\n\t * Node ids the server named authoritative in its last accepted handshake response\n\t * (protocol v2). Null until a protocol-2 server answered in this engine's lifetime;\n\t * loaded from persistence on start. See {@link SyncEngine.getAuthoritativeNodeIds}.\n\t */\n\tprivate authoritativeNodeIds: string[] | null = null\n\t/** Protocol version of the server in the current session (1 for a beta.12-era server). */\n\tprivate serverProtocolVersion = 1\n\t/** Whether any server answered a handshake (serverProtocolVersion is then real). */\n\tprivate serverProtocolKnown = false\n\tprivate lastAckedServerVector: VersionVector = new Map()\n\tprivate cachedUnsyncedCount = 0\n\tprivate lastSyncedAt: number | null = null\n\tprivate lastSuccessfulPush: number | null = null\n\tprivate lastSuccessfulPull: number | null = null\n\tprivate conflictCount = 0\n\t/**\n\t * Every outbound batch taken from the queue and not yet resolved, keyed by queue batch\n\t * id: handshake-delta and streaming batches alike. An ack resolves only the batch it\n\t * names (SYNC-4); a close returns all of them to the queue.\n\t */\n\tprivate readonly inFlightUploads = new Map<string, InFlightUpload>()\n\t/** Wire message id -> queue batch id, for ack and rejection correlation. */\n\tprivate readonly uploadByMessageId = new Map<string, string>()\n\t/** Incremented on every connect and every disconnect; stale async sends check it. */\n\tprivate sessionEpoch = 0\n\t/** Set by destroy(): the engine never connects or arms a timer again. */\n\tprivate destroyed = false\n\tprivate readonly statusListeners = new Set<() => void>()\n\t/**\n\t * Contiguous acknowledged prefix of this device's own operations (W3 step 2): every\n\t * own op with sequence <= this is stored on the server, terminally rejected and\n\t * recorded, or not upload-eligible. Never advanced by a server-advertised vector.\n\t */\n\tprivate ownAckedThrough = 0\n\t/** Node id the prefix belongs to (a rotation starts a new prefix at 0). */\n\tprivate ownTrackingNodeId: string | null = null\n\t/** Highest own sequence read from the op log this session (above the prefix). */\n\tprivate ownScannedThrough = 0\n\t/**\n\t * Own sequences above the prefix that this session knows about, mapped to the ids of\n\t * their operations that are still unresolved. An empty set marks a sequence resolved.\n\t */\n\tprivate readonly ownUnresolved = new Map<number, Set<string>>()\n\t/** Ids resolved at sequences the log scan has not reached yet (skip on scan). */\n\tprivate readonly ownResolvedAhead = new Map<string, number>()\n\tprivate ownTrackingChain: Promise<void> = Promise.resolve()\n\t/** Inbound quarantine kept in memory when the engine has no sync-state persistence. */\n\tprivate readonly memoryQuarantine = new Map<string, QuarantinedOperation>()\n\tprivate outboundRetryAttempt = 0\n\tprivate outboundRetryTimer: ReturnType<typeof setTimeout> | null = null\n\t/** {@link pushOperation} calls not yet enqueued; the streaming flush waits for 0. */\n\tprivate pendingPushes = 0\n\tprivate reconnecting = false\n\tprivate schemaBlocked = false\n\tprivate clockBlocked = false\n\tprivate clockSkewMs: number | null = null\n\tprivate blobStorageEnabled = false\n\tprivate blobPossessionProof = false\n\tprivate suspensionReason: string | null = null\n\tprivate authRejected = false\n\t/**\n\t * Set when the server ended the session with AUTH_EXPIRED or AUTH_REVOKED. The\n\t * next connect asks the auth callback for a refreshed token (never the cached\n\t * one the server just refused) and is cleared by an accepted handshake.\n\t */\n\tprivate credentialRefreshRequired = false\n\tprivate serverFrontier: number | null = null\n\tprivate hasInFlightDeliveryBatch = false\n\tprivate blockedFailure: import('../types').ActiveApplyFailure | null = null\n\tprivate outboundAckTimer: ReturnType<typeof setTimeout> | null = null\n\tprivate startPromise: Promise<void> | null = null\n\tprivate stopPromise: Promise<void> | null = null\n\tprivate reconnectPromise: Promise<void> | null = null\n\t/** A node-id rotation started by NODE_ID_CLAIMED; the next connect waits for it (RT-21). */\n\tprivate nodeRotation: Promise<void> | null = null\n\t/**\n\t * Node id of the current (or last) session. Usually the store's node id; while the\n\t * engine adopts the unsynced writes of a node no live tab uses (RT-40) it is that\n\t * node. A session uploads only operations authored under its node id.\n\t */\n\tprivate sessionNodeId: string | null = null\n\t/** Node id the in-memory node token belongs to. */\n\tprivate nodeTokenNodeId: string | null = null\n\t/** Releases the lock held on an adopted node (RT-40); null when not adopting. */\n\tprivate releaseAdoption: (() => void) | null = null\n\t/**\n\t * Ids of operations the server refused for good (RT-36). Cache of the durable markers,\n\t * and the only record when the persistence layer has none (engine lifetime).\n\t */\n\tprivate readonly terminalRejected = new Set<string>()\n\t/** Nodes accepted at a handshake, when the persistence layer keeps no registry. */\n\tprivate readonly memoryAcceptedNodes = new Set<string>()\n\t/** Every node id this database authored operations under (loaded at start). */\n\tprivate localNodeIds = new Set<string>()\n\t/** Local nodes whose unsynced writes are held for another principal (RT-38). */\n\tprivate heldNodeIds = new Set<string>()\n\t/** Unsynced, unqueued operations of other uploadable local nodes (RT-40), counted pending. */\n\tprivate otherNodesPending = 0\n\t/** Unsynced, unqueued operations of held nodes (RT-38), reported separately. */\n\tprivate heldPending = 0\n\t/** Held local nodes as of the last session start (RT-50), for status.heldNodes. */\n\tprivate heldNodeInfos: HeldNodeInfo[] = []\n\t/**\n\t * The server holds operations of this node that the device lost (RT-35): the delivery\n\t * watermark was reset so the next session resyncs from 0, which fetches them back.\n\t * The session ends once its in-flight uploads are resolved.\n\t */\n\tprivate ownRecoveryPending = false\n\t/**\n\t * The signed-in user as resolved for the current session (RT-42): a user id, null\n\t * (nobody signed in) or undefined (the app does not tell the engine).\n\t */\n\tprivate principal: string | null | undefined = undefined\n\t/**\n\t * The user whose credential the current session's handshake carried (RT-50, RT-52):\n\t * re-read after the credential fetch, so an accepted handshake binds a node to the\n\t * user the server accepted it for, never to a user who signed out meanwhile.\n\t */\n\tprivate sessionPrincipal: string | null | undefined = undefined\n\t/** Serializes store bindings to the signed-in user (RT-52). */\n\tprivate principalChain: Promise<void> = Promise.resolve()\n\t/** Adoption schedule kept in memory when the persistence layer has none (RT-46). */\n\tprivate memorySchedule: AdoptionScheduleInfo = { progress: 0, parked: {} }\n\t/** The schedule had parked adoptions when last read or written (RT-46). */\n\tprivate scheduleHasParked = false\n\t/**\n\t * Local nodes with unsynced writes this session did not adopt (RT-46): parked (no\n\t * progress last time) or waiting for another local node's unsynced parent.\n\t */\n\tprivate deferredAdoptions: Array<{\n\t\tnodeId: string\n\t\treason: 'parked' | 'causal'\n\t\tparkedAtMs: number\n\t\tuntilMs: number\n\t\tprogressMark: number\n\t}> = []\n\t/** Operations acknowledged in this session (RT-46: a yield needs progress). */\n\tprivate sessionAcked = 0\n\t/** An upload of the adopted node came back unprocessed in this session (RT-46). */\n\tprivate adoptionStalled = false\n\t/**\n\t * The session node's entry in this session's handshake vector (RT-44). The server held\n\t * nothing of the node above it then; a SEQUENCE_CONFLICT above it was caused by an\n\t * operation stored during this session by another live copy of this database.\n\t */\n\tprivate handshakeOwnEntry = 0\n\t/** When this engine last reset delivery for a SEQUENCE_CONFLICT recovery (RT-44). */\n\tprivate lastFullResyncAt = Number.NEGATIVE_INFINITY\n\t/** A recovery full resync was rate-limited; the next session start after the interval runs it. */\n\tprivate fullResyncDeferred = false\n\t/** Consecutive failed durability barriers before an upload (RT-49). */\n\tprivate durabilityFailures = 0\n\t/** Uploads proceed without local durability: the barrier failed persistently (RT-49). */\n\tprivate durabilityDegraded = false\n\n\t// Track delta exchange state\n\tprivate deltaBatchesReceived = 0\n\tprivate deltaReceiveComplete = false\n\tprivate deltaSendComplete = false\n\t/** Outbound delta batch message IDs awaiting ACK when strictHandshake is enabled */\n\tprivate pendingDeltaBatchAcks = new Set<string>()\n\n\t/**\n\t * The effective scope for this sync session.\n\t * Starts as the configured scopeMap. After handshake, may be replaced\n\t * with the server-accepted scope (server is authoritative).\n\t */\n\tprivate activeScope: SyncScopeMap | undefined\n\t/** Server-authoritative upload authorization, separate from the downloaded view. */\n\tprivate activeUplinkScope: SyncScopeMap | undefined\n\t/**\n\t * The downlink scope the server accepted at the last handshake that named one\n\t * (SYNC-11), persisted. The next handshake reports its key and that view's watermark\n\t * so the server can resume the stream of an unchanged grant. Never sent as the\n\t * requested scope: a later-widened grant must not be narrowed to it.\n\t */\n\tprivate lastAcceptedScope: SyncScopeMap | null = null\n\n\t/** Live query subsets registered from reactive subscriptions */\n\tprivate querySubsets = new Map<string, SyncQuerySubset>()\n\tprivate staticQuerySubsets: SyncQuerySubset[] = []\n\tprivate querySubsetReconnectTimer: ReturnType<typeof setTimeout> | null = null\n\t/**\n\t * Fires when the earliest parked adoption's backoff runs out (RT-53): an idle session of\n\t * this tab's own node then hands over to it, so a closed tab's deferred writes are\n\t * retried even if this tab never writes or reconnects.\n\t */\n\tprivate adoptionRetryTimer: ReturnType<typeof setTimeout> | null = null\n\n\t/** Resume cursor for paginated initial sync (persisted across reconnects) */\n\tprivate resumeDeltaCursor: DeltaCursor | null = null\n\tprivate initialSyncTotalBatches = 0\n\n\t/**\n\t * The delivery watermark for the CURRENT sync view: the highest server delivery\n\t * sequence up to which every operation in the current view (scope + active query\n\t * subscriptions) has been contiguously applied. Advanced only through the server's\n\t * gap-free delivery stream (never through live relay), so it is a durable lower bound\n\t * on what this client holds for this view. Reported at handshake to resume the stream.\n\t *\n\t * The watermark is keyed by view: switching views (a scope or subscription change)\n\t * saves the current view's watermark and loads the target view's, so returning to a\n\t * previously-synced view resumes exactly where it left off instead of re-syncing. This\n\t * is correct because a view's watermark is a lower bound specific to that view's\n\t * filter; a widened view is simply a different (initially unsynced) view.\n\t */\n\tprivate deliveryWatermark = 0\n\t/**\n\t * In-memory cache of watermark per view signature, preloaded from persistence at start\n\t * so a view switch resolves synchronously (no race with the reconnect it triggers).\n\t */\n\tprivate readonly deliverySignatureWatermarks = new Map<string, number>()\n\t/** Schema transforms: the engine checks views, the store folds them (RT-84). */\n\tprivate readonly operationTransforms: readonly OperationTransform[]\n\tprivate deliveryGapRepeatCount = 0\n\tprivate lastDeliveryGapKey: string | null = null\n\n\tconstructor(options: SyncEngineOptions) {\n\t\tthis.transport = options.transport\n\t\tthis.store = options.store\n\t\tthis.config = options.config\n\t\tthis.operationTransforms = resolveOperationTransforms(options.config, options.store)\n\t\tthis.serializer = options.serializer ?? new NegotiatedMessageSerializer('json')\n\t\tthis.emitter = options.emitter ?? null\n\t\tthis.batchSize = options.config.batchSize ?? DEFAULT_BATCH_SIZE\n\t\tthis.outboundAckTimeoutMs =\n\t\t\toptions.config.outboundAckTimeoutMs ?? DEFAULT_OUTBOUND_ACK_TIMEOUT_MS\n\t\tthis.outboundRetryBaseDelayMs = Math.max(\n\t\t\t0,\n\t\t\toptions.config.outboundRetryBaseDelayMs ?? DEFAULT_OUTBOUND_RETRY_BASE_DELAY_MS,\n\t\t)\n\t\tthis.outboundRetryMaxDelayMs = Math.max(\n\t\t\tthis.outboundRetryBaseDelayMs,\n\t\t\toptions.config.outboundRetryMaxDelayMs ?? DEFAULT_OUTBOUND_RETRY_MAX_DELAY_MS,\n\t\t)\n\t\tthis.encryptor = options.encryptor ?? null\n\t\tthis.keyring = options.keyring ?? null\n\t\tthis.keyServiceTimeoutMs = options.keyServiceTimeoutMs ?? DEFAULT_KEY_SERVICE_TIMEOUT_MS\n\t\tthis.syncState = options.syncState ?? null\n\t\tthis.activeScope = options.config.scopeMap\n\t\tthis.activeUplinkScope = options.config.scopeMap\n\n\t\tconst queueStorage = options.queueStorage ?? new MemoryQueueStorage()\n\t\tthis.outboundQueue = new OutboundQueue(queueStorage)\n\t\tthis.rejectedStorage = options.rejectedStorage ?? new MemoryRejectedOperationStorage()\n\n\t\tthis.metricsCollector = new SyncMetricsCollector(options.metricsConfig)\n\t\tif (this.emitter) {\n\t\t\tthis.metricsCollector.attachEmitter(this.emitter)\n\t\t}\n\n\t\tthis.awarenessManager = new AwarenessManager({\n\t\t\temitter: this.emitter ?? undefined,\n\t\t})\n\n\t\tthis.richtextDocChannel = new RichtextDocChannel({\n\t\t\tlargeDocThreshold: options.config.richtextDocChannelThreshold,\n\t\t\tonSend: (message: YjsDocUpdateMessage) => {\n\t\t\t\tif (this.state !== 'streaming') {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.transport.send(message)\n\t\t\t},\n\t\t})\n\n\t\tthis.blobChunkChannel = new BlobChunkChannel({\n\t\t\tonSend: (\n\t\t\t\tmessage: BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage,\n\t\t\t) => {\n\t\t\t\t// Blob transfer needs an accepted handshake. Pushes may also go out while\n\t\t\t\t// syncing, so the bytes behind a handshake-delta operation reach the server\n\t\t\t\t// before the operation does (RT-11). A request dropped here is safe: the\n\t\t\t\t// puller times out and retries, and blob transfer is resumable.\n\t\t\t\tconst live =\n\t\t\t\t\tthis.state === 'streaming' ||\n\t\t\t\t\t(this.state === 'syncing' && message.type === 'blob-chunk-push')\n\t\t\t\tif (!live) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.transport.send(message)\n\t\t\t},\n\t\t})\n\n\t\t// Wire awareness manager to send messages through the transport\n\t\tthis.awarenessManager.onSend((message: AwarenessMessage) => {\n\t\t\tif (this.state !== 'streaming') return\n\n\t\t\tconst wireMessage: SyncMessage = {\n\t\t\t\ttype: 'awareness-update',\n\t\t\t\tmessageId: generateMessageId(),\n\t\t\t\tclientId: message.clientId,\n\t\t\t\tstates: awarenessStatesToWire(message.states),\n\t\t\t}\n\t\t\tthis.transport.send(wireMessage)\n\t\t})\n\t}\n\n\t/**\n\t * Start the sync engine: connect → handshake → delta exchange → streaming.\n\t */\n\tasync start(): Promise<void> {\n\t\tif (this.state === 'streaming') return\n\t\tif (this.startPromise) return this.startPromise\n\n\t\tthis.startPromise = this.startInternal().finally(() => {\n\t\t\tthis.startPromise = null\n\t\t})\n\t\treturn this.startPromise\n\t}\n\n\tprivate async startInternal(principalRestarts = 0): Promise<void> {\n\t\tif (this.stopPromise) {\n\t\t\tawait this.stopPromise\n\t\t}\n\t\tif (this.nodeRotation) {\n\t\t\tawait this.nodeRotation\n\t\t}\n\t\tif (this.state === 'streaming') return\n\t\tif (this.destroyed) return\n\t\tif (this.state !== 'disconnected') {\n\t\t\tawait this.stop()\n\t\t}\n\t\tif (this.authRejected) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:suspended',\n\t\t\t\treason: this.suspensionReason ?? 'auth-rejected',\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tif (this.config.authState) {\n\t\t\tconst authState = await this.config.authState()\n\t\t\tif (authState.state === 'loading' || authState.state === 'signed-out') {\n\t\t\t\tthis.suspensionReason = authState.state === 'loading' ? 'auth-loading' : 'auth-required'\n\t\t\t\tthis.emitter?.emit({ type: 'sync:suspended', reason: this.suspensionReason })\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tthis.suspensionReason = null\n\t\tif (this.keyring && !this.keyring.canConnect()) {\n\t\t\t// Nothing to open the keyring with: sync stays paused (local reads and writes go\n\t\t\t// on) until the app unlocks it. No connection, so no plaintext can leave.\n\t\t\tthis.encryptionLocked = true\n\t\t\tthis.emitter?.emit({ type: 'sync:suspended', reason: 'encryption-locked' })\n\t\t\treturn\n\t\t}\n\t\tthis.encryptionLocked = false\n\t\t// Each handshake reports the watermark of the REQUESTED view (SYNC-11). The server\n\t\t// resumes from it only when it serves exactly the requested scope; when it serves\n\t\t// another (it restarts that stream from 0), the response switches to that view.\n\t\t// Reporting the previously accepted view's watermark instead could make a server\n\t\t// that now serves the requested scope skip operations of it.\n\t\t{\n\t\t\tconst previousSignature = this.deliverySignature()\n\t\t\tthis.activeScope = this.config.scopeMap\n\t\t\tthis.switchDeliveryView(previousSignature)\n\t\t}\n\t\tawait this.outboundQueue.initialize()\n\t\tthis.ownRecoveryPending = false\n\t\tthis.sessionAcked = 0\n\t\tthis.adoptionStalled = false\n\t\tif (\n\t\t\tthis.fullResyncDeferred &&\n\t\t\tDate.now() - this.lastFullResyncAt >= FULL_RESYNC_MIN_INTERVAL_MS\n\t\t) {\n\t\t\tthis.fullResyncDeferred = false\n\t\t\tthis.lastFullResyncAt = Date.now()\n\t\t\tawait this.resetDeliveryForFullResync()\n\t\t}\n\t\tif (!(await this.withPrincipalLock(() => this.applyPrincipal()))) return\n\t\t// The user this session's node was chosen for (RT-52): re-checked after the\n\t\t// credential fetch, before the handshake.\n\t\tconst boundPrincipal = this.principal\n\t\tawait this.chooseSessionNode()\n\t\t{\n\t\t\tconst sessionNode = this.currentNodeId()\n\t\t\tif (this.nodeTokenNodeId !== sessionNode) {\n\t\t\t\t// A token belongs to one node id: never present another node's (RT-38, RT-40).\n\t\t\t\tthis.nodeToken = null\n\t\t\t\tthis.nodeTokenNodeId = sessionNode\n\t\t\t}\n\t\t}\n\t\tif (this.syncState) {\n\t\t\tthis.lastAckedServerVector = await this.syncState.loadLastAckedServerVector()\n\t\t\tif (this.nodeToken === null && this.syncState.loadNodeToken) {\n\t\t\t\tthis.nodeToken = await this.syncState.loadNodeToken(this.currentNodeId())\n\t\t\t}\n\t\t\tif (this.authoritativeNodeIds === null && this.syncState.loadAuthoritativeNodeIds) {\n\t\t\t\tthis.authoritativeNodeIds = await this.syncState.loadAuthoritativeNodeIds()\n\t\t\t}\n\t\t\tif (this.syncState.loadDeltaCursor) {\n\t\t\t\tthis.resumeDeltaCursor = await this.syncState.loadDeltaCursor()\n\t\t\t}\n\t\t\t// Preload every view's watermark so a view switch resolves synchronously.\n\t\t\tif (this.syncState.loadAllDeliveryWatermarks) {\n\t\t\t\tconst all = await this.syncState.loadAllDeliveryWatermarks()\n\t\t\t\tfor (const [signature, watermark] of Object.entries(all)) {\n\t\t\t\t\tthis.deliverySignatureWatermarks.set(signature, watermark)\n\t\t\t\t}\n\t\t\t} else if (this.syncState.loadDeliveryWatermark) {\n\t\t\t\t// Fallback: load just the current (default) view's watermark.\n\t\t\t\tthis.deliverySignatureWatermarks.set('', await this.syncState.loadDeliveryWatermark(''))\n\t\t\t}\n\t\t\tthis.deliveryWatermark = this.deliverySignatureWatermarks.get(this.deliverySignature()) ?? 0\n\t\t\tif (this.syncState.loadAcceptedDownlinkScope) {\n\t\t\t\tthis.lastAcceptedScope = await this.syncState.loadAcceptedDownlinkScope()\n\t\t\t}\n\t\t\t// Bound a set that predates the retention cap (older clients persisted views\n\t\t\t// without a limit); this one-time trim removes cold rows down to the cap.\n\t\t\tthis.evictColdViewWatermarks()\n\t\t}\n\t\tawait this.loadOwnTracking()\n\t\tawait this.reconcileOutboundFromOpLog()\n\t\tawait this.replayQuarantine()\n\t\tawait this.refreshPendingCount()\n\t\tif (this.destroyed) return\n\n\t\t// Set up transport handlers\n\t\tthis.transport.onMessage((msg) => this.enqueueMessage(msg))\n\t\tthis.transport.onClose((reason) => this.handleTransportClose(reason))\n\t\tthis.transport.onError((err) => this.handleTransportError(err))\n\n\t\tif (this.schemaBlocked) {\n\t\t\tthrow new SyncError(\n\t\t\t\t'Sync is blocked due to schema version mismatch. Upgrade the app schema or align sync.schemaVersion with the server.',\n\t\t\t\t{ code: 'SCHEMA_MISMATCH_BLOCKED' },\n\t\t\t)\n\t\t}\n\t\tthis.transitionTo('connecting')\n\n\t\ttry {\n\t\t\tconst forceRefresh = this.credentialRefreshRequired\n\t\t\tconst authToken = this.config.auth\n\t\t\t\t? (await (forceRefresh ? this.config.auth({ forceRefresh: true }) : this.config.auth()))\n\t\t\t\t\t\t.token\n\t\t\t\t: undefined\n\t\t\tif (this.state !== 'connecting') return\n\t\t\tif (forceRefresh && !authToken) {\n\t\t\t\t// The refresh did not produce a token yet (offline, auth server down).\n\t\t\t\t// Handshaking with an empty or stale token would be refused as AUTH_FAILED\n\t\t\t\t// and suspend sync for good; fail this attempt so reconnection retries.\n\t\t\t\tthrow new SyncError(\n\t\t\t\t\t'Waiting for refreshed credentials before reconnecting: the server ended the previous session because its credential expired or was revoked.',\n\t\t\t\t\t{\n\t\t\t\t\t\tcode: 'AUTH_REFRESH_PENDING',\n\t\t\t\t\t\tfix: 'Sync reconnects automatically once the auth client can refresh its token. If the user was signed out, sign in again.',\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tawait this.transport.connect(this.config.url, { authToken })\n\t\t\tif (this.state !== 'connecting' || this.destroyed) {\n\t\t\t\tawait this.transport.disconnect()\n\t\t\t\tthis.ensureDisconnected()\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// RT-52: the signed-in user may have changed while the credential was fetched\n\t\t\t// or the transport connected (up to the connect timeout). The node was chosen\n\t\t\t// for the previous user and the credential may be the next one's: never\n\t\t\t// handshake that pair. Start over (the store is rebound to the current user).\n\t\t\tconst principalNow = await this.resolvePrincipal()\n\t\t\tif (this.config.principal && principalNow !== boundPrincipal) {\n\t\t\t\tawait this.transport.disconnect()\n\t\t\t\tthis.ensureDisconnected()\n\t\t\t\tif (principalRestarts >= MAX_PRINCIPAL_RESTARTS) {\n\t\t\t\t\tthrow new SyncError(\n\t\t\t\t\t\t'The signed-in user kept changing while sync was connecting; this attempt was abandoned so no write is uploaded as the wrong user.',\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcode: 'PRINCIPAL_CHANGED',\n\t\t\t\t\t\t\tfix: 'Sync retries automatically once the signed-in user is stable.',\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn this.startInternal(principalRestarts + 1)\n\t\t\t}\n\t\t\tthis.sessionPrincipal = principalNow\n\t\t\tthis.sessionEpoch++\n\t\t\tthis.transitionTo('handshaking')\n\n\t\t\t// Send handshake\n\t\t\tconst localVector = this.store.getVersionVector()\n\t\t\tconst activeQuerySubsets = this.getActiveQuerySubsets()\n\t\t\tconst handshake: SyncMessage = {\n\t\t\t\ttype: 'handshake',\n\t\t\t\tmessageId: generateMessageId(),\n\t\t\t\tnodeId: this.currentNodeId(),\n\t\t\t\tversionVector: versionVectorToWire(localVector),\n\t\t\t\tschemaVersion: this.config.schemaVersion ?? DEFAULT_SCHEMA_VERSION,\n\t\t\t\tauthToken,\n\t\t\t\t// JSON only (SYNC-9): the transports frame with their own serializer, so a\n\t\t\t\t// negotiated protobuf switch would never reach the wire. Protobuf stays an\n\t\t\t\t// explicit, both-ends transport choice.\n\t\t\t\tsupportedWireFormats: ['json'],\n\t\t\t\t...(this.config.scopeMap ? { syncScope: this.config.scopeMap } : {}),\n\t\t\t\t...(this.config.scopeExit ? { scopeExitPolicy: this.config.scopeExit } : {}),\n\t\t\t\t...(activeQuerySubsets.length > 0 ? { syncQueries: activeQuerySubsets } : {}),\n\t\t\t\t...(this.resumeDeltaCursor\n\t\t\t\t\t? { deltaCursor: encodeDeltaCursor(this.resumeDeltaCursor) }\n\t\t\t\t\t: {}),\n\t\t\t\t// Resume the server's gap-free delivery stream from our watermark. A server\n\t\t\t\t// that understands it drives server->client sync from delivery sequences; an\n\t\t\t\t// older server ignores it and falls back to the version-vector delta.\n\t\t\t\tlastDeliverySequence: this.deliveryWatermark,\n\t\t\t\t// The accepted view this client last streamed under, with its own watermark\n\t\t\t\t// (SYNC-11): a server that resolves the same scope again resumes from it.\n\t\t\t\t...this.acceptedViewHandshakeFields(),\n\t\t\t\t...(this.nodeToken ? { nodeToken: this.nodeToken } : {}),\n\t\t\t\t// Sequence numbers are reserved inside the writing transaction (W6), and a\n\t\t\t\t// SEQUENCE_CONFLICT is recovered from (RT-35), so the server may enforce\n\t\t\t\t// (node, sequence) uniqueness against this client (RT-37).\n\t\t\t\tsequenceReservation: true,\n\t\t\t\t// Protocol v2: hash-version-2 ids, the encryption envelope v2 (D2).\n\t\t\t\tprotocolVersion: SYNC_PROTOCOL_VERSION,\n\t\t\t}\n\t\t\tthis.transport.send(handshake)\n\t\t} catch (err) {\n\t\t\t// Transport error/close handlers may have already transitioned to disconnected.\n\t\t\t// Guard against invalid state transitions.\n\t\t\tthis.ensureDisconnected()\n\t\t\tthrow err\n\t\t}\n\t}\n\n\t/**\n\t * Stop the sync engine. Disconnects the transport.\n\t */\n\tasync stop(): Promise<void> {\n\t\tif (this.stopPromise) return this.stopPromise\n\n\t\tthis.stopPromise = this.stopInternal().finally(() => {\n\t\t\tthis.stopPromise = null\n\t\t})\n\t\treturn this.stopPromise\n\t}\n\n\tprivate async stopInternal(): Promise<void> {\n\t\t// Every timer goes first, before any early return (SYNC-10): an engine that is\n\t\t// already disconnected (the server closed the session) still owns the awareness\n\t\t// cleanup interval and a pending query-subset reconnect.\n\t\tthis.clearAllTimers()\n\t\tthis.sessionEpoch++\n\t\tthis.returnAllInFlightUploads()\n\n\t\tif (this.state === 'disconnected') {\n\t\t\t// The session may have ended without the transport being closed (an error the\n\t\t\t// engine gave up on): close it now so no socket is left open (SYNC-5).\n\t\t\tawait this.closeTransportQuietly()\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.transport.disconnect()\n\t\t} finally {\n\t\t\t// The transport.disconnect() callback may have already transitioned\n\t\t\t// to 'disconnected' via handleTransportClose. Re-read the mutable field.\n\t\t\tthis.ensureDisconnected()\n\t\t}\n\t}\n\n\t/**\n\t * Stop for good (`app.close()`): disconnect, clear every timer and listener, and refuse\n\t * any later start. Unlike stop(), nothing the engine scheduled can run afterwards.\n\t */\n\tasync destroy(): Promise<void> {\n\t\tthis.destroyed = true\n\t\tawait this.stop()\n\t\tthis.clearAllTimers()\n\t\tthis.endAdoption()\n\t\tthis.statusListeners.clear()\n\t}\n\n\t/** Clear every timer the engine owns. */\n\tprivate clearAllTimers(): void {\n\t\tthis.clearOutboundAckTimer()\n\t\tthis.clearOutboundRetryTimer()\n\t\tthis.awarenessManager.stopCleanupTimer()\n\t\tif (this.querySubsetReconnectTimer) {\n\t\t\tclearTimeout(this.querySubsetReconnectTimer)\n\t\t\tthis.querySubsetReconnectTimer = null\n\t\t}\n\t\tthis.clearAdoptionRetryTimer()\n\t}\n\n\tprivate clearAdoptionRetryTimer(): void {\n\t\tif (!this.adoptionRetryTimer) return\n\t\tclearTimeout(this.adoptionRetryTimer)\n\t\tthis.adoptionRetryTimer = null\n\t}\n\n\t/** Close the transport, logging nothing: it may already be closed. */\n\tprivate async closeTransportQuietly(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.transport.disconnect()\n\t\t} catch {\n\t\t\t// Already closed or never opened; the engine is disconnected either way.\n\t\t}\n\t}\n\n\t/**\n\t * The engine gave up on this session (a message it could not process, a transport\n\t * error, a server error): move to disconnected and close the transport (SYNC-5). The\n\t * close starts synchronously in this same frame, so the socket is released before\n\t * auto-reconnect (which is asynchronous) can open another one.\n\t */\n\tprivate abandonSession(reason: string): void {\n\t\tthis.handleTransportClose(reason)\n\t\tvoid this.closeTransportQuietly()\n\t}\n\n\t/**\n\t * Subscribe to status changes that no event announces (an upload ack lowering the\n\t * pending count, reaching streaming). Status bridges refresh on it, so a waiter such\n\t * as `waitForSettled` sees the change immediately (RT-28).\n\t */\n\tonStatusChange(listener: () => void): () => void {\n\t\tthis.statusListeners.add(listener)\n\t\treturn () => {\n\t\t\tthis.statusListeners.delete(listener)\n\t\t}\n\t}\n\n\tprivate notifyStatusChange(): void {\n\t\tfor (const listener of this.statusListeners) {\n\t\t\ttry {\n\t\t\t\tlistener()\n\t\t\t} catch {\n\t\t\t\t// A listener's failure must not affect sync.\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate ensureDisconnected(): void {\n\t\tif (this.state !== 'disconnected') {\n\t\t\tthis.transitionTo('disconnected')\n\t\t}\n\t}\n\n\t/**\n\t * Push a local operation to the outbound queue.\n\t * If streaming, flushes immediately.\n\t *\n\t * Uploads are judged against the UPLINK scope only. Query subsets narrow what\n\t * this client downloads, never what it uploads: an edit that moves a record out\n\t * of a reactive query's view must still reach the server (SYNC-1).\n\t *\n\t * Operations on collections that do not sync at all stay local-only by design.\n\t * An operation on a synced collection that falls outside the uplink scope would\n\t * be refused by the server, so it is recorded in the rejected store and\n\t * surfaced as `sync:operation-rejected` (code `OUT_OF_UPLINK_SCOPE`) rather than\n\t * kept as a silent local fork.\n\t */\n\tasync pushOperation(op: Operation): Promise<void> {\n\t\t// A committed write publishes all of its operations synchronously (a delete and\n\t\t// its cascades, a transaction). The streaming flush waits until every push that\n\t\t// started is enqueued, so the commit block uploads in one batch: the server then\n\t\t// sees a delete's authored cascades with the delete and does not derive a second\n\t\t// copy of each (RT-69).\n\t\tthis.pendingPushes += 1\n\t\ttry {\n\t\t\tif (!(await this.operationAllowedForUpload(op))) {\n\t\t\t\tawait this.recordOutOfUplinkScope(op)\n\t\t\t\t// Not upload-eligible: resolved for the contiguous prefix (W3 step 2).\n\t\t\t\tawait this.withOwnTracking(async () => {\n\t\t\t\t\tthis.trackOwnOperation(op, false)\n\t\t\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tawait this.withOwnTracking(async () => {\n\t\t\t\tawait this.outboundQueue.enqueue(op)\n\t\t\t\tthis.trackOwnOperation(op, true)\n\t\t\t})\n\t\t\tawait this.refreshPendingCount()\n\t\t} finally {\n\t\t\tthis.pendingPushes -= 1\n\t\t\tif (this.pendingPushes === 0 && this.state === 'streaming') {\n\t\t\t\tthis.flushQueue()\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Mark the engine as being in a reconnection loop. When reconnecting,\n\t * `getStatus()` returns 'offline' instead of 'syncing' for intermediate\n\t * states (connecting, handshaking, syncing), since the user is effectively\n\t * disconnected until reconnection succeeds.\n\t */\n\tsetReconnecting(value: boolean): void {\n\t\tthis.reconnecting = value\n\t}\n\n\t/**\n\t * Get the current developer-facing sync status.\n\t */\n\t/**\n\t * Node ids the sync server named authoritative (protocol v2): operations from these\n\t * nodes are server-authored and are the only ones that may carry `fieldVersions` or\n\t * `foldState`. Null until a protocol-2 server answered (or one answered in an earlier\n\t * session and the list was persisted).\n\t */\n\tgetAuthoritativeNodeIds(): readonly string[] | null {\n\t\treturn this.authoritativeNodeIds\n\t}\n\n\t/** Protocol version of the server in the current (or last) session; 1 before any. */\n\tgetServerProtocolVersion(): number {\n\t\treturn this.serverProtocolVersion\n\t}\n\n\tgetStatus(): SyncStatusInfo {\n\t\tconst pendingOperations = this.computePendingCount()\n\t\tconst locked = this.isLockedSuspension()\n\t\tconst base = {\n\t\t\tphase: this.resolvePhase(),\n\t\t\t...(this.suspensionReason\n\t\t\t\t? { reason: this.suspensionReason }\n\t\t\t\t: locked\n\t\t\t\t\t? { reason: 'encryption-locked' }\n\t\t\t\t\t: {}),\n\t\t\treconnecting: this.reconnecting,\n\t\t\tpendingOperations,\n\t\t\tlastSyncedAt: this.lastSyncedAt,\n\t\t\tlastSuccessfulPush: this.lastSuccessfulPush,\n\t\t\tlastSuccessfulPull: this.lastSuccessfulPull,\n\t\t\tconflicts: this.conflictCount,\n\t\t\theldOperations: this.computeHeldCount(),\n\t\t\theldNodes: this.heldNodeInfos.map((node) => ({ ...node })),\n\t\t\tlocalDurability: this.durabilityDegraded ? ('degraded' as const) : ('durable' as const),\n\t\t\tserverProtocolVersion: this.serverProtocolKnown ? this.serverProtocolVersion : null,\n\t\t\tprotocolDeprecated:\n\t\t\t\tthis.serverProtocolKnown && this.serverProtocolVersion < SYNC_PROTOCOL_VERSION,\n\t\t\tclockSkewMs: this.clockSkewMs,\n\t\t\tinFlightUploadOperations: this.inFlightUploadCount(),\n\t\t\thasInFlightDeliveryBatch: this.hasInFlightDeliveryBatch,\n\t\t\tactiveViewId: this.deliverySignature(),\n\t\t\tactiveViewComplete:\n\t\t\t\tthis.state === 'streaming' &&\n\t\t\t\t!this.hasInFlightDeliveryBatch &&\n\t\t\t\tthis.blockedFailure === null &&\n\t\t\t\t(this.serverFrontier === null || this.deliveryWatermark >= this.serverFrontier),\n\t\t\tinitialSync: {\n\t\t\t\tcomplete: this.deltaReceiveComplete && this.deltaSendComplete,\n\t\t\t\treceivedBatches: this.deltaBatchesReceived,\n\t\t\t\ttotalBatches: this.initialSyncTotalBatches || null,\n\t\t\t\tprogress:\n\t\t\t\t\tthis.initialSyncTotalBatches > 0\n\t\t\t\t\t\t? Math.min(1, this.deltaBatchesReceived / this.initialSyncTotalBatches)\n\t\t\t\t\t\t: null,\n\t\t\t},\n\t\t\tdeliveryWatermark: this.deliveryWatermark,\n\t\t\tserverFrontier: this.serverFrontier,\n\t\t\tblockedFailure: this.blockedFailure,\n\t\t}\n\t\tif (this.suspensionReason) return { ...base, status: 'auth-required' }\n\t\tif (locked) return { ...base, status: 'encryption-locked' }\n\t\tswitch (this.state) {\n\t\t\tcase 'disconnected':\n\t\t\t\t// A durable block outranks plain offline: the user must act\n\t\t\t\t// (fix the clock / upgrade the schema) before sync can resume.\n\t\t\t\tif (this.clockBlocked) {\n\t\t\t\t\treturn { ...base, status: 'clock-error' }\n\t\t\t\t}\n\t\t\t\tif (this.schemaBlocked) {\n\t\t\t\t\treturn { ...base, status: 'schema-mismatch' }\n\t\t\t\t}\n\t\t\t\treturn { ...base, status: 'offline' }\n\t\t\tcase 'connecting':\n\t\t\tcase 'handshaking':\n\t\t\tcase 'syncing':\n\t\t\t\treturn { ...base, status: this.reconnecting ? 'reconnecting' : 'syncing' }\n\t\t\tcase 'streaming':\n\t\t\t\treturn { ...base, status: pendingOperations > 0 ? 'syncing' : 'synced' }\n\t\t\tcase 'error':\n\t\t\t\tif (this.clockBlocked) {\n\t\t\t\t\treturn { ...base, status: 'clock-error' }\n\t\t\t\t}\n\t\t\t\treturn { ...base, status: this.schemaBlocked ? 'schema-mismatch' : 'error' }\n\t\t}\n\t}\n\n\t/**\n\t * Sync is paused on a locked keyring (ENC-1): no session runs until the app unlocks\n\t * it. Reported as a suspension (`phase: 'suspended'`, `reason: 'encryption-locked'`,\n\t * `status: 'encryption-locked'`) rather than as plain offline, so the UI can ask for\n\t * the passphrase and `waitForSettled` reports why it cannot settle.\n\t */\n\tprivate isLockedSuspension(): boolean {\n\t\treturn this.encryptionLocked && (this.state === 'disconnected' || this.state === 'error')\n\t}\n\n\tprivate resolvePhase(): import('../types').SyncPhase {\n\t\tif (this.suspensionReason) return 'suspended'\n\t\tif (this.isLockedSuspension()) return 'suspended'\n\t\tif (this.blockedFailure || this.schemaBlocked || this.clockBlocked) return 'blocked'\n\t\tif (this.hasInFlightDeliveryBatch) return 'applying'\n\t\tif (this.inFlightUploads.size > 0) return 'uploading'\n\t\tswitch (this.state) {\n\t\t\tcase 'disconnected':\n\t\t\t\treturn 'offline'\n\t\t\tcase 'connecting':\n\t\t\t\treturn 'connecting'\n\t\t\tcase 'handshaking':\n\t\t\t\treturn 'handshaking'\n\t\t\tcase 'syncing':\n\t\t\t\treturn 'receiving'\n\t\t\tcase 'streaming':\n\t\t\t\treturn 'streaming'\n\t\t\tcase 'error':\n\t\t\t\treturn 'blocked'\n\t\t}\n\t}\n\n\t/**\n\t * True when the server rejected the client's schema version at handshake.\n\t * Sync stays blocked until the app schema is upgraded or sync config changes.\n\t */\n\tisSchemaBlocked(): boolean {\n\t\treturn this.schemaBlocked\n\t}\n\n\t/**\n\t * True when sync is blocked because this device's clock is too far ahead.\n\t * Local writes continue to work and queue; only sync is paused.\n\t */\n\tisClockBlocked(): boolean {\n\t\treturn this.clockBlocked\n\t}\n\n\t/**\n\t * True when sync is paused because the end-to-end encryption keyring is locked (no\n\t * passphrase, a wrong one, or a key record that must not be used). Local writes go\n\t * on and queue; `retryNow()` after an unlock resumes sync.\n\t */\n\tisEncryptionLocked(): boolean {\n\t\treturn this.encryptionLocked\n\t}\n\n\t/**\n\t * The key service over the live sync session, for keyring management (rotation,\n\t * passphrase change, recovery). Null while not connected: those calls need the\n\t * server.\n\t */\n\tgetKeyServiceChannel(): KeyServiceChannel | null {\n\t\tif (this.state !== 'syncing' && this.state !== 'streaming') return null\n\t\treturn this.liveKeyServiceChannel()\n\t}\n\n\t/** serverTime - localTime measured at the last handshake, or null before first connect. */\n\tgetClockSkewMs(): number | null {\n\t\treturn this.clockSkewMs\n\t}\n\n\t/**\n\t * Clears the clock block after the user corrects the device clock.\n\t * Moves the engine back to `disconnected` so `start()` can run again.\n\t */\n\tclearClockBlock(): void {\n\t\tthis.clockBlocked = false\n\t\tif (this.state === 'error') {\n\t\t\tthis.transitionTo('disconnected')\n\t\t}\n\t}\n\n\t/**\n\t * Clears schema-mismatch block after upgrading the local schema / sync config.\n\t * Moves the engine back to `disconnected` so `start()` can run again.\n\t */\n\tclearSchemaBlock(): void {\n\t\tthis.schemaBlocked = false\n\t\tif (this.state === 'error') {\n\t\t\tthis.transitionTo('disconnected')\n\t\t}\n\t}\n\n\t/**\n\t * Record a merge conflict. Called by the merge-aware sync store\n\t * to increment the conflict counter for status reporting.\n\t */\n\trecordConflict(): void {\n\t\tthis.conflictCount++\n\t}\n\n\t/**\n\t * Count of this device's own operations not yet stored on the server (or terminally\n\t * rejected and recorded): queued, in flight, or above the acknowledged prefix.\n\t */\n\tasync getUnsyncedOperationCount(): Promise<number> {\n\t\tawait this.refreshPendingCount()\n\t\treturn this.getStatus().pendingOperations\n\t}\n\n\t/**\n\t * Surface an inbound apply failure. `blocking` marks the stream as stalled on this op\n\t * (the watermark does not pass it); `quarantined` means the op is durably recorded and\n\t * the stream moves on, so it never marks the stream blocked, whatever `retriable` says\n\t * (retriable there means the quarantine replays it later).\n\t */\n\tprivate emitApplyFailure(\n\t\top: Operation,\n\t\tresult: Exclude<ApplyResult, 'applied' | 'duplicate'>,\n\t\toverrides?: Partial<ApplyFailureReason>,\n\t\tmode: 'default' | 'blocking' | 'quarantined' = 'default',\n\t): void {\n\t\tconst reason = defaultApplyFailureReason(result, overrides)\n\t\tif (mode === 'blocking' || (mode === 'default' && reason.retriable)) {\n\t\t\tconst prior = this.blockedFailure\n\t\t\tthis.blockedFailure = {\n\t\t\t\toperationId: op.id,\n\t\t\t\tcollection: op.collection,\n\t\t\t\trecordId: op.recordId,\n\t\t\t\tcode: reason.code,\n\t\t\t\tmessage: reason.message,\n\t\t\t\tretriable: reason.retriable,\n\t\t\t\tfirstSeenAt: prior?.operationId === op.id ? prior.firstSeenAt : Date.now(),\n\t\t\t\tretryCount: prior?.operationId === op.id ? prior.retryCount + 1 : 0,\n\t\t\t}\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: prior?.operationId === op.id ? 'sync:apply-retrying' : 'sync:apply-blocked',\n\t\t\t\tfailure: this.blockedFailure,\n\t\t\t})\n\t\t}\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:apply-failed',\n\t\t\toperationId: op.id,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tcode: reason.code,\n\t\t\tmessage: reason.message,\n\t\t\tretriable: reason.retriable,\n\t\t})\n\t}\n\n\t/**\n\t * Force an immediate reconnection attempt. If the engine is disconnected\n\t * or in error state, restarts the sync. If already connected, no-op.\n\t */\n\tasync retryNow(): Promise<void> {\n\t\tif (this.schemaBlocked) return\n\t\tthis.encryptionLocked = false\n\t\tthis.authRejected = false\n\t\tthis.suspensionReason = null\n\t\tif (this.state === 'disconnected' || this.state === 'error') {\n\t\t\tthis.reconnecting = false\n\t\t\tawait this.start()\n\t\t}\n\t}\n\n\t/** Wake a permanent auth suspension after the binding reports a new auth state. */\n\tnotifyAuthChanged(): void {\n\t\tthis.authRejected = false\n\t\tthis.suspensionReason = null\n\t}\n\n\t/**\n\t * The signed-in user may have changed (RT-42): end the live session (it belongs to the\n\t * previous user), then bind the store to the user `config.principal` reports, so the\n\t * next local write is authored under that user's own node. Call it before the next\n\t * write after an auth change; `createApp` does so for `sync.authClient`. The next\n\t * `start()` connects as that user's node.\n\t */\n\tasync refreshPrincipal(): Promise<void> {\n\t\tif (!this.config.principal || !this.store.bindPrincipal) return\n\t\tlet next: string | null | undefined\n\t\ttry {\n\t\t\tnext = await this.config.principal()\n\t\t} catch {\n\t\t\treturn\n\t\t}\n\t\t// A token or scope refresh of the same user keeps the session.\n\t\tif (next === this.principal) return\n\t\tif (this.nodeRotation) await this.nodeRotation\n\t\tif (this.state !== 'disconnected') await this.stop()\n\t\tawait this.withPrincipalLock(() => this.applyPrincipal())\n\t}\n\n\t/**\n\t * Bind the store to the user the app reports NOW (RT-52), without ending or waiting\n\t * for any session: call it on every auth event, before queueing the reconnect that\n\t * follows. Writes made from then on are authored under that user's node, even while\n\t * an earlier reconnect is still fetching a credential or connecting; that attempt\n\t * notices the change before its handshake and starts over. A live session keeps\n\t * running as the previous user's node and uploads only that node's writes, until the\n\t * queued reconnect replaces it.\n\t */\n\tasync bindSignedInUser(): Promise<void> {\n\t\tif (!this.config.principal || !this.store.bindPrincipal) return\n\t\tawait this.withPrincipalLock(async () => {\n\t\t\tconst next = await this.resolvePrincipal()\n\t\t\tif (next === this.principal) return\n\t\t\tawait this.applyPrincipal({ keepSession: this.state !== 'disconnected' })\n\t\t})\n\t}\n\n\t/** The signed-in user as `config.principal` reports it (undefined when it throws). */\n\tprivate async resolvePrincipal(): Promise<string | null | undefined> {\n\t\tif (!this.config.principal) return undefined\n\t\ttry {\n\t\t\treturn await this.config.principal()\n\t\t} catch {\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\t/** Run `fn` after every earlier principal binding (one store binding at a time). */\n\tprivate withPrincipalLock<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst run = this.principalChain.then(fn, fn)\n\t\tthis.principalChain = run.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t)\n\t\treturn run\n\t}\n\n\t/**\n\t * Resolve the signed-in user and bind the store's node to them (RT-42).\n\t *\n\t * @returns false when sync must stay suspended: the store's node id is pinned and\n\t * belongs to another user, so nothing on this database may upload as this user.\n\t */\n\tprivate async applyPrincipal(options: { keepSession?: boolean } = {}): Promise<boolean> {\n\t\tif (!this.config.principal) return true\n\t\t// Unknown (undefined, or the resolver threw): no binding is changed, and no bound\n\t\t// node is judged against it.\n\t\tconst principal = await this.resolvePrincipal()\n\t\tthis.principal = principal\n\t\tif (!principal || !this.store.bindPrincipal) return true\n\t\tconst binding = await this.store.bindPrincipal(principal)\n\t\tif (binding.conflict) {\n\t\t\tthis.suspensionReason = 'node-owned-by-another-user'\n\t\t\tthis.emitter?.emit({ type: 'sync:suspended', reason: this.suspensionReason })\n\t\t\treturn false\n\t\t}\n\t\tif (binding.switched) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:local-node',\n\t\t\t\tnodeId: binding.nodeId,\n\t\t\t\taction: 'principal-switched',\n\t\t\t})\n\t\t\t// A live session stays the previous user's node until it is replaced: only\n\t\t\t// that node's writes upload on it (RT-52). The next session starts from the\n\t\t\t// store's new node.\n\t\t\tif (!options.keepSession) await this.afterIdentityChange()\n\t\t}\n\t\treturn true\n\t}\n\n\t/**\n\t * Atomically refresh the transport session. Overlapping callers share one\n\t * reconnect, in-flight outbound batches are returned to the queue by stop(), and the\n\t * next start handshakes with the latest auth token, sync scope, and query subsets.\n\t */\n\tasync reconnect(): Promise<void> {\n\t\tif (this.schemaBlocked) return\n\t\tif (this.reconnectPromise) return this.reconnectPromise\n\n\t\tthis.reconnecting = false\n\t\tthis.reconnectPromise = this.reconnectInternal().finally(() => {\n\t\t\tthis.reconnectPromise = null\n\t\t})\n\t\treturn this.reconnectPromise\n\t}\n\n\tprivate async reconnectInternal(): Promise<void> {\n\t\tawait this.stop()\n\t\tawait this.start()\n\t}\n\n\t/**\n\t * Export a diagnostics snapshot for debugging and support tickets.\n\t * Contains connection state, timing info, and queue metrics.\n\t */\n\texportDiagnostics(): SyncDiagnostics {\n\t\treturn {\n\t\t\tstate: this.state,\n\t\t\tstatus: this.getStatus(),\n\t\t\tnodeId: this.currentNodeId(),\n\t\t\turl: this.config.url,\n\t\t\tschemaVersion: this.config.schemaVersion ?? DEFAULT_SCHEMA_VERSION,\n\t\t\tlastSyncedAt: this.lastSyncedAt,\n\t\t\tlastSuccessfulPush: this.lastSuccessfulPush,\n\t\t\tlastSuccessfulPull: this.lastSuccessfulPull,\n\t\t\tconflicts: this.conflictCount,\n\t\t\tpendingOperations: this.outboundQueue.totalPending,\n\t\t\thasInFlightBatch: this.inFlightUploads.size > 0,\n\t\t\treconnecting: this.reconnecting,\n\t\t\tdeliveryWatermark: this.deliveryWatermark,\n\t\t\tdeliveryGapRepeatCount: this.deliveryGapRepeatCount,\n\t\t\ttimestamp: Date.now(),\n\t\t}\n\t}\n\n\t/**\n\t * Get the current internal state (for testing).\n\t */\n\tgetState(): SyncState {\n\t\treturn this.state\n\t}\n\n\t/**\n\t * Subscribe to internal state changes (SYNC-8). The reconnection loop uses it to\n\t * count an attempt as successful only once the session reaches `streaming`, not\n\t * when the handshake is merely sent.\n\t *\n\t * @param listener - Called with the new state after every transition\n\t * @returns Unsubscribe function\n\t */\n\tonStateChange(listener: (state: SyncState) => void): () => void {\n\t\tthis.stateListeners.add(listener)\n\t\treturn () => {\n\t\t\tthis.stateListeners.delete(listener)\n\t\t}\n\t}\n\n\t/**\n\t * Get the outbound queue (for testing).\n\t */\n\tgetOutboundQueue(): OutboundQueue {\n\t\treturn this.outboundQueue\n\t}\n\n\t/**\n\t * Update the sync scope map. Takes effect on the next connection attempt.\n\t *\n\t * When the scope changes (e.g., user switches organization), call this method\n\t * then reconnect. The new scope will be sent in the handshake, and the server\n\t * will send back data matching the new scope.\n\t *\n\t * Data that no longer matches the new scope is NOT deleted locally.\n\t * It simply stops being synced.\n\t *\n\t * @param scopeMap - New per-collection scope filters, or undefined to remove scope\n\t */\n\tupdateScope(scopeMap: SyncScopeMap | undefined): void {\n\t\tconst previousSignature = this.deliverySignature()\n\t\tthis.activeScope = scopeMap\n\t\t// Until the server accepts distinct directional maps, preserve the legacy\n\t\t// shorthand semantics locally as well.\n\t\tthis.activeUplinkScope = scopeMap\n\t\t// Also update the config so that the next handshake sends the new scope\n\t\tthis.config.scopeMap = scopeMap\n\t\tthis.switchDeliveryView(previousSignature)\n\t}\n\n\t/**\n\t * A stable identifier for the current sync view: the auth/tenant scope plus the set of\n\t * active query subscriptions, canonicalized so the same view always produces the same\n\t * string. The delivery watermark is keyed by this, so each distinct view resumes from\n\t * its own last position.\n\t */\n\tprivate deliverySignature(): string {\n\t\treturn this.deliverySignatureFor(this.activeScope)\n\t}\n\n\t/** The view signature {@link deliverySignature} gives under `scope` and today's subsets. */\n\tprivate deliverySignatureFor(scope: SyncScopeMap | undefined): string {\n\t\tconst subsets = this.getActiveQuerySubsets()\n\t\tif (!scope && subsets.length === 0) {\n\t\t\treturn '' // the default, unfiltered view (maps to the legacy watermark key)\n\t\t}\n\t\tconst normalizedSubsets = subsets\n\t\t\t.map((s) => `${s.collection}:${stableStringify(s.where)}`)\n\t\t\t.sort()\n\t\treturn stableStringify({ scope: scope ?? null, subsets: normalizedSubsets })\n\t}\n\n\t/**\n\t * Handshake fields naming the accepted view this client last streamed under (SYNC-11):\n\t * its canonical scope key and its delivery watermark. Omitted when no scope was ever\n\t * accepted, or when it equals the requested scope (the requested watermark covers it).\n\t */\n\tprivate acceptedViewHandshakeFields(): {\n\t\tacceptedScopeKey?: string\n\t\tacceptedScopeWatermark?: number\n\t} {\n\t\tconst accepted = this.lastAcceptedScope\n\t\tif (accepted === null) return {}\n\t\tconst key = scopeViewKey(accepted)\n\t\tif (key === scopeViewKey(this.activeScope)) return {}\n\t\tconst watermark = this.deliverySignatureWatermarks.get(this.deliverySignatureFor(accepted))\n\t\tif (watermark === undefined) return {}\n\t\treturn { acceptedScopeKey: key, acceptedScopeWatermark: watermark }\n\t}\n\n\t/** Persist the downlink scope the server accepted at this handshake (null: none). */\n\tprivate rememberAcceptedScope(accepted: SyncScopeMap | null): void {\n\t\tif (scopeViewKey(accepted) === scopeViewKey(this.lastAcceptedScope)) return\n\t\tthis.lastAcceptedScope = accepted\n\t\tif (this.syncState?.saveAcceptedDownlinkScope) {\n\t\t\tthis.syncState.saveAcceptedDownlinkScope(accepted).catch((error: unknown) => {\n\t\t\t\t// Losing it only costs a rescan from 0 at the next handshake.\n\t\t\t\tthis.emitter?.emit({\n\t\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\t\tmessage: error instanceof Error ? error.message : 'Saving the accepted scope failed',\n\t\t\t\t\tcode: 'ACCEPTED_SCOPE_SAVE_FAILED',\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Switch the active delivery watermark to the current view after a scope or subscription\n\t * change. The prior view's watermark is saved so returning to it resumes; the new view's\n\t * watermark is restored from the in-memory cache (0 for a never-synced view, which then\n\t * does a one-time resync under that view). No data is lost: a view's watermark is a lower\n\t * bound for that specific view's filter.\n\t */\n\tprivate switchDeliveryView(previousSignature: string): void {\n\t\tconst nextSignature = this.deliverySignature()\n\t\tif (nextSignature === previousSignature) {\n\t\t\treturn\n\t\t}\n\t\tthis.setViewWatermark(previousSignature, this.deliveryWatermark)\n\t\tvoid this.persistDeliveryWatermark(this.deliveryWatermark, previousSignature)\n\t\tthis.deliveryWatermark = this.deliverySignatureWatermarks.get(nextSignature) ?? 0\n\t}\n\n\t/**\n\t * Record a view's watermark in the in-memory cache as the most-recently-used entry (the\n\t * Map preserves insertion order, so re-inserting moves it to the tail), then evict cold\n\t * views past the retention cap. This is the single place the cache is written, so LRU\n\t * order and the size bound are always maintained together.\n\t */\n\tprivate setViewWatermark(signature: string, watermark: number): void {\n\t\tthis.deliverySignatureWatermarks.delete(signature)\n\t\tthis.deliverySignatureWatermarks.set(signature, watermark)\n\t\tthis.evictColdViewWatermarks()\n\t}\n\n\t/**\n\t * Trim the per-view watermark cache to `MAX_DELIVERY_VIEW_WATERMARKS`, evicting the\n\t * least-recently-used views. The default view ('') and the live view are never evicted\n\t * (evicting the live view would force an immediate re-scan of what we are actively\n\t * syncing). Each eviction also removes the persisted row, bounding storage. Safe by\n\t * construction: an evicted view simply back-fills from 0 (deduplicated) when next seen.\n\t */\n\tprivate evictColdViewWatermarks(): void {\n\t\tif (this.deliverySignatureWatermarks.size <= MAX_DELIVERY_VIEW_WATERMARKS) {\n\t\t\treturn\n\t\t}\n\t\tconst liveSignature = this.deliverySignature()\n\t\tfor (const signature of this.deliverySignatureWatermarks.keys()) {\n\t\t\tif (this.deliverySignatureWatermarks.size <= MAX_DELIVERY_VIEW_WATERMARKS) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif (signature === '' || signature === liveSignature) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tthis.deliverySignatureWatermarks.delete(signature)\n\t\t\tvoid this.deleteDeliveryWatermark(signature)\n\t\t}\n\t}\n\n\t/**\n\t * Remove a view's persisted watermark row (best-effort; a persistence layer that omits\n\t * the delete simply keeps the row, which is harmless).\n\t */\n\tprivate async deleteDeliveryWatermark(signature: string): Promise<void> {\n\t\tif (!this.syncState?.deleteDeliveryWatermark) {\n\t\t\treturn\n\t\t}\n\t\tawait this.syncState.deleteDeliveryWatermark(signature)\n\t}\n\n\t/**\n\t * Get the currently active scope map. Returns undefined if no scope is configured.\n\t */\n\tgetActiveScope(): SyncScopeMap | undefined {\n\t\treturn this.activeScope\n\t}\n\n\t/** Get the server-accepted upload authorization scope. */\n\tgetActiveUplinkScope(): SyncScopeMap | undefined {\n\t\treturn this.activeUplinkScope\n\t}\n\n\t/**\n\t * Register a live query subset that narrows synced data for a collection.\n\t * Takes effect on the next connection; reconnects when already connected.\n\t */\n\tregisterQuerySubset(subset: SyncQuerySubset): () => void {\n\t\tif ((this.config.querySubsets?.mode ?? 'reactive') !== 'reactive') return () => {}\n\t\tconst id = `query-${nextQuerySubsetId++}`\n\t\tconst previousSignature = this.deliverySignature()\n\t\tconst previousEffective = this.getActiveQuerySubsets()\n\t\tthis.querySubsets.set(id, subset)\n\t\t// Registering or unregistering a subset changes the sync view. Switch the watermark\n\t\t// to the new view (resuming it if seen before, or resyncing it once if new); returning\n\t\t// to a previously-synced view resumes instead of re-syncing. The debounce coalesces a\n\t\t// burst of subscription changes into a single reconnect.\n\t\tthis.switchDeliveryViewWithInheritance(previousSignature, previousEffective)\n\t\tif (this.deliverySignature() !== previousSignature) this.scheduleQuerySubsetReconnect()\n\t\treturn () => {\n\t\t\tconst sigBeforeRemove = this.deliverySignature()\n\t\t\tconst effectiveBeforeRemove = this.getActiveQuerySubsets()\n\t\t\tthis.querySubsets.delete(id)\n\t\t\tthis.switchDeliveryViewWithInheritance(sigBeforeRemove, effectiveBeforeRemove)\n\t\t\tif (this.deliverySignature() !== sigBeforeRemove) this.scheduleQuerySubsetReconnect()\n\t\t}\n\t}\n\n\tprivate switchDeliveryViewWithInheritance(\n\t\tpreviousSignature: string,\n\t\tpreviousSubsets: SyncQuerySubset[],\n\t): void {\n\t\tconst nextSubsets = this.getActiveQuerySubsets()\n\t\tconst previousWatermark = this.deliveryWatermark\n\t\tthis.switchDeliveryView(previousSignature)\n\t\tif (this.deliveryWatermark === 0 && querySubsetContains(previousSubsets, nextSubsets)) {\n\t\t\tthis.deliveryWatermark = previousWatermark\n\t\t\tthis.setViewWatermark(this.deliverySignature(), previousWatermark)\n\t\t\tvoid this.persistDeliveryWatermark(previousWatermark)\n\t\t}\n\t}\n\n\t/** Atomically replace the manifest used by static query-subset mode. */\n\tsetQuerySubsets(subsets: SyncQuerySubset[]): void {\n\t\tif ((this.config.querySubsets?.mode ?? 'reactive') !== 'static')\n\t\t\tthrow new Error('setQuerySubsets() requires sync.querySubsets.mode = \"static\"')\n\t\tconst previousSignature = this.deliverySignature()\n\t\tconst previousEffective = this.getActiveQuerySubsets()\n\t\tthis.staticQuerySubsets = dedupeQuerySubsets(subsets)\n\t\tthis.switchDeliveryViewWithInheritance(previousSignature, previousEffective)\n\t\tif (this.deliverySignature() !== previousSignature) this.scheduleQuerySubsetReconnect()\n\t}\n\n\t/**\n\t * Returns deduplicated active query subsets from registered subscriptions.\n\t */\n\tgetActiveQuerySubsets(): SyncQuerySubset[] {\n\t\tconst mode = this.config.querySubsets?.mode ?? 'reactive'\n\t\tif (mode === 'disabled') return []\n\t\treturn mode === 'static'\n\t\t\t? this.staticQuerySubsets\n\t\t\t: dedupeQuerySubsets([...this.querySubsets.values()])\n\t}\n\n\t/**\n\t * Get the awareness manager for collaborative presence.\n\t * Use this to set local presence, observe remote collaborators,\n\t * and track cursor positions in richtext fields.\n\t */\n\tgetAwarenessManager(): AwarenessManager {\n\t\treturn this.awarenessManager\n\t}\n\n\t/**\n\t * Optional side channel for incremental Yjs updates on large richtext fields.\n\t */\n\tgetRichtextDocChannel(): RichtextDocChannel {\n\t\treturn this.richtextDocChannel\n\t}\n\n\t/**\n\t * Side channel for out-of-band blob chunk transfer over the sync connection.\n\t * The app layer bridges this to a `ChunkMessagePort` to pull/serve blob bytes.\n\t */\n\tgetBlobChunkChannel(): BlobChunkChannel {\n\t\treturn this.blobChunkChannel\n\t}\n\n\t/**\n\t * Whether the connected server persists blob bytes centrally (learned from the\n\t * handshake response). When true, the app uploads the bytes behind `blob`\n\t * fields so they stay available after the authoring device goes offline.\n\t */\n\tisBlobStorageEnabled(): boolean {\n\t\treturn this.blobStorageEnabled\n\t}\n\n\t/**\n\t * Whether the connected server (peer-relay mode, no central storage) asks for the\n\t * bytes behind blob references as proof of possession (RT-23). The server verifies\n\t * and drops them; the app uploads exactly as for central storage.\n\t */\n\tisBlobPossessionProofRequested(): boolean {\n\t\treturn this.blobPossessionProof\n\t}\n\n\t/**\n\t * Upload a blob chunk (or manifest) to the server, for central persistence or as\n\t * proof of possession. A no-op unless the handshake was accepted (syncing or\n\t * streaming) and the server advertised blob storage or asked for proofs.\n\t */\n\tuploadBlobChunk(hash: string, bytes: Uint8Array): void {\n\t\tif (\n\t\t\t(this.state !== 'streaming' && this.state !== 'syncing') ||\n\t\t\t!(this.blobStorageEnabled || this.blobPossessionProof)\n\t\t) {\n\t\t\treturn\n\t\t}\n\t\tthis.blobChunkChannel.send({ type: 'blob-chunk-push', hash, bytes })\n\t}\n\n\t/**\n\t * Register work that must reach the server before each outbound operation batch,\n\t * on the same connection (messages are processed in order). Used to upload the\n\t * bytes behind blob references first: the server only accepts a reference to\n\t * content the writer can read or has uploaded (RT-11). Errors are ignored; the\n\t * batch is sent regardless.\n\t *\n\t * @param preparer - Called with the operations about to be sent; null to remove\n\t * @returns A function that removes this preparer\n\t */\n\tsetOutboundPreparer(preparer: ((operations: Operation[]) => Promise<void>) | null): () => void {\n\t\tthis.outboundPreparer = preparer\n\t\treturn () => {\n\t\t\tif (this.outboundPreparer === preparer) this.outboundPreparer = null\n\t\t}\n\t}\n\n\t// --- Private methods ---\n\n\tprivate messageChain: Promise<void> = Promise.resolve()\n\n\tprivate enqueueMessage(message: SyncMessage): void {\n\t\t// The server sends a credential-ending error and closes the socket right away.\n\t\t// The close is handled synchronously (and may start a reconnect) before this\n\t\t// queued message is processed, so record the need to refresh now (AUTH-11).\n\t\tif (message.type === 'error' && isCredentialEndingCode(message.code)) {\n\t\t\tthis.credentialRefreshRequired = true\n\t\t}\n\t\t// A key-service answer resolves its request directly, outside the message chain:\n\t\t// the handshake response awaits it inside the chain (ENC-1).\n\t\tif (message.type === 'encryption-key-response' && message.requestId !== undefined) {\n\t\t\tconst pending = this.keyRequests.get(message.requestId)\n\t\t\tif (pending) {\n\t\t\t\tthis.keyRequests.delete(message.requestId)\n\t\t\t\tclearTimeout(pending.timer)\n\t\t\t\tpending.resolve(message)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tthis.messageChain = this.messageChain\n\t\t\t.then(() => this.handleMessageAsync(message))\n\t\t\t.catch((error) => this.handleMessageFailure(error))\n\t}\n\n\tprivate async handleMessageAsync(message: SyncMessage): Promise<void> {\n\t\tswitch (message.type) {\n\t\t\tcase 'handshake-response':\n\t\t\t\tawait this.handleHandshakeResponse(message)\n\t\t\t\tbreak\n\t\t\tcase 'operation-batch':\n\t\t\t\tawait this.handleOperationBatch(message)\n\t\t\t\tbreak\n\t\t\tcase 'acknowledgment':\n\t\t\t\tawait this.handleAcknowledgment(message)\n\t\t\t\tbreak\n\t\t\tcase 'error':\n\t\t\t\tthis.handleError(message)\n\t\t\t\tbreak\n\t\t\tcase 'operation-rejected':\n\t\t\t\tawait this.handleOperationRejected(message)\n\t\t\t\tbreak\n\t\t\tcase 'awareness-update':\n\t\t\t\tthis.handleAwarenessUpdate(message)\n\t\t\t\tbreak\n\t\t\tcase 'yjs-doc-update':\n\t\t\t\tthis.richtextDocChannel.deliver(message)\n\t\t\t\tbreak\n\t\t\tcase 'blob-chunk-request':\n\t\t\tcase 'blob-chunk-response':\n\t\t\tcase 'blob-chunk-push':\n\t\t\t\tthis.blobChunkChannel.deliver(message)\n\t\t\t\tbreak\n\t\t\tcase 'encryption-key-response':\n\t\t\t\t// Unsolicited: another device of this user rotated keys or changed the\n\t\t\t\t// passphrase. In the chain, so operations after it decrypt with the new key.\n\t\t\t\tawait this.handlePushedKeyRecord(message)\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t/** Adopt a key record the server pushed; stop the session if it locks the keyring. */\n\tprivate async handlePushedKeyRecord(message: EncryptionKeyResponseMessage): Promise<void> {\n\t\tif (!this.keyring || message.status !== 'ok' || message.record === null) return\n\t\tif (message.keyring !== this.keyring.name) return\n\t\tconst before = this.keyring.getStatus().availableVersions.length\n\t\tconst outcome = await this.keyring.adoptPushed(message.record)\n\t\tif (outcome === 'ready') {\n\t\t\t// Operations that arrived under a key this device did not hold yet decrypt now.\n\t\t\tif (this.keyring.getStatus().availableVersions.length > before) {\n\t\t\t\tawait this.replayQuarantine()\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tthis.pauseForLockedKeyring()\n\t}\n\n\t/**\n\t * An operation sealed under a key version newer than any this device holds: another\n\t * device rotated keys and the server's push did not reach this session (another\n\t * server instance). Fetch the record once, then replay the quarantine in the chain.\n\t */\n\tprivate maybeRefreshKeyring(op: Operation): void {\n\t\tconst keyring = this.keyring\n\t\tconst keyId = op.encrypted?.keyId\n\t\tif (!keyring || keyId === undefined || this.keyRefreshInFlight) return\n\t\t// By key id, not version: after a forked ring is merged (RT-104), an operation can\n\t\t// name a key under a version number this device already uses for another key.\n\t\tif (keyring.getEncryptor() === null || keyring.holdsKeyId(keyId)) return\n\t\tconst channel = this.getKeyServiceChannel()\n\t\tif (!channel) return\n\t\tthis.keyRefreshInFlight = true\n\t\tvoid keyring\n\t\t\t.synchronize(channel, this.sessionPrincipal ?? null)\n\t\t\t.then((outcome) => {\n\t\t\t\tif (outcome !== 'ready') {\n\t\t\t\t\tthis.pauseForLockedKeyring()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.messageChain = this.messageChain\n\t\t\t\t\t.then(async () => {\n\t\t\t\t\t\tawait this.replayQuarantine()\n\t\t\t\t\t})\n\t\t\t\t\t.catch((error) => this.handleMessageFailure(error))\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\t// The next handshake opens the record again.\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tthis.keyRefreshInFlight = false\n\t\t\t})\n\t}\n\n\t/**\n\t * End the session because the keyring is locked; no reconnect until unlocked. A lock\n\t * caused by the server's record (rolled back, lost, malformed, forked: RT-96, RT-104)\n\t * needs no user action, so the session ends without pausing and the reconnection\n\t * loop retries with backoff; the next handshake re-uploads, merges or opens it.\n\t */\n\tprivate pauseForLockedKeyring(): void {\n\t\tif (this.keyring?.isRetryableLock()) {\n\t\t\tconst status = this.keyring.getStatus()\n\t\t\tthis.abandonSession(\n\t\t\t\t`End-to-end encryption: ${status.code ?? 'key record'} (${status.message ?? 'retrying'})`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tthis.encryptionLocked = true\n\t\tthis.emitter?.emit({ type: 'sync:suspended', reason: 'encryption-locked' })\n\t\tthis.abandonSession('End-to-end encryption is locked')\n\t}\n\n\t/** One key-service round trip over the live session. */\n\tprivate keyServiceRequest(\n\t\trequest:\n\t\t\t| { kind: 'fetch'; keyring: string }\n\t\t\t| { kind: 'put'; keyring: string; record: WrappedKeyRecord; expectedRevision: number },\n\t): Promise<KeyServiceReply> {\n\t\tif (this.serverProtocolVersion < SYNC_PROTOCOL_VERSION) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew EncryptionKeyError(\n\t\t\t\t\t'The sync server does not provide end-to-end encryption keys. Upgrade @korajs/server to 1.0.0-beta.13 or later.',\n\t\t\t\t\t{ code: 'KEY_SERVICE_UNSUPPORTED' },\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tconst requestId = generateMessageId()\n\t\tconst response = new Promise<EncryptionKeyResponseMessage>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.keyRequests.delete(requestId)\n\t\t\t\treject(\n\t\t\t\t\tnew EncryptionKeyError(\n\t\t\t\t\t\t`The sync server did not answer a key request within ${this.keyServiceTimeoutMs}ms.`,\n\t\t\t\t\t\t{ code: 'KEY_SERVICE_TIMEOUT' },\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}, this.keyServiceTimeoutMs)\n\t\t\tthis.keyRequests.set(requestId, { resolve, reject, timer })\n\t\t})\n\t\ttry {\n\t\t\tthis.transport.send(\n\t\t\t\trequest.kind === 'fetch'\n\t\t\t\t\t? {\n\t\t\t\t\t\t\ttype: 'encryption-key-request',\n\t\t\t\t\t\t\tmessageId: generateMessageId(),\n\t\t\t\t\t\t\trequestId,\n\t\t\t\t\t\t\tkeyring: request.keyring,\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\ttype: 'encryption-key-put',\n\t\t\t\t\t\t\tmessageId: generateMessageId(),\n\t\t\t\t\t\t\trequestId,\n\t\t\t\t\t\t\tkeyring: request.keyring,\n\t\t\t\t\t\t\trecord: request.record,\n\t\t\t\t\t\t\texpectedRevision: request.expectedRevision,\n\t\t\t\t\t\t},\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tconst pending = this.keyRequests.get(requestId)\n\t\t\tif (pending) {\n\t\t\t\tclearTimeout(pending.timer)\n\t\t\t\tthis.keyRequests.delete(requestId)\n\t\t\t}\n\t\t\treturn Promise.reject(error)\n\t\t}\n\t\treturn response.then((message) => keyServiceReply(message))\n\t}\n\n\t/** Fail every pending key-service request (the session ended). */\n\tprivate rejectKeyRequests(reason: string): void {\n\t\tfor (const [requestId, pending] of this.keyRequests) {\n\t\t\tclearTimeout(pending.timer)\n\t\t\tpending.reject(\n\t\t\t\tnew EncryptionKeyError(\n\t\t\t\t\t`The sync session ended before the key service answered (${reason}).`,\n\t\t\t\t\t{\n\t\t\t\t\t\tcode: 'KEY_SERVICE_DISCONNECTED',\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t)\n\t\t\tthis.keyRequests.delete(requestId)\n\t\t}\n\t}\n\n\tprivate handleMessageFailure(error: unknown): void {\n\t\tthis.hasInFlightDeliveryBatch = false\n\t\tconst reason = error instanceof Error ? error.message : 'Message handling failed'\n\t\tthis.abandonSession(reason)\n\t}\n\n\t/**\n\t * Compares server wall-clock time from the handshake with local time.\n\t * Negative skew = this device's clock is fast (dangerous for LWW and rejected\n\t * by server ingest beyond 60s). Positive skew = slow (accepted but surfaced).\n\t * Zero developer work required: the result flows into sync status and events.\n\t */\n\tprivate evaluateClockSkew(serverTime: number): void {\n\t\tconst skewMs = serverTime - Date.now()\n\t\tthis.clockSkewMs = skewMs\n\t\tconst FAST_BLOCK_MS = 60_000\n\t\tconst SLOW_WARN_MS = 10 * 60_000\n\t\tlet severity: 'info' | 'slow-warning' | 'fast-blocked' = 'info'\n\t\tif (skewMs < -FAST_BLOCK_MS) {\n\t\t\tseverity = 'fast-blocked'\n\t\t} else if (skewMs > SLOW_WARN_MS) {\n\t\t\tseverity = 'slow-warning'\n\t\t}\n\t\tthis.emitter?.emit({ type: 'sync:clock-skew', skewMs, severity, source: 'handshake' })\n\t\tif (severity === 'fast-blocked') {\n\t\t\tthis.clockBlocked = true\n\t\t} else {\n\t\t\t// Auto-heal: an acceptable measured skew is authoritative proof the\n\t\t\t// device clock has been corrected, so a block engaged earlier (at a\n\t\t\t// previous handshake or via a server INVALID_TIMESTAMP reject) no\n\t\t\t// longer applies and must not require a manual clearClockBlock().\n\t\t\tthis.clockBlocked = false\n\t\t}\n\t}\n\n\tprivate async handleHandshakeResponse(msg: HandshakeResponseMessage): Promise<void> {\n\t\tif (this.state !== 'handshaking') return\n\n\t\tthis.blobStorageEnabled = msg.blobStorageEnabled === true\n\t\tthis.blobPossessionProof = msg.blobPossessionProof === true\n\n\t\tif (typeof msg.serverTime === 'number') {\n\t\t\tthis.evaluateClockSkew(msg.serverTime)\n\t\t\tif (this.clockBlocked) {\n\t\t\t\tthis.metricsCollector.updateStatus('error')\n\t\t\t\tthis.transitionTo('error')\n\t\t\t\tvoid this.transport.disconnect()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif (msg.accepted) {\n\t\t\tthis.credentialRefreshRequired = false\n\t\t\t// W7: server node ids authoritative for merge('server-authoritative') fields.\n\t\t\t// Read structurally: the protocol field lands with protocol v2.\n\t\t\tconst authoritative = (msg as { authoritativeNodeIds?: unknown }).authoritativeNodeIds\n\t\t\tif (Array.isArray(authoritative) && this.store.setAuthoritativeNodeIds) {\n\t\t\t\tawait this.store.setAuthoritativeNodeIds(\n\t\t\t\t\tauthoritative.filter((id): id is string => typeof id === 'string'),\n\t\t\t\t\trevokedAuthoritativeIds(msg),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tif (!msg.accepted) {\n\t\t\tconst reason = msg.rejectReason ?? 'Handshake rejected'\n\t\t\tif (isSchemaMismatchReject(msg.rejectReason)) {\n\t\t\t\tthis.schemaBlocked = true\n\t\t\t\tconst supportedMin = msg.supportedSchemaMin ?? msg.schemaVersion\n\t\t\t\tconst supportedMax = msg.supportedSchemaMax ?? msg.schemaVersion\n\t\t\t\tthis.emitter?.emit({\n\t\t\t\t\ttype: 'sync:schema-mismatch',\n\t\t\t\t\tclientSchemaVersion: this.config.schemaVersion ?? DEFAULT_SCHEMA_VERSION,\n\t\t\t\t\tserverSchemaVersion: msg.schemaVersion,\n\t\t\t\t\tsupportedMin,\n\t\t\t\t\tsupportedMax,\n\t\t\t\t\treason,\n\t\t\t\t})\n\t\t\t\tthis.metricsCollector.updateStatus('error')\n\t\t\t\tthis.transitionTo('error')\n\t\t\t\tvoid this.transport.disconnect()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.transitionTo('error')\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:disconnected',\n\t\t\t\treason,\n\t\t\t})\n\t\t\tthis.transitionTo('disconnected')\n\t\t\tvoid this.closeTransportQuietly()\n\t\t\treturn\n\t\t}\n\n\t\tconst sessionNode = this.currentNodeId()\n\t\t// Accepted as this node: from now on a refusal of it means another principal owns\n\t\t// it (RT-38). Recorded before anything is uploaded in this session.\n\t\tawait this.recordNodeAccepted(sessionNode)\n\n\t\tif (typeof msg.nodeToken === 'string' && msg.nodeToken.length > 0) {\n\t\t\tthis.nodeToken = msg.nodeToken\n\t\t\tthis.nodeTokenNodeId = sessionNode\n\t\t\tawait this.syncState?.saveNodeToken?.(msg.nodeToken, sessionNode)\n\t\t\t// The claim stays provisional until the server knows the token is saved:\n\t\t\t// confirm it now, so a lost response never locks this device out (RT-21).\n\t\t\t// Without persistence the token lives for this engine only; confirming would\n\t\t\t// tie the node to a secret the next process does not have, so don't.\n\t\t\tif (this.syncState?.saveNodeToken) {\n\t\t\t\tthis.transport.send({\n\t\t\t\t\ttype: 'acknowledgment',\n\t\t\t\t\tmessageId: generateMessageId(),\n\t\t\t\t\tacknowledgedMessageId: msg.messageId,\n\t\t\t\t\tlastSequenceNumber: 0,\n\t\t\t\t\tnodeToken: msg.nodeToken,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tthis.serverProtocolVersion = declaredProtocolVersion(msg.protocolVersion)\n\t\tthis.serverProtocolKnown = true\n\n\t\t// Open the encryption keyring against the server's record BEFORE any operation is\n\t\t// exchanged (ENC-1): inbound batches wait behind this in the message chain, and\n\t\t// nothing is uploaded until it is unlocked with the user's shared keys.\n\t\tif (this.keyring) {\n\t\t\tconst keySession = this.sessionEpoch\n\t\t\tconst outcome = await this.keyring.synchronize(\n\t\t\t\tthis.liveKeyServiceChannel(),\n\t\t\t\tthis.sessionPrincipal ?? null,\n\t\t\t)\n\t\t\tif (this.state !== 'handshaking' || this.sessionEpoch !== keySession) return\n\t\t\tif (outcome !== 'ready') {\n\t\t\t\tthis.pauseForLockedKeyring()\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Operations quarantined while the keyring could not open them decrypt now.\n\t\t\tawait this.replayQuarantine()\n\t\t}\n\t\tif (Array.isArray(msg.authoritativeNodeIds)) {\n\t\t\t// The union of every explicit id learned, never a replacement (RT-75): each\n\t\t\t// server instance lists what it knows, and `kora:server:` ids are\n\t\t\t// authoritative by prefix, so they are not kept.\n\t\t\t// Ids the server revoked (RT-81) leave the union and are never learned again (the\n\t\t\t// store persists them, and its union, which this list mirrors, filters them).\n\t\t\tconst revoked = new Set(revokedAuthoritativeIds(msg))\n\t\t\tconst previous = (this.authoritativeNodeIds ?? []).filter((id) => !isServerNodeId(id))\n\t\t\tconst known = new Set(previous.filter((id) => !revoked.has(id)))\n\t\t\tconst learned = msg.authoritativeNodeIds.filter(\n\t\t\t\t(id): id is string =>\n\t\t\t\t\ttypeof id === 'string' && !isServerNodeId(id) && !known.has(id) && !revoked.has(id),\n\t\t\t)\n\t\t\tif (\n\t\t\t\tlearned.length > 0 ||\n\t\t\t\tknown.size < previous.length ||\n\t\t\t\tthis.authoritativeNodeIds === null\n\t\t\t) {\n\t\t\t\tthis.authoritativeNodeIds = [...known, ...learned].sort()\n\t\t\t\tawait this.syncState?.saveAuthoritativeNodeIds?.(this.authoritativeNodeIds)\n\t\t\t}\n\t\t}\n\n\t\tthis.remoteVector = wireToVersionVector(msg.versionVector)\n\t\tvoid this.persistLastAckedServerVector(this.remoteVector)\n\n\t\t// The server's accepted downlink scope is authoritative for what it delivers, so\n\t\t// the delivery watermark belongs to that view. When it differs from the requested\n\t\t// view (the one the handshake watermark was read under), switch views (SYNC-11):\n\t\t// the server restarts such a stream from 0, and only that view's own watermark\n\t\t// can tell the duplicates in it from operations this client never saw.\n\t\tconst previousSignature = this.deliverySignature()\n\t\tconst acceptedDownlink = msg.acceptedDownlinkScopes ?? msg.acceptedScope\n\t\t// A grant equal to the request (by canonical key) keeps the requested view: the\n\t\t// server resumed from the requested view's watermark, and a re-ordered copy of the\n\t\t// same scope must not switch to a differently keyed watermark.\n\t\tif (acceptedDownlink && scopeViewKey(acceptedDownlink) !== scopeViewKey(this.activeScope)) {\n\t\t\tthis.activeScope = acceptedDownlink\n\t\t}\n\t\tthis.activeUplinkScope = msg.acceptedUplinkScopes ?? msg.acceptedScope ?? this.activeScope\n\t\tthis.switchDeliveryView(previousSignature)\n\t\tthis.rememberAcceptedScope(acceptedDownlink ?? null)\n\n\t\t// If our watermark is ahead of the server's frontier, the server's log was rolled\n\t\t// back (for example a backup restore reset the delivery sequence). Reset to a full\n\t\t// resync so we do not sit above operations the server will re-send from the start.\n\t\t// The server independently resyncs such a client from 0, so the stream that follows\n\t\t// this response chains from 0 and this reset lets us apply it.\n\t\tif (\n\t\t\ttypeof msg.serverMaxDeliverySequence === 'number' &&\n\t\t\tthis.deliveryWatermark > msg.serverMaxDeliverySequence\n\t\t) {\n\t\t\tthis.deliveryWatermark = 0\n\t\t\tthis.setViewWatermark(this.deliverySignature(), 0)\n\t\t\tawait this.persistDeliveryWatermark(0)\n\t\t}\n\t\tthis.serverFrontier = msg.serverMaxDeliverySequence ?? null\n\n\t\tif (msg.selectedWireFormat) {\n\t\t\tthis.setSerializerWireFormat(msg.selectedWireFormat)\n\t\t}\n\n\t\tif (this.config.scopeExit === 'retract' && this.activeScope && this.store.applyScopeNarrowing) {\n\t\t\tconst narrowed = await this.store.applyScopeNarrowing(this.activeScope)\n\t\t\tfor (const retraction of narrowed) {\n\t\t\t\tawait this.applyScopeRetraction(retraction, false)\n\t\t\t}\n\t\t}\n\n\t\t// A server that holds MORE of this node's operations than the device's log (the\n\t\t// device lost the tail of its log, RT-35) is never trusted as an acknowledgment,\n\t\t// but the numbers it holds are taken: the counter moves past them before the next\n\t\t// write, and a full resync fetches the operations back.\n\t\tconst advertisedOwn = this.remoteVector.get(sessionNode)\n\t\tthis.handshakeOwnEntry = advertisedOwn ?? 0\n\t\tconst localOwn = this.store.getVersionVector().get(sessionNode) ?? 0\n\t\t// The persisted counter decides (raiseSequenceFloor reads it in a transaction): the\n\t\t// in-memory vector of a tab sharing the node may simply lag another tab's writes.\n\t\tif (\n\t\t\tadvertisedOwn !== undefined &&\n\t\t\tadvertisedOwn > localOwn &&\n\t\t\tthis.store.raiseSequenceFloor &&\n\t\t\t(await this.store.raiseSequenceFloor(sessionNode, advertisedOwn))\n\t\t) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:local-node',\n\t\t\t\tnodeId: sessionNode,\n\t\t\t\taction: 'history-behind',\n\t\t\t\tlocalSequence: localOwn,\n\t\t\t\tserverSequence: advertisedOwn,\n\t\t\t})\n\t\t\tawait this.resetDeliveryForFullResync()\n\t\t\tthis.abandonSession(\n\t\t\t\t'The server holds operations of this device that its local database lost; resyncing them',\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\t// A server that holds FEWER of this device's operations than the device believes\n\t\t// (a restored backup) lowers the acknowledged prefix, so the device re-uploads\n\t\t// them; the server dedups by id. A higher value is never trusted (RT-12, W3).\n\t\t// An ABSENT own entry means the server holds none of them (RT-45): every server\n\t\t// names the session's own node whenever it holds an operation of it (the entry is\n\t\t// never scope-filtered), so absence is the strongest \"behind\", never \"unknown\".\n\t\tconst serverHoldsOwn = advertisedOwn ?? 0\n\t\tif (serverHoldsOwn < this.ownAckedThrough && this.ownTrackingNodeId === sessionNode) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:local-node',\n\t\t\t\tnodeId: sessionNode,\n\t\t\t\taction: 'server-behind',\n\t\t\t\tlocalSequence: this.ownAckedThrough,\n\t\t\t\tserverSequence: serverHoldsOwn,\n\t\t\t})\n\t\t\tawait this.withOwnTracking(async () => {\n\t\t\t\tawait this.lowerOwnPrefixLocked(serverHoldsOwn)\n\t\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t\t})\n\t\t}\n\t\tawait this.refreshPendingCount()\n\n\t\tthis.emitter?.emit({ type: 'sync:connected', nodeId: sessionNode })\n\t\tthis.metricsCollector.recordConnected()\n\t\tthis.metricsCollector.updateStatus('syncing')\n\t\tthis.metricsCollector.recordSyncStarted()\n\n\t\tthis.transitionTo('syncing')\n\t\tthis.deltaBatchesReceived = 0\n\t\tthis.deltaReceiveComplete = false\n\t\tthis.deltaSendComplete = false\n\t\tthis.pendingDeltaBatchAcks.clear()\n\t\tthis.initialSyncTotalBatches = 0\n\n\t\t// Rebase must finish BEFORE the delta exchange starts so only the\n\t\t// re-stamped operation ids ever reach the wire.\n\t\tif (typeof msg.serverTime === 'number') {\n\t\t\tawait this.maybeRebaseQueuedOperations(msg.serverTime)\n\t\t}\n\n\t\t// Send our delta to the server. Not awaited: the outbound preparer (blob bytes)\n\t\t// may need server messages that this message chain has yet to process.\n\t\tvoid this.sendDelta().catch((error) => {\n\t\t\tthis.abandonSession(error instanceof Error ? error.message : 'Delta upload failed')\n\t\t})\n\t\tthis.notifyStatusChange()\n\t}\n\n\t/**\n\t * The server refused this session's node id (`NODE_ID_CLAIMED`). What happens to the\n\t * node's unsynced writes depends on whether this database was ever accepted as it:\n\t *\n\t * - Never accepted (RT-21: the claim's token was lost before the device saw it, or\n\t * another device took the id first): no principal ever received these writes under\n\t * this node, so the never-sent ones are re-authored under a fresh node id and\n\t * uploaded. An operation flagged sent is never re-authored (RT-38): it may already\n\t * be stored, and a copy under a new id would store the change twice.\n\t * - Accepted before (RT-38: another user signed in on a database shared between\n\t * users): the node belongs to the principal that used it, so its unsynced writes\n\t * are held for that principal, never re-authored or uploaded under another one. The\n\t * device moves to a node the signed-in principal may own (a held node not yet tried\n\t * in this refusal cycle, typically that user's own earlier node) or to a fresh one.\n\t * - An adopted node (RT-40) is held; the store's own node is left as it is.\n\t */\n\tprivate async rotateNodeIdentity(): Promise<void> {\n\t\tconst refused = this.currentNodeId()\n\t\tconst storeNode = this.store.getNodeId()\n\t\tthis.returnAllInFlightUploads()\n\t\ttry {\n\t\t\t// The server says the node is not the signed-in user's (RT-50): a guessed\n\t\t\t// binding was wrong, and an unbound node is never tried for them again.\n\t\t\tconst principal = this.sessionPrincipal\n\t\t\tif (typeof principal === 'string') {\n\t\t\t\tawait this.syncState?.recordLocalNodeRefusedFor?.(refused, principal)\n\t\t\t}\n\t\t\tif (refused !== storeNode) {\n\t\t\t\tconst node = (await this.syncState?.listLocalNodes?.())?.find(\n\t\t\t\t\t(entry) => entry.nodeId === refused,\n\t\t\t\t)\n\t\t\t\t// An unbound node (learning its owner) is not held for good: another user's\n\t\t\t\t// session may still be accepted for it.\n\t\t\t\tconst unbound = node !== undefined && (node.principal ?? null) === null\n\t\t\t\tawait this.syncState?.markLocalNodeRefused?.(refused, !unbound)\n\t\t\t\tthis.heldNodeIds.add(refused)\n\t\t\t\tthis.endAdoption()\n\t\t\t\tthis.emitter?.emit({ type: 'sync:local-node', nodeId: refused, action: 'adoption-refused' })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst rotate = this.store.rotateNodeId?.bind(this.store)\n\t\t\tif (!rotate) {\n\t\t\t\tthis.emitter?.emit({ type: 'sync:suspended', reason: 'node-id-claimed' })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (await this.wasNodeAccepted(refused)) {\n\t\t\t\tawait this.holdNodeAndSwitch(refused, rotate)\n\t\t\t} else {\n\t\t\t\tawait this.reauthorUnsentAndRotate(refused, rotate)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Node id rotation failed',\n\t\t\t\tcode: 'NODE_ROTATION_FAILED',\n\t\t\t})\n\t\t}\n\t}\n\n\t/** RT-38: hold a refused, previously accepted node's writes and move to another node. */\n\tprivate async holdNodeAndSwitch(\n\t\trefused: string,\n\t\trotate: (ids: string[]) => Promise<{ nodeId: string; operations: Operation[] }>,\n\t): Promise<void> {\n\t\tawait this.syncState?.markLocalNodeRefused?.(refused, true)\n\t\tconst held = await this.countUnsyncedOfNode(refused)\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:local-node',\n\t\t\tnodeId: refused,\n\t\t\taction: 'held',\n\t\t\toperationCount: held.count,\n\t\t})\n\t\tlet target: string | null = null\n\t\tconst switchTo = this.store.switchNodeId?.bind(this.store)\n\t\tif (switchTo && this.syncState?.listLocalNodes) {\n\t\t\t// A held node refused before the last accepted handshake was not tried in this\n\t\t\t// refusal cycle: it may be the signed-in principal's own node.\n\t\t\tconst cycle = (await this.syncState.loadAcceptedCycle?.()) ?? 0\n\t\t\tconst nodes = await this.syncState.listLocalNodes()\n\t\t\tconst candidate = nodes.find(\n\t\t\t\t(node) =>\n\t\t\t\t\tnode.nodeId !== refused &&\n\t\t\t\t\tnode.held &&\n\t\t\t\t\t!this.belongsToAnotherPrincipal(node) &&\n\t\t\t\t\t// An unbound node is never handed to the signed-in user (RT-50).\n\t\t\t\t\t!(typeof this.principal === 'string' && (node.principal ?? null) === null) &&\n\t\t\t\t\t(node.refusedCycle === null || node.refusedCycle < cycle),\n\t\t\t)\n\t\t\tif (candidate) {\n\t\t\t\tawait switchTo(candidate.nodeId)\n\t\t\t\ttarget = candidate.nodeId\n\t\t\t}\n\t\t}\n\t\tif (target === null) target = (await rotate([])).nodeId\n\t\tawait this.afterIdentityChange()\n\t\tawait this.bindFreshNode()\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:node-id-rotated',\n\t\t\tpreviousNodeId: refused,\n\t\t\tnodeId: target,\n\t\t\treenqueuedCount: 0,\n\t\t\theldCount: held.count,\n\t\t})\n\t}\n\n\t/** RT-21: re-author a never-accepted node's never-sent writes under a fresh node. */\n\tprivate async reauthorUnsentAndRotate(\n\t\trefused: string,\n\t\trotate: (ids: string[]) => Promise<{ nodeId: string; operations: Operation[] }>,\n\t): Promise<void> {\n\t\tawait this.syncState?.markLocalNodeRefused?.(refused, false)\n\t\tawait this.rotateUnsent(refused, rotate)\n\t}\n\n\t/**\n\t * Move `nodeId`'s never-sent unsynced operations to a fresh node id (re-authored, so\n\t * they upload under it) and start using it. An operation flagged sent stays: it may\n\t * already be stored under `nodeId` (RT-38).\n\t *\n\t * @returns How many unsynced operations stayed under `nodeId` (sent, unacknowledged)\n\t */\n\tprivate async rotateUnsent(\n\t\tnodeId: string,\n\t\trotate: (ids: string[]) => Promise<{ nodeId: string; operations: Operation[] }>,\n\t): Promise<number> {\n\t\t// A database a beta.12 (or older) client synced has no acknowledged prefix yet (its\n\t\t// one-time re-upload restarts at 0 and queues its whole history), but that server\n\t\t// acknowledged the node through the persisted vector entry: operations at or below\n\t\t// it are on the server under this node, and a copy under the new node would apply\n\t\t// them twice (an increment counted twice; RT-92). They are not re-authored, and the\n\t\t// refused node's prefix moves up to that entry so they no longer count as unsynced.\n\t\tconst ackedFloor = Math.max(this.ownAckedThrough, this.lastAckedServerVector.get(nodeId) ?? 0)\n\t\tconst unsent = this.outboundQueue\n\t\t\t.getAll()\n\t\t\t.filter((op) => op.nodeId === nodeId && !this.outboundQueue.wasSent(op.id))\n\t\tconst queuedIds = unsent.filter((op) => op.sequenceNumber > ackedFloor).map((op) => op.id)\n\t\tconst acknowledgedIds = unsent\n\t\t\t.filter((op) => op.sequenceNumber <= ackedFloor)\n\t\t\t.map((op) => op.id)\n\t\tconst ids = new Set(queuedIds)\n\t\tlet staying = this.outboundQueue.countMatching(\n\t\t\t(op) => op.nodeId === nodeId && this.outboundQueue.wasSent(op.id),\n\t\t)\n\t\t// Own operations above the acknowledged prefix that were never read into the\n\t\t// queue (the unscanned tail) are unsynced too.\n\t\tconst localSeq = this.store.getVersionVector().get(nodeId) ?? 0\n\t\tif (localSeq > ackedFloor) {\n\t\t\tconst above = await this.store.getOperationRange(nodeId, ackedFloor + 1, localSeq)\n\t\t\tconst candidates = above.filter(\n\t\t\t\t(op) =>\n\t\t\t\t\top.nodeId === nodeId &&\n\t\t\t\t\top.sequenceNumber > ackedFloor &&\n\t\t\t\t\t!this.isOwnOperationResolved(op) &&\n\t\t\t\t\t!this.outboundQueue.has(op.id),\n\t\t\t)\n\t\t\tconst terminal = await this.findTerminalRejections(candidates.map((op) => op.id))\n\t\t\tfor (const op of candidates) {\n\t\t\t\tif (terminal.has(op.id)) continue\n\t\t\t\tif (this.outboundQueue.wasSent(op.id)) staying++\n\t\t\t\telse ids.add(op.id)\n\t\t\t}\n\t\t}\n\t\tconst result = await rotate([...ids])\n\t\tawait this.outboundQueue.replace([...queuedIds, ...acknowledgedIds], result.operations)\n\t\tif (ackedFloor > this.ownAckedThrough) {\n\t\t\tawait this.syncState?.saveOwnAckedThrough?.(nodeId, ackedFloor)\n\t\t}\n\t\tawait this.afterIdentityChange()\n\t\tawait this.bindFreshNode()\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:node-id-rotated',\n\t\t\tpreviousNodeId: nodeId,\n\t\t\tnodeId: result.nodeId,\n\t\t\treenqueuedCount: result.operations.length,\n\t\t})\n\t\treturn staying\n\t}\n\n\t/** A fresh node id belongs to the signed-in user, when the app says who that is (RT-42). */\n\tprivate async bindFreshNode(): Promise<void> {\n\t\tif (typeof this.principal !== 'string' || !this.store.bindPrincipal) return\n\t\ttry {\n\t\t\tawait this.store.bindPrincipal(this.principal)\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Binding the node to the user failed',\n\t\t\t\tcode: 'NODE_REGISTRY_FAILED',\n\t\t\t})\n\t\t}\n\t}\n\n\t/** The store moved to another node id: the next session starts from it. */\n\tprivate async afterIdentityChange(): Promise<void> {\n\t\tthis.nodeToken = null\n\t\tthis.nodeTokenNodeId = null\n\t\tthis.sessionNodeId = this.store.getNodeId()\n\t\t// The new node id has its own acknowledged prefix.\n\t\tawait this.withOwnTracking(async () => {\n\t\t\tthis.ownTrackingNodeId = null\n\t\t\tawait this.loadOwnTrackingLocked()\n\t\t})\n\t\tawait this.refreshPendingCount()\n\t}\n\n\t/** Whether a handshake as `nodeId` was ever accepted on this database. */\n\tprivate async wasNodeAccepted(nodeId: string): Promise<boolean> {\n\t\tif (this.memoryAcceptedNodes.has(nodeId)) return true\n\t\tif (!this.syncState?.listLocalNodes) return false\n\t\tconst nodes = await this.syncState.listLocalNodes()\n\t\treturn nodes.some((node) => node.nodeId === nodeId && node.accepted)\n\t}\n\n\t/**\n\t * Re-stamps queued (never-acknowledged) operations whose timestamps are far\n\t * enough in the future that the server would reject them, using the server's\n\t * own handshake time as the trusted \"now\". This is the automatic recovery\n\t * path after a user corrects a fast device clock: the queue drains\n\t * immediately instead of waiting for real time to catch up.\n\t */\n\tprivate async maybeRebaseQueuedOperations(serverTime: number): Promise<void> {\n\t\t// Optional store capability — hand-rolled SyncStore implementations\n\t\t// without it silently keep the old (blocked-until-time-catches-up) behavior.\n\t\tconst rebase = this.store.rebaseUnsyncedOperations?.bind(this.store)\n\t\tif (!rebase) return\n\n\t\t// Only operations that never reached the wire may be re-stamped (W3 step 4): a\n\t\t// sent one may already be stored on the server with its ack lost, and re-stamping\n\t\t// it would upload the same change twice under two ids.\n\t\tconst queued = this.outboundQueue\n\t\t\t.getAll()\n\t\t\t.filter((op) => op.nodeId === this.currentNodeId() && !this.outboundQueue.wasSent(op.id))\n\t\tif (queued.length === 0) return\n\n\t\tlet maxQueuedWallTime = Number.NEGATIVE_INFINITY\n\t\tfor (const op of queued) {\n\t\t\tif (op.timestamp.wallTime > maxQueuedWallTime) {\n\t\t\t\tmaxQueuedWallTime = op.timestamp.wallTime\n\t\t\t}\n\t\t}\n\n\t\t// Mirror the server's ingest tolerance: ops within +60s of server time\n\t\t// would be accepted as-is, so rewriting them would churn ids for nothing.\n\t\tconst SERVER_FUTURE_TOLERANCE_MS = 60_000\n\t\tif (maxQueuedWallTime <= serverTime + SERVER_FUTURE_TOLERANCE_MS) return\n\n\t\ttry {\n\t\t\tconst result = await rebase(\n\t\t\t\tqueued.map((op) => op.id),\n\t\t\t\tserverTime,\n\t\t\t)\n\t\t\tawait this.withOwnTracking(async () => {\n\t\t\t\tawait this.outboundQueue.replace(\n\t\t\t\t\tqueued.map((op) => op.id),\n\t\t\t\t\tresult.operations,\n\t\t\t\t)\n\t\t\t\t// The rewritten ops keep their sequence numbers but get new ids.\n\t\t\t\tfor (const [oldId, newId] of Object.entries(result.idMapping)) {\n\t\t\t\t\tfor (const ids of this.ownUnresolved.values()) {\n\t\t\t\t\t\tif (ids.delete(oldId)) ids.add(newId)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:clock-rebase',\n\t\t\t\trebasedCount: result.rebasedCount,\n\t\t\t\tmaxSkewMs: maxQueuedWallTime - serverTime,\n\t\t\t})\n\t\t} catch (error) {\n\t\t\t// Never let a failed rebase crash the handshake: the old ops stay\n\t\t\t// queued, the server will reject them with INVALID_TIMESTAMP, and the\n\t\t\t// existing clock-block path takes over. Surface the failure through an\n\t\t\t// existing event type rather than swallowing it.\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Timestamp rebase failed',\n\t\t\t\tcode: 'CLOCK_REBASE_FAILED',\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Upload this device's unsynced operations as the handshake delta. The delta is the\n\t * outbound queue itself (rebuilt from the op log above the acknowledged prefix), sent\n\t * as tracked batches: nothing leaves the queue until the server acknowledges the batch\n\t * that carried it (SYNC-4).\n\t */\n\tprivate async sendDelta(): Promise<void> {\n\t\tconst epoch = this.sessionEpoch\n\t\tconst strict = this.config.strictHandshake === true\n\t\tconst entries: InFlightUpload[] = []\n\t\tfor (let entry = this.takeUpload(); entry; entry = this.takeUpload()) {\n\t\t\tentries.push(entry)\n\t\t}\n\n\t\tif (entries.length === 0) {\n\t\t\tconst messageId = generateMessageId()\n\t\t\tif (strict) {\n\t\t\t\tthis.pendingDeltaBatchAcks.add(messageId)\n\t\t\t}\n\t\t\tconst emptyBatch: SyncMessage = {\n\t\t\t\ttype: 'operation-batch',\n\t\t\t\tmessageId,\n\t\t\t\toperations: [],\n\t\t\t\tisFinal: true,\n\t\t\t\tbatchIndex: 0,\n\t\t\t\ttotalBatches: 1,\n\t\t\t}\n\t\t\tthis.transport.send(emptyBatch)\n\t\t\tif (!strict) {\n\t\t\t\tthis.deltaSendComplete = true\n\t\t\t\tvoid this.checkDeltaComplete()\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tconst totalBatches = entries.length\n\t\tthis.initialSyncTotalBatches = Math.max(this.initialSyncTotalBatches, totalBatches)\n\t\t// One durability barrier for the whole delta (RT-35), not one per batch: flag every\n\t\t// batch sent first (flagging writes, which would dirty the store again between\n\t\t// batches), then make it all durable. sendUpload re-checks both, as no-ops; a\n\t\t// failure here surfaces there, per batch, and postpones that batch.\n\t\ttry {\n\t\t\tfor (const entry of entries) await this.outboundQueue.markSent(entry.batch.batchId)\n\t\t\tawait this.awaitUploadDurability()\n\t\t} catch {\n\t\t\t// Reported by sendUpload, which retries the barrier before each batch.\n\t\t}\n\t\tfor (const [i, entry] of entries.entries()) {\n\t\t\tif (this.sessionEpoch !== epoch) {\n\t\t\t\t// The session ended mid-delta: the rest goes back to the queue.\n\t\t\t\tif (this.inFlightUploads.get(entry.batch.batchId) === entry) this.returnUpload(entry)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst cursor = createDeltaCursorFromBatch(entry.batch.operations, i)\n\t\t\tawait this.sendUpload(entry, {\n\t\t\t\tisFinal: i === totalBatches - 1,\n\t\t\t\tbatchIndex: i,\n\t\t\t\ttotalBatches,\n\t\t\t\t...(cursor ? { cursor: encodeDeltaCursor(cursor) } : {}),\n\t\t\t\tbeforeSend: (messageId) => {\n\t\t\t\t\tif (strict) this.pendingDeltaBatchAcks.add(messageId)\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\n\t\tif (!strict && this.sessionEpoch === epoch) {\n\t\t\tthis.deltaSendComplete = true\n\t\t\tvoid this.checkDeltaComplete()\n\t\t}\n\t}\n\n\tprivate markDeltaSendCompleteIfReady(): void {\n\t\tif (this.config.strictHandshake && this.pendingDeltaBatchAcks.size > 0) {\n\t\t\treturn\n\t\t}\n\t\tthis.deltaSendComplete = true\n\t\tvoid this.checkDeltaComplete()\n\t}\n\n\t/** Take the next batch from the queue and track it until it is resolved. */\n\tprivate takeUpload(): InFlightUpload | null {\n\t\tconst batch = this.outboundQueue.takeBatch(this.batchSize, this.isSessionOperation)\n\t\tif (!batch) return null\n\t\tconst entry: InFlightUpload = {\n\t\t\tbatch,\n\t\t\tmessageId: null,\n\t\t\tepoch: this.sessionEpoch,\n\t\t\tretryBackoff: false,\n\t\t}\n\t\tthis.inFlightUploads.set(batch.batchId, entry)\n\t\treturn entry\n\t}\n\n\t/** Whether a taken batch still belongs to the live session (SYNC-10). */\n\tprivate isUploadCurrent(entry: InFlightUpload): boolean {\n\t\treturn (\n\t\t\tthis.inFlightUploads.get(entry.batch.batchId) === entry &&\n\t\t\tentry.epoch === this.sessionEpoch &&\n\t\t\t(this.state === 'syncing' || this.state === 'streaming')\n\t\t)\n\t}\n\n\t/** Return one taken batch to the queue and forget its tracking. */\n\tprivate returnUpload(entry: InFlightUpload): void {\n\t\tthis.outboundQueue.returnBatch(entry.batch.batchId)\n\t\tthis.inFlightUploads.delete(entry.batch.batchId)\n\t\tif (entry.messageId) this.uploadByMessageId.delete(entry.messageId)\n\t\tif (this.inFlightUploads.size === 0) this.clearOutboundAckTimer()\n\t}\n\n\t/** Return every taken batch to the queue (the session ended; nothing was resolved). */\n\tprivate returnAllInFlightUploads(): void {\n\t\tfor (const entry of [...this.inFlightUploads.values()]) {\n\t\t\tthis.outboundQueue.returnBatch(entry.batch.batchId)\n\t\t}\n\t\tthis.inFlightUploads.clear()\n\t\tthis.uploadByMessageId.clear()\n\t\tthis.clearOutboundAckTimer()\n\t}\n\n\t/**\n\t * The oldest batch taken from the queue and not yet resolved (diagnostics and\n\t * tests); null when nothing is in flight.\n\t */\n\tprivate get currentBatch(): OutboundBatch | null {\n\t\tconst first = this.inFlightUploads.values().next()\n\t\treturn first.done ? null : first.value.batch\n\t}\n\n\tprivate inFlightUploadCount(): number {\n\t\tlet count = 0\n\t\tfor (const entry of this.inFlightUploads.values()) count += entry.batch.operations.length\n\t\treturn count\n\t}\n\n\t/**\n\t * Prepare, encrypt and send one taken batch, then register its message id so the ack\n\t * that names it resolves exactly this batch. Every await is followed by a check that\n\t * the batch still belongs to the live session; a send that throws returns the batch\n\t * to the queue instead of rejecting unhandled (SYNC-10).\n\t *\n\t * @returns The wire message id, or null when the batch was not sent.\n\t */\n\tprivate async sendUpload(\n\t\tentry: InFlightUpload,\n\t\tframe: {\n\t\t\tisFinal: boolean\n\t\t\tbatchIndex: number\n\t\t\ttotalBatches?: number\n\t\t\tcursor?: string\n\t\t\tbeforeSend?: (messageId: string) => void\n\t\t},\n\t): Promise<string | null> {\n\t\t// The server acknowledges a batch as \"processed through sequence n\", in the order\n\t\t// it processes them; sending own operations in sequence order keeps that a prefix.\n\t\tconst operations = [...entry.batch.operations].sort(compareForUpload)\n\t\tconst preparer = this.outboundPreparer\n\t\tif (preparer) {\n\t\t\t// Let the app send what the server needs BEFORE these operations (blob bytes,\n\t\t\t// so the server sees proof of possession before the reference, RT-11).\n\t\t\ttry {\n\t\t\t\tawait preparer(operations)\n\t\t\t} catch {\n\t\t\t\t// Best effort: the server decides on the operations themselves.\n\t\t\t}\n\t\t\tif (!this.isUploadCurrent(entry)) return null\n\t\t}\n\n\t\tlet wireOperations = operations\n\t\tif (this.encryptionConfigured()) {\n\t\t\ttry {\n\t\t\t\twireOperations = await this.requireEncryptor().encryptBatch(operations)\n\t\t\t} catch (err) {\n\t\t\t\t// Nothing was sent: return the batch so no data is lost.\n\t\t\t\tif (this.inFlightUploads.get(entry.batch.batchId) === entry) this.returnUpload(entry)\n\t\t\t\tthis.emitter?.emit({\n\t\t\t\t\ttype: 'sync:disconnected',\n\t\t\t\t\treason: err instanceof Error ? err.message : 'Encryption failed',\n\t\t\t\t})\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (!this.isUploadCurrent(entry)) return null\n\t\t}\n\n\t\tif (!this.isUploadCurrent(entry)) return null\n\t\t// Flag the operations as sent BEFORE they reach the wire: from then on they may be\n\t\t// stored on the server even if no ack ever arrives, so a rebase or a node rotation\n\t\t// must leave them (RT-38). Then make every local write durable (RT-35): the server\n\t\t// must never hold an operation, or the device lack a sent flag, that a reload could\n\t\t// lose. The in-memory flag is set synchronously, before the first await.\n\t\ttry {\n\t\t\tawait this.outboundQueue.markSent(entry.batch.batchId)\n\t\t\tawait this.awaitUploadDurability()\n\t\t} catch (error) {\n\t\t\tif (this.inFlightUploads.get(entry.batch.batchId) === entry) this.returnUpload(entry)\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage:\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? `Upload postponed: local writes are not durable yet (${error.message})`\n\t\t\t\t\t\t: 'Upload postponed: local writes are not durable yet',\n\t\t\t\tcode: 'UPLOAD_NOT_DURABLE',\n\t\t\t})\n\t\t\tthis.scheduleOutboundRetry()\n\t\t\treturn null\n\t\t}\n\t\tif (!this.isUploadCurrent(entry)) return null\n\n\t\tconst messageId = generateMessageId()\n\t\tconst message: SyncMessage = {\n\t\t\ttype: 'operation-batch',\n\t\t\tmessageId,\n\t\t\toperations: wireOperations.map((op) => this.serializer.encodeOperation(op)),\n\t\t\tisFinal: frame.isFinal,\n\t\t\tbatchIndex: frame.batchIndex,\n\t\t\t...(frame.totalBatches !== undefined ? { totalBatches: frame.totalBatches } : {}),\n\t\t\t...(frame.cursor !== undefined ? { cursor: frame.cursor } : {}),\n\t\t}\n\t\tentry.messageId = messageId\n\t\tthis.uploadByMessageId.set(messageId, entry.batch.batchId)\n\t\tframe.beforeSend?.(messageId)\n\t\ttry {\n\t\t\tthis.transport.send(message)\n\t\t} catch (error) {\n\t\t\tthis.returnUpload(entry)\n\t\t\tthis.pendingDeltaBatchAcks.delete(messageId)\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:disconnected',\n\t\t\t\treason: error instanceof Error ? error.message : 'Send failed',\n\t\t\t})\n\t\t\treturn null\n\t\t}\n\t\tthis.startOutboundAckTimer()\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:sent',\n\t\t\toperations,\n\t\t\tbatchSize: operations.length,\n\t\t})\n\t\tthis.notifyStatusChange()\n\t\treturn messageId\n\t}\n\n\t/**\n\t * The durability barrier before an upload (RT-35), with a bounded number of failures\n\t * (RT-49). Throws while the failures may be transient; after\n\t * {@link DURABILITY_FAILURES_BEFORE_DEGRADED} consecutive failures it returns anyway\n\t * (degraded mode, `localDurability: 'degraded'`, `sync:durability-degraded`) so the\n\t * server keeps a durable copy. The barrier is still attempted every time: the first\n\t * success leaves degraded mode (`sync:durability-restored`).\n\t */\n\tprivate async awaitUploadDurability(): Promise<void> {\n\t\tif (!this.store.ensureDurable) return\n\t\ttry {\n\t\t\tawait this.store.ensureDurable()\n\t\t} catch (error) {\n\t\t\tthis.durabilityFailures++\n\t\t\tif (this.durabilityDegraded) return\n\t\t\tif (this.durabilityFailures < DURABILITY_FAILURES_BEFORE_DEGRADED) throw error\n\t\t\tthis.durabilityDegraded = true\n\t\t\tconst message = error instanceof Error ? error.message : 'The local database is not durable'\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:durability-degraded',\n\t\t\t\tmessage,\n\t\t\t\tfailedAttempts: this.durabilityFailures,\n\t\t\t})\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: `Uploading without local durability after ${this.durabilityFailures} failed attempts; the server holds the only durable copy of new writes (${message})`,\n\t\t\t\tcode: 'DURABILITY_DEGRADED',\n\t\t\t})\n\t\t\tthis.notifyStatusChange()\n\t\t\treturn\n\t\t}\n\t\tthis.durabilityFailures = 0\n\t\tif (this.durabilityDegraded) {\n\t\t\tthis.durabilityDegraded = false\n\t\t\tthis.emitter?.emit({ type: 'sync:durability-restored' })\n\t\t\tthis.notifyStatusChange()\n\t\t}\n\t}\n\n\tprivate async handleOperationBatch(msg: OperationBatchMessage): Promise<void> {\n\t\tconst isDeliveryBatch = msg.maxDeliverySequence !== undefined\n\n\t\t// Delivery-stream chain control. The server sends in-scope operations in delivery\n\t\t// order, each batch chaining base -> max. The watermark stays a sound, gap-free\n\t\t// lower bound, and a dropped batch recovers without a lost operation:\n\t\tif (isDeliveryBatch) {\n\t\t\tconst base = msg.baseDeliverySequence ?? 0\n\t\t\tconst max = msg.maxDeliverySequence ?? base\n\t\t\tif (base > this.deliveryWatermark) {\n\t\t\t\t// A gap: an earlier batch has not arrived. Do not apply out of order and do\n\t\t\t\t// not acknowledge, so the server's reliable retransmit (or the next handshake\n\t\t\t\t// resend from the watermark) redelivers the missing batch first.\n\t\t\t\tconst gapKey = `${this.deliveryWatermark}:${base}`\n\t\t\t\tthis.deliveryGapRepeatCount =\n\t\t\t\t\tthis.lastDeliveryGapKey === gapKey ? this.deliveryGapRepeatCount + 1 : 1\n\t\t\t\tthis.lastDeliveryGapKey = gapKey\n\t\t\t\tthis.emitter?.emit({\n\t\t\t\t\ttype: 'sync:delivery-gap',\n\t\t\t\t\texpectedBase: this.deliveryWatermark,\n\t\t\t\t\treceivedBase: base,\n\t\t\t\t\tcurrentWatermark: this.deliveryWatermark,\n\t\t\t\t\tmessageId: msg.messageId,\n\t\t\t\t\trepeatCount: this.deliveryGapRepeatCount,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (base < this.deliveryWatermark && max <= this.deliveryWatermark) {\n\t\t\t\t// Entirely below the watermark: a retransmit that crossed an ack, or a server\n\t\t\t\t// restarting the stream from 0 (it does so whenever the resolved scope differs\n\t\t\t\t// from the handshake scope). Re-acknowledge without re-applying, but still run\n\t\t\t\t// the initial-sync bookkeeping: a final batch must complete the handshake, or\n\t\t\t\t// the engine would sit in 'syncing' forever (NEW-SYNC-1).\n\t\t\t\tthis.sendDeliveryAck(msg.messageId, this.deliveryWatermark)\n\t\t\t\tawait this.recordReceivedBatch(msg, true, true, [], [])\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// base === watermark continues the chain. base < watermark < max straddles it:\n\t\t\t// the batch is applied whole (re-applying the part below the watermark is an\n\t\t\t// idempotent no-op) and the watermark advances to its max, so the next chained\n\t\t\t// batch (base == max) is not mistaken for a gap.\n\t\t\tthis.hasInFlightDeliveryBatch = true\n\t\t}\n\n\t\tconst operations = msg.operations.map((s) => this.serializer.decodeOperation(s))\n\t\tconst deliverySequence = isDeliveryBatch ? (msg.maxDeliverySequence ?? null) : null\n\n\t\t// Whether every operation in this batch was durably applied, was a harmless\n\t\t// duplicate, or was durably quarantined. A retriable failure clears this so the\n\t\t// delivery watermark does not advance past the failed operation.\n\t\tlet fullyApplied = true\n\n\t\tfor (const retraction of msg.retractions ?? []) {\n\t\t\ttry {\n\t\t\t\tawait this.applyScopeRetraction(retraction, true)\n\t\t\t} catch {\n\t\t\t\tfullyApplied = false\n\t\t\t}\n\t\t}\n\t\tif ((msg.retractions?.length ?? 0) > 0) {\n\t\t\tawait this.refreshPendingCount()\n\t\t}\n\n\t\t// Apply what the server delivered, in order (SYNC-2, NEW-SYNC-3): the server already\n\t\t// enforced the downlink scope and query view, and judging delivered operations\n\t\t// against the client's uplink scope dropped read-only data. Anything the client\n\t\t// deliberately does not apply is quarantined, never silently skipped.\n\t\tconst received: Operation[] = []\n\t\tconst quarantined: QuarantinedOperation[] = []\n\t\tfor (const delivered of operations) {\n\t\t\tconst outcome = await this.applyInbound(delivered, deliverySequence)\n\t\t\tif (outcome.operation) received.push(outcome.operation)\n\t\t\tif (outcome.kind === 'quarantine') quarantined.push(outcome.entry)\n\t\t\telse if (outcome.kind === 'stall') fullyApplied = false\n\t\t}\n\n\t\tif (received.length > 0) {\n\t\t\tthis.lastSuccessfulPull = Date.now()\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:received',\n\t\t\t\toperations: received,\n\t\t\t\tbatchSize: received.length,\n\t\t\t})\n\t\t}\n\n\t\t// Make the outcome durable BEFORE acknowledging. For a chained delivery batch the\n\t\t// quarantine rows and the watermark advance commit in one transaction, so the\n\t\t// watermark never passes an operation that is neither applied nor quarantined.\n\t\tconst advance = isDeliveryBatch && fullyApplied && msg.maxDeliverySequence !== undefined\n\t\tif (advance && msg.maxDeliverySequence !== undefined) {\n\t\t\tthis.deliveryGapRepeatCount = 0\n\t\t\tthis.lastDeliveryGapKey = null\n\t\t\tconst watermark = Math.max(this.deliveryWatermark, msg.maxDeliverySequence)\n\t\t\tawait this.commitDeliveryProgress(quarantined, watermark)\n\t\t\tthis.deliveryWatermark = watermark\n\t\t\tthis.setViewWatermark(this.deliverySignature(), watermark)\n\t\t\tif (\n\t\t\t\tthis.blockedFailure &&\n\t\t\t\treceived.some((op) => op.id === this.blockedFailure?.operationId)\n\t\t\t) {\n\t\t\t\tconst recovered = this.blockedFailure\n\t\t\t\tthis.blockedFailure = null\n\t\t\t\tthis.emitter?.emit({ type: 'sync:apply-recovered', failure: recovered })\n\t\t\t}\n\t\t} else if (!isDeliveryBatch && quarantined.length > 0) {\n\t\t\t// A legacy (version-vector) or relay batch has no watermark to move, but its\n\t\t\t// quarantined operations must still be recorded before the ack releases them.\n\t\t\tawait this.commitDeliveryProgress(quarantined, null)\n\t\t}\n\n\t\t// Acknowledge the batch. A legacy (version-vector) batch is always acknowledged.\n\t\t// A delivery-stream batch is acknowledged only when it fully applied: a failed\n\t\t// delivery batch must NOT be acknowledged, so the server keeps re-sending it from\n\t\t// the client's last acknowledged position rather than releasing it.\n\t\tif (!isDeliveryBatch || advance) {\n\t\t\tconst lastOp = operations[operations.length - 1]\n\t\t\tconst ack: SyncMessage = {\n\t\t\t\ttype: 'acknowledgment',\n\t\t\t\tmessageId: generateMessageId(),\n\t\t\t\tacknowledgedMessageId: msg.messageId,\n\t\t\t\tlastSequenceNumber: lastOp ? lastOp.sequenceNumber : 0,\n\t\t\t\t...(advance && msg.maxDeliverySequence !== undefined\n\t\t\t\t\t? { deliverySequence: this.deliveryWatermark }\n\t\t\t\t\t: {}),\n\t\t\t}\n\t\t\tthis.transport.send(ack)\n\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:acknowledged',\n\t\t\t\tsequenceNumber: lastOp ? lastOp.sequenceNumber : 0,\n\t\t\t})\n\t\t}\n\t\tthis.hasInFlightDeliveryBatch = false\n\n\t\tconst streaming = this.state === 'streaming'\n\t\tawait this.recordReceivedBatch(msg, isDeliveryBatch, fullyApplied, received, operations)\n\t\t// A streaming device is caught up again at the end of every fully applied final\n\t\t// batch, so it settles the provisional effects of remote deletes (RT-69) there too,\n\t\t// exactly as at a catch-up. A server copy that confirms an effect is stored with\n\t\t// its delete and arrives with it; an effect no copy confirms (a protocol-1 server,\n\t\t// beta.12 and older, derives no cascades, RT-93; a delete this server derived\n\t\t// nothing for, RT-94) was otherwise kept until the next reconnect, while every\n\t\t// device that received the same delete during a catch-up dropped it.\n\t\tif (streaming && msg.isFinal && (!isDeliveryBatch || fullyApplied)) {\n\t\t\tawait this.settleAfterCatchUp({ provisionalOnly: true })\n\t\t}\n\t\tthis.notifyStatusChange()\n\t}\n\n\t/**\n\t * Initial-sync bookkeeping for a received batch: progress, the legacy resume cursor,\n\t * and completion on the final batch. Runs for duplicate batches too (NEW-SYNC-1).\n\t */\n\tprivate async recordReceivedBatch(\n\t\tmsg: OperationBatchMessage,\n\t\tisDeliveryBatch: boolean,\n\t\tfullyApplied: boolean,\n\t\treceived: Operation[],\n\t\toperations: Operation[],\n\t): Promise<void> {\n\t\tif (this.state !== 'syncing') {\n\t\t\treturn\n\t\t}\n\t\tthis.deltaBatchesReceived++\n\t\tconst totalBatches = msg.totalBatches ?? this.initialSyncTotalBatches\n\t\tif (msg.totalBatches !== undefined) {\n\t\t\tthis.initialSyncTotalBatches = msg.totalBatches\n\t\t}\n\t\tthis.metricsCollector.updateInitialSyncProgress(this.deltaBatchesReceived, totalBatches)\n\n\t\t// The version-vector resume cursor is only used for the legacy (non-delivery)\n\t\t// delta path. A delivery stream resumes from the watermark instead.\n\t\tif (!isDeliveryBatch) {\n\t\t\tconst cursorFromBatch =\n\t\t\t\tmsg.cursor !== undefined\n\t\t\t\t\t? decodeDeltaCursor(msg.cursor)\n\t\t\t\t\t: createDeltaCursorFromBatch(received.length > 0 ? received : operations, msg.batchIndex)\n\t\t\tif (cursorFromBatch) {\n\t\t\t\tthis.resumeDeltaCursor = cursorFromBatch\n\t\t\t\tawait this.persistDeltaCursor(cursorFromBatch)\n\t\t\t}\n\t\t}\n\n\t\t// A delivery batch with a retriable failure must not complete initial sync (its\n\t\t// operations still need to arrive), so completion is gated on fullyApplied.\n\t\tif (msg.isFinal && (!isDeliveryBatch || fullyApplied)) {\n\t\t\tthis.deltaReceiveComplete = true\n\t\t\tthis.resumeDeltaCursor = null\n\t\t\tawait this.persistDeltaCursor(null)\n\t\t\tawait this.settleAfterCatchUp()\n\t\t\tthis.metricsCollector.recordSyncCompleted()\n\t\t\tawait this.checkDeltaComplete()\n\t\t}\n\t}\n\n\t/**\n\t * Apply one delivered operation. Outcomes:\n\t * - `applied`: applied, a duplicate, or a merge-decided skip (the record is settled);\n\t * - `quarantine`: deliberately not applied and to be recorded durably (unknown\n\t * collection, transform unavailable, rejected or deferred apply, far-future\n\t * timestamp, undecryptable payload, referential failure), with `sync:apply-failed`;\n\t * - `stall`: a retriable failure; the watermark must not pass it (or the engine\n\t * cannot record a quarantine durably), so the server re-sends it.\n\t */\n\tprivate async applyInbound(\n\t\tdelivered: Operation,\n\t\tdeliverySequence: number | null,\n\t): Promise<\n\t\t| { kind: 'applied'; operation: Operation }\n\t\t| { kind: 'quarantine'; entry: QuarantinedOperation; operation: Operation | null }\n\t\t| { kind: 'stall'; operation: Operation | null }\n\t> {\n\t\tconst quarantine = (\n\t\t\top: Operation,\n\t\t\tcode: string,\n\t\t\tmessage: string,\n\t\t\tresult: Exclude<ApplyResult, 'applied' | 'duplicate'>,\n\t\t\tretriable: boolean,\n\t\t\tdecrypted: Operation | null,\n\t\t):\n\t\t\t| { kind: 'quarantine'; entry: QuarantinedOperation; operation: Operation | null }\n\t\t\t| { kind: 'stall'; operation: Operation | null } => {\n\t\t\tif (!this.canQuarantine()) {\n\t\t\t\t// No durable quarantine next to a durable watermark: stall rather than lose.\n\t\t\t\tthis.emitApplyFailure(op, result, { code, message, retriable }, 'blocking')\n\t\t\t\treturn { kind: 'stall', operation: decrypted }\n\t\t\t}\n\t\t\tthis.emitApplyFailure(op, result, { code, message, retriable }, 'quarantined')\n\t\t\treturn {\n\t\t\t\tkind: 'quarantine',\n\t\t\t\toperation: decrypted,\n\t\t\t\tentry: { operation: op, deliverySequence, code, message, quarantinedAt: Date.now() },\n\t\t\t}\n\t\t}\n\n\t\t// Decrypt per operation (ENC-2): one undecryptable operation (another key, a\n\t\t// corrupted payload) is quarantined; the session and every other operation go on.\n\t\tlet op = delivered\n\t\tif (this.encryptionConfigured() && !this.isServerAuthoredCleartext(delivered)) {\n\t\t\ttry {\n\t\t\t\top = await this.requireEncryptor().decryptOperation(delivered)\n\t\t\t} catch (error) {\n\t\t\t\tthis.maybeRefreshKeyring(delivered)\n\t\t\t\treturn quarantine(\n\t\t\t\t\tdelivered,\n\t\t\t\t\tQUARANTINE_CODES.DECRYPT_FAILED,\n\t\t\t\t\terror instanceof Error ? error.message : 'Failed to decrypt operation',\n\t\t\t\t\t'rejected',\n\t\t\t\t\ttrue,\n\t\t\t\t\tnull,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// Content-addressed id check (CORE-1, protocol v2), on the plaintext and before\n\t\t// any transform. A forged or altered operation is kept in quarantine, never\n\t\t// applied; it never verifies later, so the replay leaves it there.\n\t\tconst integrity = await verifyInboundOperation(op, {\n\t\t\tencrypted: delivered.encrypted !== undefined,\n\t\t})\n\t\tif (!integrity.ok) {\n\t\t\treturn quarantine(delivered, integrity.code, integrity.message, 'rejected', false, null)\n\t\t}\n\n\t\t// A far-future timestamp is never applied: adopting it would make every later\n\t\t// local edit lose to it until real time caught up (SYNC-7). The HLC also refuses\n\t\t// it once warm; this check covers a cold clock and does not depend on the store.\n\t\tconst reference = Date.now() + (this.clockSkewMs ?? 0)\n\t\tif (op.timestamp.wallTime > reference + MAX_REMOTE_FUTURE_MS) {\n\t\t\treturn quarantine(\n\t\t\t\top,\n\t\t\t\tQUARANTINE_CODES.REMOTE_CLOCK_DRIFT,\n\t\t\t\t`Operation timestamp ${op.timestamp.wallTime} is more than ${MAX_REMOTE_FUTURE_MS}ms ahead of the reference time ${reference}; it is kept and retried once time catches up.`,\n\t\t\t\t'rejected',\n\t\t\t\ttrue,\n\t\t\t\top,\n\t\t\t)\n\t\t}\n\n\t\t// A beta.12 clear the id proves (delivered by a beta.12 server, which stored the\n\t\t// body without it) is made explicit before the body is stored (RT-85): the fold\n\t\t// folds bodies as written.\n\t\top = await canonicalizeProvenLegacyClear(op)\n\n\t\t// Transforms at fold time (RT-84): the operation is stored exactly as delivered and\n\t\t// the store folds its view. Here the view is only checked: an operation with no\n\t\t// view (authored under a newer schema than this device's) is kept aside and\n\t\t// replayed after an upgrade, as before.\n\t\tconst targetSchemaVersion = this.config.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\tlet transformed: Operation | null\n\t\ttry {\n\t\t\ttransformed = operationSchemaView(op, targetSchemaVersion, this.operationTransforms)\n\t\t} catch (error) {\n\t\t\treturn quarantine(\n\t\t\t\top,\n\t\t\t\tQUARANTINE_CODES.TRANSFORM_INVALID,\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t'rejected',\n\t\t\t\tfalse,\n\t\t\t\top,\n\t\t\t)\n\t\t}\n\t\tif (transformed === null) {\n\t\t\treturn quarantine(\n\t\t\t\top,\n\t\t\t\tQUARANTINE_CODES.TRANSFORM_UNAVAILABLE,\n\t\t\t\t`No transform from schema v${op.schemaVersion} to v${targetSchemaVersion} for this operation; it is kept and replayed after an upgrade.`,\n\t\t\t\t'skipped',\n\t\t\t\tfalse,\n\t\t\t\top,\n\t\t\t)\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.store.applyRemoteOperation(op)\n\t\t\tif (result === 'applied' || result === 'duplicate') {\n\t\t\t\treturn { kind: 'applied', operation: op }\n\t\t\t}\n\t\t\tif (result === 'skipped' && this.store.hasCollection?.(transformed.collection) === true) {\n\t\t\t\t// A merge decision (for example a local update outranking a remote delete):\n\t\t\t\t// the record is settled; nothing to keep.\n\t\t\t\tthis.emitApplyFailure(transformed, result, undefined, 'quarantined')\n\t\t\t\treturn { kind: 'applied', operation: op }\n\t\t\t}\n\t\t\tconst reason = defaultApplyFailureReason(result)\n\t\t\treturn quarantine(transformed, reason.code, reason.message, result, reason.retriable, op)\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : 'Apply failed'\n\t\t\tconst code =\n\t\t\t\terror instanceof SyncError\n\t\t\t\t\t? error.code\n\t\t\t\t\t: error instanceof KoraError\n\t\t\t\t\t\t? error.code\n\t\t\t\t\t\t: error instanceof Error && 'code' in error && typeof error.code === 'string'\n\t\t\t\t\t\t\t? error.code\n\t\t\t\t\t\t\t: APPLY_FAILURE_CODES.APPLY_FAILED\n\t\t\tif (error instanceof ClockDriftError) {\n\t\t\t\t// This device's own clock is broken: a local condition, not the operation's\n\t\t\t\t// fault. Stall (blocking) so it is retried once the clock is fixed.\n\t\t\t\tthis.emitApplyFailure(\n\t\t\t\t\ttransformed,\n\t\t\t\t\t'rejected',\n\t\t\t\t\t{ code: APPLY_FAILURE_CODES.CLOCK_DRIFT, message: error.message, retriable: false },\n\t\t\t\t\t'blocking',\n\t\t\t\t)\n\t\t\t\treturn { kind: 'stall', operation: op }\n\t\t\t}\n\t\t\tif (error instanceof RemoteClockDriftError || error instanceof InvalidTimestampError) {\n\t\t\t\treturn quarantine(transformed, code, message, 'rejected', true, op)\n\t\t\t}\n\t\t\tif (code === FOLD_STATE_INVALID_CODE) {\n\t\t\t\t// The record's fold state cannot take this operation even after the store\n\t\t\t\t// re-folded it once (RT-63; for example a carried state of an unknown plan).\n\t\t\t\t// Not transient: quarantined (and replayed on start, after an upgrade)\n\t\t\t\t// rather than stalling every later operation of the delivery stream.\n\t\t\t\treturn quarantine(transformed, code, message, 'rejected', false, op)\n\t\t\t}\n\t\t\tif (code === APPLY_FAILURE_CODES.REFERENTIAL_INTEGRITY) {\n\t\t\t\t// Not appliable now (for example a child whose parent is outside this view);\n\t\t\t\t// kept and replayed rather than stalling every later operation.\n\t\t\t\treturn quarantine(transformed, code, message, 'rejected', false, op)\n\t\t\t}\n\t\t\t// Anything else may be transient (a busy database): the store did not take\n\t\t\t// custody, so stall; the server re-sends it.\n\t\t\tthis.emitApplyFailure(transformed, 'rejected', { code, message, retriable: true }, 'blocking')\n\t\t\treturn { kind: 'stall', operation: op }\n\t\t}\n\t}\n\n\t/**\n\t * The delivery stream caught up: let the store retire provisional cascades of\n\t * remote deletes (RT-69) and settle row-snapshot records after a full resync\n\t * (RT-68). A failure is reported, never fatal: both are retried on the next\n\t * catch-up.\n\t */\n\tprivate async settleAfterCatchUp(options?: { provisionalOnly?: boolean }): Promise<void> {\n\t\tif (!this.store.settleAfterCatchUp) return\n\t\ttry {\n\t\t\tawait this.store.settleAfterCatchUp(options)\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: '',\n\t\t\t\tmessage: `Settling provisional effects and row snapshots after catch-up failed: ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`,\n\t\t\t\tcode: 'SETTLE_AFTER_CATCH_UP_FAILED',\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Whether a not-applied operation can be recorded so the watermark may pass it: with\n\t * a durable quarantine, or when nothing is durable (no persisted watermark either, so\n\t * a restart re-delivers everything anyway).\n\t */\n\tprivate canQuarantine(): boolean {\n\t\tif (!this.syncState) return true\n\t\tif (this.syncState.saveQuarantine) return true\n\t\treturn !this.syncState.saveDeliveryWatermark\n\t}\n\n\t/**\n\t * Record quarantined operations and (for a delivery batch) the watermark advance in\n\t * one durable step.\n\t */\n\tprivate async commitDeliveryProgress(\n\t\tquarantined: QuarantinedOperation[],\n\t\twatermark: number | null,\n\t): Promise<void> {\n\t\tconst signature = this.deliverySignature()\n\t\tif (quarantined.length > 0 && this.syncState?.saveQuarantine) {\n\t\t\tawait this.syncState.saveQuarantine(\n\t\t\t\tquarantined,\n\t\t\t\twatermark === null ? undefined : { signature, watermark },\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tfor (const entry of quarantined) {\n\t\t\tthis.memoryQuarantine.set(entry.operation.id, entry)\n\t\t}\n\t\tif (watermark !== null) {\n\t\t\tawait this.persistDeliveryWatermark(watermark, signature)\n\t\t}\n\t}\n\n\t/** Every inbound operation held in quarantine (durable when persistence supports it). */\n\tasync getQuarantinedOperations(): Promise<QuarantinedOperation[]> {\n\t\tif (this.syncState?.loadQuarantine) {\n\t\t\treturn this.syncState.loadQuarantine()\n\t\t}\n\t\treturn [...this.memoryQuarantine.values()]\n\t}\n\n\t/**\n\t * Try to apply every quarantined operation again, oldest delivery first and in HLC\n\t * order within a delivery. Runs on start (after a schema upgrade the unknown\n\t * collection now exists; after time catches up a far-future op is acceptable) and on\n\t * demand. Applied operations leave the quarantine; the rest stay.\n\t *\n\t * @returns How many operations left the quarantine.\n\t */\n\tasync retryQuarantinedOperations(): Promise<number> {\n\t\treturn this.replayQuarantine()\n\t}\n\n\tprivate async replayQuarantine(): Promise<number> {\n\t\tconst entries = await this.getQuarantinedOperations()\n\t\tif (entries.length === 0) return 0\n\t\tentries.sort(\n\t\t\t(a, b) =>\n\t\t\t\t(a.deliverySequence ?? 0) - (b.deliverySequence ?? 0) ||\n\t\t\t\tHybridLogicalClock.compare(a.operation.timestamp, b.operation.timestamp),\n\t\t)\n\t\tconst released: string[] = []\n\t\tfor (const entry of entries) {\n\t\t\tlet outcome: Awaited<ReturnType<SyncEngine['applyInboundQuietly']>>\n\t\t\ttry {\n\t\t\t\toutcome = await this.applyInboundQuietly(entry.operation)\n\t\t\t} catch {\n\t\t\t\toutcome = false\n\t\t\t}\n\t\t\tif (outcome) {\n\t\t\t\treleased.push(entry.operation.id)\n\t\t\t\tthis.emitter?.emit({\n\t\t\t\t\ttype: 'sync:apply-recovered',\n\t\t\t\t\tfailure: {\n\t\t\t\t\t\toperationId: entry.operation.id,\n\t\t\t\t\t\tcollection: entry.operation.collection,\n\t\t\t\t\t\trecordId: entry.operation.recordId,\n\t\t\t\t\t\tcode: entry.code,\n\t\t\t\t\t\tmessage: entry.message,\n\t\t\t\t\t\tretriable: true,\n\t\t\t\t\t\tfirstSeenAt: entry.quarantinedAt,\n\t\t\t\t\t\tretryCount: 0,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tif (released.length === 0) return 0\n\t\tif (this.syncState?.removeQuarantine) {\n\t\t\tawait this.syncState.removeQuarantine(released)\n\t\t}\n\t\tfor (const id of released) this.memoryQuarantine.delete(id)\n\t\treturn released.length\n\t}\n\n\t/**\n\t * Under end-to-end encryption, a plaintext operation authored by the server (a node\n\t * the handshake named authoritative: cascades, set-nulls, constraint corrections,\n\t * route writes) that touches only the collection's cleartext fields is accepted\n\t * without an envelope: the server holds no key, so it cannot seal its own writes, and\n\t * such an operation carries nothing the server cannot already read. Any sealed field\n\t * (or an operation from any other node) still has to arrive sealed.\n\t */\n\tprivate isServerAuthoredCleartext(op: Operation): boolean {\n\t\tconst encryptor = this.activeEncryptor()\n\t\tif (!encryptor || op.encrypted !== undefined) return false\n\t\t// A server node: `kora:server:<id>` by prefix, or a legacy id the handshake listed.\n\t\tif (!isAuthoritativeNodeId(op.nodeId, new Set(this.authoritativeNodeIds ?? []))) return false\n\t\treturn encryptor.isCleartextOnly(op)\n\t}\n\n\t/** Whether end-to-end encryption is configured (a keyring or a fixed encryptor). */\n\tprivate encryptionConfigured(): boolean {\n\t\treturn this.keyring !== null || this.encryptor !== null\n\t}\n\n\t/** The encryptor in force: the unlocked keyring's, or the fixed one; null when locked. */\n\tprivate activeEncryptor(): SyncEncryptor | null {\n\t\treturn this.keyring ? this.keyring.getEncryptor() : this.encryptor\n\t}\n\n\t/**\n\t * The encryptor in force. With encryption configured and the keyring locked this\n\t * throws: an operation is never sent or applied unencrypted because a key is missing.\n\t */\n\tprivate requireEncryptor(): SyncEncryptor {\n\t\tconst encryptor = this.activeEncryptor()\n\t\tif (!encryptor) {\n\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t'End-to-end encryption is locked: unlock it with the passphrase before syncing.',\n\t\t\t\t{ code: 'ENCRYPTION_LOCKED' },\n\t\t\t)\n\t\t}\n\t\treturn encryptor\n\t}\n\n\t/** The key service over this session (the handshake has been accepted). */\n\tprivate liveKeyServiceChannel(): KeyServiceChannel {\n\t\treturn {\n\t\t\tfetch: (keyring) => this.keyServiceRequest({ kind: 'fetch', keyring }),\n\t\t\tput: (keyring, record, expectedRevision) =>\n\t\t\t\tthis.keyServiceRequest({ kind: 'put', keyring, record, expectedRevision }),\n\t\t}\n\t}\n\n\t/** Re-apply a quarantined operation; true when it no longer needs to be kept. */\n\tprivate async applyInboundQuietly(stored: Operation): Promise<boolean> {\n\t\tlet op = stored\n\t\tif (this.encryptionConfigured() && !this.isServerAuthoredCleartext(stored)) {\n\t\t\ttry {\n\t\t\t\top = await this.requireEncryptor().decryptOperation(stored)\n\t\t\t} catch {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\tif (!(await verifyInboundOperation(op, { encrypted: stored.encrypted !== undefined })).ok) {\n\t\t\treturn false\n\t\t}\n\t\tconst reference = Date.now() + (this.clockSkewMs ?? 0)\n\t\tif (op.timestamp.wallTime > reference + MAX_REMOTE_FUTURE_MS) return false\n\t\top = await canonicalizeProvenLegacyClear(op)\n\t\tconst targetSchemaVersion = this.config.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\ttry {\n\t\t\tif (operationSchemaView(op, targetSchemaVersion, this.operationTransforms) === null) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t\tconst result = await this.store.applyRemoteOperation(op)\n\t\tif (result === 'applied' || result === 'duplicate') return true\n\t\treturn result === 'skipped' && this.store.hasCollection?.(op.collection) === true\n\t}\n\n\t/** Acknowledge a delivery batch (used for duplicates) without re-applying it. */\n\tprivate sendDeliveryAck(acknowledgedMessageId: string, deliverySequence: number): void {\n\t\tconst ack: SyncMessage = {\n\t\t\ttype: 'acknowledgment',\n\t\t\tmessageId: generateMessageId(),\n\t\t\tacknowledgedMessageId,\n\t\t\tlastSequenceNumber: 0,\n\t\t\tdeliverySequence,\n\t\t}\n\t\tthis.transport.send(ack)\n\t}\n\n\t/**\n\t * An acknowledgment resolves exactly the upload batch it names (SYNC-4): operations\n\t * the server processed (sequence <= `lastSequenceNumber`) leave the queue and count\n\t * toward the acknowledged prefix; the rest return to the queue for retry. An ack that\n\t * names no tracked batch (a node-token confirmation echo, a batch from an older\n\t * session) resolves nothing.\n\t */\n\tprivate async handleAcknowledgment(msg: AcknowledgmentMessage): Promise<void> {\n\t\tif (this.state === 'syncing' && this.config.strictHandshake) {\n\t\t\tthis.pendingDeltaBatchAcks.delete(msg.acknowledgedMessageId)\n\t\t\tthis.markDeltaSendCompleteIfReady()\n\t\t}\n\n\t\tconst batchId = this.uploadByMessageId.get(msg.acknowledgedMessageId)\n\t\tconst entry = batchId === undefined ? undefined : this.inFlightUploads.get(batchId)\n\t\tif (!entry || batchId === undefined) {\n\t\t\treturn\n\t\t}\n\t\tthis.uploadByMessageId.delete(msg.acknowledgedMessageId)\n\t\tthis.inFlightUploads.delete(batchId)\n\t\tif (this.inFlightUploads.size === 0) {\n\t\t\tthis.clearOutboundAckTimer()\n\t\t} else {\n\t\t\tthis.startOutboundAckTimer()\n\t\t}\n\n\t\tconst { acknowledged, returned } = await this.outboundQueue.acknowledgeThrough(\n\t\t\tbatchId,\n\t\t\tmsg.lastSequenceNumber,\n\t\t)\n\t\tif (acknowledged.length > 0) {\n\t\t\tconst now = Date.now()\n\t\t\tthis.lastSyncedAt = now\n\t\t\tthis.lastSuccessfulPush = now\n\t\t}\n\t\tawait this.withOwnTracking(async () => {\n\t\t\tfor (const op of acknowledged) this.markOwnResolved(op)\n\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t})\n\t\tif (acknowledged.length > 0) {\n\t\t\tthis.sessionAcked += acknowledged.length\n\t\t\tawait this.recordUploadProgress(this.currentNodeId())\n\t\t}\n\t\tif (returned.length > 0 && this.currentNodeId() !== this.store.getNodeId()) {\n\t\t\t// The server did not process part of an adopted node's batch (a retriable\n\t\t\t// rejection): retrying it in this session would hold every other node's\n\t\t\t// uploads behind it (RT-46). The session ends and the node is parked.\n\t\t\tthis.adoptionStalled = true\n\t\t}\n\t\tawait this.refreshPendingCount()\n\t\tif (await this.maybeEndSessionForNodeWork()) return\n\n\t\t// Continue flushing if more ops in queue\n\t\tconst backoff = entry.retryBackoff || returned.length > 0\n\t\tif (this.state === 'streaming' && this.hasUploadable()) {\n\t\t\tif (backoff) {\n\t\t\t\tthis.scheduleOutboundRetry()\n\t\t\t} else {\n\t\t\t\tthis.outboundRetryAttempt = 0\n\t\t\t\tthis.flushQueue()\n\t\t\t}\n\t\t} else if (!backoff) {\n\t\t\tthis.outboundRetryAttempt = 0\n\t\t}\n\t\tthis.notifyStatusChange()\n\t}\n\n\tprivate handleError(msg: { code: string; message: string; retriable: boolean }): void {\n\t\tif (msg.code === 'INVALID_TIMESTAMP') {\n\t\t\t// The server refused the batch at its first future-stamped operation and stored\n\t\t\t// none of the future-stamped ones, so a clock rebase may re-stamp them.\n\t\t\tconst limit = Date.now() + (this.clockSkewMs ?? 0) + 60_000\n\t\t\tconst refused = this.outboundQueue\n\t\t\t\t.getInFlight()\n\t\t\t\t.filter((op) => op.timestamp.wallTime > limit)\n\t\t\t\t.map((op) => op.id)\n\t\t\tthis.outboundQueue.clearSent(refused)\n\t\t}\n\t\t// In-flight outbound batches will never be acknowledged now. Return them to the\n\t\t// queue so they are retried, instead of wedging every future flush behind a batch\n\t\t// that can never clear.\n\t\tthis.sessionEpoch++\n\t\tthis.returnAllInFlightUploads()\n\t\t// The server usually closes the socket right after an error, so the close may\n\t\t// already have moved the engine to disconnected. The error still carries\n\t\t// meaning (auth rejection, clock block, credential refresh): record it either way.\n\t\tconst live = this.state !== 'disconnected'\n\t\tif (live) {\n\t\t\tthis.transitionTo('error')\n\t\t}\n\t\tif (isCredentialEndingCode(msg.code)) {\n\t\t\t// The server ended this session because its credential expired or was\n\t\t\t// revoked (AUTH-11). Not fatal and not a sign-out: refresh, then reconnect.\n\t\t\t// A truly revoked device is refused at the next handshake (AUTH_FAILED), or\n\t\t\t// its refresh is rejected and the auth client signs it out.\n\t\t\tthis.credentialRefreshRequired = true\n\t\t}\n\t\tif (msg.code === 'NODE_ID_CLAIMED' && !this.nodeRotation) {\n\t\t\t// Another device holds this node id (for example the node token issued at the\n\t\t\t// first claim was lost). Move to a fresh node id with every unsynced write;\n\t\t\t// the next connect uses it (RT-21).\n\t\t\tthis.nodeRotation = this.rotateNodeIdentity().finally(() => {\n\t\t\t\tthis.nodeRotation = null\n\t\t\t})\n\t\t}\n\t\tif (msg.code === 'AUTH_FAILED' || msg.code === 'DEVICE_REVOKED') {\n\t\t\tthis.authRejected = true\n\t\t\tthis.suspensionReason = msg.code === 'DEVICE_REVOKED' ? 'device-revoked' : 'auth-rejected'\n\t\t\tthis.emitter?.emit({ type: 'sync:auth-failed', reason: msg.message })\n\t\t\tthis.emitter?.emit({ type: 'sync:suspended', reason: this.suspensionReason })\n\t\t}\n\t\tif (msg.code === 'INVALID_TIMESTAMP') {\n\t\t\t// The server refused an operation stamped too far in the future: this\n\t\t\t// device's clock is (or was) fast. Block sync so the queue is preserved\n\t\t\t// and the app can tell the user to fix the clock.\n\t\t\tthis.clockBlocked = true\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:clock-skew',\n\t\t\t\tskewMs: this.clockSkewMs ?? Number.NaN,\n\t\t\t\tseverity: 'fast-blocked',\n\t\t\t\tsource: 'server-reject',\n\t\t\t})\n\t\t\tif (live) {\n\t\t\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason: msg.message })\n\t\t\t}\n\t\t\t// Stay in 'error' (clock-error status) until the server closes the session or\n\t\t\t// the app clears the block; the block, not the socket, is what stops sync.\n\t\t\treturn\n\t\t}\n\t\tif (live) {\n\t\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason: msg.message })\n\t\t\tthis.transitionTo('disconnected')\n\t\t\t// The engine ended this session: release the socket too (SYNC-5).\n\t\t\tvoid this.closeTransportQuietly()\n\t\t}\n\t}\n\n\t/**\n\t * Handle a per-operation rejection: divert the op out of the pending outbound\n\t * queue (so it is never retried or resurrected on reconnect), record it in the\n\t * durable rejected store (so it is kept and explainable), and emit an event so\n\t * the app can reconcile. Unlike {@link handleError}, this is a normal per-op\n\t * signal, so the connection stays up.\n\t */\n\tprivate async applyScopeRetraction(\n\t\tretraction: { collection: string; recordId: string },\n\t\tapplyToStore: boolean,\n\t): Promise<void> {\n\t\tif (applyToStore) {\n\t\t\tif (!this.store.applyScopeRetraction) {\n\t\t\t\tthrow new Error('The configured sync store does not support scope retractions')\n\t\t\t}\n\t\t\tawait this.store.applyScopeRetraction(retraction.collection, retraction.recordId)\n\t\t}\n\t\tconst quarantined = await this.outboundQueue.rejectRecord(\n\t\t\tretraction.collection,\n\t\t\tretraction.recordId,\n\t\t)\n\t\tawait this.recordTerminalRejections(quarantined, 'SCOPE_RETRACTED')\n\t\tfor (const operation of quarantined) {\n\t\t\tawait this.rejectedStorage.record({\n\t\t\t\toperationId: operation.id,\n\t\t\t\tcollection: operation.collection,\n\t\t\t\trecordId: operation.recordId,\n\t\t\t\tcode: 'SCOPE_RETRACTED',\n\t\t\t\tmessage: 'Authorization changed before this operation could be uploaded.',\n\t\t\t\tretriable: false,\n\t\t\t\trejectedAt: Date.now(),\n\t\t\t})\n\t\t}\n\t\tif (quarantined.length > 0) {\n\t\t\tawait this.withOwnTracking(async () => {\n\t\t\t\tfor (const operation of quarantined) this.markOwnResolved(operation)\n\t\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t\t})\n\t\t}\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:scope-retracted',\n\t\t\tcollection: retraction.collection,\n\t\t\trecordId: retraction.recordId,\n\t\t\tquarantinedOperationIds: quarantined.map((operation) => operation.id),\n\t\t})\n\t}\n\n\t/**\n\t * The local nodes whose unsynced writes are held now (RT-38, RT-50), for the user the\n\t * app reports as signed in. See {@link SyncStatusInfo.heldNodes}.\n\t */\n\tasync getHeldNodes(): Promise<HeldNodeInfo[]> {\n\t\tconst nodes = (await this.syncState?.listLocalNodes?.()) ?? []\n\t\tconst principal = this.config.principal ? await this.resolvePrincipal() : this.principal\n\t\tconst storeNode = this.store.getNodeId()\n\t\tconst held: HeldNodeInfo[] = []\n\t\tfor (const node of nodes) {\n\t\t\tif (node.nodeId === storeNode) continue\n\t\t\tconst reason = this.holdReason(node, principal)\n\t\t\tif (!reason) continue\n\t\t\tconst unsynced = await this.countUnsyncedOfNode(node.nodeId)\n\t\t\tif (unsynced.count === 0) continue\n\t\t\theld.push({\n\t\t\t\tnodeId: node.nodeId,\n\t\t\t\toperationCount: unsynced.count,\n\t\t\t\treason,\n\t\t\t\tprincipal: node.principal ?? null,\n\t\t\t})\n\t\t}\n\t\treturn held\n\t}\n\n\t/**\n\t * Assign the `unassigned` held writes of `nodeId` to the signed-in user (RT-50): writes\n\t * made on this device before the app knew who was signed in, on a node that never\n\t * synced. Only the app can know whose they are (for example, a single-user device).\n\t * They upload on the user's next session (the server then binds the node to them).\n\t *\n\t * @throws {SyncError} `HELD_ASSIGN_NO_USER` when nobody is signed in;\n\t * `HELD_NODE_NOT_ASSIGNABLE` when `nodeId` is not an unassigned held node (it belongs\n\t * to a user, or the server refused it for this one)\n\t */\n\tasync assignHeld(nodeId: string): Promise<void> {\n\t\tconst principal = await this.resolvePrincipal()\n\t\tif (typeof principal !== 'string') {\n\t\t\tthrow new SyncError('Held writes can only be assigned to a signed-in user.', {\n\t\t\t\tcode: 'HELD_ASSIGN_NO_USER',\n\t\t\t\tnodeId,\n\t\t\t\tfix: 'Call assignHeld after the user signed in.',\n\t\t\t})\n\t\t}\n\t\tconst node = (await this.syncState?.listLocalNodes?.())?.find(\n\t\t\t(entry) => entry.nodeId === nodeId,\n\t\t)\n\t\tconst assigned =\n\t\t\tnode !== undefined &&\n\t\t\tnodeId !== this.store.getNodeId() &&\n\t\t\tthis.holdReason(node, principal) === 'unassigned' &&\n\t\t\t((await this.syncState?.assignLocalNodePrincipal?.(nodeId, principal)) ?? false)\n\t\tif (!assigned) {\n\t\t\tthrow new SyncError(\n\t\t\t\t`Node \"${nodeId}\" holds no unassigned writes: only writes nobody can attribute can be assigned.`,\n\t\t\t\t{\n\t\t\t\t\tcode: 'HELD_NODE_NOT_ASSIGNABLE',\n\t\t\t\t\tnodeId,\n\t\t\t\t\tfix: 'Pick a node from status.heldNodes whose reason is \"unassigned\".',\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t\tthis.emitter?.emit({ type: 'sync:local-node', nodeId, action: 'held-assigned' })\n\t\tthis.heldNodeInfos = this.heldNodeInfos.filter((entry) => entry.nodeId !== nodeId)\n\t\tthis.notifyStatusChange()\n\t}\n\n\t/**\n\t * Discard the `unassigned` held writes of `nodeId` from sync (RT-50): they are never\n\t * uploaded. They are NOT rolled back: they stay in this device's local database, and\n\t * count as synced from now on (`deleteDatabase` no longer protects them). Writes held\n\t * for another user (`other-user`) cannot be discarded by this user.\n\t *\n\t * @returns How many writes were discarded\n\t * @throws {SyncError} `HELD_NODE_NOT_DISCARDABLE` when `nodeId` is not an unassigned\n\t * held node\n\t */\n\tasync discardHeld(nodeId: string): Promise<number> {\n\t\tconst principal = this.config.principal ? await this.resolvePrincipal() : this.principal\n\t\tconst node = (await this.syncState?.listLocalNodes?.())?.find(\n\t\t\t(entry) => entry.nodeId === nodeId,\n\t\t)\n\t\tif (\n\t\t\t!node ||\n\t\t\tnodeId === this.store.getNodeId() ||\n\t\t\tthis.holdReason(node, principal) !== 'unassigned'\n\t\t) {\n\t\t\tthrow new SyncError(\n\t\t\t\t`Node \"${nodeId}\" holds no unassigned writes: only writes nobody can attribute can be discarded.`,\n\t\t\t\t{\n\t\t\t\t\tcode: 'HELD_NODE_NOT_DISCARDABLE',\n\t\t\t\t\tnodeId,\n\t\t\t\t\tfix: 'Pick a node from status.heldNodes whose reason is \"unassigned\".',\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t\tconst unsynced = await this.countUnsyncedOfNode(nodeId)\n\t\tconst localSeq = this.store.getVersionVector().get(nodeId) ?? 0\n\t\tconst ops = (await this.store.getOperationRange(nodeId, 1, localSeq)).filter((op) =>\n\t\t\tunsynced.ids.has(op.id),\n\t\t)\n\t\tawait this.recordTerminalRejections(ops, HELD_DISCARDED_CODE)\n\t\tawait this.outboundQueue.removeByIds(ops.map((op) => op.id))\n\t\tawait this.syncState?.dropLocalNode?.(nodeId)\n\t\tthis.heldNodeInfos = this.heldNodeInfos.filter((entry) => entry.nodeId !== nodeId)\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:local-node',\n\t\t\tnodeId,\n\t\t\taction: 'held-discarded',\n\t\t\toperationCount: ops.length,\n\t\t})\n\t\tawait this.refreshPendingCount()\n\t\tthis.notifyStatusChange()\n\t\treturn ops.length\n\t}\n\n\tprivate async handleOperationRejected(msg: OperationRejectedMessage): Promise<void> {\n\t\tif (msg.retriable) {\n\t\t\t// The op stays in its batch; the batch's ack stops short of it and returns it\n\t\t\t// to the queue. Back off before retrying that batch's leftovers.\n\t\t\tfor (const entry of this.inFlightUploads.values()) {\n\t\t\t\tif (entry.batch.operations.some((op) => op.id === msg.operationId)) {\n\t\t\t\t\tentry.retryBackoff = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait this.refreshPendingCount()\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:operation-rejected',\n\t\t\t\toperationId: msg.operationId,\n\t\t\t\tcollection: msg.collection,\n\t\t\t\trecordId: msg.recordId,\n\t\t\t\tcode: msg.code,\n\t\t\t\tmessage: msg.message,\n\t\t\t\tretriable: true,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tif (msg.code === SEQUENCE_CONFLICT && (await this.recoverSequenceConflict(msg.operationId))) {\n\t\t\treturn\n\t\t}\n\n\t\t// The durable marker comes first (RT-36): once the prefix passes this op, only the\n\t\t// marker stops a later rescan of the device's history from submitting it again.\n\t\tconst refusedOp = this.findQueuedOperation(msg.operationId)\n\t\tif (refusedOp && !NON_TERMINAL_REJECTION_CODES.has(msg.code)) {\n\t\t\tawait this.recordTerminalRejections([refusedOp], msg.code)\n\t\t}\n\t\tconst removed = await this.outboundQueue.reject(msg.operationId)\n\n\t\tawait this.rejectedStorage.record({\n\t\t\toperationId: msg.operationId,\n\t\t\tcollection: msg.collection,\n\t\t\trecordId: msg.recordId,\n\t\t\tcode: msg.code,\n\t\t\tmessage: msg.message,\n\t\t\tretriable: msg.retriable,\n\t\t\trejectedAt: Date.now(),\n\t\t})\n\t\t// Terminally rejected AND recorded: resolved for the acknowledged prefix.\n\t\tif (removed) {\n\t\t\tawait this.withOwnTracking(async () => {\n\t\t\t\tthis.markOwnResolved(removed)\n\t\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t\t})\n\t\t}\n\n\t\t// The rejected op left the pending set, so the app-visible pending count\n\t\t// must be refreshed or it would over-count forever.\n\t\tawait this.refreshPendingCount()\n\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:operation-rejected',\n\t\t\toperationId: msg.operationId,\n\t\t\tcollection: msg.collection,\n\t\t\trecordId: msg.recordId,\n\t\t\tcode: msg.code,\n\t\t\tmessage: msg.message,\n\t\t\tretriable: msg.retriable,\n\t\t})\n\t}\n\n\t/**\n\t * Every operation the server rejected that has not yet been reconciled. The app\n\t * uses this (alongside the `sync:operation-rejected` event) to surface failed\n\t * submissions and decide whether to roll back or resubmit.\n\t */\n\tgetRejectedOperations(): Promise<RejectedOperation[]> {\n\t\treturn this.rejectedStorage.list()\n\t}\n\n\t/**\n\t * Forget rejected operations by id once the app has reconciled them (rolled the\n\t * optimistic write back or resubmitted a corrected op).\n\t */\n\tclearRejectedOperations(operationIds: string[]): Promise<void> {\n\t\treturn this.rejectedStorage.remove(operationIds)\n\t}\n\n\tprivate async checkDeltaComplete(): Promise<void> {\n\t\tif (!this.deltaSendComplete || !this.deltaReceiveComplete) {\n\t\t\treturn\n\t\t}\n\n\t\t// Idempotent: multiple final delta batches can race during handshake.\n\t\tif (this.state !== 'syncing') {\n\t\t\treturn\n\t\t}\n\n\t\tthis.lastSyncedAt = Date.now()\n\t\tthis.transitionTo('streaming')\n\n\t\t// Start awareness cleanup timer now that we're streaming\n\t\tthis.awarenessManager.startCleanupTimer()\n\n\t\t// Re-broadcast local awareness state to the new connection\n\t\tconst localState = this.awarenessManager.getLocalState()\n\t\tif (localState) {\n\t\t\tthis.awarenessManager.setLocalState(localState)\n\t\t}\n\n\t\t// Flush any queued operations accumulated during delta exchange. Delta batches\n\t\t// still awaiting their acks stay in flight: only their acks resolve them. A\n\t\t// stalled adoption is not retried in this session (RT-46).\n\t\tif (this.hasUploadable() && !this.adoptionStalled) {\n\t\t\tthis.flushQueue()\n\t\t}\n\n\t\tawait this.refreshPendingCount()\n\t\tthis.notifyStatusChange()\n\t\tif (await this.maybeEndSessionForNodeWork()) return\n\t\tthis.armAdoptionRetryTimer()\n\t}\n\n\t/**\n\t * In a session of this tab's own node, arm a timer for the earliest parked adoption's\n\t * backoff expiry (RT-53). Without it a parked node is retried only at a session start\n\t * or after this tab's own uploads made progress, so an idle tab (a dashboard, a kiosk)\n\t * would never retry a closed tab's deferred writes while its connection holds.\n\t */\n\tprivate armAdoptionRetryTimer(): void {\n\t\tthis.clearAdoptionRetryTimer()\n\t\tif (this.destroyed || this.state !== 'streaming') return\n\t\tif (this.currentNodeId() !== this.store.getNodeId()) return\n\t\tlet earliest = Number.POSITIVE_INFINITY\n\t\tfor (const deferred of this.deferredAdoptions) {\n\t\t\tif (deferred.reason === 'parked') earliest = Math.min(earliest, deferred.untilMs)\n\t\t}\n\t\tif (!Number.isFinite(earliest)) return\n\t\tconst epoch = this.sessionEpoch\n\t\tthis.adoptionRetryTimer = setTimeout(\n\t\t\t() => {\n\t\t\t\tthis.adoptionRetryTimer = null\n\t\t\t\tvoid this.onAdoptionRetryTimer(epoch)\n\t\t\t},\n\t\t\tMath.max(0, earliest - Date.now()),\n\t\t)\n\t}\n\n\t/**\n\t * A parked adoption's backoff ran out during this session (RT-53). An idle own-node\n\t * session hands over now: it reconnects, and the next session adopts the node (one\n\t * retry per backoff; a node deferred again is parked again for longer). A busy session\n\t * hands over when its uploads drain (maybeYieldToDeferredNode sees the expiry).\n\t */\n\tprivate async onAdoptionRetryTimer(epoch: number): Promise<void> {\n\t\tif (epoch !== this.sessionEpoch || this.destroyed) return\n\t\tif (this.state !== 'streaming' || this.currentNodeId() !== this.store.getNodeId()) return\n\t\tif (this.inFlightUploads.size > 0 || this.hasUploadable()) return\n\t\tthis.deferredAdoptions = []\n\t\ttry {\n\t\t\tawait this.reconnect()\n\t\t} catch {\n\t\t\t// The new session failed to start: the engine is disconnected, and the app's\n\t\t\t// reconnection loop (or the next sync) takes over, adopting the node then.\n\t\t}\n\t}\n\n\t// --- Own-operation upload tracking (W3: contiguous acknowledged prefix) ---\n\n\t/** Serialize every read-modify-write of the own-operation tracking. */\n\tprivate withOwnTracking<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst run = this.ownTrackingChain.then(fn)\n\t\tthis.ownTrackingChain = run.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t)\n\t\treturn run\n\t}\n\n\tprivate async loadOwnTracking(): Promise<void> {\n\t\tawait this.withOwnTracking(() => this.loadOwnTrackingLocked())\n\t}\n\n\t/**\n\t * Load the acknowledged prefix for the current node id, once per node id (an engine\n\t * restart keeps its in-memory progress). A persistence layer that has never stored a\n\t * prefix under this contract means an upgrade from a release that persisted a MAX:\n\t * that value can hide a lost op below it, so the prefix restarts at 0 and the device\n\t * re-uploads its own history once, chunk by chunk (the server dedups by id). The\n\t * prefix is persisted as it advances, so the re-upload resumes where it stopped.\n\t */\n\tprivate async loadOwnTrackingLocked(): Promise<void> {\n\t\tconst nodeId = this.currentNodeId()\n\t\tif (this.ownTrackingNodeId === nodeId) return\n\t\tlet prefix = 0\n\t\tif (this.syncState?.loadOwnAckedThrough) {\n\t\t\tconst stored = await this.syncState.loadOwnAckedThrough(nodeId)\n\t\t\tif (stored === null) {\n\t\t\t\tawait this.syncState.saveOwnAckedThrough?.(nodeId, 0)\n\t\t\t} else {\n\t\t\t\tprefix = stored\n\t\t\t}\n\t\t} else if (this.syncState) {\n\t\t\tprefix = this.lastAckedServerVector.get(nodeId) ?? 0\n\t\t}\n\t\tthis.ownTrackingNodeId = nodeId\n\t\tthis.ownAckedThrough = prefix\n\t\tthis.ownScannedThrough = prefix\n\t\tthis.ownUnresolved.clear()\n\t\tthis.ownResolvedAhead.clear()\n\t}\n\n\t/**\n\t * Register an own operation. `unresolved` ops are queued for upload; the others are\n\t * already resolved (not upload-eligible). An op at or below the prefix (a sequence\n\t * that was a gap when the prefix passed it) lowers the prefix: re-uploading is safe,\n\t * skipping is not.\n\t */\n\tprivate trackOwnOperation(op: Operation, unresolved: boolean): void {\n\t\tif (op.nodeId !== this.ownTrackingNodeId) return\n\t\tconst seq = op.sequenceNumber\n\t\tif (seq <= this.ownAckedThrough) {\n\t\t\tif (!unresolved) return\n\t\t\tthis.ownAckedThrough = seq - 1\n\t\t\tthis.ownScannedThrough = Math.min(this.ownScannedThrough, seq - 1)\n\t\t\tvoid this.persistOwnPrefix()\n\t\t}\n\t\tconst ids = this.ownUnresolved.get(seq) ?? new Set<string>()\n\t\tif (unresolved) ids.add(op.id)\n\t\tthis.ownUnresolved.set(seq, ids)\n\t}\n\n\t/** Mark an own operation resolved (stored, terminally rejected, or not eligible). */\n\tprivate markOwnResolved(op: Operation): void {\n\t\tif (op.nodeId !== this.ownTrackingNodeId || op.sequenceNumber <= this.ownAckedThrough) return\n\t\tconst ids = this.ownUnresolved.get(op.sequenceNumber)\n\t\tif (ids) {\n\t\t\tids.delete(op.id)\n\t\t} else if (op.sequenceNumber <= this.ownScannedThrough) {\n\t\t\tthis.ownUnresolved.set(op.sequenceNumber, new Set())\n\t\t}\n\t\tif (op.sequenceNumber > this.ownScannedThrough) {\n\t\t\t// The scan has not reached this sequence: remember the id so the scan does not\n\t\t\t// take the op for an unsent one and upload it again.\n\t\t\tthis.ownResolvedAhead.set(op.id, op.sequenceNumber)\n\t\t}\n\t}\n\n\tprivate isOwnOperationResolved(op: Operation): boolean {\n\t\tif (op.sequenceNumber <= this.ownAckedThrough) return true\n\t\tconst ids = this.ownUnresolved.get(op.sequenceNumber)\n\t\treturn ids !== undefined && !ids.has(op.id) && !this.outboundQueue.has(op.id)\n\t}\n\n\t/**\n\t * Lower the prefix (the server holds fewer of this device's operations than it\n\t * believed) and forget what this session knew above it: the next advance rescans the\n\t * log from there and re-enqueues what is missing.\n\t */\n\tprivate async lowerOwnPrefixLocked(sequence: number): Promise<void> {\n\t\tconst target = Math.max(0, sequence)\n\t\tif (target >= this.ownAckedThrough) return\n\t\tthis.ownAckedThrough = target\n\t\tthis.ownScannedThrough = target\n\t\tthis.ownUnresolved.clear()\n\t\tthis.ownResolvedAhead.clear()\n\t\tawait this.persistOwnPrefix()\n\t}\n\n\t/**\n\t * Advance the prefix over every resolved own sequence, reading the next chunk of the\n\t * op log whenever it reaches a sequence this session has not examined yet. Reading a\n\t * chunk enqueues its upload-eligible operations (and flushes when streaming), so the\n\t * one-time upgrade re-upload proceeds chunk by chunk as acks arrive.\n\t */\n\tprivate async advanceOwnPrefixLocked(): Promise<void> {\n\t\tif (this.ownTrackingNodeId !== this.currentNodeId()) return\n\t\tconst localSeq = this.store.getVersionVector().get(this.ownTrackingNodeId) ?? 0\n\t\tconst start = this.ownAckedThrough\n\t\tlet scanned = false\n\t\tfor (;;) {\n\t\t\tconst next = this.ownAckedThrough + 1\n\t\t\tif (next > localSeq && !this.ownUnresolved.has(next)) break\n\t\t\tconst ids = this.ownUnresolved.get(next)\n\t\t\tif (ids === undefined) {\n\t\t\t\tif (next > this.ownScannedThrough) {\n\t\t\t\t\tawait this.scanOwnLog(next, Math.min(localSeq, next + OWN_LOG_SCAN_CHUNK - 1))\n\t\t\t\t\tscanned = true\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// Examined, yet nothing registered: a hole in the log (a compacted or never\n\t\t\t\t// written sequence). Nothing there can be uploaded.\n\t\t\t\tthis.ownUnresolved.set(next, new Set())\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (ids.size > 0) break\n\t\t\tthis.ownUnresolved.delete(next)\n\t\t\tthis.ownAckedThrough = next\n\t\t\t// Everything at or below the prefix is settled, so it counts as examined.\n\t\t\tif (next > this.ownScannedThrough) this.ownScannedThrough = next\n\t\t}\n\t\tif (this.ownAckedThrough !== start) {\n\t\t\tfor (const [id, seq] of this.ownResolvedAhead) {\n\t\t\t\tif (seq <= this.ownScannedThrough) this.ownResolvedAhead.delete(id)\n\t\t\t}\n\t\t\tawait this.persistOwnPrefix()\n\t\t}\n\t\tif (scanned && this.state === 'streaming' && this.hasUploadable()) {\n\t\t\tthis.flushQueue()\n\t\t}\n\t}\n\n\t/**\n\t * Read own operations [from, to] from the op log and register each sequence: an\n\t * upload-eligible op not yet queued is enqueued; an ineligible one is recorded and\n\t * resolved; a sequence with no op is resolved. Every sequence in the range ends up\n\t * registered, which guarantees the advance loop makes progress.\n\t */\n\tprivate async scanOwnLog(from: number, to: number): Promise<void> {\n\t\tconst nodeId = this.ownTrackingNodeId\n\t\tif (nodeId === null || to < from) {\n\t\t\tthis.ownScannedThrough = Math.max(this.ownScannedThrough, to)\n\t\t\treturn\n\t\t}\n\t\tconst ops = await this.store.getOperationRange(nodeId, from, to)\n\t\tconst bySeq = new Map<number, Operation[]>()\n\t\tfor (const op of ops) {\n\t\t\tif (op.nodeId !== nodeId || op.sequenceNumber < from || op.sequenceNumber > to) continue\n\t\t\tconst list = bySeq.get(op.sequenceNumber) ?? []\n\t\t\tlist.push(op)\n\t\t\tbySeq.set(op.sequenceNumber, list)\n\t\t}\n\t\t// Operations the server refused for good stay refused (RT-36): they are resolved,\n\t\t// never enqueued again, whatever today's state would make of them.\n\t\tconst terminal = await this.findTerminalRejections(\n\t\t\tops.filter((op) => !this.outboundQueue.has(op.id)).map((op) => op.id),\n\t\t)\n\t\tconst eligible: Operation[] = []\n\t\tfor (let seq = from; seq <= to; seq++) {\n\t\t\tconst ids = this.ownUnresolved.get(seq) ?? new Set<string>()\n\t\t\tthis.ownUnresolved.set(seq, ids)\n\t\t\tfor (const op of bySeq.get(seq) ?? []) {\n\t\t\t\tif (this.ownResolvedAhead.delete(op.id)) continue\n\t\t\t\tif (ids.has(op.id)) continue\n\t\t\t\tif (terminal.has(op.id)) continue\n\t\t\t\tif (this.outboundQueue.has(op.id)) {\n\t\t\t\t\tids.add(op.id)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (await this.operationAllowedForUpload(op)) {\n\t\t\t\t\teligible.push(op)\n\t\t\t\t\tids.add(op.id)\n\t\t\t\t} else {\n\t\t\t\t\tawait this.recordOutOfUplinkScope(op)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// One causal re-sort for the whole chunk, not one per operation.\n\t\tawait this.outboundQueue.enqueueMany(eligible)\n\t\tthis.ownScannedThrough = Math.max(this.ownScannedThrough, to)\n\t}\n\n\tprivate async persistOwnPrefix(): Promise<void> {\n\t\tconst nodeId = this.ownTrackingNodeId\n\t\tif (!this.syncState || nodeId === null) return\n\t\tconst prefix = this.ownAckedThrough\n\t\tawait this.syncState.saveOwnAckedThrough?.(nodeId, prefix)\n\t\t// Mirror the prefix (exactly, never a max) into the acked server vector, which\n\t\t// compaction reads to decide which own operations the server already holds.\n\t\tconst vector = new Map(this.lastAckedServerVector)\n\t\tvector.set(nodeId, prefix)\n\t\tthis.lastAckedServerVector = vector\n\t\tawait this.syncState.saveLastAckedServerVector(vector)\n\t}\n\n\tprivate async persistLastAckedServerVector(vector: VersionVector): Promise<void> {\n\t\tif (!this.syncState) {\n\t\t\treturn\n\t\t}\n\t\t// Other nodes' entries record what the server advertised; a local node's entry is\n\t\t// only ever its acknowledged prefix (persistOwnPrefix), never an advertised value:\n\t\t// compaction reads it, and an advertised max can hide an unsynced op below it.\n\t\tconst nodeId = this.currentNodeId()\n\t\tconst others = new Map(vector)\n\t\tothers.delete(nodeId)\n\t\tfor (const local of this.localNodeIds) others.delete(local)\n\t\tconst merged = this.syncState.mergeServerVectors(this.lastAckedServerVector, others)\n\t\tif (this.ownTrackingNodeId === nodeId) merged.set(nodeId, this.ownAckedThrough)\n\t\tthis.lastAckedServerVector = merged\n\t\tawait this.syncState.saveLastAckedServerVector(merged)\n\t}\n\n\t/**\n\t * Refresh the pending count: own operations not yet stored on the server (queued or\n\t * in flight), plus the own operations above the prefix not read from the log yet.\n\t * Computed from the client's own acknowledgments only, so it drops the moment the\n\t * server acknowledges, without waiting for a later handshake (RT-28).\n\t */\n\tasync refreshPendingCount(): Promise<void> {\n\t\tconst previous = this.cachedUnsyncedCount\n\t\tthis.cachedUnsyncedCount = this.computePendingCount()\n\t\tif (this.cachedUnsyncedCount !== previous) this.notifyStatusChange()\n\t}\n\n\t/** Synchronous pending count (see {@link refreshPendingCount}). */\n\tprivate computePendingCount(): number {\n\t\tlet unscanned = 0\n\t\tconst nodeId = this.ownTrackingNodeId\n\t\tif (nodeId !== null) {\n\t\t\tconst localSeq = this.store.getVersionVector().get(nodeId) ?? 0\n\t\t\tunscanned = Math.max(0, localSeq - this.ownScannedThrough)\n\t\t\tfor (const seq of this.ownUnresolved.keys()) {\n\t\t\t\tif (seq > this.ownScannedThrough && seq <= localSeq) unscanned--\n\t\t\t}\n\t\t\tunscanned = Math.max(0, unscanned)\n\t\t}\n\t\t// Queued operations of held nodes are reported separately (heldOperations, RT-38).\n\t\tconst heldQueued =\n\t\t\tthis.heldNodeIds.size === 0\n\t\t\t\t? 0\n\t\t\t\t: this.outboundQueue.countMatching((op) => this.heldNodeIds.has(op.nodeId))\n\t\treturn this.outboundQueue.totalPending - heldQueued + unscanned + this.otherNodesPending\n\t}\n\n\t/** Unsynced operations held for a principal that is not signed in (RT-38). */\n\tprivate computeHeldCount(): number {\n\t\tif (this.heldNodeIds.size === 0) return 0\n\t\treturn (\n\t\t\tthis.heldPending + this.outboundQueue.countMatching((op) => this.heldNodeIds.has(op.nodeId))\n\t\t)\n\t}\n\n\t/**\n\t * This device's operations not yet stored on the server (queued or in flight).\n\t */\n\tasync getPendingSyncOperations(): Promise<Operation[]> {\n\t\treturn [\n\t\t\t...this.outboundQueue.getInFlight(),\n\t\t\t...this.outboundQueue.peek(Number.MAX_SAFE_INTEGER),\n\t\t]\n\t}\n\n\t/**\n\t * Rebuild the upload set from the op log: every own operation above the acknowledged\n\t * prefix that is upload-eligible is queued (first chunk now, the rest as acks move\n\t * the prefix). Persisted queue entries that are already resolved are dropped.\n\t */\n\tprivate async reconcileOutboundFromOpLog(): Promise<void> {\n\t\tawait this.withOwnTracking(async () => {\n\t\t\tconst nodeId = this.ownTrackingNodeId\n\t\t\tconst stale = this.outboundQueue\n\t\t\t\t.getAll()\n\t\t\t\t.filter((op) => op.nodeId === nodeId && op.sequenceNumber <= this.ownAckedThrough)\n\t\t\t\t.map((op) => op.id)\n\t\t\tif (stale.length > 0) {\n\t\t\t\tawait this.outboundQueue.removeByIds(stale)\n\t\t\t}\n\t\t\tfor (const op of this.outboundQueue.getAll()) {\n\t\t\t\tthis.trackOwnOperation(op, true)\n\t\t\t}\n\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t})\n\t}\n\n\t// --- Local nodes, terminal rejections and own-history recovery (Phase 2) ---\n\n\t/** Node id of the current session: the store's own, or an adopted node's (RT-40). */\n\tprivate currentNodeId(): string {\n\t\treturn this.sessionNodeId ?? this.store.getNodeId()\n\t}\n\n\t/** A session uploads only operations authored under its own node id (RT-38, RT-40). */\n\tprivate readonly isSessionOperation = (op: Operation): boolean =>\n\t\top.nodeId === this.currentNodeId()\n\n\t/** Whether the queue holds an operation this session may upload. */\n\tprivate hasUploadable(): boolean {\n\t\treturn this.outboundQueue.hasOperationsMatching(this.isSessionOperation)\n\t}\n\n\t/** A queued or in-flight operation by id. */\n\tprivate findQueuedOperation(operationId: string): Operation | undefined {\n\t\treturn (\n\t\t\tthis.outboundQueue.getInFlight().find((op) => op.id === operationId) ??\n\t\t\tthis.outboundQueue.getAll().find((op) => op.id === operationId)\n\t\t)\n\t}\n\n\t/**\n\t * Pick the node this session runs as (RT-40, RT-46). A session uploads under one node\n\t * id (the server confines uploads to the session's node), so the database's local\n\t * nodes take turns, each for one session:\n\t *\n\t * - A local node other than the store's own that still has unsynced writes, is not\n\t * held for another principal (RT-38) or bound to another user (RT-42), was not\n\t * refused in this refusal cycle, and is used by no live tab (its lock is free) may\n\t * be adopted: the session runs as that node and uploads its writes with their\n\t * original identity, then ends; the next session picks again.\n\t * - Never one whose writes depend causally on another local node's unsynced writes:\n\t * the parent's node goes first (a child is never uploaded before its parent).\n\t * - Never a parked one (its last adoption made no progress, for example the server\n\t * kept deferring a write) until something else uploaded or its backoff ran out, so\n\t * one stuck node never blocks the others or this tab's own writes.\n\t * - Otherwise the session runs as the store's own node; it yields to a deferred node\n\t * once its own uploads made that node worth retrying (see maybeEndSessionForNodeWork).\n\t *\n\t * Also counts the other local nodes' unsynced writes, so they are reported (pending,\n\t * or held).\n\t */\n\tprivate async chooseSessionNode(): Promise<void> {\n\t\tthis.endAdoption()\n\t\tconst storeNode = this.store.getNodeId()\n\t\tthis.sessionNodeId = storeNode\n\t\tthis.otherNodesPending = 0\n\t\tthis.heldPending = 0\n\t\tthis.heldNodeIds = new Set()\n\t\tthis.heldNodeInfos = []\n\t\tthis.localNodeIds = new Set([storeNode])\n\t\tthis.deferredAdoptions = []\n\t\tconst syncState = this.syncState\n\t\tif (!syncState?.listLocalNodes) return\n\t\tlet nodes: LocalNodeInfo[]\n\t\tlet cycle = 0\n\t\ttry {\n\t\t\tnodes = await syncState.listLocalNodes()\n\t\t\tcycle = (await syncState.loadAcceptedCycle?.()) ?? 0\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Reading the local node registry failed',\n\t\t\t\tcode: 'NODE_REGISTRY_FAILED',\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tconst schedule = await this.loadSchedule()\n\t\tconst now = Date.now()\n\t\tfor (const node of nodes) this.localNodeIds.add(node.nodeId)\n\n\t\tinterface Candidate {\n\t\t\tnodeId: string\n\t\t\tunsynced: UnsyncedOfNode\n\t\t}\n\t\tconst candidates: Candidate[] = []\n\t\tfor (const node of nodes) {\n\t\t\tif (node.nodeId === storeNode) continue\n\t\t\t// Held for its principal (RT-38), bound to another user (RT-42), refused for\n\t\t\t// this one or never attributed (RT-50): it is never uploaded on this session.\n\t\t\tconst heldReason = this.holdReason(node)\n\t\t\tif (heldReason) this.heldNodeIds.add(node.nodeId)\n\t\t\tconst unsynced = await this.countUnsyncedOfNode(node.nodeId)\n\t\t\tif (unsynced.count === 0) {\n\t\t\t\tif (!node.held) await this.forgetDrainedNode(node.nodeId)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (heldReason) {\n\t\t\t\tthis.heldPending += unsynced.notQueued\n\t\t\t\tthis.heldNodeInfos.push({\n\t\t\t\t\tnodeId: node.nodeId,\n\t\t\t\t\toperationCount: unsynced.count,\n\t\t\t\t\treason: heldReason,\n\t\t\t\t\tprincipal: node.principal ?? null,\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst refusedThisCycle = node.refusedCycle !== null && node.refusedCycle >= cycle\n\t\t\tif (refusedThisCycle || !this.store.claimLocalNode) {\n\t\t\t\tthis.otherNodesPending += unsynced.notQueued\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcandidates.push({ nodeId: node.nodeId, unsynced })\n\t\t}\n\t\tif (candidates.length === 0) return\n\n\t\t// Unsynced operation ids per uploadable node, to order adoptions by causality.\n\t\tconst own = await this.countUnsyncedOfNode(storeNode)\n\t\tconst pendingIds = new Map<string, ReadonlySet<string>>([[storeNode, own.ids]])\n\t\tfor (const candidate of candidates) pendingIds.set(candidate.nodeId, candidate.unsynced.ids)\n\t\tconst dependsOnAnotherNode = (candidate: Candidate): boolean => {\n\t\t\tfor (const dep of candidate.unsynced.deps) {\n\t\t\t\tfor (const [nodeId, ids] of pendingIds) {\n\t\t\t\t\tif (nodeId !== candidate.nodeId && ids.has(dep)) return true\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\n\t\tconst eligible: Candidate[] = []\n\t\tconst causallyDeferred: Candidate[] = []\n\t\tfor (const candidate of candidates) {\n\t\t\tconst parked = schedule.parked[candidate.nodeId]\n\t\t\tif (parked && schedule.progress <= parked.progressMark && now < parked.untilMs) {\n\t\t\t\tthis.deferredAdoptions.push({ nodeId: candidate.nodeId, reason: 'parked', ...parked })\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (dependsOnAnotherNode(candidate)) {\n\t\t\t\tcausallyDeferred.push(candidate)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\teligible.push(candidate)\n\t\t}\n\t\tif (eligible.length === 0 && causallyDeferred.length > 0) {\n\t\t\t// Every candidate waits for another one: a cycle among them (or the parent is\n\t\t\t// parked). Break it in registry order unless one waits for this tab's own\n\t\t\t// writes, which this session uploads first.\n\t\t\tconst breaker = causallyDeferred.find(\n\t\t\t\t(candidate) => ![...candidate.unsynced.deps].some((dep) => own.ids.has(dep)),\n\t\t\t)\n\t\t\tif (breaker) {\n\t\t\t\tcausallyDeferred.splice(causallyDeferred.indexOf(breaker), 1)\n\t\t\t\teligible.push(breaker)\n\t\t\t}\n\t\t}\n\t\tfor (const candidate of causallyDeferred) {\n\t\t\tthis.deferredAdoptions.push({\n\t\t\t\tnodeId: candidate.nodeId,\n\t\t\t\treason: 'causal',\n\t\t\t\tparkedAtMs: now,\n\t\t\t\tuntilMs: now,\n\t\t\t\tprogressMark: schedule.progress,\n\t\t\t})\n\t\t}\n\n\t\tlet adopted: Candidate | null = null\n\t\tfor (const candidate of eligible) {\n\t\t\tconst release = await this.store.claimLocalNode?.(candidate.nodeId)\n\t\t\tif (!release) continue\n\t\t\tadopted = candidate\n\t\t\tthis.releaseAdoption = release\n\t\t\tthis.sessionNodeId = candidate.nodeId\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:local-node',\n\t\t\t\tnodeId: candidate.nodeId,\n\t\t\t\taction: 'adoption-started',\n\t\t\t\toperationCount: candidate.unsynced.count,\n\t\t\t})\n\t\t\tbreak\n\t\t}\n\t\tfor (const candidate of candidates) {\n\t\t\tif (candidate !== adopted) this.otherNodesPending += candidate.unsynced.notQueued\n\t\t}\n\t\tif (adopted) {\n\t\t\t// While adopting, the store's own unsynced tail is not tracked by this session.\n\t\t\tthis.otherNodesPending += own.notQueued\n\t\t}\n\t}\n\n\t/**\n\t * Why a local node other than the store's own must not upload on this session, or\n\t * null when it may (RT-38, RT-42, RT-50):\n\t *\n\t * - Bound to another user than the signed-in one (or refused after acceptance,\n\t * RT-38): `other-user`.\n\t * - Unbound (its owner was never recorded, RT-50) while a user is signed in: its\n\t * owner is learned from the server, never guessed. If it was accepted before, the\n\t * server holds a claim on it: it is tried (adopted) on this user's session unless\n\t * the server already refused it for them, and an accepted handshake binds it to\n\t * them. If it never synced nobody can tell whose its writes are: `unassigned`,\n\t * held until the app assigns or discards them.\n\t */\n\tprivate holdReason(\n\t\tnode: LocalNodeInfo,\n\t\tprincipal: string | null | undefined = this.principal,\n\t): HeldReason | null {\n\t\tconst owner = node.principal ?? null\n\t\tif (this.config.principal && typeof principal === 'string') {\n\t\t\tif (owner !== null) return owner !== principal || node.held ? 'other-user' : null\n\t\t\tif (!node.accepted) return 'unassigned'\n\t\t\tif ((node.refusedPrincipals ?? []).includes(principal)) return 'other-user'\n\t\t\treturn null\n\t\t}\n\t\tif (node.held) return 'other-user'\n\t\tif (this.config.principal && principal === null && owner !== null) return 'other-user'\n\t\treturn null\n\t}\n\n\t/** A node bound to another user than the signed-in one (RT-42). */\n\tprivate belongsToAnotherPrincipal(node: LocalNodeInfo): boolean {\n\t\tif (!this.config.principal || this.principal === undefined) return false\n\t\tconst owner = node.principal ?? null\n\t\treturn owner !== null && owner !== this.principal\n\t}\n\n\t/**\n\t * A local node's own operations above its acknowledged prefix that are not\n\t * terminally rejected: `count` all of them, `notQueued` those not in the queue, their\n\t * `ids`, and the causal parents they name outside this set (`deps`).\n\t */\n\tprivate async countUnsyncedOfNode(nodeId: string): Promise<UnsyncedOfNode> {\n\t\tconst localSeq = this.store.getVersionVector().get(nodeId) ?? 0\n\t\tlet prefix = 0\n\t\tif (this.syncState?.loadOwnAckedThrough) {\n\t\t\tprefix = (await this.syncState.loadOwnAckedThrough(nodeId)) ?? 0\n\t\t} else {\n\t\t\tprefix = this.lastAckedServerVector.get(nodeId) ?? 0\n\t\t}\n\t\tconst empty: UnsyncedOfNode = { count: 0, notQueued: 0, ids: new Set(), deps: new Set() }\n\t\tif (localSeq <= prefix) return empty\n\t\tconst ops = (await this.store.getOperationRange(nodeId, prefix + 1, localSeq)).filter(\n\t\t\t(op) => op.nodeId === nodeId && op.sequenceNumber > prefix,\n\t\t)\n\t\tconst terminal = await this.findTerminalRejections(ops.map((op) => op.id))\n\t\tconst unsynced = ops.filter((op) => !terminal.has(op.id))\n\t\tconst ids = new Set(unsynced.map((op) => op.id))\n\t\tconst deps = new Set<string>()\n\t\tfor (const op of unsynced) {\n\t\t\tfor (const dep of op.causalDeps ?? []) if (!ids.has(dep)) deps.add(dep)\n\t\t}\n\t\treturn {\n\t\t\tcount: unsynced.length,\n\t\t\tnotQueued: unsynced.filter((op) => !this.outboundQueue.has(op.id)).length,\n\t\t\tids,\n\t\t\tdeps,\n\t\t}\n\t}\n\n\t/** The adoption schedule (RT-46), from persistence when it keeps one. */\n\tprivate async loadSchedule(): Promise<AdoptionScheduleInfo> {\n\t\tlet schedule = this.memorySchedule\n\t\tif (this.syncState?.loadAdoptionSchedule) {\n\t\t\ttry {\n\t\t\t\tschedule = await this.syncState.loadAdoptionSchedule()\n\t\t\t} catch {\n\t\t\t\t// Unreadable: the in-memory copy decides (at worst an early retry).\n\t\t\t}\n\t\t}\n\t\tthis.scheduleHasParked = Object.keys(schedule.parked).length > 0\n\t\treturn { progress: schedule.progress, parked: { ...schedule.parked } }\n\t}\n\n\tprivate async saveSchedule(schedule: AdoptionScheduleInfo): Promise<void> {\n\t\tthis.memorySchedule = schedule\n\t\tthis.scheduleHasParked = Object.keys(schedule.parked).length > 0\n\t\ttry {\n\t\t\tawait this.syncState?.saveAdoptionSchedule?.(schedule)\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Saving the adoption schedule failed',\n\t\t\t\tcode: 'ADOPTION_SCHEDULE_FAILED',\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Operations of `nodeId` were stored (RT-46): a parked adoption may have waited for\n\t * them, so the progress counter moves; the node itself is no longer parked.\n\t */\n\tprivate async recordUploadProgress(nodeId: string): Promise<void> {\n\t\tif (!this.scheduleHasParked) return\n\t\tconst schedule = await this.loadSchedule()\n\t\tschedule.progress += 1\n\t\tdelete schedule.parked[nodeId]\n\t\tawait this.saveSchedule(schedule)\n\t}\n\n\t/** Park the adopted node: it made no progress in this session (RT-46). */\n\tprivate async parkAdoption(nodeId: string): Promise<void> {\n\t\tconst schedule = await this.loadSchedule()\n\t\tconst now = Date.now()\n\t\tconst count = (schedule.parked[nodeId]?.count ?? 0) + 1\n\t\tconst backoff = Math.min(ADOPTION_PARK_MAX_MS, ADOPTION_PARK_BASE_MS * 2 ** (count - 1))\n\t\tschedule.parked[nodeId] = {\n\t\t\tprogressMark: schedule.progress,\n\t\t\tuntilMs: now + backoff,\n\t\t\tcount,\n\t\t\tparkedAtMs: now,\n\t\t}\n\t\tawait this.saveSchedule(schedule)\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:local-node',\n\t\t\tnodeId,\n\t\t\taction: 'adoption-parked',\n\t\t\toperationCount: (await this.countUnsyncedOfNode(nodeId)).count,\n\t\t})\n\t}\n\n\t/** Forget a drained, non-held local node no live tab uses (bounds the registry). */\n\tprivate async forgetDrainedNode(nodeId: string): Promise<void> {\n\t\tif (!this.syncState?.forgetLocalNode || !this.store.claimLocalNode) return\n\t\tconst release = await this.store.claimLocalNode(nodeId)\n\t\tif (!release) return\n\t\ttry {\n\t\t\tawait this.syncState.forgetLocalNode(nodeId)\n\t\t\tthis.localNodeIds.delete(nodeId)\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\t/** Release the lock on an adopted node (RT-40). */\n\tprivate endAdoption(): void {\n\t\tconst release = this.releaseAdoption\n\t\tthis.releaseAdoption = null\n\t\trelease?.()\n\t}\n\n\t/**\n\t * Record an accepted handshake as `nodeId` (RT-38), durably when supported. The server\n\t * accepted it for the session's user, so an unbound node is now theirs (RT-50).\n\t */\n\tprivate async recordNodeAccepted(nodeId: string): Promise<void> {\n\t\tthis.memoryAcceptedNodes.add(nodeId)\n\t\tthis.heldNodeIds.delete(nodeId)\n\t\ttry {\n\t\t\tawait this.syncState?.markLocalNodeAccepted?.(nodeId)\n\t\t\tconst principal = this.sessionPrincipal\n\t\t\tif (this.config.principal && typeof principal === 'string') {\n\t\t\t\tawait this.syncState?.confirmLocalNodePrincipal?.(nodeId, principal)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Recording the accepted node failed',\n\t\t\t\tcode: 'NODE_REGISTRY_FAILED',\n\t\t\t})\n\t\t}\n\t}\n\n\t/** Which of these operation ids the server refused for good (RT-36). */\n\tprivate async findTerminalRejections(operationIds: string[]): Promise<Set<string>> {\n\t\tconst found = new Set<string>()\n\t\tconst unknown: string[] = []\n\t\tfor (const id of operationIds) {\n\t\t\tif (this.terminalRejected.has(id)) found.add(id)\n\t\t\telse unknown.push(id)\n\t\t}\n\t\tif (unknown.length > 0 && this.syncState?.findTerminalRejections) {\n\t\t\tfor (const id of await this.syncState.findTerminalRejections(unknown)) {\n\t\t\t\tfound.add(id)\n\t\t\t\tthis.terminalRejected.add(id)\n\t\t\t}\n\t\t}\n\t\treturn found\n\t}\n\n\t/** Record durable terminal-rejection markers (RT-36). */\n\tprivate async recordTerminalRejections(operations: Operation[], code: string): Promise<void> {\n\t\tif (operations.length === 0 || NON_TERMINAL_REJECTION_CODES.has(code)) return\n\t\tfor (const op of operations) this.terminalRejected.add(op.id)\n\t\tconst rejectedAt = Date.now()\n\t\tawait this.syncState?.recordTerminalRejections?.(\n\t\t\toperations.map((op) => ({\n\t\t\t\toperationId: op.id,\n\t\t\t\tnodeId: op.nodeId,\n\t\t\t\tsequenceNumber: op.sequenceNumber,\n\t\t\t\tcode,\n\t\t\t\trejectedAt,\n\t\t\t})),\n\t\t)\n\t}\n\n\t/**\n\t * Restart server->client delivery from 0 at the next session, for the requested and the\n\t * accepted views. A full resync includes this client's own operations (a resumed one\n\t * does not), which is how operations the device lost come back (RT-35).\n\t */\n\tprivate async resetDeliveryForFullResync(): Promise<void> {\n\t\tconst signatures = new Set([\n\t\t\tthis.deliverySignature(),\n\t\t\tthis.deliverySignatureFor(this.config.scopeMap),\n\t\t])\n\t\tif (this.lastAcceptedScope) signatures.add(this.deliverySignatureFor(this.lastAcceptedScope))\n\t\tthis.deliveryWatermark = 0\n\t\tfor (const signature of signatures) {\n\t\t\tthis.setViewWatermark(signature, 0)\n\t\t\tawait this.persistDeliveryWatermark(0, signature)\n\t\t}\n\t}\n\n\t/**\n\t * The server refused an upload with `SEQUENCE_CONFLICT`: it holds another operation of\n\t * this node under that number, one this device lost (RT-35: a reload inside the\n\t * IndexedDB snapshot window, a restored older copy). Not a refusal of the write: it\n\t * gets a fresh number above everything the server holds (same id and content) and is\n\t * uploaded again, and the session ends once its uploads are resolved so the next one\n\t * resyncs from 0 and fetches the lost operation back.\n\t *\n\t * @returns Whether the conflict was recovered (false: treat as a plain rejection)\n\t */\n\tprivate async recoverSequenceConflict(operationId: string): Promise<boolean> {\n\t\tconst node = this.currentNodeId()\n\t\tconst op = this.findQueuedOperation(operationId)\n\t\tif (\n\t\t\top &&\n\t\t\top.nodeId === node &&\n\t\t\tnode === this.store.getNodeId() &&\n\t\t\top.sequenceNumber > this.handshakeOwnEntry &&\n\t\t\tthis.store.rotateNodeId\n\t\t) {\n\t\t\treturn this.recoverFromClone(op)\n\t\t}\n\t\tconst resequence = this.store.resequenceOperation?.bind(this.store)\n\t\tif (!op || op.nodeId !== node || !resequence) return false\n\t\tconst floor = Math.max(this.remoteVector.get(node) ?? 0, op.sequenceNumber)\n\t\t// A version-2 op is re-hashed under a new id; queued operations of this node that\n\t\t// were never put on the wire (the server cannot hold them) and name it in their\n\t\t// causalDeps are rewritten with it (seam 4).\n\t\tconst unsentDependents = this.outboundQueue\n\t\t\t.getAll()\n\t\t\t.filter(\n\t\t\t\t(queued) =>\n\t\t\t\t\tqueued.nodeId === node && queued.id !== op.id && !this.outboundQueue.wasSent(queued.id),\n\t\t\t)\n\t\t\t.map((queued) => queued.id)\n\t\tconst resequenced = await resequence(op.id, node, floor, unsentDependents)\n\t\tconst renumbered = resequenced?.operation ?? null\n\t\tawait this.withOwnTracking(async () => {\n\t\t\t// Out of its batch, so the batch's ack cannot resolve it under the old number.\n\t\t\tawait this.outboundQueue.reject(op.id)\n\t\t\t// The old number belongs to the server's operation.\n\t\t\tthis.markOwnResolved(op)\n\t\t\tif (renumbered) {\n\t\t\t\tawait this.outboundQueue.enqueue(renumbered)\n\t\t\t\tthis.trackOwnOperation(renumbered, true)\n\t\t\t}\n\t\t\tif (resequenced && resequenced.dependents.length > 0) {\n\t\t\t\tconst previousId = new Map<string, string>()\n\t\t\t\tfor (const [oldId, newId] of Object.entries(resequenced.idMapping)) {\n\t\t\t\t\tpreviousId.set(newId, oldId)\n\t\t\t\t}\n\t\t\t\tconst oldIds = resequenced.dependents.map((dep) => previousId.get(dep.id) ?? dep.id)\n\t\t\t\tawait this.outboundQueue.replace(oldIds, resequenced.dependents)\n\t\t\t\t// Same sequence, new id: the tracked id moves with it, or the prefix would\n\t\t\t\t// wait forever for the old one.\n\t\t\t\tfor (const dependent of resequenced.dependents) {\n\t\t\t\t\tconst oldId = previousId.get(dependent.id)\n\t\t\t\t\tif (oldId) this.ownUnresolved.get(dependent.sequenceNumber)?.delete(oldId)\n\t\t\t\t\tthis.trackOwnOperation(dependent, true)\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t})\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:local-node',\n\t\t\tnodeId: node,\n\t\t\taction: 'history-behind',\n\t\t\tlocalSequence: op.sequenceNumber,\n\t\t\tserverSequence: floor,\n\t\t})\n\t\t// A full resync downloads the whole dataset again: at most one per interval\n\t\t// (RT-44, a clone this store cannot move away from keeps colliding). A later\n\t\t// one is deferred to the first session start after the interval.\n\t\tif (Date.now() - this.lastFullResyncAt >= FULL_RESYNC_MIN_INTERVAL_MS) {\n\t\t\tthis.lastFullResyncAt = Date.now()\n\t\t\tawait this.resetDeliveryForFullResync()\n\t\t\tthis.ownRecoveryPending = true\n\t\t} else {\n\t\t\tthis.fullResyncDeferred = true\n\t\t}\n\t\tawait this.refreshPendingCount()\n\t\tawait this.maybeEndSessionForNodeWork()\n\t\treturn true\n\t}\n\n\t/**\n\t * A SEQUENCE_CONFLICT above this session's handshake entry (RT-44): the server held\n\t * nothing of this node above that entry when the session began, so the operation now\n\t * holding the number was stored during this session, and not by this session. Another\n\t * live copy of this database uses the same node id (app data copied to a new machine,\n\t * a restored image used alongside the original). Renumbering would only collide again,\n\t * and each copy's writes count as the other's own operations (never delivered on a\n\t * resumed stream), so this copy moves to a fresh node id: the refused write (refused,\n\t * so certainly not stored) and every never-sent one are re-authored under it, the\n\t * session ends, and the next one resyncs from 0 to fetch what the other copy wrote.\n\t */\n\tprivate async recoverFromClone(op: Operation): Promise<boolean> {\n\t\tconst rotate = this.store.rotateNodeId?.bind(this.store)\n\t\tif (!rotate) return false\n\t\tconst cloned = op.nodeId\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:local-node',\n\t\t\tnodeId: cloned,\n\t\t\taction: 'clone-detected',\n\t\t\tlocalSequence: op.sequenceNumber,\n\t\t\tserverSequence: this.handshakeOwnEntry,\n\t\t})\n\t\t// The session belongs to the cloned node: end it before the identity moves, so\n\t\t// nothing of the new node is ever uploaded on it.\n\t\tthis.abandonSession(\n\t\t\t'Another live copy of this database uses the same node id; moving this copy to a new one',\n\t\t)\n\t\tthis.outboundQueue.clearSent([op.id])\n\t\ttry {\n\t\t\tconst staying = await this.rotateUnsent(cloned, rotate)\n\t\t\tawait this.resetDeliveryForFullResync()\n\t\t\tif (staying === 0) {\n\t\t\t\t// Nothing of this copy waits under the cloned node: stop tracking it, so\n\t\t\t\t// the other copy's writes under it are never taken for unsynced ones.\n\t\t\t\tawait this.syncState?.forgetLocalNode?.(cloned)\n\t\t\t\tthis.localNodeIds.delete(cloned)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Moving to a new node id failed',\n\t\t\t\tcode: 'NODE_ROTATION_FAILED',\n\t\t\t})\n\t\t}\n\t\tawait this.refreshPendingCount()\n\t\treturn true\n\t}\n\n\t/**\n\t * End the session when it has done its node work and nothing is in flight:\n\t * - an own-history recovery is pending (RT-35: reconnect to resync from 0);\n\t * - an adopted node's writes are all stored (RT-40: reconnect as the next node, or\n\t * this tab's own);\n\t * - an adopted node stalled (RT-46: the server deferred part of it): it is parked and\n\t * the next session serves another node;\n\t * - this tab's own uploads are drained, made progress, and a deferred node is now\n\t * worth retrying (RT-46: its parent was among them, or its park has expired).\n\t *\n\t * @returns Whether the session was ended\n\t */\n\tprivate async maybeEndSessionForNodeWork(): Promise<boolean> {\n\t\tif (this.state !== 'streaming' && this.state !== 'syncing') return false\n\t\tif (this.inFlightUploads.size > 0) return false\n\t\tif (this.ownRecoveryPending) {\n\t\t\tthis.ownRecoveryPending = false\n\t\t\tthis.abandonSession('Resyncing operations of this device that its local database lost')\n\t\t\treturn true\n\t\t}\n\t\tconst node = this.currentNodeId()\n\t\tif (node === this.store.getNodeId()) return this.maybeYieldToDeferredNode()\n\t\tif (this.state !== 'streaming') return false\n\t\tif (this.adoptionStalled) {\n\t\t\tthis.adoptionStalled = false\n\t\t\tconst epoch = this.sessionEpoch\n\t\t\tawait this.parkAdoption(node)\n\t\t\tif (epoch !== this.sessionEpoch || this.currentNodeId() !== node) return true\n\t\t\tthis.endAdoption()\n\t\t\tthis.abandonSession('An adopted node made no upload progress; serving the other nodes first')\n\t\t\treturn true\n\t\t}\n\t\tif (this.hasUploadable()) return false\n\t\tconst localSeq = this.store.getVersionVector().get(node) ?? 0\n\t\tif (this.ownTrackingNodeId !== node || this.ownAckedThrough < localSeq) return false\n\t\tthis.emitter?.emit({ type: 'sync:local-node', nodeId: node, action: 'adoption-completed' })\n\t\tthis.endAdoption()\n\t\tthis.abandonSession('Uploaded the unsynced writes of a closed tab; reconnecting')\n\t\treturn true\n\t}\n\n\t/**\n\t * This tab's own session: once its uploads are drained and it stored something, end\n\t * it when a node deferred at session start is worth retrying now (RT-46), so other\n\t * nodes' writes do not wait for an unrelated reconnect. Bounded: each yield needs new\n\t * own progress, and a parked node at most once per {@link ADOPTION_YIELD_MIN_MS}.\n\t */\n\tprivate async maybeYieldToDeferredNode(): Promise<boolean> {\n\t\tif (this.state !== 'streaming' || this.deferredAdoptions.length === 0) return false\n\t\tif (this.hasUploadable()) return false\n\t\tconst now = Date.now()\n\t\tconst schedule = await this.loadSchedule()\n\t\t// A park whose backoff ran out is due whatever this session did (RT-53: one retry\n\t\t// per backoff). Anything else waits for this tab's own progress (RT-46).\n\t\tconst progressed = this.sessionAcked > 0\n\t\tconst ready = this.deferredAdoptions.some((deferred) =>\n\t\t\tdeferred.reason === 'causal'\n\t\t\t\t? progressed\n\t\t\t\t: now >= deferred.untilMs ||\n\t\t\t\t\t(progressed &&\n\t\t\t\t\t\tschedule.progress > deferred.progressMark &&\n\t\t\t\t\t\tnow - deferred.parkedAtMs >= ADOPTION_YIELD_MIN_MS),\n\t\t)\n\t\tif (!ready || this.state !== 'streaming' || this.inFlightUploads.size > 0) return false\n\t\tthis.deferredAdoptions = []\n\t\tthis.abandonSession(\n\t\t\t\"Uploaded this tab's writes; serving another local node that waited for them\",\n\t\t)\n\t\treturn true\n\t}\n\n\tprivate flushQueue(): void {\n\t\tif (this.state !== 'streaming') return\n\t\tif (this.inFlightUploads.size > 0) return // one streaming batch in flight at a time\n\t\tif (this.outboundRetryTimer) return // Transient rejection backoff is active\n\t\tif (!this.hasUploadable()) return\n\n\t\tconst entry = this.takeUpload()\n\t\tif (!entry) return\n\t\tvoid this.sendUpload(entry, { isFinal: true, batchIndex: 0 }).catch(() => {\n\t\t\tif (this.inFlightUploads.get(entry.batch.batchId) === entry) this.returnUpload(entry)\n\t\t})\n\t}\n\n\tprivate startOutboundAckTimer(): void {\n\t\tthis.clearOutboundAckTimer()\n\t\tif (this.outboundAckTimeoutMs <= 0 || this.destroyed) return\n\t\tconst epoch = this.sessionEpoch\n\t\tthis.outboundAckTimer = setTimeout(() => {\n\t\t\tvoid this.handleOutboundAckTimeout(epoch)\n\t\t}, this.outboundAckTimeoutMs)\n\t}\n\n\tprivate clearOutboundAckTimer(): void {\n\t\tif (!this.outboundAckTimer) return\n\t\tclearTimeout(this.outboundAckTimer)\n\t\tthis.outboundAckTimer = null\n\t}\n\n\tprivate scheduleOutboundRetry(): void {\n\t\tif (this.outboundRetryTimer || !this.hasUploadable() || this.destroyed) return\n\t\tconst delay = Math.min(\n\t\t\tthis.outboundRetryMaxDelayMs,\n\t\t\tthis.outboundRetryBaseDelayMs * 2 ** this.outboundRetryAttempt,\n\t\t)\n\t\tthis.outboundRetryAttempt += 1\n\t\tthis.outboundRetryTimer = setTimeout(() => {\n\t\t\tthis.outboundRetryTimer = null\n\t\t\tif (this.state === 'streaming') this.flushQueue()\n\t\t}, delay)\n\t}\n\n\tprivate clearOutboundRetryTimer(): void {\n\t\tif (!this.outboundRetryTimer) return\n\t\tclearTimeout(this.outboundRetryTimer)\n\t\tthis.outboundRetryTimer = null\n\t}\n\n\t/** No ack arrived in time: return every batch in flight and reconnect. */\n\tprivate async handleOutboundAckTimeout(epoch: number): Promise<void> {\n\t\tthis.outboundAckTimer = null\n\t\tif (epoch !== this.sessionEpoch || this.inFlightUploads.size === 0) return\n\n\t\tconst batchIds = [...this.inFlightUploads.keys()].join(', ')\n\t\tthis.sessionEpoch++\n\t\tthis.returnAllInFlightUploads()\n\t\tawait this.refreshPendingCount()\n\n\t\tif (this.state === 'disconnected') return\n\n\t\tconst reason = `Timed out waiting for acknowledgment of outbound batch ${batchIds}`\n\t\ttry {\n\t\t\tawait this.transport.disconnect()\n\t\t} finally {\n\t\t\tthis.ensureDisconnected()\n\t\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason })\n\t\t}\n\t}\n\n\tprivate handleAwarenessUpdate(msg: AwarenessUpdateMessage): void {\n\t\tconst awarenessMessage: AwarenessMessage = {\n\t\t\ttype: 'awareness',\n\t\t\tclientId: msg.clientId,\n\t\t\tstates: wireToAwarenessStates(msg.states),\n\t\t}\n\t\tthis.awarenessManager.handleRemoteMessage(awarenessMessage)\n\t}\n\n\tprivate handleTransportClose(reason: string): void {\n\t\tthis.rejectKeyRequests(reason)\n\t\tthis.clearOutboundRetryTimer()\n\t\tthis.awarenessManager.stopCleanupTimer()\n\t\t// Return in-flight batches to the queue: nothing unacknowledged is resolved.\n\t\tthis.sessionEpoch++\n\t\tthis.returnAllInFlightUploads()\n\n\t\tif (this.schemaBlocked) {\n\t\t\treturn\n\t\t}\n\n\t\tif (this.state !== 'disconnected') {\n\t\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason })\n\t\t\tthis.transitionTo('disconnected')\n\t\t\tthis.notifyStatusChange()\n\t\t}\n\t}\n\n\tprivate handleTransportError(err: Error): void {\n\t\t// A transport error ends the session; close the transport so the socket is\n\t\t// released before any reconnect opens another (SYNC-5).\n\t\tif (this.state !== 'disconnected') {\n\t\t\tthis.transitionTo('error')\n\t\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason: err.message })\n\t\t\tthis.transitionTo('disconnected')\n\t\t\tthis.sessionEpoch++\n\t\t\tthis.returnAllInFlightUploads()\n\t\t\tvoid this.closeTransportQuietly()\n\t\t\tthis.notifyStatusChange()\n\t\t}\n\t}\n\n\tprivate transitionTo(newState: SyncState): void {\n\t\tconst validTargets = VALID_TRANSITIONS[this.state]\n\t\tif (!validTargets.includes(newState)) {\n\t\t\tthrow new SyncError(`Invalid sync state transition: ${this.state} → ${newState}`, {\n\t\t\t\tfrom: this.state,\n\t\t\t\tto: newState,\n\t\t\t})\n\t\t}\n\t\tthis.state = newState\n\t\tfor (const listener of [...this.stateListeners]) {\n\t\t\ttry {\n\t\t\t\tlistener(newState)\n\t\t\t} catch {\n\t\t\t\t// A listener must never break a state transition.\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate setSerializerWireFormat(format: WireFormat): void {\n\t\tif (typeof this.serializer.setWireFormat === 'function') {\n\t\t\tthis.serializer.setWireFormat(format)\n\t\t}\n\t}\n\n\t/**\n\t * Upload predicate: the UPLINK scope only, never query subsets (SYNC-1). Query\n\t * subsets describe what this client wants to download; the server authorizes\n\t * uploads independently of the client's downloaded view.\n\t */\n\tprivate async operationAllowedForUpload(op: Operation): Promise<boolean> {\n\t\t// Fast path: the bare operation already carries the scope fields.\n\t\tif (this.matchesUplinkScope(op)) {\n\t\t\treturn true\n\t\t}\n\t\t// A partial update or delete may not restate the scope fields. Backfill the\n\t\t// record's current fields before deciding, so an in-scope edit to an\n\t\t// unrelated field is never dropped.\n\t\tconst fullRecord = await this.readRecordForBackfill(op)\n\t\tif (!fullRecord) {\n\t\t\treturn false\n\t\t}\n\t\treturn this.matchesUplinkScope(op, fullRecord)\n\t}\n\n\t/**\n\t * Record a local operation that the uplink scope refuses, so it is visible to\n\t * the app instead of silently diverging from the server. Operations on\n\t * collections that are not synced in either direction are local-only by design\n\t * and are not recorded.\n\t *\n\t * Such an operation counts as resolved for the contiguous acknowledged prefix: it is\n\t * recorded here and never uploaded (W3 step 2).\n\t */\n\tprivate async recordOutOfUplinkScope(op: Operation): Promise<void> {\n\t\tconst uplink = this.activeUplinkScope\n\t\tif (!uplink) return\n\t\t// Local-only means the CLIENT leaves the collection out: its own scope map, or, with\n\t\t// none, the schema's synced collections. A synced collection the server's grant\n\t\t// omits is a refusal to surface, never a silent local fork (RT-89: a token-auth\n\t\t// grant that named no collection dropped every write with status \"synced\").\n\t\tconst requested = this.config.scopeMap\n\t\tconst syncsCollection =\n\t\t\tuplink[op.collection] !== undefined ||\n\t\t\tthis.activeScope?.[op.collection] !== undefined ||\n\t\t\t(requested !== undefined\n\t\t\t\t? requested[op.collection] !== undefined\n\t\t\t\t: (this.config.syncedCollections?.includes(op.collection) ?? false))\n\t\tif (!syncsCollection) return\n\n\t\tconst message = `Operation on \"${op.collection}\" record \"${op.recordId}\" is outside this client's upload scope and was not sent to the server. The local change is not synced; roll it back or move the record back into scope.`\n\t\tawait this.rejectedStorage.record({\n\t\t\toperationId: op.id,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tcode: OUT_OF_UPLINK_SCOPE,\n\t\t\tmessage,\n\t\t\tretriable: false,\n\t\t\trejectedAt: Date.now(),\n\t\t})\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:operation-rejected',\n\t\t\toperationId: op.id,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tcode: OUT_OF_UPLINK_SCOPE,\n\t\t\tmessage,\n\t\t\tretriable: false,\n\t\t})\n\t}\n\n\t/**\n\t * Whether an operation falls inside the uplink scope. Query subsets never apply to\n\t * uploads (SYNC-1), and nothing applies to inbound operations: the client applies\n\t * what the server delivered (SYNC-2).\n\t */\n\tprivate matchesUplinkScope(op: Operation, fullRecord?: Record<string, unknown> | null): boolean {\n\t\t// A client judging its own local view may trust previousData: this is not a\n\t\t// cross-tenant visibility decision (the server judges those on its own rows).\n\t\treturn operationMatchesScope(op, this.activeUplinkScope, fullRecord, {\n\t\t\tincludePreviousData: true,\n\t\t})\n\t}\n\n\tprivate async readRecordForBackfill(op: Operation): Promise<Record<string, unknown> | null> {\n\t\tif (!this.store.readRecordFields) {\n\t\t\treturn null\n\t\t}\n\t\ttry {\n\t\t\treturn await this.store.readRecordFields(op.collection, op.recordId)\n\t\t} catch {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tprivate async persistDeltaCursor(cursor: DeltaCursor | null): Promise<void> {\n\t\tif (!this.syncState?.saveDeltaCursor) {\n\t\t\treturn\n\t\t}\n\t\tawait this.syncState.saveDeltaCursor(cursor)\n\t}\n\n\tprivate async persistDeliveryWatermark(\n\t\twatermark: number,\n\t\tsignature = this.deliverySignature(),\n\t): Promise<void> {\n\t\tif (!this.syncState?.saveDeliveryWatermark) {\n\t\t\treturn\n\t\t}\n\t\tawait this.syncState.saveDeliveryWatermark(signature, watermark)\n\t}\n\n\tprivate scheduleQuerySubsetReconnect(): void {\n\t\tif (this.querySubsetReconnectTimer) {\n\t\t\tclearTimeout(this.querySubsetReconnectTimer)\n\t\t}\n\n\t\tthis.querySubsetReconnectTimer = setTimeout(() => {\n\t\t\tthis.querySubsetReconnectTimer = null\n\t\t\tif (this.state === 'streaming' || this.state === 'syncing' || this.state === 'handshaking') {\n\t\t\t\tvoid this.reconnectForQuerySubsets()\n\t\t\t}\n\t\t}, 500)\n\t}\n\n\tprivate async reconnectForQuerySubsets(): Promise<void> {\n\t\tawait this.reconnect()\n\t}\n}\n\n/**\n * Upload order inside a batch: by node, then sequence. For this device's own operations\n * sequence order is a causal order (each local op follows the ones before it), and it\n * makes the server's \"processed through sequence n\" ack an exact prefix of the batch.\n */\nfunction compareForUpload(a: Operation, b: Operation): number {\n\tif (a.nodeId !== b.nodeId) return a.nodeId < b.nodeId ? -1 : 1\n\treturn a.sequenceNumber - b.sequenceNumber\n}\n\n// --- Awareness wire format conversion helpers ---\n\n/**\n * Convert internal awareness states to wire format for transport.\n */\nfunction awarenessStatesToWire(\n\tstates: Record<number, AwarenessState | null>,\n): Record<string, AwarenessStateWire | null> {\n\tconst wire: Record<string, AwarenessStateWire | null> = {}\n\tfor (const [clientId, state] of Object.entries(states)) {\n\t\tif (state === null) {\n\t\t\twire[clientId] = null\n\t\t} else {\n\t\t\tconst wireState: AwarenessStateWire = {\n\t\t\t\tuser: { ...state.user },\n\t\t\t}\n\t\t\tif (state.cursor) {\n\t\t\t\twireState.cursor = { ...state.cursor }\n\t\t\t}\n\t\t\twire[clientId] = wireState\n\t\t}\n\t}\n\treturn wire\n}\n\n/**\n * Convert wire format awareness states to internal representation.\n */\nfunction wireToAwarenessStates(\n\twire: Record<string, AwarenessStateWire | null>,\n): Record<number, AwarenessState | null> {\n\tconst states: Record<number, AwarenessState | null> = {}\n\tfor (const [clientId, wireState] of Object.entries(wire)) {\n\t\tif (wireState === null) {\n\t\t\tstates[Number(clientId)] = null\n\t\t} else {\n\t\t\tconst state: AwarenessState = {\n\t\t\t\tuser: { ...wireState.user },\n\t\t\t}\n\t\t\tif (wireState.cursor) {\n\t\t\t\tstate.cursor = { ...wireState.cursor }\n\t\t\t}\n\t\t\tstates[Number(clientId)] = state\n\t\t}\n\t}\n\treturn states\n}\n\n/**\n * The explicit authoritative ids a handshake response revokes (RT-81), read structurally\n * (the field is optional, protocol v2).\n */\nfunction revokedAuthoritativeIds(msg: unknown): string[] {\n\tconst revoked = (msg as { revokedAuthoritativeNodeIds?: unknown }).revokedAuthoritativeNodeIds\n\treturn Array.isArray(revoked)\n\t\t? revoked.filter((id): id is string => typeof id === 'string' && !isServerNodeId(id))\n\t\t: []\n}\n\n/**\n * The schema transforms of an engine: its own `operationTransforms`, or the store's.\n * Both sides must use the same list, because the store folds what the engine judges\n * (transforms at fold time, RT-84): a mismatch is refused at construction.\n */\nfunction resolveOperationTransforms(\n\tconfig: SyncConfig,\n\tstore: SyncStore,\n): readonly OperationTransform[] {\n\tconst own = config.operationTransforms\n\tconst stores = store.getOperationTransforms?.()\n\tif (own === undefined) return stores ?? []\n\tif (stores !== undefined) {\n\t\tconst version = config.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\tif (\n\t\t\toperationTransformsFingerprint(version, own) !==\n\t\t\toperationTransformsFingerprint(version, stores)\n\t\t) {\n\t\t\tthrow new SyncError(\n\t\t\t\t'The sync engine and the local store were given different operationTransforms. The store folds every operation through its transforms (transforms at fold time); pass the same list to both (createApp does).',\n\t\t\t\t{ engineTransforms: own.length, storeTransforms: stores.length },\n\t\t\t)\n\t\t}\n\t}\n\treturn own\n}\n\n/** Map a key-service response to the keyring's reply, or the error it stands for. */\nfunction keyServiceReply(message: EncryptionKeyResponseMessage): KeyServiceReply {\n\tif (message.status === 'ok' || message.status === 'conflict') {\n\t\treturn {\n\t\t\tstatus: message.status,\n\t\t\trecord: message.record,\n\t\t\t...(Array.isArray(message.knownKeyIds)\n\t\t\t\t? {\n\t\t\t\t\t\tknownKeyIds: message.knownKeyIds.filter(\n\t\t\t\t\t\t\t(keyId): keyId is string => typeof keyId === 'string',\n\t\t\t\t\t\t),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t}\n\t}\n\tconst codes: Record<Exclude<EncryptionKeyResponseStatus, 'ok' | 'conflict'>, string> = {\n\t\tforbidden: 'KEY_SERVICE_FORBIDDEN',\n\t\tinvalid: 'KEY_RECORD_REFUSED',\n\t\tunsupported: 'KEY_SERVICE_UNSUPPORTED',\n\t\tthrottled: 'KEY_SERVICE_THROTTLED',\n\t}\n\tthrow new EncryptionKeyError(\n\t\tmessage.message ?? `The sync server refused the key request (${message.status}).`,\n\t\t{ code: codes[message.status] },\n\t)\n}\n","import type { KoraEventEmitter } from '@korajs/core'\nimport type { AwarenessChange, AwarenessMessage, AwarenessState } from './types'\n\n/**\n * Callback for awareness change events.\n */\ntype AwarenessChangeListener = (change: AwarenessChange) => void\n\n// Timeout after which a remote client's awareness is considered stale\n// and will be cleaned up. This is a safety net -- normally the server\n// sends explicit removal on disconnect.\nconst DEFAULT_TIMEOUT_MS = 30_000\n\nlet nextLocalClientId = 1\n\n/**\n * Manages collaborative awareness state (cursor positions, user presence).\n *\n * Awareness is ephemeral -- states are never persisted, only shared with\n * currently connected peers via the sync transport. This implements a\n * lightweight protocol compatible with Yjs awareness semantics.\n *\n * Each AwarenessManager has a unique client ID and tracks both its own\n * local state and all remote clients' states.\n */\nexport class AwarenessManager {\n\t/** Unique client ID for this instance */\n\treadonly clientId: number\n\n\tprivate localState: AwarenessState | null = null\n\tprivate readonly remoteStates = new Map<number, AwarenessState>()\n\tprivate readonly listeners = new Set<AwarenessChangeListener>()\n\tprivate readonly emitter: KoraEventEmitter | null\n\tprivate readonly timeoutMs: number\n\n\t// Track when we last received an update from each remote client.\n\t// Used for timeout-based cleanup as a safety net in case the server\n\t// does not send an explicit removal on disconnect.\n\tprivate readonly lastUpdated = new Map<number, number>()\n\tprivate cleanupTimer: ReturnType<typeof setInterval> | null = null\n\n\tprivate sendHandler: ((message: AwarenessMessage) => void) | null = null\n\tprivate destroyed = false\n\n\tconstructor(options?: {\n\t\tclientId?: number\n\t\temitter?: KoraEventEmitter\n\t\ttimeoutMs?: number\n\t}) {\n\t\tthis.clientId = options?.clientId ?? nextLocalClientId++\n\t\tthis.emitter = options?.emitter ?? null\n\t\tthis.timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\t}\n\n\t/**\n\t * Set the local user's awareness state and broadcast to peers.\n\t *\n\t * @param state - The awareness state to share. Pass null to clear presence.\n\t */\n\tsetLocalState(state: AwarenessState | null): void {\n\t\tif (this.destroyed) return\n\n\t\tthis.localState = state\n\n\t\tconst message: AwarenessMessage = {\n\t\t\ttype: 'awareness',\n\t\t\tclientId: this.clientId,\n\t\t\tstates: { [this.clientId]: state },\n\t\t}\n\n\t\tthis.sendHandler?.(message)\n\n\t\tthis.emitAwarenessEvent()\n\t}\n\n\t/**\n\t * Get the local awareness state.\n\t */\n\tgetLocalState(): AwarenessState | null {\n\t\treturn this.localState\n\t}\n\n\t/**\n\t * Get all known awareness states (local + remote).\n\t * Returns a new Map on each call.\n\t */\n\tgetStates(): Map<number, AwarenessState> {\n\t\tconst result = new Map<number, AwarenessState>()\n\t\tif (this.localState) {\n\t\t\tresult.set(this.clientId, this.localState)\n\t\t}\n\t\tfor (const [id, state] of this.remoteStates) {\n\t\t\tresult.set(id, state)\n\t\t}\n\t\treturn result\n\t}\n\n\t/**\n\t * Handle an incoming awareness message from the transport.\n\t * This processes remote state updates and notifies listeners.\n\t */\n\thandleRemoteMessage(message: AwarenessMessage): void {\n\t\tif (this.destroyed) return\n\n\t\tconst added: number[] = []\n\t\tconst updated: number[] = []\n\t\tconst removed: number[] = []\n\t\tconst now = Date.now()\n\n\t\tfor (const [clientIdStr, state] of Object.entries(message.states)) {\n\t\t\tconst clientId = Number(clientIdStr)\n\n\t\t\t// Ignore our own state echoed back\n\t\t\tif (clientId === this.clientId) continue\n\n\t\t\tif (state === null) {\n\t\t\t\t// Removal\n\t\t\t\tif (this.remoteStates.has(clientId)) {\n\t\t\t\t\tthis.remoteStates.delete(clientId)\n\t\t\t\t\tthis.lastUpdated.delete(clientId)\n\t\t\t\t\tremoved.push(clientId)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (this.remoteStates.has(clientId)) {\n\t\t\t\t\tthis.remoteStates.set(clientId, state)\n\t\t\t\t\tthis.lastUpdated.set(clientId, now)\n\t\t\t\t\tupdated.push(clientId)\n\t\t\t\t} else {\n\t\t\t\t\tthis.remoteStates.set(clientId, state)\n\t\t\t\t\tthis.lastUpdated.set(clientId, now)\n\t\t\t\t\tadded.push(clientId)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (added.length > 0 || updated.length > 0 || removed.length > 0) {\n\t\t\tconst change: AwarenessChange = { added, updated, removed }\n\t\t\tthis.notifyListeners(change)\n\t\t\tthis.emitAwarenessEvent()\n\t\t}\n\t}\n\n\t/**\n\t * Remove a specific remote client's awareness state.\n\t * Called when the server notifies that a client has disconnected.\n\t */\n\tremoveClient(clientId: number): void {\n\t\tif (this.destroyed) return\n\t\tif (!this.remoteStates.has(clientId)) return\n\n\t\tthis.remoteStates.delete(clientId)\n\t\tthis.lastUpdated.delete(clientId)\n\n\t\tconst change: AwarenessChange = {\n\t\t\tadded: [],\n\t\t\tupdated: [],\n\t\t\tremoved: [clientId],\n\t\t}\n\t\tthis.notifyListeners(change)\n\t\tthis.emitAwarenessEvent()\n\t}\n\n\t/**\n\t * Register a handler for sending awareness messages through the transport.\n\t * The sync engine calls this to wire outgoing awareness messages to the transport.\n\t */\n\tonSend(handler: (message: AwarenessMessage) => void): void {\n\t\tthis.sendHandler = handler\n\t}\n\n\t/**\n\t * Register a listener for awareness state changes.\n\t * Returns an unsubscribe function.\n\t */\n\ton(_event: 'change', listener: AwarenessChangeListener): () => void {\n\t\tthis.listeners.add(listener)\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener)\n\t\t}\n\t}\n\n\t/**\n\t * Remove a specific change listener.\n\t */\n\toff(_event: 'change', listener: AwarenessChangeListener): void {\n\t\tthis.listeners.delete(listener)\n\t}\n\n\t/**\n\t * Start the cleanup timer that removes stale remote states.\n\t * Called when the sync engine transitions to streaming state.\n\t */\n\tstartCleanupTimer(): void {\n\t\tif (this.cleanupTimer) return\n\n\t\tthis.cleanupTimer = setInterval(() => {\n\t\t\tthis.cleanupStaleStates()\n\t\t}, this.timeoutMs)\n\t}\n\n\t/**\n\t * Stop the cleanup timer.\n\t */\n\tstopCleanupTimer(): void {\n\t\tif (this.cleanupTimer) {\n\t\t\tclearInterval(this.cleanupTimer)\n\t\t\tthis.cleanupTimer = null\n\t\t}\n\t}\n\n\t/**\n\t * Clean up all resources. After calling destroy(), the manager\n\t * will no longer send or receive awareness updates.\n\t * Broadcasts removal of local state before shutting down.\n\t */\n\tdestroy(): void {\n\t\tif (this.destroyed) return\n\t\tthis.destroyed = true\n\n\t\t// Broadcast removal of our local state before shutting down\n\t\tif (this.localState) {\n\t\t\tconst message: AwarenessMessage = {\n\t\t\t\ttype: 'awareness',\n\t\t\t\tclientId: this.clientId,\n\t\t\t\tstates: { [this.clientId]: null },\n\t\t\t}\n\t\t\tthis.sendHandler?.(message)\n\t\t}\n\n\t\tthis.localState = null\n\t\tthis.remoteStates.clear()\n\t\tthis.lastUpdated.clear()\n\t\tthis.listeners.clear()\n\t\tthis.sendHandler = null\n\t\tthis.stopCleanupTimer()\n\t}\n\n\t// --- Private ---\n\n\tprivate cleanupStaleStates(): void {\n\t\tconst now = Date.now()\n\t\tconst removed: number[] = []\n\n\t\tfor (const [clientId, lastTime] of this.lastUpdated) {\n\t\t\tif (now - lastTime > this.timeoutMs) {\n\t\t\t\tthis.remoteStates.delete(clientId)\n\t\t\t\tthis.lastUpdated.delete(clientId)\n\t\t\t\tremoved.push(clientId)\n\t\t\t}\n\t\t}\n\n\t\tif (removed.length > 0) {\n\t\t\tconst change: AwarenessChange = { added: [], updated: [], removed }\n\t\t\tthis.notifyListeners(change)\n\t\t\tthis.emitAwarenessEvent()\n\t\t}\n\t}\n\n\tprivate notifyListeners(change: AwarenessChange): void {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener(change)\n\t\t}\n\t}\n\n\tprivate emitAwarenessEvent(): void {\n\t\t// Cast states to Map<number, unknown> to satisfy KoraEvent's generic type\n\t\tconst states: Map<number, unknown> = this.getStates()\n\t\tthis.emitter?.emit({ type: 'awareness:updated', states })\n\t}\n}\n","import { generateUUIDv7 } from '@korajs/core'\nimport type {\n\tBlobChunkPushMessage,\n\tBlobChunkRequestMessage,\n\tBlobChunkResponseMessage,\n} from '../protocol/messages'\n\n/**\n * A blob-chunk message in decoded form (chunk bytes as a `Uint8Array` rather\n * than the base64 wire string). This is the shape callers of the channel work\n * with; the channel handles base64 encode/decode at the wire boundary.\n *\n * The shape mirrors `@korajs/store`'s `ChunkMessage` so an app-layer adapter can\n * bridge the two with no field translation.\n */\nexport type BlobChunkChannelMessage =\n\t| { type: 'blob-chunk-request'; requestId: string; hash: string }\n\t| {\n\t\t\ttype: 'blob-chunk-response'\n\t\t\trequestId: string\n\t\t\tbytes: Uint8Array | null\n\t\t\t/** The server refused the request for rate; retry after `retryAfterMs` (RT-24). */\n\t\t\tthrottled?: boolean\n\t\t\tretryAfterMs?: number\n\t }\n\t| { type: 'blob-chunk-push'; hash: string; bytes: Uint8Array }\n\n/** Encode chunk bytes to a base64 string for the JSON wire. */\nexport function encodeBlobChunkBytes(bytes: Uint8Array): string {\n\tlet binary = ''\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\tbinary += String.fromCharCode(bytes[i] as number)\n\t}\n\treturn btoa(binary)\n}\n\n/** Decode base64 chunk bytes from the wire back to a `Uint8Array`. */\nexport function decodeBlobChunkBytes(encoded: string): Uint8Array {\n\tconst binary = atob(encoded)\n\tconst out = new Uint8Array(binary.length)\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tout[i] = binary.charCodeAt(i)\n\t}\n\treturn out\n}\n\nexport interface BlobChunkChannelOptions {\n\t/**\n\t * Called when a local blob-chunk message should go out on the wire. The\n\t * caller (the sync engine) decides whether the connection is ready to send.\n\t */\n\tonSend?: (\n\t\tmessage: BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage,\n\t) => void\n}\n\n/**\n * Ephemeral side channel for out-of-band blob chunk transfer over the sync\n * connection. Symmetric: a peer can both request chunks it needs and answer\n * requests for chunks it holds. Never persisted in the operation log — durable\n * state is the `BlobRef` inside a record's fields; this channel only moves the\n * referenced bytes.\n *\n * The channel owns the base64 wire encoding so callers work purely in\n * `Uint8Array`, and its {@link BlobChunkChannelMessage} shape mirrors\n * `@korajs/store`'s `ChunkMessage` so the app layer can expose it as a\n * `ChunkMessagePort` without translating fields.\n */\nexport class BlobChunkChannel {\n\tprivate readonly onSend:\n\t\t| ((message: BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage) => void)\n\t\t| null\n\tprivate readonly handlers = new Set<(message: BlobChunkChannelMessage) => void>()\n\n\tconstructor(options?: BlobChunkChannelOptions) {\n\t\tthis.onSend = options?.onSend ?? null\n\t}\n\n\t/**\n\t * Send a blob-chunk message to the peer. Response bytes are base64-encoded\n\t * onto the wire here; callers pass a `Uint8Array`.\n\t */\n\tsend(message: BlobChunkChannelMessage): void {\n\t\tif (!this.onSend) {\n\t\t\treturn\n\t\t}\n\t\tif (message.type === 'blob-chunk-request') {\n\t\t\tthis.onSend({\n\t\t\t\ttype: 'blob-chunk-request',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\trequestId: message.requestId,\n\t\t\t\thash: message.hash,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tif (message.type === 'blob-chunk-push') {\n\t\t\tthis.onSend({\n\t\t\t\ttype: 'blob-chunk-push',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\thash: message.hash,\n\t\t\t\tbytes: encodeBlobChunkBytes(message.bytes),\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tthis.onSend({\n\t\t\ttype: 'blob-chunk-response',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\trequestId: message.requestId,\n\t\t\tbytes: message.bytes === null ? null : encodeBlobChunkBytes(message.bytes),\n\t\t\t...(message.throttled ? { throttled: true } : {}),\n\t\t\t...(message.retryAfterMs !== undefined ? { retryAfterMs: message.retryAfterMs } : {}),\n\t\t})\n\t}\n\n\t/**\n\t * Register a handler for inbound blob-chunk messages. Multiple handlers are\n\t * allowed; each receives every delivered message.\n\t */\n\tonMessage(handler: (message: BlobChunkChannelMessage) => void): () => void {\n\t\tthis.handlers.add(handler)\n\t\treturn () => {\n\t\t\tthis.handlers.delete(handler)\n\t\t}\n\t}\n\n\t/**\n\t * Deliver an inbound wire message to registered handlers, decoding response\n\t * bytes from base64 back to a `Uint8Array` first.\n\t */\n\tdeliver(\n\t\tmessage: BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage,\n\t): void {\n\t\tif (this.handlers.size === 0) {\n\t\t\treturn\n\t\t}\n\t\tconst decoded: BlobChunkChannelMessage =\n\t\t\tmessage.type === 'blob-chunk-request'\n\t\t\t\t? { type: 'blob-chunk-request', requestId: message.requestId, hash: message.hash }\n\t\t\t\t: message.type === 'blob-chunk-push'\n\t\t\t\t\t? {\n\t\t\t\t\t\t\ttype: 'blob-chunk-push',\n\t\t\t\t\t\t\thash: message.hash,\n\t\t\t\t\t\t\tbytes: decodeBlobChunkBytes(message.bytes),\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\ttype: 'blob-chunk-response',\n\t\t\t\t\t\t\trequestId: message.requestId,\n\t\t\t\t\t\t\tbytes: message.bytes === null ? null : decodeBlobChunkBytes(message.bytes),\n\t\t\t\t\t\t\t...(message.throttled ? { throttled: true } : {}),\n\t\t\t\t\t\t\t...(message.retryAfterMs !== undefined ? { retryAfterMs: message.retryAfterMs } : {}),\n\t\t\t\t\t\t}\n\t\tfor (const handler of this.handlers) {\n\t\t\thandler(decoded)\n\t\t}\n\t}\n}\n","import type { TimeSource } from '@korajs/core'\n\n/**\n * A single bandwidth measurement sample: bytes transferred over a known duration.\n */\ninterface BandwidthSample {\n\tbytes: number\n\tdurationMs: number\n\ttimestamp: number\n}\n\n/**\n * Estimates effective bandwidth by tracking byte transfers over time.\n *\n * Maintains a sliding window of transfer samples and computes a\n * weighted average bandwidth, giving more weight to recent samples.\n * This provides a smooth estimate that adapts to changing network conditions.\n */\nexport class BandwidthEstimator {\n\tprivate readonly maxSamples: number\n\tprivate readonly timeSource: TimeSource\n\tprivate readonly samples: BandwidthSample[] = []\n\n\t/**\n\t * @param maxSamples - Maximum number of samples to retain. Defaults to 20.\n\t * @param timeSource - Injectable time source for deterministic testing.\n\t */\n\tconstructor(maxSamples = 20, timeSource?: TimeSource) {\n\t\tthis.maxSamples = maxSamples\n\t\tthis.timeSource = timeSource ?? { now: () => Date.now() }\n\t}\n\n\t/**\n\t * Record a transfer of `bytes` that took `durationMs` to complete.\n\t * Samples with zero or negative duration are ignored to avoid division errors.\n\t */\n\trecordTransfer(bytes: number, durationMs: number): void {\n\t\tif (durationMs <= 0 || bytes <= 0) return\n\n\t\tthis.samples.push({\n\t\t\tbytes,\n\t\t\tdurationMs,\n\t\t\ttimestamp: this.timeSource.now(),\n\t\t})\n\n\t\t// Evict oldest samples beyond the window\n\t\twhile (this.samples.length > this.maxSamples) {\n\t\t\tthis.samples.shift()\n\t\t}\n\t}\n\n\t/**\n\t * Estimate current effective bandwidth in bytes per second.\n\t *\n\t * Uses exponential weighting so recent samples have more influence.\n\t * Returns null if fewer than 2 samples exist (not enough data).\n\t */\n\testimate(): number | null {\n\t\tif (this.samples.length < 2) return null\n\n\t\tlet weightedSum = 0\n\t\tlet totalWeight = 0\n\n\t\t// Exponential decay: most recent sample gets weight 1.0,\n\t\t// each older sample is multiplied by 0.9\n\t\tconst decayFactor = 0.9\n\t\tfor (let i = this.samples.length - 1; i >= 0; i--) {\n\t\t\tconst sample = this.samples[i]\n\t\t\tif (!sample) continue\n\t\t\tconst bytesPerSec = (sample.bytes / sample.durationMs) * 1000\n\t\t\tconst age = this.samples.length - 1 - i\n\t\t\tconst weight = decayFactor ** age\n\n\t\t\tweightedSum += bytesPerSec * weight\n\t\t\ttotalWeight += weight\n\t\t}\n\n\t\tif (totalWeight === 0) return null\n\t\treturn Math.round(weightedSum / totalWeight)\n\t}\n\n\t/**\n\t * Reset all recorded samples.\n\t */\n\treset(): void {\n\t\tthis.samples.length = 0\n\t}\n\n\t/**\n\t * Get the number of samples currently stored.\n\t */\n\tsampleCount(): number {\n\t\treturn this.samples.length\n\t}\n}\n","/**\n * Sliding window percentile calculator.\n *\n * Maintains a fixed-size circular buffer of numeric samples and\n * computes percentiles (p50, p95, p99, etc.) on demand by sorting\n * the current window. This approach is simple, correct, and fast\n * for the expected window sizes (100 samples).\n */\nexport class SlidingWindowPercentile {\n\tprivate readonly samples: number[]\n\tprivate readonly maxSize: number\n\tprivate writeIndex = 0\n\tprivate count = 0\n\n\t/**\n\t * @param maxSize - Maximum number of samples in the sliding window.\n\t * Older samples are overwritten when the buffer is full.\n\t */\n\tconstructor(maxSize: number) {\n\t\tif (maxSize < 1) {\n\t\t\tthrow new Error(`SlidingWindowPercentile maxSize must be >= 1, got ${maxSize}`)\n\t\t}\n\t\tthis.maxSize = maxSize\n\t\tthis.samples = new Array<number>(maxSize)\n\t}\n\n\t/**\n\t * Add a sample to the sliding window.\n\t * If the window is full, the oldest sample is overwritten.\n\t */\n\taddSample(value: number): void {\n\t\tthis.samples[this.writeIndex] = value\n\t\tthis.writeIndex = (this.writeIndex + 1) % this.maxSize\n\t\tif (this.count < this.maxSize) {\n\t\t\tthis.count++\n\t\t}\n\t}\n\n\t/**\n\t * Compute a percentile value from the current window.\n\t *\n\t * Uses the nearest-rank method: the percentile value is the smallest\n\t * value in the dataset such that at least p% of the data is <= that value.\n\t *\n\t * @param p - Percentile to compute (0-100). E.g., 50 for median, 95 for p95.\n\t * @returns The percentile value, or 0 if no samples have been recorded.\n\t */\n\tpercentile(p: number): number {\n\t\tif (this.count === 0) return 0\n\n\t\t// Extract only the valid portion of the buffer and sort\n\t\tconst sorted = this.samples.slice(0, this.count).sort((a, b) => a - b)\n\n\t\t// Nearest-rank method: index = ceil(p/100 * n) - 1, clamped to [0, n-1]\n\t\tconst rank = Math.ceil((p / 100) * sorted.length) - 1\n\t\tconst index = Math.max(0, Math.min(rank, sorted.length - 1))\n\t\treturn sorted[index] ?? 0\n\t}\n\n\t/**\n\t * Get the most recently added sample, or 0 if no samples exist.\n\t */\n\tlatest(): number {\n\t\tif (this.count === 0) return 0\n\t\t// writeIndex points to the next write position, so the last written is one behind\n\t\tconst lastIndex = (this.writeIndex - 1 + this.maxSize) % this.maxSize\n\t\treturn this.samples[lastIndex] ?? 0\n\t}\n\n\t/**\n\t * Get the number of samples currently in the window.\n\t */\n\tsize(): number {\n\t\treturn this.count\n\t}\n\n\t/**\n\t * Reset the sliding window, clearing all samples.\n\t */\n\treset(): void {\n\t\tthis.writeIndex = 0\n\t\tthis.count = 0\n\t}\n}\n","import type {\n\tConnectionQuality,\n\tKoraEventEmitter,\n\tSyncDiagnosticsSnapshot,\n\tTimeSource,\n} from '@korajs/core'\nimport type { SyncStatus } from '../types'\nimport { BandwidthEstimator } from './bandwidth-estimator'\nimport { SlidingWindowPercentile } from './percentile'\n\n/**\n * Configuration for the SyncMetricsCollector.\n */\nexport interface MetricsCollectorConfig {\n\t/** Number of RTT samples to keep for percentile calculations. Defaults to 100. */\n\trttWindowSize?: number\n\t/** Number of bandwidth samples to keep. Defaults to 20. */\n\tbandwidthWindowSize?: number\n\t/** Interval in ms between periodic diagnostics emissions. Defaults to 5000. */\n\tdiagnosticsInterval?: number\n\t/** Injectable time source for deterministic testing. */\n\ttimeSource?: TimeSource\n}\n\n/**\n * Collects and aggregates sync metrics for diagnostics and DevTools integration.\n *\n * Tracks connection timing, RTT percentiles, throughput counters, queue depth,\n * initial sync progress, errors, and connection quality. Periodically emits\n * a `sync:diagnostics` event with a full snapshot.\n */\nexport class SyncMetricsCollector {\n\tprivate readonly rttWindow: SlidingWindowPercentile\n\tprivate readonly inboundBandwidth: BandwidthEstimator\n\tprivate readonly outboundBandwidth: BandwidthEstimator\n\tprivate readonly timeSource: TimeSource\n\tprivate readonly diagnosticsInterval: number\n\n\t// Connection\n\tprivate connectedAt: number | null = null\n\tprivate disconnectedAt: number | null = null\n\tprivate reconnectAttempts = 0\n\n\t// Throughput\n\tprivate operationsSent = 0\n\tprivate operationsReceived = 0\n\tprivate bytesSent = 0\n\tprivate bytesReceived = 0\n\n\t// Queue\n\tprivate pendingOperations = 0\n\tprivate outboundQueueSize = 0\n\n\t// Sync progress\n\tprivate lastSyncedAt: number | null = null\n\tprivate syncStartTime: number | null = null\n\tprivate syncDuration: number | null = null\n\tprivate initialSyncComplete = false\n\tprivate initialSyncTotalBatches = 0\n\tprivate initialSyncReceivedBatches = 0\n\n\t// Errors\n\tprivate lastError: string | null = null\n\tprivate errorCount = 0\n\n\t// Status\n\tprivate currentStatus: SyncStatus = 'offline'\n\tprivate currentQuality: ConnectionQuality = 'offline'\n\n\t// Periodic emission\n\tprivate emitter: KoraEventEmitter | null = null\n\tprivate periodicTimer: ReturnType<typeof setInterval> | null = null\n\n\tconstructor(config?: MetricsCollectorConfig) {\n\t\tthis.rttWindow = new SlidingWindowPercentile(config?.rttWindowSize ?? 100)\n\t\tthis.inboundBandwidth = new BandwidthEstimator(\n\t\t\tconfig?.bandwidthWindowSize ?? 20,\n\t\t\tconfig?.timeSource,\n\t\t)\n\t\tthis.outboundBandwidth = new BandwidthEstimator(\n\t\t\tconfig?.bandwidthWindowSize ?? 20,\n\t\t\tconfig?.timeSource,\n\t\t)\n\t\tthis.timeSource = config?.timeSource ?? { now: () => Date.now() }\n\t\tthis.diagnosticsInterval = config?.diagnosticsInterval ?? 5000\n\t}\n\n\t/**\n\t * Attach an event emitter for periodic diagnostics emission.\n\t * Starts emitting `sync:diagnostics` events at the configured interval\n\t * while connected.\n\t */\n\tattachEmitter(emitter: KoraEventEmitter): void {\n\t\tthis.emitter = emitter\n\t}\n\n\t/**\n\t * Record that a connection has been established.\n\t */\n\trecordConnected(): void {\n\t\tthis.connectedAt = this.timeSource.now()\n\t\tthis.reconnectAttempts = 0\n\t\tthis.startPeriodicEmission()\n\t}\n\n\t/**\n\t * Record that the connection has been lost.\n\t */\n\trecordDisconnected(): void {\n\t\tthis.disconnectedAt = this.timeSource.now()\n\t\tthis.stopPeriodicEmission()\n\t}\n\n\t/**\n\t * Record a reconnection attempt.\n\t */\n\trecordReconnectAttempt(): void {\n\t\tthis.reconnectAttempts++\n\t}\n\n\t/**\n\t * Record a round-trip time measurement.\n\t */\n\trecordRtt(ms: number): void {\n\t\tthis.rttWindow.addSample(ms)\n\t}\n\n\t/**\n\t * Record operations sent with their serialized byte size.\n\t */\n\trecordSent(operationCount: number, byteSize: number, durationMs: number): void {\n\t\tthis.operationsSent += operationCount\n\t\tthis.bytesSent += byteSize\n\t\tif (durationMs > 0 && byteSize > 0) {\n\t\t\tthis.outboundBandwidth.recordTransfer(byteSize, durationMs)\n\t\t\tthis.emitBandwidthEvent('out')\n\t\t}\n\t}\n\n\t/**\n\t * Record operations received with their serialized byte size.\n\t */\n\trecordReceived(operationCount: number, byteSize: number, durationMs: number): void {\n\t\tthis.operationsReceived += operationCount\n\t\tthis.bytesReceived += byteSize\n\t\tif (durationMs > 0 && byteSize > 0) {\n\t\t\tthis.inboundBandwidth.recordTransfer(byteSize, durationMs)\n\t\t\tthis.emitBandwidthEvent('in')\n\t\t}\n\t}\n\n\t/**\n\t * Update the pending operations count and estimated outbound queue size.\n\t */\n\tupdateQueue(pendingOps: number, estimatedBytes: number): void {\n\t\tthis.pendingOperations = pendingOps\n\t\tthis.outboundQueueSize = estimatedBytes\n\t}\n\n\t/**\n\t * Record that sync has started (for measuring sync duration).\n\t */\n\trecordSyncStarted(): void {\n\t\tthis.syncStartTime = this.timeSource.now()\n\t}\n\n\t/**\n\t * Record that a sync cycle has completed.\n\t */\n\trecordSyncCompleted(): void {\n\t\tif (this.syncStartTime !== null) {\n\t\t\tthis.syncDuration = this.timeSource.now() - this.syncStartTime\n\t\t\tthis.syncStartTime = null\n\t\t}\n\t\tthis.lastSyncedAt = this.timeSource.now()\n\t\tif (!this.initialSyncComplete) {\n\t\t\tthis.initialSyncComplete = true\n\t\t}\n\t}\n\n\t/**\n\t * Update initial sync progress.\n\t * @param receivedBatches - Number of delta batches received so far.\n\t * @param totalBatches - Estimated total batches (0 if unknown).\n\t */\n\tupdateInitialSyncProgress(receivedBatches: number, totalBatches: number): void {\n\t\tthis.initialSyncReceivedBatches = receivedBatches\n\t\tthis.initialSyncTotalBatches = totalBatches\n\n\t\tconst progress =\n\t\t\ttotalBatches > 0 ? Math.min(1, receivedBatches / totalBatches) : receivedBatches > 0 ? 0.5 : 0\n\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:initial-sync-progress',\n\t\t\tprogress,\n\t\t\ttotalBatches,\n\t\t\treceivedBatches,\n\t\t})\n\t}\n\n\t/**\n\t * Record an error.\n\t */\n\trecordError(message: string): void {\n\t\tthis.lastError = message\n\t\tthis.errorCount++\n\t}\n\n\t/**\n\t * Update the current developer-facing status.\n\t */\n\tupdateStatus(status: SyncStatus): void {\n\t\tthis.currentStatus = status\n\t}\n\n\t/**\n\t * Update the connection quality.\n\t */\n\tupdateQuality(quality: ConnectionQuality): void {\n\t\tthis.currentQuality = quality\n\t}\n\n\t/**\n\t * Compute and return a full diagnostics snapshot.\n\t */\n\tgetSnapshot(): SyncDiagnosticsSnapshot {\n\t\treturn {\n\t\t\t// Connection\n\t\t\tstatus: this.currentStatus,\n\t\t\tconnectedAt: this.connectedAt,\n\t\t\tdisconnectedAt: this.disconnectedAt,\n\t\t\treconnectAttempts: this.reconnectAttempts,\n\n\t\t\t// Latency\n\t\t\trttMs: this.rttWindow.latest(),\n\t\t\trttP50Ms: this.rttWindow.percentile(50),\n\t\t\trttP95Ms: this.rttWindow.percentile(95),\n\t\t\trttP99Ms: this.rttWindow.percentile(99),\n\n\t\t\t// Throughput\n\t\t\toperationsSent: this.operationsSent,\n\t\t\toperationsReceived: this.operationsReceived,\n\t\t\tbytesSent: this.bytesSent,\n\t\t\tbytesReceived: this.bytesReceived,\n\n\t\t\t// Queue\n\t\t\tpendingOperations: this.pendingOperations,\n\t\t\toutboundQueueSize: this.outboundQueueSize,\n\n\t\t\t// Sync Progress\n\t\t\tlastSyncedAt: this.lastSyncedAt,\n\t\t\tsyncDuration: this.syncDuration,\n\t\t\tinitialSyncComplete: this.initialSyncComplete,\n\t\t\tinitialSyncProgress: this.computeInitialSyncProgress(),\n\n\t\t\t// Errors\n\t\t\tlastError: this.lastError,\n\t\t\terrorCount: this.errorCount,\n\n\t\t\t// Connection Quality\n\t\t\tquality: this.currentQuality,\n\t\t\teffectiveBandwidth: this.computeEffectiveBandwidth(),\n\t\t}\n\t}\n\n\t/**\n\t * Assess connection quality from current metrics.\n\t *\n\t * Quality is derived from RTT percentiles, error rate, and bandwidth:\n\t * - excellent: p95 < 100ms, no errors\n\t * - good: p95 < 300ms, errorCount <= 1\n\t * - fair: p95 < 1000ms, errorCount <= 3\n\t * - poor: p95 >= 1000ms or errorCount > 3\n\t * - offline: no connection\n\t */\n\tassessQuality(): ConnectionQuality {\n\t\tif (this.currentStatus === 'offline') return 'offline'\n\n\t\tconst p95 = this.rttWindow.percentile(95)\n\t\tconst hasSamples = this.rttWindow.size() > 0\n\n\t\tif (!hasSamples) {\n\t\t\t// No RTT data yet, fall back to error-based assessment\n\t\t\tif (this.errorCount > 3) return 'poor'\n\t\t\tif (this.errorCount > 0) return 'fair'\n\t\t\treturn 'good'\n\t\t}\n\n\t\tif (this.errorCount > 3) return 'poor'\n\t\tif (p95 < 100 && this.errorCount === 0) return 'excellent'\n\t\tif (p95 < 300 && this.errorCount <= 1) return 'good'\n\t\tif (p95 < 1000 && this.errorCount <= 3) return 'fair'\n\t\treturn 'poor'\n\t}\n\n\t/**\n\t * Reset all collected metrics. Call when starting a new session.\n\t */\n\treset(): void {\n\t\tthis.rttWindow.reset()\n\t\tthis.inboundBandwidth.reset()\n\t\tthis.outboundBandwidth.reset()\n\n\t\tthis.connectedAt = null\n\t\tthis.disconnectedAt = null\n\t\tthis.reconnectAttempts = 0\n\n\t\tthis.operationsSent = 0\n\t\tthis.operationsReceived = 0\n\t\tthis.bytesSent = 0\n\t\tthis.bytesReceived = 0\n\n\t\tthis.pendingOperations = 0\n\t\tthis.outboundQueueSize = 0\n\n\t\tthis.lastSyncedAt = null\n\t\tthis.syncStartTime = null\n\t\tthis.syncDuration = null\n\t\tthis.initialSyncComplete = false\n\t\tthis.initialSyncTotalBatches = 0\n\t\tthis.initialSyncReceivedBatches = 0\n\n\t\tthis.lastError = null\n\t\tthis.errorCount = 0\n\n\t\tthis.currentStatus = 'offline'\n\t\tthis.currentQuality = 'offline'\n\n\t\tthis.stopPeriodicEmission()\n\t}\n\n\t/**\n\t * Stop periodic emission and clean up resources.\n\t */\n\tdispose(): void {\n\t\tthis.stopPeriodicEmission()\n\t\tthis.emitter = null\n\t}\n\n\t// --- Private helpers ---\n\n\tprivate computeInitialSyncProgress(): number {\n\t\tif (this.initialSyncComplete) return 1\n\t\tif (this.initialSyncTotalBatches > 0) {\n\t\t\treturn Math.min(1, this.initialSyncReceivedBatches / this.initialSyncTotalBatches)\n\t\t}\n\t\t// Unknown total: if we have received some batches, report 0.5 as indeterminate progress\n\t\treturn this.initialSyncReceivedBatches > 0 ? 0.5 : 0\n\t}\n\n\tprivate computeEffectiveBandwidth(): number | null {\n\t\t// Use the lower of inbound and outbound as effective bandwidth,\n\t\t// since the bottleneck determines the effective rate.\n\t\tconst inbound = this.inboundBandwidth.estimate()\n\t\tconst outbound = this.outboundBandwidth.estimate()\n\n\t\tif (inbound === null && outbound === null) return null\n\t\tif (inbound === null) return outbound\n\t\tif (outbound === null) return inbound\n\t\treturn Math.min(inbound, outbound)\n\t}\n\n\tprivate startPeriodicEmission(): void {\n\t\tif (this.periodicTimer !== null) return\n\t\tif (!this.emitter) return\n\n\t\tthis.periodicTimer = setInterval(() => {\n\t\t\tthis.emitDiagnostics()\n\t\t}, this.diagnosticsInterval)\n\t}\n\n\tprivate stopPeriodicEmission(): void {\n\t\tif (this.periodicTimer !== null) {\n\t\t\tclearInterval(this.periodicTimer)\n\t\t\tthis.periodicTimer = null\n\t\t}\n\t}\n\n\tprivate emitDiagnostics(): void {\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:diagnostics',\n\t\t\tdiagnostics: this.getSnapshot(),\n\t\t})\n\t}\n\n\tprivate emitBandwidthEvent(direction: 'in' | 'out'): void {\n\t\tconst estimator = direction === 'in' ? this.inboundBandwidth : this.outboundBandwidth\n\t\tconst bps = estimator.estimate()\n\t\tif (bps !== null) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:bandwidth',\n\t\t\t\tbytesPerSecond: bps,\n\t\t\t\tdirection,\n\t\t\t})\n\t\t}\n\t}\n}\n","import { SyncError } from '@korajs/core'\nimport type { CachedKeyring, KeyCache } from './key-cache'\nimport { MemoryKeyCache } from './key-cache'\nimport { DEFAULT_PBKDF2_ITERATIONS } from './key-derivation'\nimport type { WrappedDataKey, WrappedKeyRecord } from './key-record'\nimport {\n\tDEFAULT_KEYRING,\n\tKEY_RECORD_FORMAT,\n\tcanonicalJson,\n\tisKeyId,\n\tisMasterId,\n\tvalidateKeyRecord,\n} from './key-record'\nimport type { MasterKeys } from './keyring-crypto'\nimport {\n\tKEK_SALT_BYTES,\n\tKeyUnwrapError,\n\tbytesEqual,\n\tcreateRecoveryKeyPair,\n\tderiveKeyEncryptionKey,\n\tformatRecoveryKey,\n\tfromBase64,\n\tgenerateDataKey,\n\tgenerateMasterKey,\n\timportMasterKey,\n\tnewKeyId,\n\tnewRingId,\n\tparseRecoveryKey,\n\trandomBytes,\n\trecoveryAnchor,\n\tsealRecord,\n\ttoBase64,\n\ttoNonExtractable,\n\tunwrapDataKey,\n\tunwrapMasterKey,\n\tunwrapMasterWithRecovery,\n\tverifyRecordMac,\n\twrapDataKey,\n\twrapMasterForRecovery,\n\twrapMasterKey,\n} from './keyring-crypto'\nimport type { EncryptorOptions } from './sync-encryptor'\nimport { SyncEncryptor } from './sync-encryptor'\nimport type { VersionedKey } from './types'\n\n/** Lock state of an encryption keyring. */\nexport type EncryptionLockState = 'locked' | 'unlocking' | 'unlocked' | 'error'\n\n/** Why a keyring is locked or in error (or, while unlocked, what needs attention). */\nexport type EncryptionStatusCode =\n\t| 'NO_PASSPHRASE'\n\t| 'AWAITING_SERVER'\n\t| 'LOCKED_BY_APP'\n\t| 'WRONG_PASSPHRASE'\n\t| 'PASSPHRASE_REQUIRED'\n\t| 'KEY_RECORD_INVALID'\n\t| 'KEY_RECORD_ROLLBACK'\n\t| 'KEY_RECORD_MISSING'\n\t| 'KEY_RING_FORK'\n\t| 'KEY_SERVICE_FORBIDDEN'\n\t| 'KEY_SERVICE_UNSUPPORTED'\n\t| 'RECOVERY_FAILED'\n\n/** Status of an encryption keyring, as `app.encryption.getStatus()` reports it. */\nexport interface EncryptionStatus {\n\tstate: EncryptionLockState\n\tkeyring: string\n\t/** Key version new operations are encrypted with, or null while locked. */\n\tkeyVersion: number | null\n\t/** Key id of that version, or null while locked. */\n\tkeyId: string | null\n\t/** Every key version this device can decrypt. */\n\tavailableVersions: number[]\n\t/** Where unlocked keys are kept between starts. */\n\tcache: KeyCache['kind']\n\t/**\n\t * Set while locked or in error. While `unlocked`, `PASSPHRASE_REQUIRED` means the\n\t * passphrase changed on another device: this device keeps working with the keys it\n\t * holds, and needs the current passphrase for key management.\n\t */\n\tcode?: EncryptionStatusCode\n\tmessage?: string\n}\n\n/** The server's answer to a key-record fetch or write. */\nexport interface KeyServiceReply {\n\t/** 'conflict': the write lost a compare-and-set; `record` is the current one. */\n\tstatus: 'ok' | 'conflict'\n\trecord: WrappedKeyRecord | null\n\t/**\n\t * With no record: key ids the server's stored operations of this owner are encrypted\n\t * with (a sample). Non-empty means encrypted history exists, so the record was lost\n\t * and a device that holds the ring must re-upload it; nobody may start a new ring.\n\t */\n\tknownKeyIds?: string[]\n}\n\n/** How a keyring reaches the server's key service (the sync connection). */\nexport interface KeyServiceChannel {\n\tfetch(keyring: string): Promise<KeyServiceReply>\n\tput(keyring: string, record: WrappedKeyRecord, expectedRevision: number): Promise<KeyServiceReply>\n}\n\n/** Options of {@link EncryptionKeyring}. */\nexport interface EncryptionKeyringOptions {\n\t/** Keyring name (one per user by default). */\n\tkeyring?: string\n\t/** Passphrase source; without one the keyring starts locked until `unlock()`. */\n\tpassphrase?: string | (() => Promise<string>)\n\t/** Where unlocked keys are kept. Defaults to memory (the app picks IndexedDB in browsers). */\n\tcache?: KeyCache\n\t/**\n\t * PBKDF2 iterations for new records, and the minimum accepted from the server\n\t * (a server cannot downgrade the KDF). Defaults to 600,000. Lower only in tests.\n\t */\n\tkdfIterations?: number\n\t/** Encryptor options (cleartext scope fields, plaintext migration window). */\n\tencryptor?: EncryptorOptions\n}\n\n/** Thrown by keyring management calls. `context.code` names the reason. */\nexport class EncryptionKeyError extends SyncError {\n\tconstructor(message: string, context: Record<string, unknown> & { code: string }) {\n\t\tsuper(message, { ...context, errorType: 'ENCRYPTION_KEY_ERROR' })\n\t\tthis.name = 'EncryptionKeyError'\n\t}\n\n\t/** The reason code (`context.code`), e.g. WRONG_PASSPHRASE. */\n\tget keyCode(): string {\n\t\treturn String(this.context?.code ?? '')\n\t}\n}\n\n/** Failed unlocks before the client-side backoff starts. */\nconst FREE_UNLOCK_ATTEMPTS = 3\nconst MAX_UNLOCK_BACKOFF_MS = 60_000\n/** Compare-and-set retries of one key-record write (and nested adopt/merge rounds). */\nconst MAX_WRITE_ATTEMPTS = 4\n\n/** A record this device authenticated: the master keys that verify it, and its KEK if known. */\ninterface OpenedRing {\n\tmaster: MasterKeys\n\t/** The KEK of the record's own salt, when this device derived (or holds) it. */\n\tkek: CryptoKey | null\n}\n\n/**\n * A device's view of one encryption keyring (ENC-1, decision D4b; record format 2): the\n * server-stored, passphrase-wrapped ring, opened and authenticated locally.\n *\n * - **Shared key material.** Every device of a user opens the same record, so every\n * device holds the same data keys and decrypts everything.\n * - **Authenticated record.** The record carries an HMAC under a key derived from the\n * ring's master key; a device acts only on records it verified (RT-95). The server\n * can store, withhold or replay records, nothing more.\n * - **Forward only.** A device pins the ring id and highest revision it accepted and\n * refuses a lower revision (`KEY_RECORD_ROLLBACK`, RT-96), keeping its keys and\n * re-uploading its newer record when connected.\n * - **Writes re-read.** Every compare-and-set retry adopts the server's current record\n * first and wraps under that record's master key only (RT-97).\n * - **Lost records.** A device holding the ring re-uploads it unchanged; a new device\n * that finds no record but encrypted history waits (`KEY_RECORD_MISSING`); two rings\n * that exist anyway are merged by the first device holding both (RT-104).\n * - **Offline.** Once unlocked, keys are cached (non-extractable) and the keyring needs\n * no server; a cached record also lets `unlock()` work offline.\n */\nexport class EncryptionKeyring {\n\treadonly name: string\n\tprivate passphraseSource: string | (() => Promise<string>) | null\n\tprivate readonly cache: KeyCache\n\tprivate readonly kdfIterations: number\n\tprivate readonly encryptorOptions: EncryptorOptions\n\tprivate loadedPrincipal: string | null | undefined = undefined\n\t/** The last record this device authenticated and accepted: the pin. */\n\tprivate record: WrappedKeyRecord | null = null\n\t/** Keys derived from the pinned record's master key. */\n\tprivate master: MasterKeys | null = null\n\tprivate kek: CryptoKey | null = null\n\t/** Salt (base64) the KEK was derived with. */\n\tprivate kekSalt: string | null = null\n\t/** Held data keys by key id. Invariant: every held key id is in `record`. */\n\tprivate readonly dataKeys = new Map<string, CryptoKey>()\n\t/**\n\t * Ids of master keys a passphrase change retired, from every record this device\n\t * authenticated (persisted with the cache): a record authenticated only by one of\n\t * them is refused (RT-107).\n\t */\n\tprivate readonly retiredMasters = new Set<string>()\n\t/**\n\t * Ids of master keys this device held when the server served a newer revision of the\n\t * same ring that they do not authenticate (most likely a passphrase change made\n\t * elsewhere). A record authenticated only by one of them is not adopted without an\n\t * explicit `unlock(passphrase)`: after a genuine change, no later genuine record is\n\t * authenticated by the old master key (RT-107).\n\t */\n\tprivate readonly supersededMasters = new Set<string>()\n\tprivate encryptor: SyncEncryptor | null = null\n\tprivate status: EncryptionStatus\n\tprivate readonly listeners = new Set<(status: EncryptionStatus) => void>()\n\tprivate lockedByApp = false\n\tprivate pendingPassphrase: string | null = null\n\t/** A recovery requested while offline, run at the next handshake. */\n\tprivate pendingRecovery: { recoveryKey: string; newPassphrase: string } | null = null\n\t/** The configured passphrase that failed for a salt (not retried for that salt). */\n\tprivate failedSource: { salt: string; passphrase: string } | null = null\n\t/** The current lock is a server-side condition a later session can clear (no user action). */\n\tprivate retryable = false\n\t/** startNewKeyring() was called: a missing record with encrypted history may be replaced. */\n\tprivate allowNewRing = false\n\tprivate failedUnlocks = 0\n\tprivate nextUnlockAt = 0\n\tprivate mutex: Promise<unknown> = Promise.resolve()\n\n\tconstructor(options: EncryptionKeyringOptions = {}) {\n\t\tthis.name = options.keyring ?? DEFAULT_KEYRING\n\t\tthis.passphraseSource = options.passphrase ?? null\n\t\tthis.cache = options.cache ?? new MemoryKeyCache()\n\t\tthis.kdfIterations = options.kdfIterations ?? DEFAULT_PBKDF2_ITERATIONS\n\t\tif (!Number.isSafeInteger(this.kdfIterations) || this.kdfIterations < 1) {\n\t\t\tthrow new EncryptionKeyError('encryption.kdfIterations must be a positive integer.', {\n\t\t\t\tcode: 'INVALID_CONFIG',\n\t\t\t})\n\t\t}\n\t\tthis.encryptorOptions = options.encryptor ?? {}\n\t\tthis.status = this.makeStatus('locked', 'NO_PASSPHRASE', 'The keyring has not been opened.')\n\t}\n\n\t/** The encryptor of the unlocked keyring, or null while locked. */\n\tgetEncryptor(): SyncEncryptor | null {\n\t\treturn this.encryptor\n\t}\n\n\t/** Current status. */\n\tgetStatus(): EncryptionStatus {\n\t\treturn this.status\n\t}\n\n\t/** Whether this device holds the data key with this key id. */\n\tholdsKeyId(keyId: string): boolean {\n\t\treturn this.dataKeys.has(keyId)\n\t}\n\n\t/**\n\t * Whether the current lock is a server-side condition (a rolled-back, missing,\n\t * malformed or forked record) that a later sync session can clear without the user:\n\t * sync should reconnect with backoff rather than wait for `unlock()`.\n\t */\n\tisRetryableLock(): boolean {\n\t\treturn this.retryable && this.status.state !== 'unlocked'\n\t}\n\n\t/** Subscribe to status changes. Returns an unsubscribe function. */\n\tonStatusChange(listener: (status: EncryptionStatus) => void): () => void {\n\t\tthis.listeners.add(listener)\n\t\treturn () => this.listeners.delete(listener)\n\t}\n\n\t/**\n\t * Whether sync may connect: the keyring is unlocked, or a passphrase is available to\n\t * open (or create) the server record during the handshake.\n\t */\n\tcanConnect(): boolean {\n\t\tif (this.encryptor) return true\n\t\tif (this.pendingPassphrase !== null || this.pendingRecovery !== null) return true\n\t\treturn !this.lockedByApp && this.passphraseSource !== null\n\t}\n\n\t/**\n\t * Load the cached keyring of a principal (the signed-in user, or null). A principal\n\t * change drops everything held for the previous one first, so one user's keys never\n\t * encrypt another user's operations.\n\t */\n\tload(principal: string | null): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(() => this.loadLocked(principal))\n\t}\n\n\t/**\n\t * Open the keyring against the server's record during a sync handshake: fetch it,\n\t * create it on a user's first device, adopt a newer revision, re-upload a lost or\n\t * rolled-back one, or merge a forked ring.\n\t *\n\t * @returns 'ready' when unlocked, 'locked' when a passphrase is needed (or wrong), or\n\t * when the server's record must change first ({@link isRetryableLock})\n\t * @throws When the key service fails (the session is retried later)\n\t */\n\tsynchronize(channel: KeyServiceChannel, principal: string | null): Promise<'ready' | 'locked'> {\n\t\treturn this.exclusive(async () => {\n\t\t\tawait this.loadLocked(principal)\n\t\t\tif (this.encryptor === null) this.setStatus(this.makeStatus('unlocking'))\n\t\t\tconst recovery = this.pendingRecovery\n\t\t\tif (recovery !== null) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.recoverLocked(recovery.recoveryKey, recovery.newPassphrase, channel)\n\t\t\t\t\treturn 'ready'\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof EncryptionKeyError || error instanceof KeyUnwrapError) {\n\t\t\t\t\t\tthis.lockWith('error', 'RECOVERY_FAILED', errorMessage(error))\n\t\t\t\t\t\treturn 'locked'\n\t\t\t\t\t}\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst reply = await channel.fetch(this.name)\n\t\t\t\tif (reply.record === null) {\n\t\t\t\t\treturn await this.createOrRestoreLocked(channel, reply.knownKeyIds ?? [])\n\t\t\t\t}\n\t\t\t\treturn await this.adoptLocked(reply.record, null, channel)\n\t\t\t} catch (error) {\n\t\t\t\t// Permanent refusals: retrying the session would not change them.\n\t\t\t\tif (\n\t\t\t\t\terror instanceof EncryptionKeyError &&\n\t\t\t\t\t(error.keyCode === 'KEY_SERVICE_FORBIDDEN' || error.keyCode === 'KEY_SERVICE_UNSUPPORTED')\n\t\t\t\t) {\n\t\t\t\t\tthis.lockWith('error', error.keyCode, error.message)\n\t\t\t\t\treturn 'locked'\n\t\t\t\t}\n\t\t\t\tif (this.encryptor === null && this.status.state === 'unlocking') {\n\t\t\t\t\tthis.setStatus(this.makeStatus('locked', 'AWAITING_SERVER', errorMessage(error)))\n\t\t\t\t}\n\t\t\t\tthrow error\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Adopt a record the server pushed (another device rotated keys, changed the\n\t * passphrase or set up recovery). Without a connection to write to, a rollback or a\n\t * fork is reported and resolved at the next handshake.\n\t *\n\t * @returns 'ready' when still unlocked, 'locked' when the passphrase is needed or the\n\t * record was refused\n\t */\n\tadoptPushed(record: WrappedKeyRecord): Promise<'ready' | 'locked'> {\n\t\treturn this.exclusive(() => this.adoptLocked(record, null, null))\n\t}\n\n\t/**\n\t * Unlock with a passphrase. With a cached record this works offline; otherwise the\n\t * passphrase is kept until the next handshake opens (or creates) the server record,\n\t * and the returned promise settles then (pass `channel` when connected).\n\t *\n\t * Repeated wrong passphrases are slowed down on the device (backoff after three).\n\t * This is a usability guard, not a security boundary: whoever holds the record can\n\t * try passphrases offline, so passphrase strength and PBKDF2 are the protection.\n\t *\n\t * @throws {EncryptionKeyError} WRONG_PASSPHRASE, UNLOCK_THROTTLED, KEY_RECORD_MISSING\n\t */\n\tunlock(passphrase: string, channel: KeyServiceChannel | null): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(async () => {\n\t\t\tconst now = Date.now()\n\t\t\tif (now < this.nextUnlockAt) {\n\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t`Too many wrong passphrases on this device. Try again in ${Math.ceil((this.nextUnlockAt - now) / 1000)}s.`,\n\t\t\t\t\t{ code: 'UNLOCK_THROTTLED', retryAfterMs: this.nextUnlockAt - now },\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (passphrase.length === 0) {\n\t\t\t\tthrow new EncryptionKeyError('The passphrase must not be empty.', {\n\t\t\t\t\tcode: 'EMPTY_PASSPHRASE',\n\t\t\t\t})\n\t\t\t}\n\t\t\tthis.lockedByApp = false\n\t\t\tlet record = this.record\n\t\t\tif (channel) {\n\t\t\t\tconst reply = await channel.fetch(this.name)\n\t\t\t\tif (reply.record === null) {\n\t\t\t\t\t// Kept for the next handshake when the record is missing (KEY_RECORD_MISSING).\n\t\t\t\t\tthis.pendingPassphrase = passphrase\n\t\t\t\t\tconst outcome = await this.createOrRestoreLocked(channel, reply.knownKeyIds ?? [])\n\t\t\t\t\tif (outcome !== 'ready') throw this.statusError()\n\t\t\t\t\treturn this.status\n\t\t\t\t}\n\t\t\t\trecord = reply.record\n\t\t\t}\n\t\t\tif (record === null) {\n\t\t\t\t// No record known yet: open it at the next handshake.\n\t\t\t\tthis.pendingPassphrase = passphrase\n\t\t\t\tthis.setStatus(\n\t\t\t\t\tthis.makeStatus(\n\t\t\t\t\t\t'locked',\n\t\t\t\t\t\t'AWAITING_SERVER',\n\t\t\t\t\t\t'The passphrase is kept until the sync server provides the key record.',\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn this.status\n\t\t\t}\n\t\t\tthis.pendingPassphrase = passphrase\n\t\t\tconst outcome = await this.adoptLocked(record, passphrase, channel)\n\t\t\tif (outcome !== 'ready') {\n\t\t\t\tthis.pendingPassphrase = null\n\t\t\t\tthrow this.statusError()\n\t\t\t}\n\t\t\treturn this.status\n\t\t})\n\t}\n\n\t/**\n\t * Lock: forget the data keys, the master keys and the KEK on this device (cache\n\t * included). Sync stops until `unlock()`; local data stays readable (it is not\n\t * encrypted at rest by this layer). The pinned record is kept (it is the rollback\n\t * floor). The configured passphrase is not used again until `unlock()`.\n\t */\n\tlock(): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(async () => {\n\t\t\tthis.lockedByApp = true\n\t\t\tthis.pendingPassphrase = null\n\t\t\tthis.dropKeys()\n\t\t\tconst record = this.record\n\t\t\tif (record) {\n\t\t\t\tawait this.cache.save(this.cacheId(), {\n\t\t\t\t\trecord,\n\t\t\t\t\tkek: null,\n\t\t\t\t\tmaster: null,\n\t\t\t\t\tkeys: [],\n\t\t\t\t\tretiredMasters: [...this.retiredMasters],\n\t\t\t\t\tsupersededMasters: [...this.supersededMasters],\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tawait this.cache.clear(this.cacheId())\n\t\t\t}\n\t\t\tthis.lockWith('locked', 'LOCKED_BY_APP', 'Locked by the app. Call unlock(passphrase).')\n\t\t\treturn this.status\n\t\t})\n\t}\n\n\t/**\n\t * Rotate: create a new data key version. New operations use it; old versions stay\n\t * in the record so history decrypts. Needs the server (compare-and-set). Each retry\n\t * adopts the server's current record first and wraps under ITS master key only.\n\t */\n\trotate(channel: KeyServiceChannel): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(async () => {\n\t\t\tfor (let attempt = 0; ; attempt++) {\n\t\t\t\tconst record = this.requireRecord()\n\t\t\t\tconst master = this.requireMaster('rotate the key')\n\t\t\t\tconst dataKey = await generateDataKey()\n\t\t\t\tconst keyVersion = record.currentVersion + 1\n\t\t\t\tconst keyId = newKeyId()\n\t\t\t\tconst next = await sealRecord(\n\t\t\t\t\t{\n\t\t\t\t\t\t...record,\n\t\t\t\t\t\trevision: record.revision + 1,\n\t\t\t\t\t\tcurrentVersion: keyVersion,\n\t\t\t\t\t\tkeys: [\n\t\t\t\t\t\t\t...record.keys,\n\t\t\t\t\t\t\tawait wrapDataKey(dataKey, master.wrapKey, this.name, keyVersion, keyId),\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\tmaster.macKey,\n\t\t\t\t)\n\t\t\t\tconst reply = await channel.put(this.name, next, record.revision)\n\t\t\t\tif (reply.status === 'ok') {\n\t\t\t\t\tthis.dataKeys.set(keyId, await toNonExtractable(dataKey))\n\t\t\t\t\tawait this.commitLocked(next)\n\t\t\t\t\treturn this.status\n\t\t\t\t}\n\t\t\t\tawait this.followConflictLocked(reply, attempt, null, channel)\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Change the passphrase: a NEW master key, sealed under a KEK derived from the new\n\t * passphrase and a new salt, re-wraps every data key version (and the recovery wrap,\n\t * if any). No operation is re-encrypted. The record lists the old master key as\n\t * retired (`retiredMasters`): a device that authenticated this record (or a later\n\t * one) refuses every record authenticated only by the old master key, however it is\n\t * opened. Other devices keep working with their cached data keys (status\n\t * `PASSPHRASE_REQUIRED` while unlocked); they need the new passphrase for a key they\n\t * do not hold and for key management. A device that was served the new record but\n\t * could not read it no longer adopts, without an explicit `unlock()`, a record that\n\t * only its old master key authenticates.\n\t *\n\t * **A passphrase change alone does not contain a leaked passphrase.** The ring is a\n\t * shared secret: a device that still holds only the OLD master key cannot tell a\n\t * record the server forged under it (with a data key of the attacker's) from a\n\t * genuine one, and the server decides which records it forwards. After a suspected\n\t * leak: change the passphrase, `rotate()`, call `enableRecovery()` again (a recovery\n\t * key made before the change can be steered to a ring the old passphrase built) and\n\t * re-unlock EVERY other device with the new passphrase (`lock()`, then\n\t * `unlock(newPassphrase)`, and update any configured `key`; never type the old\n\t * passphrase again: an explicit unlock with it is the user's decision). A device that\n\t * is not re-unlocked, or an old recovery key that is still used, remains exposed.\n\t *\n\t * @param newPassphrase - The new passphrase\n\t * @param channel - The key service\n\t * @param currentPassphrase - Optional: verified against the record first\n\t */\n\tchangePassphrase(\n\t\tnewPassphrase: string,\n\t\tchannel: KeyServiceChannel,\n\t\tcurrentPassphrase?: string,\n\t): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(async () => {\n\t\t\tif (newPassphrase.length === 0) {\n\t\t\t\tthrow new EncryptionKeyError('The new passphrase must not be empty.', {\n\t\t\t\t\tcode: 'EMPTY_PASSPHRASE',\n\t\t\t\t})\n\t\t\t}\n\t\t\tfor (let attempt = 0; ; attempt++) {\n\t\t\t\tconst record = this.requireRecord()\n\t\t\t\tconst master =\n\t\t\t\t\tcurrentPassphrase !== undefined\n\t\t\t\t\t\t? (await this.openWithPassphrase(record, currentPassphrase)).master\n\t\t\t\t\t\t: this.requireMaster('change the passphrase')\n\t\t\t\tconst raw = generateMasterKey()\n\t\t\t\tconst salt = randomBytes(KEK_SALT_BYTES)\n\t\t\t\tlet next: WrappedKeyRecord\n\t\t\t\tlet newMaster: MasterKeys\n\t\t\t\tlet newKek: CryptoKey\n\t\t\t\ttry {\n\t\t\t\t\tnewMaster = await importMasterKey(raw)\n\t\t\t\t\tnewKek = await deriveKeyEncryptionKey(newPassphrase, salt, this.kdfIterations)\n\t\t\t\t\tconst keys: WrappedDataKey[] = []\n\t\t\t\t\tfor (const entry of record.keys) {\n\t\t\t\t\t\tconst dataKey = await unwrapDataKey(entry, master.wrapKey, this.name, true)\n\t\t\t\t\t\tkeys.push(\n\t\t\t\t\t\t\tawait wrapDataKey(\n\t\t\t\t\t\t\t\tdataKey,\n\t\t\t\t\t\t\t\tnewMaster.wrapKey,\n\t\t\t\t\t\t\t\tthis.name,\n\t\t\t\t\t\t\t\tentry.keyVersion,\n\t\t\t\t\t\t\t\tentry.keyId,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tconst { recovery: previousRecovery, ...rest } = record\n\t\t\t\t\tnext = await sealRecord(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t...rest,\n\t\t\t\t\t\t\trevision: record.revision + 1,\n\t\t\t\t\t\t\tkdf: {\n\t\t\t\t\t\t\t\tname: 'PBKDF2',\n\t\t\t\t\t\t\t\thash: 'SHA-256',\n\t\t\t\t\t\t\t\titerations: this.kdfIterations,\n\t\t\t\t\t\t\t\tsalt: toBase64(salt),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tmaster: await wrapMasterKey(raw, newKek, this.name, record.ringId),\n\t\t\t\t\t\t\tkeys,\n\t\t\t\t\t\t\t// Authenticated under the NEW master: a device that accepts this record\n\t\t\t\t\t\t\t// never again accepts one authenticated only by the old one (RT-107).\n\t\t\t\t\t\t\tretiredMasters: [...new Set([...(record.retiredMasters ?? []), master.id])],\n\t\t\t\t\t\t\t// The recovery public key was authenticated with the record; the new\n\t\t\t\t\t\t\t// master key is wrapped to it, so the recovery key keeps working.\n\t\t\t\t\t\t\t...(previousRecovery\n\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\trecovery: await wrapMasterForRecovery(\n\t\t\t\t\t\t\t\t\t\t\traw,\n\t\t\t\t\t\t\t\t\t\t\tpreviousRecovery.publicKey,\n\t\t\t\t\t\t\t\t\t\t\tthis.name,\n\t\t\t\t\t\t\t\t\t\t\trecord.ringId,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnewMaster.macKey,\n\t\t\t\t\t)\n\t\t\t\t} finally {\n\t\t\t\t\traw.fill(0)\n\t\t\t\t}\n\t\t\t\tconst reply = await channel.put(this.name, next, record.revision)\n\t\t\t\tif (reply.status === 'ok') {\n\t\t\t\t\tthis.master = newMaster\n\t\t\t\t\tthis.kek = newKek\n\t\t\t\t\tthis.kekSalt = next.kdf.salt\n\t\t\t\t\tthis.failedSource = null\n\t\t\t\t\tawait this.commitLocked(next)\n\t\t\t\t\treturn this.status\n\t\t\t\t}\n\t\t\t\tawait this.followConflictLocked(reply, attempt, currentPassphrase ?? null, channel)\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Set up (or replace) the recovery key: the ring's master key is also wrapped to a\n\t * new recovery public key, and the private half is returned ONCE. Store it offline;\n\t * it recovers the data after a lost passphrase. Without it, a lost passphrase means\n\t * the encrypted data is unrecoverable. Needs the passphrase key of the CURRENT\n\t * record (the recovery block is authenticated with the record, so only a holder of\n\t * the master key can add or replace it).\n\t *\n\t * The same write adds a new data key version (a rotation) that anchors the recovery\n\t * key to this ring: `recover()` accepts only a ring holding that key, which no older\n\t * passphrase ever opened (RT-107). After a suspected passphrase leak, call this again\n\t * once the passphrase was changed, and destroy the previous recovery key.\n\t *\n\t * @returns The recovery key (`kora-rk3-<key>.<anchor>`)\n\t */\n\tenableRecovery(channel: KeyServiceChannel): Promise<string> {\n\t\treturn this.exclusive(async () => {\n\t\t\tfor (let attempt = 0; ; attempt++) {\n\t\t\t\tconst record = this.requireRecord()\n\t\t\t\tconst master = this.requireMaster('set up a recovery key')\n\t\t\t\tconst kek = this.requireKek(record, 'set up a recovery key')\n\t\t\t\tconst raw = await unwrapMasterKey(record.master, kek, this.name, record.ringId).catch(\n\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t\t\t'This device cannot open the current key record with its passphrase key. Unlock with the current passphrase, then try again.',\n\t\t\t\t\t\t\t{ code: 'PASSPHRASE_REQUIRED', cause: errorMessage(error) },\n\t\t\t\t\t\t)\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\t// The anchor key: a new data key version created in this write under the\n\t\t\t\t// current master key only. No earlier passphrase or master key ever opened it,\n\t\t\t\t// so nobody holding an older (leaked) passphrase can build a ring the recovery\n\t\t\t\t// key accepts (RT-107). It also becomes the key new operations use.\n\t\t\t\tconst anchorKey = await generateDataKey()\n\t\t\t\tconst keyVersion = record.currentVersion + 1\n\t\t\t\tconst keyId = newKeyId()\n\t\t\t\tlet next: WrappedKeyRecord\n\t\t\t\tlet recoveryKey: string\n\t\t\t\ttry {\n\t\t\t\t\tconst { publicKey, d } = await createRecoveryKeyPair()\n\t\t\t\t\trecoveryKey = formatRecoveryKey(\n\t\t\t\t\t\td,\n\t\t\t\t\t\tawait recoveryAnchor(anchorKey, this.name, record.ringId, keyId, publicKey),\n\t\t\t\t\t)\n\t\t\t\t\tnext = await sealRecord(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t...record,\n\t\t\t\t\t\t\trevision: record.revision + 1,\n\t\t\t\t\t\t\tcurrentVersion: keyVersion,\n\t\t\t\t\t\t\tkeys: [\n\t\t\t\t\t\t\t\t...record.keys,\n\t\t\t\t\t\t\t\tawait wrapDataKey(anchorKey, master.wrapKey, this.name, keyVersion, keyId),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\trecovery: await wrapMasterForRecovery(raw, publicKey, this.name, record.ringId),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmaster.macKey,\n\t\t\t\t\t)\n\t\t\t\t} finally {\n\t\t\t\t\traw.fill(0)\n\t\t\t\t}\n\t\t\t\tconst reply = await channel.put(this.name, next, record.revision)\n\t\t\t\tif (reply.status === 'ok') {\n\t\t\t\t\tthis.dataKeys.set(keyId, await toNonExtractable(anchorKey))\n\t\t\t\t\tawait this.commitLocked(next)\n\t\t\t\t\treturn recoveryKey\n\t\t\t\t}\n\t\t\t\tawait this.followConflictLocked(reply, attempt, null, channel)\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Recover after a lost passphrase: open the ring's master key with the recovery key,\n\t * verify the whole record with it, and seal the master key under a new passphrase.\n\t * Unlocks this device. Without a channel (sync not connected) the recovery runs at\n\t * the next handshake. Devices that hold the ring keep working without a prompt (the\n\t * master key is unchanged).\n\t */\n\trecover(\n\t\trecoveryKey: string,\n\t\tnewPassphrase: string,\n\t\tchannel: KeyServiceChannel | null,\n\t): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(async () => {\n\t\t\tif (newPassphrase.length === 0) {\n\t\t\t\tthrow new EncryptionKeyError('The new passphrase must not be empty.', {\n\t\t\t\t\tcode: 'EMPTY_PASSPHRASE',\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (channel === null) {\n\t\t\t\tthis.pendingRecovery = { recoveryKey, newPassphrase }\n\t\t\t\tthis.setStatus(\n\t\t\t\t\tthis.makeStatus(\n\t\t\t\t\t\t'locked',\n\t\t\t\t\t\t'AWAITING_SERVER',\n\t\t\t\t\t\t'Recovery runs when the sync server provides the key record.',\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn this.status\n\t\t\t}\n\t\t\treturn this.recoverLocked(recoveryKey, newPassphrase, channel)\n\t\t})\n\t}\n\n\t/**\n\t * Last resort after the server lost the key record and no device that holds the ring\n\t * will come back (`KEY_RECORD_MISSING`): allow this device to create a new ring.\n\t * Operations encrypted under the lost ring stay unreadable on this device. If a device\n\t * holding the old ring reconnects later, it merges both rings and the history becomes\n\t * readable again. Without a channel (sync is not connected: a missing record ends the\n\t * session) the ring is created at the next handshake.\n\t *\n\t * @throws {EncryptionKeyError} KEY_RECORD_EXISTS when the server has a record (unlock it)\n\t */\n\tstartNewKeyring(channel: KeyServiceChannel | null): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(async () => {\n\t\t\tthis.allowNewRing = true\n\t\t\tif (channel === null) {\n\t\t\t\tthis.retryable = false\n\t\t\t\tthis.setStatus(\n\t\t\t\t\tthis.makeStatus(\n\t\t\t\t\t\t'locked',\n\t\t\t\t\t\t'AWAITING_SERVER',\n\t\t\t\t\t\t'A new keyring is created when the sync server is reached (unless it holds a record).',\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn this.status\n\t\t\t}\n\t\t\tconst reply = await channel.fetch(this.name)\n\t\t\tif (reply.record !== null) {\n\t\t\t\tthis.allowNewRing = false\n\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t'The sync server holds a key record for this keyring: unlock it with the passphrase instead.',\n\t\t\t\t\t{ code: 'KEY_RECORD_EXISTS' },\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst outcome = await this.createOrRestoreLocked(channel, reply.knownKeyIds ?? [])\n\t\t\tif (outcome !== 'ready') throw this.statusError()\n\t\t\treturn this.status\n\t\t})\n\t}\n\n\tprivate async recoverLocked(\n\t\trecoveryKey: string,\n\t\tnewPassphrase: string,\n\t\tchannel: KeyServiceChannel,\n\t): Promise<EncryptionStatus> {\n\t\tthis.pendingRecovery = null\n\t\tfor (let attempt = 0; ; attempt++) {\n\t\t\tconst reply = await channel.fetch(this.name)\n\t\t\tconst record = reply.record\n\t\t\tif (record === null || !validateKeyRecord(record, this.name).ok) {\n\t\t\t\tthrow new EncryptionKeyError('The server has no key record to recover.', {\n\t\t\t\t\tcode: 'KEY_RECORD_MISSING',\n\t\t\t\t})\n\t\t\t}\n\t\t\tconst recovery = record.recovery\n\t\t\tif (!recovery) {\n\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t'This keyring has no recovery key. Without the passphrase its data cannot be decrypted.',\n\t\t\t\t\t{ code: 'NO_RECOVERY_KEY' },\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst pinned = this.record\n\t\t\tif (pinned && pinned.ringId === record.ringId && record.revision < pinned.revision) {\n\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t`The server served revision ${record.revision} of the key record, older than revision ${pinned.revision} this device accepted. Recovery is refused on a rolled-back record.`,\n\t\t\t\t\t{ code: 'KEY_RECORD_ROLLBACK' },\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst raw = await unwrapMasterWithRecovery(recovery, recoveryKey, this.name, record.ringId)\n\t\t\tconst salt = randomBytes(KEK_SALT_BYTES)\n\t\t\tlet master: MasterKeys\n\t\t\tlet newKek: CryptoKey\n\t\t\tlet masterWrap: WrappedKeyRecord['master']\n\t\t\ttry {\n\t\t\t\tmaster = await importMasterKey(raw)\n\t\t\t\tif (!(await verifyRecordMac(record, master.macKey))) {\n\t\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t\t'The key record does not authenticate under the recovered master key: it was modified by someone without the keys. Recovery is refused.',\n\t\t\t\t\t\t{ code: 'KEY_RECORD_INVALID' },\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tif (this.isRetiredMaster(record, master)) {\n\t\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t\t'The key record is authenticated by a master key a passphrase change retired. Recovery is refused.',\n\t\t\t\t\t\t{ code: 'KEY_RECORD_INVALID' },\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\t// The recovery PUBLIC key is no secret: anyone can wrap a master key of their own\n\t\t\t\t// to it and seal a record under that master. Only the user's own ring holds the\n\t\t\t\t// anchor key created with the recovery key, which no older passphrase opened.\n\t\t\t\tif (\n\t\t\t\t\t!(await this.holdsAnchor(\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tmaster,\n\t\t\t\t\t\trecovery.publicKey,\n\t\t\t\t\t\tparseRecoveryKey(recoveryKey).anchor,\n\t\t\t\t\t))\n\t\t\t\t) {\n\t\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t\t'The key record opens with this recovery key but is not the keyring the recovery key was made for: it lacks the data key the recovery key is anchored to (the server substituted another keyring). Recovery is refused.',\n\t\t\t\t\t\t{ code: 'KEY_RECORD_INVALID' },\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tnewKek = await deriveKeyEncryptionKey(newPassphrase, salt, this.kdfIterations)\n\t\t\t\tmasterWrap = await wrapMasterKey(raw, newKek, this.name, record.ringId)\n\t\t\t} finally {\n\t\t\t\traw.fill(0)\n\t\t\t}\n\t\t\tconst opened = await this.openDataKeys(record, master)\n\t\t\tconst absorbed = await this.absorbHeldKeys(record, master)\n\t\t\tconst next = await sealRecord(\n\t\t\t\t{\n\t\t\t\t\t...record,\n\t\t\t\t\trevision: this.nextRevision(record),\n\t\t\t\t\tkdf: {\n\t\t\t\t\t\tname: 'PBKDF2',\n\t\t\t\t\t\thash: 'SHA-256',\n\t\t\t\t\t\titerations: this.kdfIterations,\n\t\t\t\t\t\tsalt: toBase64(salt),\n\t\t\t\t\t},\n\t\t\t\t\tmaster: masterWrap,\n\t\t\t\t\tkeys: [...record.keys, ...absorbed],\n\t\t\t\t\tcurrentVersion: record.currentVersion + absorbed.length,\n\t\t\t\t},\n\t\t\t\tmaster.macKey,\n\t\t\t)\n\t\t\tconst put = await channel.put(this.name, next, record.revision)\n\t\t\tif (put.status === 'ok') {\n\t\t\t\tthis.lockedByApp = false\n\t\t\t\tthis.master = master\n\t\t\t\tthis.kek = newKek\n\t\t\t\tthis.kekSalt = next.kdf.salt\n\t\t\t\tfor (const [keyId, key] of opened) this.dataKeys.set(keyId, key)\n\t\t\t\tawait this.commitLocked(next)\n\t\t\t\treturn this.status\n\t\t\t}\n\t\t\tif (attempt + 1 >= MAX_WRITE_ATTEMPTS) throw conflictError()\n\t\t}\n\t}\n\n\t// --- internals (all run under the mutex) ---\n\n\tprivate async loadLocked(principal: string | null): Promise<EncryptionStatus> {\n\t\tif (this.loadedPrincipal === principal) return this.status\n\t\tthis.dropKeys()\n\t\tthis.record = null\n\t\tthis.retiredMasters.clear()\n\t\tthis.supersededMasters.clear()\n\t\tthis.failedSource = null\n\t\tthis.allowNewRing = false\n\t\t// A passphrase given before the first load (unlock() at start-up) is kept; one given\n\t\t// for another principal is not.\n\t\tif (this.loadedPrincipal !== undefined) this.pendingPassphrase = null\n\t\tthis.loadedPrincipal = principal\n\t\tlet cached: CachedKeyring | null = null\n\t\ttry {\n\t\t\tcached = await this.cache.load(this.cacheId())\n\t\t} catch {\n\t\t\tcached = null\n\t\t}\n\t\tif (cached && validateKeyRecord(cached.record, this.name).ok) {\n\t\t\tconst record = cached.record\n\t\t\tthis.record = record\n\t\t\tfor (const id of [...(cached.retiredMasters ?? []), ...(record.retiredMasters ?? [])]) {\n\t\t\t\tif (isMasterId(id)) this.retiredMasters.add(id)\n\t\t\t}\n\t\t\tfor (const id of cached.supersededMasters ?? []) {\n\t\t\t\tif (isMasterId(id)) this.supersededMasters.add(id)\n\t\t\t}\n\t\t\tconst known = new Set(record.keys.map((key) => key.keyId))\n\t\t\tfor (const entry of cached.keys) {\n\t\t\t\tif (known.has(entry.keyId)) this.dataKeys.set(entry.keyId, entry.key)\n\t\t\t}\n\t\t\tconst current = record.keys.find((key) => key.keyVersion === record.currentVersion)\n\t\t\t// A release-candidate cache keeps master keys without an id: unlock again.\n\t\t\tif (\n\t\t\t\tcached.master &&\n\t\t\t\ttypeof cached.master.id === 'string' &&\n\t\t\t\tcurrent &&\n\t\t\t\tthis.dataKeys.has(current.keyId)\n\t\t\t) {\n\t\t\t\tthis.master = cached.master\n\t\t\t\tthis.kek = cached.kek\n\t\t\t\tthis.kekSalt = cached.kek ? record.kdf.salt : null\n\t\t\t\tthis.rebuildEncryptor()\n\t\t\t\tthis.setStatus(this.makeStatus('unlocked'))\n\t\t\t\treturn this.status\n\t\t\t}\n\t\t\tthis.dropKeys()\n\t\t}\n\t\tthis.setStatus(\n\t\t\tthis.makeStatus(\n\t\t\t\t'locked',\n\t\t\t\tthis.lockedByApp ? 'LOCKED_BY_APP' : 'NO_PASSPHRASE',\n\t\t\t\t'Unlock with the encryption passphrase.',\n\t\t\t),\n\t\t)\n\t\treturn this.status\n\t}\n\n\t/**\n\t * No record on the server. A device that holds a ring re-uploads it unchanged (same\n\t * revision and MAC, so every device's pin still accepts it). A device without one\n\t * creates the first ring, unless the server reports encrypted history: then the\n\t * record was lost, and it waits for a device that holds the ring.\n\t */\n\tprivate async createOrRestoreLocked(\n\t\tchannel: KeyServiceChannel,\n\t\tknownKeyIds: string[],\n\t): Promise<'ready' | 'locked'> {\n\t\tif (this.record !== null) {\n\t\t\tconst restored = this.record\n\t\t\tconst reply = await channel.put(this.name, restored, 0)\n\t\t\treturn this.adoptLocked(reply.status === 'ok' ? restored : reply.record, null, channel)\n\t\t}\n\t\tconst history = knownKeyIds.filter((keyId) => isKeyId(keyId))\n\t\tif (history.length > 0 && !this.allowNewRing) {\n\t\t\tthis.holdFor(\n\t\t\t\t'locked',\n\t\t\t\t'KEY_RECORD_MISSING',\n\t\t\t\t`The sync server has no key record for keyring \"${this.name}\", but it stores operations encrypted with it: the record was lost (for example a server restored without its key table). Waiting for a device that holds the keyring to reconnect and re-upload it. If no such device exists, startNewKeyring() starts a new keyring; the old history then stays unreadable.`,\n\t\t\t)\n\t\t\treturn 'locked'\n\t\t}\n\t\treturn this.createRingLocked(channel)\n\t}\n\n\t/** Create a brand-new ring (the user's first device). */\n\tprivate async createRingLocked(channel: KeyServiceChannel): Promise<'ready' | 'locked'> {\n\t\tconst passphrase = await this.availablePassphrase()\n\t\tif (passphrase === null) {\n\t\t\tthis.lockWith(\n\t\t\t\t'locked',\n\t\t\t\t'NO_PASSPHRASE',\n\t\t\t\t'No key record exists yet; unlock with a passphrase to create it.',\n\t\t\t)\n\t\t\treturn 'locked'\n\t\t}\n\t\tconst ringId = newRingId()\n\t\tconst salt = randomBytes(KEK_SALT_BYTES)\n\t\tconst kek = await deriveKeyEncryptionKey(passphrase, salt, this.kdfIterations)\n\t\tconst raw = generateMasterKey()\n\t\tlet master: MasterKeys\n\t\tlet masterWrap: WrappedKeyRecord['master']\n\t\ttry {\n\t\t\tmaster = await importMasterKey(raw)\n\t\t\tmasterWrap = await wrapMasterKey(raw, kek, this.name, ringId)\n\t\t} finally {\n\t\t\traw.fill(0)\n\t\t}\n\t\tconst dataKey = await generateDataKey()\n\t\tconst keyId = newKeyId()\n\t\tconst record = await sealRecord(\n\t\t\t{\n\t\t\t\tformat: KEY_RECORD_FORMAT,\n\t\t\t\tkeyring: this.name,\n\t\t\t\tringId,\n\t\t\t\trevision: 1,\n\t\t\t\tcurrentVersion: 1,\n\t\t\t\tkdf: {\n\t\t\t\t\tname: 'PBKDF2',\n\t\t\t\t\thash: 'SHA-256',\n\t\t\t\t\titerations: this.kdfIterations,\n\t\t\t\t\tsalt: toBase64(salt),\n\t\t\t\t},\n\t\t\t\tmaster: masterWrap,\n\t\t\t\tkeys: [await wrapDataKey(dataKey, master.wrapKey, this.name, 1, keyId)],\n\t\t\t},\n\t\t\tmaster.macKey,\n\t\t)\n\t\tconst reply = await channel.put(this.name, record, 0)\n\t\tif (reply.status === 'ok') {\n\t\t\t// Only the confirmed record is used: a key that lost the creation race would\n\t\t\t// fork the user's data into a key no other device has.\n\t\t\tthis.allowNewRing = false\n\t\t\tthis.master = master\n\t\t\tthis.kek = kek\n\t\t\tthis.kekSalt = record.kdf.salt\n\t\t\tthis.dataKeys.set(keyId, await toNonExtractable(dataKey))\n\t\t\tthis.pendingPassphrase = null\n\t\t\tawait this.commitLocked(record)\n\t\t\treturn 'ready'\n\t\t}\n\t\tif (reply.record === null) {\n\t\t\tthrow new EncryptionKeyError('The key service refused the first record without a reason.', {\n\t\t\t\tcode: 'KEY_RECORD_CONFLICT',\n\t\t\t})\n\t\t}\n\t\t// Another device created the record first: open that one.\n\t\treturn this.adoptLocked(reply.record, passphrase, channel)\n\t}\n\n\t/**\n\t * Accept a server record, after authenticating it.\n\t *\n\t * - Lower revision of the pinned ring: refused (rollback); re-upload the pin.\n\t * - Authenticated by the held master key, or by the passphrase (explicit, else the\n\t * configured one): forward when it keeps every key this device holds, else merge.\n\t * - Not authenticated: nothing is adopted. A device that holds every key of a newer\n\t * revision of its ring keeps working (the passphrase changed elsewhere); otherwise\n\t * it needs the passphrase.\n\t */\n\tprivate async adoptLocked(\n\t\tincoming: WrappedKeyRecord | null,\n\t\tpassphrase: string | null,\n\t\tchannel: KeyServiceChannel | null,\n\t\tdepth = 0,\n\t): Promise<'ready' | 'locked'> {\n\t\tif (incoming === null) {\n\t\t\treturn this.refuseRecord('KEY_RECORD_INVALID', 'The key service returned no record.')\n\t\t}\n\t\tconst validation = validateKeyRecord(incoming, this.name)\n\t\tif (!validation.ok) {\n\t\t\treturn this.refuseRecord(\n\t\t\t\t'KEY_RECORD_INVALID',\n\t\t\t\t`The server's key record is malformed (${validation.reason}).`,\n\t\t\t)\n\t\t}\n\t\tif (incoming.kdf.iterations < this.kdfIterations) {\n\t\t\t// A server must not be able to make devices derive a weaker KEK.\n\t\t\treturn this.refuseRecord(\n\t\t\t\t'KEY_RECORD_INVALID',\n\t\t\t\t`The server's key record uses ${incoming.kdf.iterations} PBKDF2 iterations, below this app's minimum of ${this.kdfIterations}.`,\n\t\t\t)\n\t\t}\n\t\tconst pinned = this.record\n\t\tconst explicit = passphrase !== null\n\t\tif (pinned !== null && incoming.ringId === pinned.ringId) {\n\t\t\tif (incoming.revision < pinned.revision) {\n\t\t\t\treturn this.rollbackLocked(incoming, passphrase, channel, depth)\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!explicit &&\n\t\t\t\tincoming.revision === pinned.revision &&\n\t\t\t\tcanonicalJson(incoming) === canonicalJson(pinned) &&\n\t\t\t\tthis.master !== null &&\n\t\t\t\tthis.encryptor !== null\n\t\t\t) {\n\t\t\t\tif (this.status.state !== 'unlocked' || this.status.code !== undefined) {\n\t\t\t\t\tthis.retryable = false\n\t\t\t\t\tthis.setStatus(this.makeStatus('unlocked'))\n\t\t\t\t}\n\t\t\t\treturn 'ready'\n\t\t\t}\n\t\t}\n\n\t\tlet opened = await this.authenticateHeld(incoming)\n\t\tlet sourceFailed = false\n\t\tif (opened === null || explicit) {\n\t\t\tconst source = passphrase ?? (await this.sourcePassphraseFor(incoming))\n\t\t\tif (source !== null) {\n\t\t\t\ttry {\n\t\t\t\t\topened = await this.openWithPassphrase(incoming, source)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!(error instanceof EncryptionKeyError)) throw error\n\t\t\t\t\tif (error.keyCode === 'KEY_RECORD_INVALID') {\n\t\t\t\t\t\treturn this.refuseRecord('KEY_RECORD_INVALID', error.message)\n\t\t\t\t\t}\n\t\t\t\t\tif (explicit) {\n\t\t\t\t\t\tthis.noteFailedUnlock()\n\t\t\t\t\t\t// A mistyped unlock() on an unlocked device changes nothing.\n\t\t\t\t\t\tif (this.encryptor !== null) throw error\n\t\t\t\t\t\tthis.lockWith('error', 'WRONG_PASSPHRASE', error.message)\n\t\t\t\t\t\treturn 'locked'\n\t\t\t\t\t}\n\t\t\t\t\tthis.failedSource = { salt: incoming.kdf.salt, passphrase: source }\n\t\t\t\t\tsourceFailed = true\n\t\t\t\t}\n\t\t\t} else if (this.failedSource?.salt === incoming.kdf.salt) {\n\t\t\t\tsourceFailed = true\n\t\t\t}\n\t\t}\n\t\tif (opened === null) return this.unverifiableLocked(incoming, sourceFailed)\n\t\tif (this.isRetiredMaster(incoming, opened.master)) return this.refuseRetired()\n\t\tif (this.isSupersededMaster(opened.master, explicit)) return this.refuseSuperseded()\n\t\treturn this.acceptLocked(incoming, opened, passphrase, channel, depth)\n\t}\n\n\t/** An authenticated record: move forward to it, or merge it with the held ring. */\n\tprivate async acceptLocked(\n\t\tincoming: WrappedKeyRecord,\n\t\topened: OpenedRing,\n\t\tpassphrase: string | null,\n\t\tchannel: KeyServiceChannel | null,\n\t\tdepth: number,\n\t): Promise<'ready' | 'locked'> {\n\t\tconst incomingIds = new Set(incoming.keys.map((key) => key.keyId))\n\t\tconst uncovered = [...this.dataKeys.keys()].some((keyId) => !incomingIds.has(keyId))\n\t\tif (uncovered) {\n\t\t\t// Another ring (the server lost the record and a device created a new one), or a\n\t\t\t// diverged revision: it lacks keys this device holds. Merge, never drop them.\n\t\t\tif (channel === null) {\n\t\t\t\tthis.holdFor(\n\t\t\t\t\t'error',\n\t\t\t\t\t'KEY_RING_FORK',\n\t\t\t\t\t'The server holds a key ring that lacks keys this device holds. Both rings are merged at the next sync session.',\n\t\t\t\t)\n\t\t\t\treturn 'locked'\n\t\t\t}\n\t\t\treturn this.mergeLocked(incoming, opened, passphrase, channel, depth)\n\t\t}\n\t\tlet keys: Map<string, CryptoKey>\n\t\ttry {\n\t\t\tkeys = await this.openDataKeys(incoming, opened.master)\n\t\t} catch (error) {\n\t\t\treturn this.refuseRecord(\n\t\t\t\t'KEY_RECORD_INVALID',\n\t\t\t\t`An authenticated key record holds a data-key wrap its master key does not open (${errorMessage(error)}).`,\n\t\t\t)\n\t\t}\n\t\tfor (const [keyId, key] of keys) this.dataKeys.set(keyId, key)\n\t\tthis.master = opened.master\n\t\tif (opened.kek !== null) {\n\t\t\tthis.kek = opened.kek\n\t\t\tthis.kekSalt = incoming.kdf.salt\n\t\t} else if (this.kekSalt !== incoming.kdf.salt) {\n\t\t\tthis.kek = null\n\t\t\tthis.kekSalt = null\n\t\t}\n\t\tif (passphrase !== null) {\n\t\t\tthis.failedUnlocks = 0\n\t\t\tthis.nextUnlockAt = 0\n\t\t}\n\t\tthis.pendingPassphrase = null\n\t\tawait this.commitLocked(incoming)\n\t\treturn 'ready'\n\t}\n\n\t/** A lower revision of the pinned ring: refuse it, keep the keys, re-upload the pin. */\n\tprivate async rollbackLocked(\n\t\tincoming: WrappedKeyRecord,\n\t\tpassphrase: string | null,\n\t\tchannel: KeyServiceChannel | null,\n\t\tdepth: number,\n\t): Promise<'ready' | 'locked'> {\n\t\tconst pinned = this.record as WrappedKeyRecord\n\t\tthis.holdFor(\n\t\t\t'error',\n\t\t\t'KEY_RECORD_ROLLBACK',\n\t\t\t`The server served revision ${incoming.revision} of the key record, older than revision ${pinned.revision} this device accepted (a rollback, or a server restored from an old backup). It is refused; this device keeps its keys${channel ? ' and re-uploads its newer record' : ' and re-uploads its newer record at the next sync session'}.`,\n\t\t)\n\t\tif (channel === null || depth >= MAX_WRITE_ATTEMPTS) return 'locked'\n\t\tconst pinnedVersions = new Map(pinned.keys.map((key) => [key.keyVersion, key.keyId]))\n\t\tconst subset = incoming.keys.every((key) => pinnedVersions.get(key.keyVersion) === key.keyId)\n\t\tif (subset) {\n\t\t\t// The pin keeps everything the older revision has: put it back as it is.\n\t\t\tconst reply = await channel.put(this.name, pinned, incoming.revision)\n\t\t\treturn this.adoptLocked(\n\t\t\t\treply.status === 'ok' ? pinned : reply.record,\n\t\t\t\tpassphrase,\n\t\t\t\tchannel,\n\t\t\t\tdepth + 1,\n\t\t\t)\n\t\t}\n\t\t// The older revision has keys this device lacks (the ring diverged after a\n\t\t// restore): merge both into a revision above either, once it is authenticated.\n\t\tlet opened = await this.authenticateHeld(incoming)\n\t\tif (opened === null) {\n\t\t\tconst source = passphrase ?? (await this.sourcePassphraseFor(incoming))\n\t\t\tif (source !== null) {\n\t\t\t\topened = await this.openWithPassphrase(incoming, source).catch(() => null)\n\t\t\t}\n\t\t}\n\t\tif (opened === null) return 'locked'\n\t\tif (this.isRetiredMaster(incoming, opened.master)) return this.refuseRetired()\n\t\tif (this.isSupersededMaster(opened.master, passphrase !== null)) {\n\t\t\treturn this.refuseSuperseded()\n\t\t}\n\t\treturn this.mergeLocked(incoming, opened, passphrase, channel, depth)\n\t}\n\n\t/**\n\t * A record authenticated only by a master key a passphrase change retired: whoever\n\t * wrote it knew an OLD passphrase (or master key), not the current one (RT-107).\n\t */\n\tprivate refuseRetired(): 'locked' {\n\t\treturn this.refuseRecord(\n\t\t\t'KEY_RECORD_INVALID',\n\t\t\t\"The server's key record is authenticated only by a master key that a passphrase change retired: it was written with an old passphrase. It is refused; this device keeps its keys.\",\n\t\t)\n\t}\n\n\t/**\n\t * A record authenticated only by a master key this device held when the server served\n\t * a newer revision of the ring that the key does not authenticate (RT-107).\n\t */\n\tprivate refuseSuperseded(): 'locked' {\n\t\treturn this.refuseRecord(\n\t\t\t'KEY_RECORD_INVALID',\n\t\t\t\"The server's key record is authenticated only by this device's previous master key, after the server served a newer revision that key does not open (the passphrase changed on another device). It is not adopted automatically: unlock with the current passphrase.\",\n\t\t)\n\t}\n\n\t/**\n\t * Whether `master` was superseded on this device. An explicit `unlock(passphrase)` is\n\t * the user's decision and clears the mark (a server that served a junk revision can\n\t * delay, never lock out, a device).\n\t */\n\tprivate isSupersededMaster(master: MasterKeys, explicit: boolean): boolean {\n\t\tif (!this.supersededMasters.has(master.id)) return false\n\t\tif (!explicit) return true\n\t\tthis.supersededMasters.delete(master.id)\n\t\treturn false\n\t}\n\n\t/** A record this device cannot authenticate: adopt nothing. */\n\tprivate async unverifiableLocked(\n\t\tincoming: WrappedKeyRecord,\n\t\tsourceFailed: boolean,\n\t): Promise<'ready' | 'locked'> {\n\t\tconst pinned = this.record\n\t\tif (\n\t\t\tthis.master !== null &&\n\t\t\tpinned !== null &&\n\t\t\tincoming.ringId === pinned.ringId &&\n\t\t\tincoming.revision > pinned.revision &&\n\t\t\t!this.supersededMasters.has(this.master.id)\n\t\t) {\n\t\t\t// A newer revision of this ring that the held master key does not authenticate:\n\t\t\t// a passphrase change elsewhere (or a forgery). After a genuine change, no later\n\t\t\t// genuine record is authenticated by the held key, so a record that only it\n\t\t\t// authenticates is never adopted automatically again, even after a restart.\n\t\t\tthis.supersededMasters.add(this.master.id)\n\t\t\tawait this.saveCacheLocked(pinned)\n\t\t}\n\t\tif (\n\t\t\tpinned !== null &&\n\t\t\tthis.encryptor !== null &&\n\t\t\tincoming.ringId === pinned.ringId &&\n\t\t\tincoming.revision === pinned.revision\n\t\t) {\n\t\t\t// Another body under the revision this device accepted, and it does not\n\t\t\t// authenticate: tampered. The held keys are unaffected.\n\t\t\treturn this.refuseRecord(\n\t\t\t\t'KEY_RECORD_INVALID',\n\t\t\t\t`The server's key record differs from revision ${pinned.revision} this device accepted and does not authenticate. It is refused.`,\n\t\t\t)\n\t\t}\n\t\tif (\n\t\t\tpinned !== null &&\n\t\t\tthis.encryptor !== null &&\n\t\t\tincoming.ringId === pinned.ringId &&\n\t\t\tincoming.revision > pinned.revision &&\n\t\t\tincoming.keys.every((key) => this.dataKeys.has(key.keyId))\n\t\t) {\n\t\t\t// The passphrase changed on another device (new master key), or the server forged\n\t\t\t// a record: either way nothing in it is used. The held keys are the ring's keys.\n\t\t\tthis.retryable = false\n\t\t\tthis.setStatus(\n\t\t\t\tthis.makeStatus(\n\t\t\t\t\t'unlocked',\n\t\t\t\t\t'PASSPHRASE_REQUIRED',\n\t\t\t\t\t'The key record changed on another device (passphrase change). This device keeps using the keys it holds; enter the current passphrase to rotate keys, change the passphrase or set up recovery here.',\n\t\t\t\t),\n\t\t\t)\n\t\t\treturn 'ready'\n\t\t}\n\t\tif (sourceFailed) {\n\t\t\tthis.lockWith(\n\t\t\t\t'error',\n\t\t\t\t'WRONG_PASSPHRASE',\n\t\t\t\t`The configured passphrase does not open keyring \"${this.name}\". Check the passphrase.`,\n\t\t\t)\n\t\t\treturn 'locked'\n\t\t}\n\t\tif (pinned !== null && incoming.ringId !== pinned.ringId) {\n\t\t\tthis.lockWith(\n\t\t\t\t'locked',\n\t\t\t\t'KEY_RING_FORK',\n\t\t\t\t'The server holds another key ring for this keyring (it lost the record and another device created a new one). Enter the passphrase: both rings are merged and every operation stays readable.',\n\t\t\t)\n\t\t\treturn 'locked'\n\t\t}\n\t\tthis.lockWith(\n\t\t\t'locked',\n\t\t\tpinned === null ? 'NO_PASSPHRASE' : 'PASSPHRASE_REQUIRED',\n\t\t\t'Enter the encryption passphrase to open the key record.',\n\t\t)\n\t\treturn 'locked'\n\t}\n\n\t/**\n\t * Merge the held ring into `base` (the server's current record, which survives: the\n\t * server only accepts append-only successors of what it stores). Keys this device\n\t * holds that `base` lacks are appended as new versions; operations keep naming them\n\t * by key id. The result has a higher revision than both and is written with\n\t * compare-and-set; a lost race adopts (or merges) the winner.\n\t */\n\tprivate async mergeLocked(\n\t\tbase: WrappedKeyRecord,\n\t\topened: OpenedRing,\n\t\tpassphrase: string | null,\n\t\tchannel: KeyServiceChannel,\n\t\tdepth: number,\n\t): Promise<'ready' | 'locked'> {\n\t\tif (depth >= MAX_WRITE_ATTEMPTS) throw conflictError()\n\t\tconst absorbed = await this.absorbHeldKeys(base, opened.master)\n\t\tconst keys = await this.openDataKeys(base, opened.master)\n\t\tconst merged = await sealRecord(\n\t\t\t{\n\t\t\t\t...base,\n\t\t\t\trevision: this.nextRevision(base),\n\t\t\t\tkeys: [...base.keys, ...absorbed],\n\t\t\t\tcurrentVersion: base.currentVersion + absorbed.length,\n\t\t\t},\n\t\t\topened.master.macKey,\n\t\t)\n\t\tconst reply = await channel.put(this.name, merged, base.revision)\n\t\tif (reply.status !== 'ok') return this.adoptLocked(reply.record, passphrase, channel, depth + 1)\n\t\tfor (const [keyId, key] of keys) this.dataKeys.set(keyId, key)\n\t\tthis.master = opened.master\n\t\tif (opened.kek !== null) {\n\t\t\tthis.kek = opened.kek\n\t\t\tthis.kekSalt = merged.kdf.salt\n\t\t} else if (this.kekSalt !== merged.kdf.salt) {\n\t\t\tthis.kek = null\n\t\t\tthis.kekSalt = null\n\t\t}\n\t\tthis.pendingPassphrase = null\n\t\tawait this.commitLocked(merged)\n\t\treturn 'ready'\n\t}\n\n\t/**\n\t * Re-wrap the held data keys `base` lacks under `target` (appended after base's\n\t * highest version). They are extracted from the pinned record with the held master.\n\t */\n\tprivate async absorbHeldKeys(\n\t\tbase: WrappedKeyRecord,\n\t\ttarget: MasterKeys,\n\t): Promise<WrappedDataKey[]> {\n\t\tconst baseIds = new Set(base.keys.map((key) => key.keyId))\n\t\tconst pinned = this.record\n\t\tconst missing = (pinned?.keys ?? []).filter(\n\t\t\t(key) => this.dataKeys.has(key.keyId) && !baseIds.has(key.keyId),\n\t\t)\n\t\tif (missing.length === 0) return []\n\t\tconst held = this.master\n\t\tif (pinned === null || held === null) {\n\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t'This device holds keys of another ring but not its master key; unlock it with the passphrase first.',\n\t\t\t\t{ code: 'KEY_RING_FORK' },\n\t\t\t)\n\t\t}\n\t\tconst out: WrappedDataKey[] = []\n\t\tlet version = base.currentVersion\n\t\tfor (const entry of [...missing].sort((a, b) => a.keyVersion - b.keyVersion)) {\n\t\t\tconst dataKey = await unwrapDataKey(entry, held.wrapKey, this.name, true)\n\t\t\tversion++\n\t\t\tout.push(await wrapDataKey(dataKey, target.wrapKey, this.name, version, entry.keyId))\n\t\t}\n\t\treturn out\n\t}\n\n\t/** Open every data key of `record` this device does not hold yet. */\n\tprivate async openDataKeys(\n\t\trecord: WrappedKeyRecord,\n\t\tmaster: MasterKeys,\n\t): Promise<Map<string, CryptoKey>> {\n\t\tconst opened = new Map<string, CryptoKey>()\n\t\tfor (const entry of record.keys) {\n\t\t\tif (this.dataKeys.has(entry.keyId)) continue\n\t\t\topened.set(entry.keyId, await unwrapDataKey(entry, master.wrapKey, this.name))\n\t\t}\n\t\treturn opened\n\t}\n\n\t/**\n\t * Whether `record` holds the anchor key of a recovery key: data keys are never removed\n\t * or re-keyed (a passphrase change, recovery or merge re-wraps them under the same key\n\t * id), so the anchor stays valid for the ring.\n\t */\n\tprivate async holdsAnchor(\n\t\trecord: WrappedKeyRecord,\n\t\tmaster: MasterKeys,\n\t\tpublicKey: { x: string; y: string },\n\t\tanchor: Uint8Array,\n\t): Promise<boolean> {\n\t\tfor (const entry of record.keys) {\n\t\t\tlet dataKey: CryptoKey\n\t\t\ttry {\n\t\t\t\tdataKey = await unwrapDataKey(entry, master.wrapKey, this.name, true)\n\t\t\t} catch {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst tag = await recoveryAnchor(dataKey, this.name, record.ringId, entry.keyId, publicKey)\n\t\t\tif (bytesEqual(tag, anchor)) return true\n\t\t}\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether `master` (which authenticated `record`) is a master key a passphrase change\n\t * retired: listed in `record` itself or in any record this device authenticated.\n\t */\n\tprivate isRetiredMaster(record: WrappedKeyRecord, master: MasterKeys): boolean {\n\t\treturn this.retiredMasters.has(master.id) || (record.retiredMasters ?? []).includes(master.id)\n\t}\n\n\t/** The next revision after `base`, above the pinned one when it is the same ring. */\n\tprivate nextRevision(base: WrappedKeyRecord): number {\n\t\tconst pinned = this.record\n\t\tconst floor = pinned !== null && pinned.ringId === base.ringId ? pinned.revision : 0\n\t\treturn Math.max(base.revision, floor) + 1\n\t}\n\n\t/** Authenticate a record with the held master key (no passphrase needed). */\n\tprivate async authenticateHeld(record: WrappedKeyRecord): Promise<OpenedRing | null> {\n\t\tif (this.master === null) return null\n\t\tif (!(await verifyRecordMac(record, this.master.macKey))) return null\n\t\treturn {\n\t\t\tmaster: this.master,\n\t\t\tkek: this.kek !== null && this.kekSalt === record.kdf.salt ? this.kek : null,\n\t\t}\n\t}\n\n\t/**\n\t * Open and authenticate a record with a passphrase.\n\t *\n\t * @throws {EncryptionKeyError} WRONG_PASSPHRASE, or KEY_RECORD_INVALID when the master\n\t * key opens but the record does not authenticate under it (tampered)\n\t */\n\tprivate async openWithPassphrase(\n\t\trecord: WrappedKeyRecord,\n\t\tpassphrase: string,\n\t): Promise<OpenedRing> {\n\t\tconst kek = await deriveKeyEncryptionKey(\n\t\t\tpassphrase,\n\t\t\tfromBase64(record.kdf.salt),\n\t\t\trecord.kdf.iterations,\n\t\t)\n\t\tlet raw: Uint8Array\n\t\ttry {\n\t\t\traw = await unwrapMasterKey(record.master, kek, this.name, record.ringId)\n\t\t} catch (error) {\n\t\t\tthrow new EncryptionKeyError(errorMessage(error), { code: 'WRONG_PASSPHRASE' })\n\t\t}\n\t\tlet master: MasterKeys\n\t\ttry {\n\t\t\tmaster = await importMasterKey(raw)\n\t\t} finally {\n\t\t\traw.fill(0)\n\t\t}\n\t\tif (!(await verifyRecordMac(record, master.macKey))) {\n\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\"The server's key record does not authenticate under its master key: it was modified by someone without the keys (wraps swapped, a recovery key or version injected). It is refused.\",\n\t\t\t\t{ code: 'KEY_RECORD_INVALID' },\n\t\t\t)\n\t\t}\n\t\treturn { master, kek }\n\t}\n\n\t/** The configured or pending passphrase, unless it already failed for this salt. */\n\tprivate async sourcePassphraseFor(record: WrappedKeyRecord): Promise<string | null> {\n\t\tconst source = await this.availablePassphrase()\n\t\tif (source === null) return null\n\t\tconst failed = this.failedSource\n\t\tif (failed !== null && failed.salt === record.kdf.salt && failed.passphrase === source) {\n\t\t\treturn null\n\t\t}\n\t\treturn source\n\t}\n\n\tprivate async followConflictLocked(\n\t\treply: KeyServiceReply,\n\t\tattempt: number,\n\t\tpassphrase: string | null,\n\t\tchannel: KeyServiceChannel,\n\t): Promise<void> {\n\t\tif (attempt + 1 >= MAX_WRITE_ATTEMPTS) throw conflictError()\n\t\tconst current = reply.record\n\t\tif (current === null) {\n\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t'The sync server lost the key record during the write. Reconnect: a device holding it re-uploads it.',\n\t\t\t\t{ code: 'KEY_RECORD_MISSING' },\n\t\t\t)\n\t\t}\n\t\tconst outcome = await this.adoptLocked(current, passphrase, channel)\n\t\tif (outcome !== 'ready') throw this.statusError()\n\t\tconst pinned = this.record\n\t\tif (\n\t\t\tpinned === null ||\n\t\t\t(pinned.ringId === current.ringId && pinned.revision < current.revision)\n\t\t) {\n\t\t\t// Nothing in the newer record could be authenticated: never write over it with\n\t\t\t// keys of an older master key.\n\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t'The key record changed on another device (its passphrase changed) and this device cannot authenticate the new one. Unlock with the current passphrase, then try again.',\n\t\t\t\t{ code: 'PASSPHRASE_REQUIRED' },\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate async commitLocked(record: WrappedKeyRecord): Promise<void> {\n\t\tconst known = new Set(record.keys.map((key) => key.keyId))\n\t\tfor (const keyId of this.dataKeys.keys()) {\n\t\t\tif (!known.has(keyId)) {\n\t\t\t\t// Every path that adopts a record first absorbs the held keys it lacks.\n\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t`Internal error: key ${keyId} would be dropped by revision ${record.revision}.`,\n\t\t\t\t\t{ code: 'KEY_RECORD_INVARIANT' },\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tthis.record = record\n\t\tfor (const id of record.retiredMasters ?? []) this.retiredMasters.add(id)\n\t\tthis.retryable = false\n\t\tthis.rebuildEncryptor()\n\t\tthis.setStatus(this.makeStatus('unlocked'))\n\t\tawait this.saveCacheLocked(record)\n\t}\n\n\t/** Persist the pinned record, the held keys and the master marks (RT-107). */\n\tprivate async saveCacheLocked(record: WrappedKeyRecord): Promise<void> {\n\t\ttry {\n\t\t\tawait this.cache.save(this.cacheId(), {\n\t\t\t\trecord,\n\t\t\t\tretiredMasters: [...this.retiredMasters],\n\t\t\t\tsupersededMasters: [...this.supersededMasters],\n\t\t\t\tkek: this.kek,\n\t\t\t\tmaster: this.master,\n\t\t\t\tkeys: record.keys.flatMap((entry) => {\n\t\t\t\t\tconst key = this.dataKeys.get(entry.keyId)\n\t\t\t\t\treturn key ? [{ keyVersion: entry.keyVersion, keyId: entry.keyId, key }] : []\n\t\t\t\t}),\n\t\t\t})\n\t\t} catch {\n\t\t\t// The cache is a convenience: the keyring works for this session without it.\n\t\t}\n\t}\n\n\tprivate rebuildEncryptor(): void {\n\t\tconst record = this.record\n\t\tconst versions: VersionedKey[] = []\n\t\tfor (const entry of record?.keys ?? []) {\n\t\t\tconst key = this.dataKeys.get(entry.keyId)\n\t\t\tif (key) versions.push({ version: entry.keyVersion, key, keyId: entry.keyId })\n\t\t}\n\t\tif (versions.length === 0 || record === null || this.master === null) {\n\t\t\tthis.encryptor = null\n\t\t\treturn\n\t\t}\n\t\tthis.encryptor = SyncEncryptor.fromKeys(versions, this.encryptorOptions)\n\t}\n\n\tprivate async availablePassphrase(): Promise<string | null> {\n\t\tif (this.pendingPassphrase !== null) return this.pendingPassphrase\n\t\tif (this.lockedByApp || this.passphraseSource === null) return null\n\t\tconst value =\n\t\t\ttypeof this.passphraseSource === 'function'\n\t\t\t\t? await this.passphraseSource()\n\t\t\t\t: this.passphraseSource\n\t\treturn value.length > 0 ? value : null\n\t}\n\n\tprivate requireMaster(action: string): MasterKeys {\n\t\tif (this.master === null || this.encryptor === null) {\n\t\t\tthrow new EncryptionKeyError(`Unlock the keyring with the passphrase to ${action}.`, {\n\t\t\t\tcode: 'PASSPHRASE_REQUIRED',\n\t\t\t})\n\t\t}\n\t\treturn this.master\n\t}\n\n\t/** The KEK of `record`'s own salt (never one derived for another record's salt). */\n\tprivate requireKek(record: WrappedKeyRecord, action: string): CryptoKey {\n\t\tif (this.kek === null || this.kekSalt !== record.kdf.salt || this.encryptor === null) {\n\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t`Unlock the keyring with the passphrase to ${action}: this device holds the data keys but not the key of the current passphrase (it changed on another device, or the keyring is locked).`,\n\t\t\t\t{ code: 'PASSPHRASE_REQUIRED' },\n\t\t\t)\n\t\t}\n\t\treturn this.kek\n\t}\n\n\tprivate requireRecord(): WrappedKeyRecord {\n\t\tif (this.record === null) {\n\t\t\tthrow new EncryptionKeyError('The keyring has no record yet.', { code: 'KEY_RECORD_MISSING' })\n\t\t}\n\t\treturn this.record\n\t}\n\n\tprivate noteFailedUnlock(): void {\n\t\tthis.failedUnlocks++\n\t\tif (this.failedUnlocks > FREE_UNLOCK_ATTEMPTS) {\n\t\t\tconst delay = Math.min(\n\t\t\t\tMAX_UNLOCK_BACKOFF_MS,\n\t\t\t\t1000 * 2 ** (this.failedUnlocks - FREE_UNLOCK_ATTEMPTS - 1),\n\t\t\t)\n\t\t\tthis.nextUnlockAt = Date.now() + delay\n\t\t}\n\t}\n\n\tprivate dropKeys(): void {\n\t\tthis.dataKeys.clear()\n\t\tthis.master = null\n\t\tthis.kek = null\n\t\tthis.kekSalt = null\n\t\tthis.encryptor = null\n\t}\n\n\t/** A record refused for what the server sent: the held keys stay, nothing is adopted. */\n\tprivate refuseRecord(code: EncryptionStatusCode, message: string): 'locked' {\n\t\tthis.holdFor('error', code, message)\n\t\treturn 'locked'\n\t}\n\n\t/** A server-side condition: keys kept, a later session (or a push) can clear it. */\n\tprivate holdFor(state: 'locked' | 'error', code: EncryptionStatusCode, message: string): void {\n\t\tthis.retryable = true\n\t\tthis.setStatus(this.makeStatus(state, code, message))\n\t}\n\n\tprivate lockWith(state: 'locked' | 'error', code: EncryptionStatusCode, message: string): void {\n\t\tthis.retryable = false\n\t\tthis.dropKeysIfUnusable(code)\n\t\tthis.setStatus(this.makeStatus(state, code, message))\n\t}\n\n\t/** Locks that need the user stop encrypting; records refused keep held keys. */\n\tprivate dropKeysIfUnusable(code: EncryptionStatusCode): void {\n\t\tif (\n\t\t\tcode === 'LOCKED_BY_APP' ||\n\t\t\tcode === 'KEY_SERVICE_FORBIDDEN' ||\n\t\t\tcode === 'KEY_SERVICE_UNSUPPORTED' ||\n\t\t\tcode === 'PASSPHRASE_REQUIRED' ||\n\t\t\tcode === 'WRONG_PASSPHRASE' ||\n\t\t\tcode === 'KEY_RING_FORK'\n\t\t) {\n\t\t\tthis.encryptor = null\n\t\t}\n\t}\n\n\tprivate statusError(): EncryptionKeyError {\n\t\treturn new EncryptionKeyError(this.status.message ?? 'The keyring is locked.', {\n\t\t\tcode: this.status.code ?? 'LOCKED',\n\t\t})\n\t}\n\n\tprivate cacheId(): string {\n\t\treturn `${this.loadedPrincipal ?? ''}\\u0000${this.name}`\n\t}\n\n\tprivate makeStatus(\n\t\tstate: EncryptionLockState,\n\t\tcode?: EncryptionStatusCode,\n\t\tmessage?: string,\n\t): EncryptionStatus {\n\t\tconst unlocked = state === 'unlocked' && this.encryptor !== null\n\t\tconst keyVersion = unlocked ? (this.encryptor?.getCurrentKeyVersion() ?? null) : null\n\t\tconst record = this.record\n\t\tconst held = (record?.keys ?? []).filter((key) => this.dataKeys.has(key.keyId))\n\t\treturn {\n\t\t\tstate,\n\t\t\tkeyring: this.name,\n\t\t\tkeyVersion,\n\t\t\tkeyId:\n\t\t\t\tkeyVersion !== null\n\t\t\t\t\t? (held.find((key) => key.keyVersion === keyVersion)?.keyId ?? null)\n\t\t\t\t\t: null,\n\t\t\tavailableVersions: unlocked ? held.map((key) => key.keyVersion).sort((a, b) => a - b) : [],\n\t\t\tcache: this.cache.kind,\n\t\t\t...(code ? { code } : {}),\n\t\t\t...(message ? { message } : {}),\n\t\t}\n\t}\n\n\tprivate setStatus(status: EncryptionStatus): void {\n\t\tconst previous = this.status\n\t\tthis.status = status\n\t\tif (\n\t\t\tprevious?.state === status.state &&\n\t\t\tprevious.code === status.code &&\n\t\t\tprevious.keyVersion === status.keyVersion &&\n\t\t\tprevious.keyId === status.keyId &&\n\t\t\tprevious.availableVersions?.length === status.availableVersions.length\n\t\t) {\n\t\t\treturn\n\t\t}\n\t\tfor (const listener of this.listeners) {\n\t\t\ttry {\n\t\t\t\tlistener(status)\n\t\t\t} catch {\n\t\t\t\t// A listener error must not break the keyring.\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate exclusive<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst run = this.mutex.then(fn, fn)\n\t\tthis.mutex = run.catch(() => undefined)\n\t\treturn run\n\t}\n}\n\nfunction conflictError(): EncryptionKeyError {\n\treturn new EncryptionKeyError(\n\t\t'The key record kept changing on the server while this device updated it. Try again.',\n\t\t{ code: 'KEY_RECORD_CONFLICT' },\n\t)\n}\n\nfunction errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error)\n}\n","import type { WrappedKeyRecord } from './key-record'\nimport type { MasterKeys } from './keyring-crypto'\n\n/**\n * What a device keeps of an unlocked keyring: the last key record it authenticated and\n * accepted (also the pin: its ring id and revision are the floor for every later\n * record), the passphrase KEK when known, the keys derived from the ring's master key\n * (to authenticate later records and wrap new data keys), and every data key it\n * opened. Keys are NON-EXTRACTABLE CryptoKeys: script on the page can use them while it\n * runs, but cannot read their bytes. A locked keyring keeps the record (the pin) and\n * nothing else.\n */\nexport interface CachedKeyring {\n\trecord: WrappedKeyRecord\n\tkek: CryptoKey | null\n\t/** Derived from the ring's master key; null while locked. */\n\tmaster?: MasterKeys | null\n\tkeys: Array<{ keyVersion: number; keyId: string; key: CryptoKey }>\n\t/**\n\t * Ids of master keys a passphrase change retired, from every record this device\n\t * authenticated (RT-107). Kept when locked: records authenticated only by one of them\n\t * stay refused across restarts.\n\t */\n\tretiredMasters?: string[]\n\t/**\n\t * Ids of master keys this device held when the server served a NEWER revision of the\n\t * same ring that they do not authenticate (a passphrase change this device could not\n\t * read yet). Records authenticated only by one of them are not adopted automatically;\n\t * an explicit `unlock(passphrase)` decides (RT-107).\n\t */\n\tsupersededMasters?: string[]\n}\n\n/** Where a device keeps unlocked keyrings between app starts. */\nexport interface KeyCache {\n\t/** Which kind of cache this is (for status and docs). */\n\treadonly kind: 'indexeddb' | 'memory' | 'none'\n\tload(id: string): Promise<CachedKeyring | null>\n\tsave(id: string, entry: CachedKeyring): Promise<void>\n\tclear(id: string): Promise<void>\n}\n\n/** Keeps unlocked keyrings for the lifetime of the process only. */\nexport class MemoryKeyCache implements KeyCache {\n\treadonly kind = 'memory' as const\n\tprivate readonly entries = new Map<string, CachedKeyring>()\n\n\tasync load(id: string): Promise<CachedKeyring | null> {\n\t\treturn this.entries.get(id) ?? null\n\t}\n\n\tasync save(id: string, entry: CachedKeyring): Promise<void> {\n\t\tthis.entries.set(id, entry)\n\t}\n\n\tasync clear(id: string): Promise<void> {\n\t\tthis.entries.delete(id)\n\t}\n}\n\n/** Keeps nothing: the device asks for the passphrase on every start. */\nexport class NoKeyCache implements KeyCache {\n\treadonly kind = 'none' as const\n\tasync load(): Promise<CachedKeyring | null> {\n\t\treturn null\n\t}\n\tasync save(): Promise<void> {}\n\tasync clear(): Promise<void> {}\n}\n\nconst IDB_STORE = 'keyrings'\n\n/**\n * Keeps unlocked keyrings in IndexedDB (structured clone keeps a CryptoKey\n * non-extractable). Survives reloads, so a device unlocks once and then works offline.\n */\nexport class IndexedDbKeyCache implements KeyCache {\n\treadonly kind = 'indexeddb' as const\n\tprivate db: Promise<IDBDatabase> | null = null\n\n\t/** @param databaseName - IndexedDB database name (one per app database) */\n\tconstructor(private readonly databaseName: string) {}\n\n\tasync load(id: string): Promise<CachedKeyring | null> {\n\t\tconst db = await this.open()\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst request = db.transaction(IDB_STORE, 'readonly').objectStore(IDB_STORE).get(id)\n\t\t\trequest.onsuccess = () => resolve((request.result as CachedKeyring | undefined) ?? null)\n\t\t\trequest.onerror = () => reject(request.error)\n\t\t})\n\t}\n\n\tasync save(id: string, entry: CachedKeyring): Promise<void> {\n\t\tconst db = await this.open()\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = db.transaction(IDB_STORE, 'readwrite')\n\t\t\ttx.objectStore(IDB_STORE).put(entry, id)\n\t\t\ttx.oncomplete = () => resolve()\n\t\t\ttx.onerror = () => reject(tx.error)\n\t\t\ttx.onabort = () => reject(tx.error)\n\t\t})\n\t}\n\n\tasync clear(id: string): Promise<void> {\n\t\tconst db = await this.open()\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = db.transaction(IDB_STORE, 'readwrite')\n\t\t\ttx.objectStore(IDB_STORE).delete(id)\n\t\t\ttx.oncomplete = () => resolve()\n\t\t\ttx.onerror = () => reject(tx.error)\n\t\t\ttx.onabort = () => reject(tx.error)\n\t\t})\n\t}\n\n\tprivate open(): Promise<IDBDatabase> {\n\t\tif (!this.db) {\n\t\t\tthis.db = new Promise((resolve, reject) => {\n\t\t\t\tconst request = indexedDB.open(this.databaseName, 1)\n\t\t\t\trequest.onupgradeneeded = () => {\n\t\t\t\t\tif (!request.result.objectStoreNames.contains(IDB_STORE)) {\n\t\t\t\t\t\trequest.result.createObjectStore(IDB_STORE)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trequest.onsuccess = () => resolve(request.result)\n\t\t\t\trequest.onerror = () => reject(request.error)\n\t\t\t})\n\t\t}\n\t\treturn this.db\n\t}\n}\n\n/**\n * The default cache: IndexedDB where it exists (browsers), memory otherwise (Node,\n * tests). See the encryption guide for what each means for offline unlock.\n *\n * @param mode - 'auto' (default), 'indexeddb', 'memory' or 'none'\n * @param databaseName - IndexedDB database name\n */\nexport function createKeyCache(\n\tmode: 'auto' | 'indexeddb' | 'memory' | 'none' = 'auto',\n\tdatabaseName = 'kora-keyring',\n): KeyCache {\n\tif (mode === 'none') return new NoKeyCache()\n\tif (mode === 'memory') return new MemoryKeyCache()\n\tconst hasIndexedDb = typeof (globalThis as { indexedDB?: unknown }).indexedDB !== 'undefined'\n\tif (mode === 'indexeddb' || hasIndexedDb) {\n\t\treturn hasIndexedDb ? new IndexedDbKeyCache(databaseName) : new MemoryKeyCache()\n\t}\n\treturn new MemoryKeyCache()\n}\n","import { SyncError } from '@korajs/core'\nimport type { VersionedKey } from './types'\n\n/**\n * Thrown when key derivation fails.\n */\nexport class KeyDerivationError extends SyncError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, { ...context, errorType: 'KEY_DERIVATION_ERROR' })\n\t\tthis.name = 'KeyDerivationError'\n\t}\n}\n\n/** Salt length in bytes. 32 bytes (256 bits) provides sufficient randomness. */\nconst SALT_LENGTH = 32\n\n/**\n * PBKDF2 iteration count. 600,000 iterations is the OWASP-recommended minimum\n * for SHA-256 as of 2024, providing strong resistance against brute-force attacks.\n *\n * This is the production default. It can be overridden per-call via the\n * `iterations` argument on {@link deriveKey}/{@link deriveVersionedKey} — used\n * by tests to keep the real crypto path exercised without paying the full\n * brute-force-resistant cost on every derivation. The default is never lowered.\n */\nexport const DEFAULT_PBKDF2_ITERATIONS = 600_000\n\n/** Derived key length in bits (AES-256). */\nconst DERIVED_KEY_LENGTH = 256\n\n/**\n * Asserts that `crypto.subtle` is available, throwing a clear error if not.\n */\nfunction assertCryptoAvailable(): void {\n\tif (typeof globalThis.crypto === 'undefined' || typeof globalThis.crypto.subtle === 'undefined') {\n\t\tthrow new KeyDerivationError(\n\t\t\t'Web Crypto API (crypto.subtle) is not available in this environment. ' +\n\t\t\t\t'Sync encryption requires crypto.subtle, which is available in modern browsers and Node.js 20+.',\n\t\t)\n\t}\n}\n\n/**\n * Generates a cryptographically random salt for PBKDF2 key derivation.\n *\n * @returns A random 32-byte Uint8Array\n * @throws {KeyDerivationError} If crypto.getRandomValues is unavailable\n */\nexport function generateSalt(): Uint8Array {\n\tif (typeof globalThis.crypto === 'undefined') {\n\t\tthrow new KeyDerivationError(\n\t\t\t'Web Crypto API (crypto) is not available. Cannot generate random salt.',\n\t\t)\n\t}\n\n\treturn globalThis.crypto.getRandomValues(new Uint8Array(SALT_LENGTH))\n}\n\n/**\n * Derives an AES-256-GCM encryption key from a passphrase using PBKDF2.\n *\n * Uses PBKDF2 with SHA-256 and 600,000 iterations (OWASP-recommended minimum)\n * to derive a 256-bit key. The derived key is deterministic: the same passphrase\n * and salt always produce the same key.\n *\n * @param passphrase - The user's passphrase (must be non-empty)\n * @param salt - Optional salt bytes. If omitted, a random 32-byte salt is generated.\n * @param iterations - Optional PBKDF2 iteration count. Defaults to\n * {@link DEFAULT_PBKDF2_ITERATIONS}. Lower it only in tests; production must\n * keep the default so derived keys stay brute-force resistant.\n * @returns The derived CryptoKey and the salt used\n * @throws {KeyDerivationError} If the passphrase is empty, crypto is unavailable, or derivation fails\n *\n * @example\n * ```typescript\n * // First time: derive key with a new salt\n * const { key, salt } = await deriveKey('my-secure-passphrase')\n *\n * // Later: re-derive the same key using the stored salt\n * const { key: sameKey } = await deriveKey('my-secure-passphrase', salt)\n * ```\n */\nexport async function deriveKey(\n\tpassphrase: string,\n\tsalt?: Uint8Array,\n\titerations: number = DEFAULT_PBKDF2_ITERATIONS,\n): Promise<{ key: CryptoKey; salt: Uint8Array }> {\n\tassertCryptoAvailable()\n\n\tif (passphrase.length === 0) {\n\t\tthrow new KeyDerivationError(\n\t\t\t'Passphrase must not be empty. Provide a non-empty string for encryption key derivation.',\n\t\t)\n\t}\n\n\tif (!Number.isInteger(iterations) || iterations < 1) {\n\t\tthrow new KeyDerivationError(\n\t\t\t`PBKDF2 iteration count must be a positive integer, received: ${iterations}`,\n\t\t\t{ iterations },\n\t\t)\n\t}\n\n\tconst usedSalt = salt ?? generateSalt()\n\n\ttry {\n\t\t// Import the passphrase as raw key material for PBKDF2\n\t\tconst passphraseBytes = new TextEncoder().encode(passphrase)\n\t\tconst baseKey = await globalThis.crypto.subtle.importKey(\n\t\t\t'raw',\n\t\t\tpassphraseBytes,\n\t\t\t'PBKDF2',\n\t\t\tfalse,\n\t\t\t['deriveBits', 'deriveKey'],\n\t\t)\n\n\t\t// Derive an AES-256-GCM key using PBKDF2 with SHA-256\n\t\tconst derivedKey = await globalThis.crypto.subtle.deriveKey(\n\t\t\t{\n\t\t\t\tname: 'PBKDF2',\n\t\t\t\tsalt: usedSalt as unknown as ArrayBuffer,\n\t\t\t\titerations,\n\t\t\t\thash: 'SHA-256',\n\t\t\t},\n\t\t\tbaseKey,\n\t\t\t{ name: 'AES-GCM', length: DERIVED_KEY_LENGTH },\n\t\t\t// extractable: true so the derived key can be exported for testing/debugging\n\t\t\ttrue,\n\t\t\t['encrypt', 'decrypt'],\n\t\t)\n\n\t\treturn { key: derivedKey, salt: usedSalt }\n\t} catch (cause) {\n\t\tif (cause instanceof KeyDerivationError) {\n\t\t\tthrow cause\n\t\t}\n\n\t\tthrow new KeyDerivationError(\n\t\t\t'Failed to derive encryption key from passphrase using PBKDF2. ' +\n\t\t\t\t'Ensure the runtime supports PBKDF2 with SHA-256.',\n\t\t\t{ cause: cause instanceof Error ? cause.message : String(cause) },\n\t\t)\n\t}\n}\n\n/**\n * Derives a versioned encryption key from a passphrase.\n *\n * Wraps {@link deriveKey} with a version number for key rotation support.\n * When the passphrase changes, create a new versioned key with a higher\n * version number.\n *\n * @param passphrase - The user's passphrase\n * @param version - Key version number (must be a positive integer)\n * @param salt - Optional salt bytes. If omitted, a random salt is generated.\n * @param iterations - Optional PBKDF2 iteration count. Defaults to\n * {@link DEFAULT_PBKDF2_ITERATIONS}. Lower it only in tests.\n * @returns A {@link VersionedKey} containing the key, version, and salt\n * @throws {KeyDerivationError} If parameters are invalid or derivation fails\n */\nexport async function deriveVersionedKey(\n\tpassphrase: string,\n\tversion: number,\n\tsalt?: Uint8Array,\n\titerations: number = DEFAULT_PBKDF2_ITERATIONS,\n): Promise<VersionedKey> {\n\tif (!Number.isInteger(version) || version < 1) {\n\t\tthrow new KeyDerivationError(`Key version must be a positive integer, received: ${version}`, {\n\t\t\tversion,\n\t\t})\n\t}\n\n\tconst { key, salt: usedSalt } = await deriveKey(passphrase, salt, iterations)\n\n\treturn { version, key, salt: usedSalt }\n}\n","/**\n * The server-stored, passphrase-wrapped key record (ENC-1, decision D4b; format 2 since\n * the final RC red team, RT-95/RT-96/RT-97/RT-104).\n *\n * Key hierarchy of one keyring (\"ring\"):\n *\n * passphrase --PBKDF2-SHA256(salt, iterations)--> KEK\n * KEK --AES-GCM--> master key (random 256 bits, the ring's secret)\n * recovery public key --ECDH P-256 + HKDF + AES-GCM--> master key (optional)\n * master key --HKDF \"data-key-wrap\"--> wrapping key --AES-GCM--> data key v1, v2, ...\n * master key --HKDF \"record-mac\"--> MAC key --HMAC-SHA256--> `mac` over the whole record\n *\n * The MAC covers every field except itself (format, keyring, ring id, revision, current\n * version, KDF parameters, the master wrap, every data-key wrap, the recovery block), so\n * a server cannot add or swap a recovery key, relabel or drop a version, or serve\n * another record under the same revision without the device noticing. Only a holder of\n * the master key (the passphrase, a device that opened the ring, or the recovery key)\n * can write a record devices accept. The server stores the record opaquely: it never\n * sees the passphrase, the KEK, the master key or a data key.\n *\n * Shared by the client keyring (which creates, opens and authenticates records) and the\n * sync server (which validates their structure and stores them with compare-and-set).\n */\n\n/** Current record format. Format 1 (beta.13 release candidates only) is not read. */\nexport const KEY_RECORD_FORMAT = 2 as const\n\n/** Name of the default keyring (one per user). */\nexport const DEFAULT_KEYRING = 'default'\n\n/** Maximum serialized size of a key record the server accepts. */\nexport const MAX_KEY_RECORD_BYTES = 64 * 1024\n\n/** Maximum number of key versions in one record. */\nexport const MAX_KEY_VERSIONS = 256\n\n/** Maximum keyring name length. */\nexport const MAX_KEYRING_NAME_LENGTH = 128\n\n/**\n * Maximum PBKDF2 iterations a device accepts. A record above it would make an unlock\n * never finish (the floor is the app's own `kdfIterations`).\n */\nexport const MAX_KDF_ITERATIONS = 10_000_000\n\n/** Recovery wrap algorithm of format 2. */\nexport const RECOVERY_ALGORITHM = 'ECDH-P256+HKDF-SHA256+AES-GCM' as const\n\n/** Key-derivation parameters of a record's passphrase KEK. */\nexport interface KeyRecordKdf {\n\tname: 'PBKDF2'\n\thash: 'SHA-256'\n\t/** PBKDF2 iteration count (600,000 by default). */\n\titerations: number\n\t/** Base64 random 32-byte salt, generated per passphrase. */\n\tsalt: string\n}\n\n/** The ring's master key, wrapped by the passphrase KEK (AES-256-GCM). */\nexport interface WrappedMasterKey {\n\t/** Base64 12-byte AES-GCM IV. */\n\tiv: string\n\t/** Base64 AES-GCM ciphertext of the 32-byte master key (with tag: 48 bytes). */\n\twrappedKey: string\n}\n\n/** One data key version, wrapped by the master key's wrapping key. */\nexport interface WrappedDataKey {\n\t/** Key version (1, 2, ...) in this ring. */\n\tkeyVersion: number\n\t/** Random key id: the envelope's `keyId`. Never derived from key material. */\n\tkeyId: string\n\t/** Base64 12-byte AES-GCM IV of the wrap. */\n\tiv: string\n\t/** Base64 AES-GCM wrap of the raw 32-byte data key (ciphertext and tag). */\n\twrappedKey: string\n}\n\n/**\n * Optional recovery: the master key, wrapped to a recovery public key (ephemeral-static\n * ECDH P-256, HKDF-SHA256 over the shared secret and both public keys, AES-256-GCM). The\n * private half is the user's recovery key; it opens the master key, hence every data key.\n */\nexport interface KeyRecordRecovery {\n\talg: typeof RECOVERY_ALGORITHM\n\t/** Recovery public key (JWK x/y, base64url). */\n\tpublicKey: { x: string; y: string }\n\t/** Ephemeral public key of the wrap (JWK x/y, base64url). */\n\tephemeralPublicKey: { x: string; y: string }\n\tiv: string\n\twrappedKey: string\n}\n\n/**\n * The record the sync server stores per (user, keyring). Opaque to the server apart\n * from its structure: it cannot unwrap any key or forge the MAC.\n */\nexport interface WrappedKeyRecord {\n\tformat: typeof KEY_RECORD_FORMAT\n\tkeyring: string\n\t/** Random id of this ring (`r-` and 32 hex digits). Two rings of one keyring are a fork. */\n\tringId: string\n\t/** Compare-and-set counter: grows on every write; devices never accept a lower one. */\n\trevision: number\n\t/** The key version new operations are encrypted with (the highest one). */\n\tcurrentVersion: number\n\tkdf: KeyRecordKdf\n\tmaster: WrappedMasterKey\n\t/** Every key version ever created. Never shrinks: old history must stay readable. */\n\tkeys: WrappedDataKey[]\n\trecovery?: KeyRecordRecovery\n\t/**\n\t * Ids (`m-` and 32 hex digits) of master keys a passphrase change retired (RT-107).\n\t * Append-only. A device that authenticated a record listing them refuses every later\n\t * record authenticated only by one of them, however it was opened (held master key,\n\t * a stale configured passphrase, the old passphrase typed again). Absent until the\n\t * first passphrase change.\n\t */\n\tretiredMasters?: string[]\n\t/** Base64 HMAC-SHA256 of every other field (see {@link keyRecordMacInput}). */\n\tmac: string\n}\n\n/** Result of a structural validation. */\nexport type KeyRecordValidation = { ok: true } | { ok: false; reason: string }\n\nconst RECORD_FIELDS: ReadonlySet<string> = new Set([\n\t'format',\n\t'keyring',\n\t'ringId',\n\t'revision',\n\t'currentVersion',\n\t'kdf',\n\t'master',\n\t'keys',\n\t'recovery',\n\t'retiredMasters',\n\t'mac',\n])\n\n/**\n * Structural validation of a key record (server and client). It does not and cannot\n * check the MAC or that the wraps open: only a holder of the master key can.\n *\n * @param value - A decoded record\n * @param keyring - The keyring the record must belong to\n */\nexport function validateKeyRecord(value: unknown, keyring: string): KeyRecordValidation {\n\tif (!isObject(value)) return fail('record is not an object')\n\tif (value.format !== KEY_RECORD_FORMAT) return fail(`unsupported format ${String(value.format)}`)\n\tfor (const field of Object.keys(value)) {\n\t\t// Strict: every field is covered by the MAC, and an unknown one has no meaning.\n\t\tif (!RECORD_FIELDS.has(field)) return fail(`unknown field \"${field}\"`)\n\t}\n\tif (value.keyring !== keyring) return fail('keyring does not match')\n\tif (!isRingId(value.ringId)) return fail('ringId is malformed')\n\tif (!isPositiveInteger(value.revision)) return fail('revision must be a positive integer')\n\tif (!isPositiveInteger(value.currentVersion)) return fail('currentVersion must be positive')\n\tconst kdf = value.kdf\n\tif (!isObject(kdf) || kdf.name !== 'PBKDF2' || kdf.hash !== 'SHA-256') {\n\t\treturn fail('kdf must be PBKDF2 with SHA-256')\n\t}\n\tif (!isPositiveInteger(kdf.iterations) || kdf.iterations > MAX_KDF_ITERATIONS) {\n\t\treturn fail(`kdf.iterations must be 1 to ${MAX_KDF_ITERATIONS}`)\n\t}\n\tif (!isBase64(kdf.salt, 16, 64)) return fail('kdf.salt must be 16 to 64 base64 bytes')\n\tconst master = value.master\n\tif (!isObject(master) || !isBase64(master.iv, 12, 12) || !isBase64(master.wrappedKey, 48, 48)) {\n\t\treturn fail('master wrap is malformed')\n\t}\n\tif (!Array.isArray(value.keys) || value.keys.length === 0) return fail('keys must not be empty')\n\tif (value.keys.length > MAX_KEY_VERSIONS) return fail('too many key versions')\n\tconst versions = new Set<number>()\n\tconst ids = new Set<string>()\n\tlet max = 0\n\tfor (const key of value.keys) {\n\t\tif (!isObject(key)) return fail('key entry is not an object')\n\t\tif (!isPositiveInteger(key.keyVersion)) return fail('keyVersion must be positive')\n\t\tif (!isKeyId(key.keyId)) return fail('keyId is malformed')\n\t\tif (!isBase64(key.iv, 12, 12)) return fail('iv must be 12 base64 bytes')\n\t\tif (!isBase64(key.wrappedKey, 48, 48)) return fail('wrappedKey must be 48 base64 bytes')\n\t\tif (versions.has(key.keyVersion) || ids.has(key.keyId)) return fail('duplicate key version')\n\t\tversions.add(key.keyVersion)\n\t\tids.add(key.keyId)\n\t\tmax = Math.max(max, key.keyVersion)\n\t}\n\tif (value.currentVersion !== max) return fail('currentVersion must be the highest key version')\n\tif (value.recovery !== undefined) {\n\t\tconst recovery = value.recovery\n\t\tif (!isObject(recovery) || recovery.alg !== RECOVERY_ALGORITHM) {\n\t\t\treturn fail(`recovery.alg must be ${RECOVERY_ALGORITHM}`)\n\t\t}\n\t\tif (!isPoint(recovery.publicKey)) return fail('recovery.publicKey is malformed')\n\t\tif (!isPoint(recovery.ephemeralPublicKey)) return fail('recovery ephemeral key is malformed')\n\t\tif (!isBase64(recovery.iv, 12, 12) || !isBase64(recovery.wrappedKey, 48, 48)) {\n\t\t\treturn fail('recovery wrap is malformed')\n\t\t}\n\t}\n\tif (value.retiredMasters !== undefined) {\n\t\tconst retired = value.retiredMasters\n\t\tif (!Array.isArray(retired) || retired.length > MAX_KEY_VERSIONS) {\n\t\t\treturn fail(`retiredMasters must be an array of at most ${MAX_KEY_VERSIONS} ids`)\n\t\t}\n\t\tif (!retired.every((id) => isMasterId(id)) || new Set(retired).size !== retired.length) {\n\t\t\treturn fail('retiredMasters must hold distinct master ids')\n\t\t}\n\t}\n\tif (!isBase64(value.mac, 32, 32)) return fail('mac must be 32 base64 bytes')\n\treturn { ok: true }\n}\n\n/**\n * Whether `next` may replace `previous` (the server's write rule): the revision grows,\n * the ring id stays, and every key version of `previous` is still present under the\n * same key id, so no history becomes unreadable. Wraps may change (a passphrase change\n * re-wraps every version). A first record (or one re-uploaded after the server lost\n * it) may carry any revision: devices pin the highest revision they accepted, so a\n * restore must keep the revision it had.\n *\n * @param previous - The stored record, or null when none exists\n * @param next - The proposed record\n */\nexport function isKeyRecordSuccessor(\n\tprevious: WrappedKeyRecord | null,\n\tnext: WrappedKeyRecord,\n): KeyRecordValidation {\n\tif (previous === null) return { ok: true }\n\tif (next.revision <= previous.revision) {\n\t\treturn fail(`revision must be greater than ${previous.revision}`)\n\t}\n\tif (next.ringId !== previous.ringId) return fail('ringId must not change')\n\tconst nextIds = new Map(next.keys.map((key) => [key.keyVersion, key.keyId]))\n\tfor (const key of previous.keys) {\n\t\tif (nextIds.get(key.keyVersion) !== key.keyId) {\n\t\t\treturn fail(`key version ${key.keyVersion} must be kept`)\n\t\t}\n\t}\n\tconst nextRetired = new Set(next.retiredMasters ?? [])\n\tfor (const id of previous.retiredMasters ?? []) {\n\t\tif (!nextRetired.has(id)) return fail(`retired master ${id} must be kept`)\n\t}\n\treturn { ok: true }\n}\n\n/**\n * Additional authenticated data of one data-key wrap: binds a wrapped data key to its\n * keyring, version and key id, so a wrap cannot be relabelled as another version's.\n *\n * @param keyring - Keyring name\n * @param keyVersion - Key version\n * @param keyId - Key id\n */\nexport function wrapAdditionalData(keyring: string, keyVersion: number, keyId: string): Uint8Array {\n\treturn new TextEncoder().encode(\n\t\tJSON.stringify(['kora-key-wrap', KEY_RECORD_FORMAT, 'data-key', keyring, keyVersion, keyId]),\n\t)\n}\n\n/**\n * Additional authenticated data of a master-key wrap (passphrase or recovery): binds it\n * to the keyring and ring, and a recovery wrap to its recipient public key.\n */\nexport function masterWrapAdditionalData(\n\tkeyring: string,\n\tringId: string,\n\tpurpose: 'passphrase' | 'recovery',\n\trecipient?: { x: string; y: string },\n): Uint8Array {\n\treturn new TextEncoder().encode(\n\t\tJSON.stringify([\n\t\t\t'kora-master-wrap',\n\t\t\tKEY_RECORD_FORMAT,\n\t\t\tpurpose,\n\t\t\tkeyring,\n\t\t\tringId,\n\t\t\trecipient?.x ?? null,\n\t\t\trecipient?.y ?? null,\n\t\t]),\n\t)\n}\n\n/**\n * The bytes the record MAC covers: a domain prefix and the canonical JSON (sorted keys,\n * no whitespace) of every field except `mac`.\n *\n * @param record - The record (its `mac`, if any, is ignored)\n */\nexport function keyRecordMacInput(\n\trecord: WrappedKeyRecord | Omit<WrappedKeyRecord, 'mac'>,\n): Uint8Array {\n\tconst { mac: _mac, ...rest } = record as WrappedKeyRecord\n\treturn new TextEncoder().encode(\n\t\t`kora-key-record-mac\\u0000${KEY_RECORD_FORMAT}\\u0000${canonicalJson(rest)}`,\n\t)\n}\n\n/** Deterministic JSON: object keys sorted, undefined members dropped, no whitespace. */\nexport function canonicalJson(value: unknown): string {\n\tif (value === null || typeof value !== 'object') {\n\t\tconst encoded = JSON.stringify(value)\n\t\treturn encoded === undefined ? 'null' : encoded\n\t}\n\tif (Array.isArray(value)) return `[${value.map((item) => canonicalJson(item)).join(',')}]`\n\tconst entries = Object.keys(value as Record<string, unknown>)\n\t\t.filter((key) => (value as Record<string, unknown>)[key] !== undefined)\n\t\t.sort()\n\t\t.map(\n\t\t\t(key) => `${JSON.stringify(key)}:${canonicalJson((value as Record<string, unknown>)[key])}`,\n\t\t)\n\treturn `{${entries.join(',')}}`\n}\n\n/** Parse a record from JSON, returning null when it is not one. */\nexport function parseKeyRecord(json: string, keyring: string): WrappedKeyRecord | null {\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(json)\n\t} catch {\n\t\treturn null\n\t}\n\treturn validateKeyRecord(parsed, keyring).ok ? (parsed as WrappedKeyRecord) : null\n}\n\n/** Whether a keyring name is acceptable. */\nexport function isValidKeyringName(value: unknown): value is string {\n\treturn (\n\t\ttypeof value === 'string' &&\n\t\tvalue.length > 0 &&\n\t\tvalue.length <= MAX_KEYRING_NAME_LENGTH &&\n\t\t/^[A-Za-z0-9._:-]+$/.test(value)\n\t)\n}\n\n/** Whether a value is a key id (`k2-` and 32 hex digits). */\nexport function isKeyId(value: unknown): value is string {\n\treturn typeof value === 'string' && /^k2-[0-9a-f]{32}$/.test(value)\n}\n\n/** Whether a value is a master id (`m-` and 32 hex digits). */\nexport function isMasterId(value: unknown): value is string {\n\treturn typeof value === 'string' && /^m-[0-9a-f]{32}$/.test(value)\n}\n\nfunction fail(reason: string): KeyRecordValidation {\n\treturn { ok: false, reason }\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isPositiveInteger(value: unknown): value is number {\n\treturn typeof value === 'number' && Number.isSafeInteger(value) && value > 0\n}\n\nfunction isRingId(value: unknown): value is string {\n\treturn typeof value === 'string' && /^r-[0-9a-f]{32}$/.test(value)\n}\n\nfunction isBase64(value: unknown, minBytes: number, maxBytes: number): boolean {\n\tif (typeof value !== 'string' || !/^[A-Za-z0-9+/]*={0,2}$/.test(value)) return false\n\tif (value.length % 4 !== 0) return false\n\tconst padding = value.endsWith('==') ? 2 : value.endsWith('=') ? 1 : 0\n\tconst bytes = (value.length / 4) * 3 - padding\n\treturn bytes >= minBytes && bytes <= maxBytes\n}\n\nfunction isPoint(value: unknown): value is { x: string; y: string } {\n\treturn (\n\t\tisObject(value) &&\n\t\ttypeof value.x === 'string' &&\n\t\ttypeof value.y === 'string' &&\n\t\t/^[A-Za-z0-9_-]{43}$/.test(value.x) &&\n\t\t/^[A-Za-z0-9_-]{43}$/.test(value.y)\n\t)\n}\n","import { SyncError } from '@korajs/core'\nimport type {\n\tKeyRecordRecovery,\n\tWrappedDataKey,\n\tWrappedKeyRecord,\n\tWrappedMasterKey,\n} from './key-record'\nimport {\n\tRECOVERY_ALGORITHM,\n\tkeyRecordMacInput,\n\tmasterWrapAdditionalData,\n\twrapAdditionalData,\n} from './key-record'\n\n/**\n * WebCrypto primitives of the key hierarchy (ENC-1, D4b, record format 2):\n *\n * passphrase --PBKDF2-SHA256(salt, iterations)--> KEK (AES-256-GCM, non-extractable)\n * KEK --AES-GCM (AAD: keyring, ring)--> master key (32 random bytes)\n * recovery public key --ECDH P-256, HKDF-SHA256, AES-GCM--> master key (optional)\n * master key --HKDF-SHA256 \"data-key-wrap\"--> wrapping key (AES-256-GCM)\n * master key --HKDF-SHA256 \"record-mac\"--> MAC key (HMAC-SHA256)\n * wrapping key --AES-GCM wrap (AAD: keyring, version, keyId)--> data key v1, v2, ...\n * master key --HKDF-SHA256 \"master-id\"--> master id (names a master a passphrase change retires)\n * recovery anchor key (a data key created WITH the recovery key) --HMAC-SHA256 over\n * (keyring, ring id, key id, recovery public key)--> anchor (16 bytes, in the recovery key)\n *\n * Raw master-key bytes exist only inside the calls that create, re-wrap or open the\n * master key, and are zeroed afterwards. What a device keeps is the two derived keys\n * ({@link MasterKeys}), both non-extractable. Data keys are extractable only for the\n * moment they are wrapped; the copy a device encrypts with is non-extractable.\n */\n\n/** Thrown when a key record cannot be opened with the given passphrase or recovery key. */\nexport class KeyUnwrapError extends SyncError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, { ...context, errorType: 'KEY_UNWRAP_ERROR' })\n\t\tthis.name = 'KeyUnwrapError'\n\t}\n}\n\n/** PBKDF2 salt length (bytes). */\nexport const KEK_SALT_BYTES = 32\n\n/** Master key length (bytes). */\nexport const MASTER_KEY_BYTES = 32\n\nconst GCM_IV_BYTES = 12\nconst RECOVERY_KEY_PREFIX = 'kora-rk3-'\n/** Bytes of a recovery key's ring anchor (see {@link recoveryAnchor}). */\nconst RECOVERY_ANCHOR_BYTES = 16\nconst HKDF_DATA_KEY_WRAP = 'kora/key-record/2/data-key-wrap'\nconst HKDF_RECORD_MAC = 'kora/key-record/2/record-mac'\nconst HKDF_MASTER_ID = 'kora/key-record/2/master-id'\nconst HKDF_RECOVERY_WRAP = 'kora/key-record/2/recovery-wrap'\nconst RECOVERY_ANCHOR_INFO = 'kora/key-record/2/recovery-anchor/3'\n\n/** The keys a device derives from (and keeps instead of) a ring's master key. */\nexport interface MasterKeys {\n\t/** Wraps and unwraps the ring's data keys. */\n\twrapKey: CryptoKey\n\t/** Authenticates the whole key record. */\n\tmacKey: CryptoKey\n\t/**\n\t * Public name of the master key (`m-` and 32 hex digits, an HKDF output of the master\n\t * key): what a record's `retiredMasters` lists after a passphrase change. Reveals\n\t * nothing about the key; only a holder of the master key can compute it.\n\t */\n\tid: string\n}\n\nfunction subtle(): SubtleCrypto {\n\tconst api = globalThis.crypto?.subtle\n\tif (!api) {\n\t\tthrow new SyncError(\n\t\t\t'Web Crypto API (crypto.subtle) is not available. Sync encryption requires a secure context (HTTPS or localhost) in browsers, or Node.js 20+.',\n\t\t\t{ code: 'CRYPTO_UNAVAILABLE' },\n\t\t)\n\t}\n\treturn api\n}\n\nfunction buffer(bytes: Uint8Array): ArrayBuffer {\n\treturn bytes as unknown as ArrayBuffer\n}\n\n/** Cryptographically random bytes. */\nexport function randomBytes(length: number): Uint8Array {\n\treturn globalThis.crypto.getRandomValues(new Uint8Array(length))\n}\n\nfunction hex(bytes: Uint8Array): string {\n\treturn Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n/** A fresh random key id (`k2-` and 32 hex digits). Names a key; reveals nothing about it. */\nexport function newKeyId(): string {\n\treturn `k2-${hex(randomBytes(16))}`\n}\n\n/** A fresh random ring id (`r-` and 32 hex digits). */\nexport function newRingId(): string {\n\treturn `r-${hex(randomBytes(16))}`\n}\n\n/**\n * Derive the key-encryption key from a passphrase. Non-extractable; usable only to\n * seal and open the ring's master key.\n *\n * @param passphrase - The user's passphrase (non-empty)\n * @param salt - The record's random salt\n * @param iterations - PBKDF2 iterations\n */\nexport async function deriveKeyEncryptionKey(\n\tpassphrase: string,\n\tsalt: Uint8Array,\n\titerations: number,\n): Promise<CryptoKey> {\n\tif (passphrase.length === 0) {\n\t\tthrow new KeyUnwrapError('The encryption passphrase must not be empty.', {\n\t\t\tcode: 'EMPTY_PASSPHRASE',\n\t\t})\n\t}\n\tconst base = await subtle().importKey(\n\t\t'raw',\n\t\tbuffer(new TextEncoder().encode(passphrase)),\n\t\t'PBKDF2',\n\t\tfalse,\n\t\t['deriveKey'],\n\t)\n\treturn subtle().deriveKey(\n\t\t{ name: 'PBKDF2', salt: buffer(salt), iterations, hash: 'SHA-256' },\n\t\tbase,\n\t\t{ name: 'AES-GCM', length: 256 },\n\t\tfalse,\n\t\t['encrypt', 'decrypt'],\n\t)\n}\n\n/** New random master-key bytes. The caller zeroes them once wrapped and imported. */\nexport function generateMasterKey(): Uint8Array {\n\treturn randomBytes(MASTER_KEY_BYTES)\n}\n\n/**\n * Derive the non-extractable wrapping and MAC keys of a master key (HKDF-SHA256 with\n * distinct info strings).\n */\nexport async function importMasterKey(raw: Uint8Array): Promise<MasterKeys> {\n\tconst base = await subtle().importKey('raw', buffer(raw), 'HKDF', false, [\n\t\t'deriveKey',\n\t\t'deriveBits',\n\t])\n\tconst hkdf = (info: string): HkdfParams => ({\n\t\tname: 'HKDF',\n\t\thash: 'SHA-256',\n\t\tsalt: new ArrayBuffer(0),\n\t\tinfo: buffer(new TextEncoder().encode(info)),\n\t})\n\tconst [wrapKey, macKey, idBits] = await Promise.all([\n\t\tsubtle().deriveKey(hkdf(HKDF_DATA_KEY_WRAP), base, { name: 'AES-GCM', length: 256 }, false, [\n\t\t\t'wrapKey',\n\t\t\t'unwrapKey',\n\t\t]),\n\t\tsubtle().deriveKey(\n\t\t\thkdf(HKDF_RECORD_MAC),\n\t\t\tbase,\n\t\t\t{ name: 'HMAC', hash: 'SHA-256', length: 256 },\n\t\t\tfalse,\n\t\t\t['sign', 'verify'],\n\t\t),\n\t\tsubtle().deriveBits(hkdf(HKDF_MASTER_ID), base, 128),\n\t])\n\treturn { wrapKey, macKey, id: `m-${hex(new Uint8Array(idBits))}` }\n}\n\n/** Seal the master key under the passphrase KEK. */\nexport async function wrapMasterKey(\n\traw: Uint8Array,\n\tkek: CryptoKey,\n\tkeyring: string,\n\tringId: string,\n): Promise<WrappedMasterKey> {\n\tconst iv = randomBytes(GCM_IV_BYTES)\n\tconst sealed = await subtle().encrypt(\n\t\t{\n\t\t\tname: 'AES-GCM',\n\t\t\tiv: buffer(iv),\n\t\t\tadditionalData: buffer(masterWrapAdditionalData(keyring, ringId, 'passphrase')),\n\t\t},\n\t\tkek,\n\t\tbuffer(raw),\n\t)\n\treturn { iv: toBase64(iv), wrappedKey: toBase64(new Uint8Array(sealed)) }\n}\n\n/**\n * Open the master key with the passphrase KEK. The returned bytes must be zeroed by\n * the caller once imported (or re-wrapped).\n *\n * @throws {KeyUnwrapError} WRONG_PASSPHRASE when the wrap does not authenticate\n */\nexport async function unwrapMasterKey(\n\twrap: WrappedMasterKey,\n\tkek: CryptoKey,\n\tkeyring: string,\n\tringId: string,\n): Promise<Uint8Array> {\n\ttry {\n\t\tconst raw = await subtle().decrypt(\n\t\t\t{\n\t\t\t\tname: 'AES-GCM',\n\t\t\t\tiv: buffer(fromBase64(wrap.iv)),\n\t\t\t\tadditionalData: buffer(masterWrapAdditionalData(keyring, ringId, 'passphrase')),\n\t\t\t},\n\t\t\tkek,\n\t\t\tbuffer(fromBase64(wrap.wrappedKey)),\n\t\t)\n\t\treturn new Uint8Array(raw)\n\t} catch (cause) {\n\t\tthrow new KeyUnwrapError(\n\t\t\t`The passphrase does not open keyring \"${keyring}\". Check the passphrase.`,\n\t\t\t{\n\t\t\t\tcode: 'WRONG_PASSPHRASE',\n\t\t\t\tcause: cause instanceof Error ? cause.message : String(cause),\n\t\t\t},\n\t\t)\n\t}\n}\n\n/**\n * Wrap a data key with the ring's wrapping key.\n *\n * @param dataKey - An extractable data key\n * @param wrapKey - The master key's wrapping key\n * @param keyring - Keyring name (bound as AAD)\n * @param keyVersion - Key version (bound as AAD)\n * @param keyId - Key id (bound as AAD)\n */\nexport async function wrapDataKey(\n\tdataKey: CryptoKey,\n\twrapKey: CryptoKey,\n\tkeyring: string,\n\tkeyVersion: number,\n\tkeyId: string,\n): Promise<WrappedDataKey> {\n\tconst iv = randomBytes(GCM_IV_BYTES)\n\tconst wrapped = await subtle().wrapKey('raw', dataKey, wrapKey, {\n\t\tname: 'AES-GCM',\n\t\tiv: buffer(iv),\n\t\tadditionalData: buffer(wrapAdditionalData(keyring, keyVersion, keyId)),\n\t})\n\treturn { keyVersion, keyId, iv: toBase64(iv), wrappedKey: toBase64(new Uint8Array(wrapped)) }\n}\n\n/**\n * Unwrap a data key with the ring's wrapping key. A tampered wrap, or one relabelled to\n * another version, fails authentication.\n *\n * @param entry - The wrapped key\n * @param wrapKey - The master key's wrapping key\n * @param keyring - Keyring name\n * @param extractable - True only to re-wrap it right away; the kept copy is non-extractable\n * @throws {KeyUnwrapError} When the wrap does not authenticate under this key\n */\nexport async function unwrapDataKey(\n\tentry: WrappedDataKey,\n\twrapKey: CryptoKey,\n\tkeyring: string,\n\textractable = false,\n): Promise<CryptoKey> {\n\ttry {\n\t\treturn await subtle().unwrapKey(\n\t\t\t'raw',\n\t\t\tbuffer(fromBase64(entry.wrappedKey)),\n\t\t\twrapKey,\n\t\t\t{\n\t\t\t\tname: 'AES-GCM',\n\t\t\t\tiv: buffer(fromBase64(entry.iv)),\n\t\t\t\tadditionalData: buffer(wrapAdditionalData(keyring, entry.keyVersion, entry.keyId)),\n\t\t\t},\n\t\t\t{ name: 'AES-GCM', length: 256 },\n\t\t\textractable,\n\t\t\t['encrypt', 'decrypt'],\n\t\t)\n\t} catch (cause) {\n\t\tthrow new KeyUnwrapError(\n\t\t\t`Key version ${entry.keyVersion} of keyring \"${keyring}\" does not open under this ring's master key.`,\n\t\t\t{\n\t\t\t\tcode: 'KEY_UNWRAP_FAILED',\n\t\t\t\tkeyVersion: entry.keyVersion,\n\t\t\t\tcause: cause instanceof Error ? cause.message : String(cause),\n\t\t\t},\n\t\t)\n\t}\n}\n\n/** The record's MAC (base64 HMAC-SHA256) under a master key's MAC key. */\nexport async function computeRecordMac(\n\trecord: Omit<WrappedKeyRecord, 'mac'> | WrappedKeyRecord,\n\tmacKey: CryptoKey,\n): Promise<string> {\n\tconst mac = await subtle().sign('HMAC', macKey, buffer(keyRecordMacInput(record)))\n\treturn toBase64(new Uint8Array(mac))\n}\n\n/** A copy of `record` with its MAC computed under `macKey`. */\nexport async function sealRecord(\n\trecord: Omit<WrappedKeyRecord, 'mac'> | WrappedKeyRecord,\n\tmacKey: CryptoKey,\n): Promise<WrappedKeyRecord> {\n\tconst { mac: _old, ...body } = record as WrappedKeyRecord\n\treturn { ...body, mac: await computeRecordMac(body, macKey) } as WrappedKeyRecord\n}\n\n/** Whether the record's MAC verifies under `macKey` (constant-time, WebCrypto verify). */\nexport async function verifyRecordMac(\n\trecord: WrappedKeyRecord,\n\tmacKey: CryptoKey,\n): Promise<boolean> {\n\ttry {\n\t\treturn await subtle().verify(\n\t\t\t'HMAC',\n\t\t\tmacKey,\n\t\t\tbuffer(fromBase64(record.mac)),\n\t\t\tbuffer(keyRecordMacInput(record)),\n\t\t)\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * A new recovery key pair: the public half goes in the record, the private scalar `d`\n * into the recovery key ({@link formatRecoveryKey}) together with the ring anchor.\n */\nexport async function createRecoveryKeyPair(): Promise<{\n\tpublicKey: { x: string; y: string }\n\td: string\n}> {\n\tconst pair = (await subtle().generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, [\n\t\t'deriveBits',\n\t])) as CryptoKeyPair\n\tconst jwk = await subtle().exportKey('jwk', pair.privateKey)\n\tif (typeof jwk.d !== 'string' || typeof jwk.x !== 'string' || typeof jwk.y !== 'string') {\n\t\tthrow new SyncError('Failed to export the recovery key.', { code: 'RECOVERY_KEY_EXPORT' })\n\t}\n\treturn { publicKey: { x: jwk.x, y: jwk.y }, d: jwk.d }\n}\n\n/** The recovery key string the user stores: `kora-rk3-<d>.<anchor>`. */\nexport function formatRecoveryKey(d: string, anchor: Uint8Array): string {\n\tif (anchor.length !== RECOVERY_ANCHOR_BYTES) {\n\t\tthrow new SyncError('A recovery key anchor is 16 bytes.', { code: 'RECOVERY_KEY_EXPORT' })\n\t}\n\treturn `${RECOVERY_KEY_PREFIX}${d}.${toBase64Url(anchor)}`\n}\n\n/**\n * A new recovery key pair with a given (or, for tests, random) anchor.\n *\n * @param anchor - The ring anchor ({@link recoveryAnchor}); random when omitted\n */\nexport async function generateRecoveryKeyPair(\n\tanchor: Uint8Array = randomBytes(RECOVERY_ANCHOR_BYTES),\n): Promise<{\n\tpublicKey: { x: string; y: string }\n\trecoveryKey: string\n}> {\n\tconst { publicKey, d } = await createRecoveryKeyPair()\n\treturn { publicKey, recoveryKey: formatRecoveryKey(d, anchor) }\n}\n\n/**\n * The ring anchor of a recovery key (RT-107): the first 16 bytes of HMAC-SHA256 keyed\n * with the raw bytes of the ring's recovery ANCHOR KEY over the keyring, the ring id,\n * the anchor key's id and the recovery public key.\n *\n * Anyone can wrap a master key of their own to the (public) recovery key, so opening a\n * master key with the recovery key proves nothing about whose ring it is. The anchor key\n * is a data key created in the same write as the recovery key, under the ring's current\n * master key: no earlier passphrase or master key ever opened it, so neither the server\n * nor a holder of an OLDER (leaked) passphrase can build a ring that matches the anchor.\n * The tag reveals nothing about the key; matching it requires the key itself.\n *\n * @param dataKey - An extractable data key\n * @param keyring - Keyring name\n * @param ringId - Ring id\n * @param keyId - The data key's key id\n * @param publicKey - The recovery public key the anchor belongs to\n */\nexport async function recoveryAnchor(\n\tdataKey: CryptoKey,\n\tkeyring: string,\n\tringId: string,\n\tkeyId: string,\n\tpublicKey: { x: string; y: string },\n): Promise<Uint8Array> {\n\tconst raw = new Uint8Array(await subtle().exportKey('raw', dataKey))\n\ttry {\n\t\tconst hmac = await subtle().importKey(\n\t\t\t'raw',\n\t\t\tbuffer(raw),\n\t\t\t{ name: 'HMAC', hash: 'SHA-256' },\n\t\t\tfalse,\n\t\t\t['sign'],\n\t\t)\n\t\tconst input = JSON.stringify([\n\t\t\tRECOVERY_ANCHOR_INFO,\n\t\t\tkeyring,\n\t\t\tringId,\n\t\t\tkeyId,\n\t\t\tpublicKey.x,\n\t\t\tpublicKey.y,\n\t\t])\n\t\tconst tag = await subtle().sign('HMAC', hmac, buffer(new TextEncoder().encode(input)))\n\t\treturn new Uint8Array(tag).slice(0, RECOVERY_ANCHOR_BYTES)\n\t} finally {\n\t\traw.fill(0)\n\t}\n}\n\n/**\n * Split a recovery key into its private scalar and ring anchor.\n *\n * @throws {KeyUnwrapError} WRONG_RECOVERY_KEY when it is not a `kora-rk3-` recovery key.\n * Keys of the beta.13 release candidates (`kora-rk1-`, `kora-rk2-`) are refused with\n * `reason: 'RECOVERY_KEY_RETIRED'`: a `kora-rk2-` anchor is the ring's first data key,\n * which an older (possibly leaked) passphrase opens (RT-107).\n */\nexport function parseRecoveryKey(recoveryKey: string): { d: string; anchor: Uint8Array } {\n\tconst trimmed = recoveryKey.trim()\n\tif (/^kora-rk[12]-/.test(trimmed)) {\n\t\tthrow new KeyUnwrapError(\n\t\t\t'This recovery key comes from a 1.0.0-beta.13 release candidate and is no longer accepted: its anchor can be matched by a keyring built with an older passphrase. Call enableRecovery() again on a device unlocked with the current passphrase and store the new \"kora-rk3-\" key.',\n\t\t\t{ code: 'WRONG_RECOVERY_KEY', reason: 'RECOVERY_KEY_RETIRED' },\n\t\t)\n\t}\n\tconst match = /^kora-rk3-([A-Za-z0-9_-]{43})\\.([A-Za-z0-9_-]{22})$/.exec(trimmed)\n\tif (match === null) {\n\t\tthrow new KeyUnwrapError(\n\t\t\t`A recovery key has the form \"${RECOVERY_KEY_PREFIX}<key>.<anchor>\", as enableRecovery() returned it.`,\n\t\t\t{ code: 'WRONG_RECOVERY_KEY' },\n\t\t)\n\t}\n\treturn { d: match[1] as string, anchor: fromBase64Url(match[2] as string) }\n}\n\n/** Whether two byte strings are equal (no early exit). */\nexport function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n\tif (a.length !== b.length) return false\n\tlet diff = 0\n\tfor (let i = 0; i < a.length; i++) diff |= (a[i] as number) ^ (b[i] as number)\n\treturn diff === 0\n}\n\n/**\n * HKDF-SHA256 over the ECDH shared secret, with both public keys in the info, to the\n * AES-GCM key of a recovery wrap (never the raw shared secret as a key).\n */\nasync function recoveryWrappingKey(\n\tprivateKey: CryptoKey,\n\tpeer: CryptoKey,\n\tephemeral: { x: string; y: string },\n\trecipient: { x: string; y: string },\n\tusage: 'encrypt' | 'decrypt',\n): Promise<CryptoKey> {\n\tconst secret = new Uint8Array(\n\t\tawait subtle().deriveBits({ name: 'ECDH', public: peer }, privateKey, 256),\n\t)\n\ttry {\n\t\tconst base = await subtle().importKey('raw', buffer(secret), 'HKDF', false, ['deriveKey'])\n\t\treturn await subtle().deriveKey(\n\t\t\t{\n\t\t\t\tname: 'HKDF',\n\t\t\t\thash: 'SHA-256',\n\t\t\t\tsalt: new ArrayBuffer(0),\n\t\t\t\tinfo: buffer(\n\t\t\t\t\tnew TextEncoder().encode(\n\t\t\t\t\t\tJSON.stringify([\n\t\t\t\t\t\t\tHKDF_RECOVERY_WRAP,\n\t\t\t\t\t\t\tephemeral.x,\n\t\t\t\t\t\t\tephemeral.y,\n\t\t\t\t\t\t\trecipient.x,\n\t\t\t\t\t\t\trecipient.y,\n\t\t\t\t\t\t]),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t},\n\t\t\tbase,\n\t\t\t{ name: 'AES-GCM', length: 256 },\n\t\t\tfalse,\n\t\t\t[usage],\n\t\t)\n\t} finally {\n\t\tsecret.fill(0)\n\t}\n}\n\n/**\n * Wrap the master key for the recovery public key (ephemeral-static ECDH P-256).\n * Needs only the public key.\n */\nexport async function wrapMasterForRecovery(\n\traw: Uint8Array,\n\trecoveryPublicKey: { x: string; y: string },\n\tkeyring: string,\n\tringId: string,\n): Promise<KeyRecordRecovery> {\n\tconst recipient = await importPoint(recoveryPublicKey)\n\tconst ephemeral = (await subtle().generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, [\n\t\t'deriveBits',\n\t])) as CryptoKeyPair\n\tconst ephemeralJwk = await subtle().exportKey('jwk', ephemeral.publicKey)\n\tconst ephemeralPublicKey = { x: String(ephemeralJwk.x), y: String(ephemeralJwk.y) }\n\tconst key = await recoveryWrappingKey(\n\t\tephemeral.privateKey,\n\t\trecipient,\n\t\tephemeralPublicKey,\n\t\trecoveryPublicKey,\n\t\t'encrypt',\n\t)\n\tconst iv = randomBytes(GCM_IV_BYTES)\n\tconst sealed = await subtle().encrypt(\n\t\t{\n\t\t\tname: 'AES-GCM',\n\t\t\tiv: buffer(iv),\n\t\t\tadditionalData: buffer(\n\t\t\t\tmasterWrapAdditionalData(keyring, ringId, 'recovery', recoveryPublicKey),\n\t\t\t),\n\t\t},\n\t\tkey,\n\t\tbuffer(raw),\n\t)\n\treturn {\n\t\talg: RECOVERY_ALGORITHM,\n\t\tpublicKey: { x: recoveryPublicKey.x, y: recoveryPublicKey.y },\n\t\tephemeralPublicKey,\n\t\tiv: toBase64(iv),\n\t\twrappedKey: toBase64(new Uint8Array(sealed)),\n\t}\n}\n\n/**\n * Open the master key with the recovery key. The caller zeroes the returned bytes.\n *\n * @throws {KeyUnwrapError} WRONG_RECOVERY_KEY when the key is malformed or does not open it\n */\nexport async function unwrapMasterWithRecovery(\n\trecovery: KeyRecordRecovery,\n\trecoveryKey: string,\n\tkeyring: string,\n\tringId: string,\n): Promise<Uint8Array> {\n\tconst { d } = parseRecoveryKey(recoveryKey)\n\ttry {\n\t\tconst privateKey = await subtle().importKey(\n\t\t\t'jwk',\n\t\t\t{\n\t\t\t\tkty: 'EC',\n\t\t\t\tcrv: 'P-256',\n\t\t\t\tx: recovery.publicKey.x,\n\t\t\t\ty: recovery.publicKey.y,\n\t\t\t\td,\n\t\t\t\text: false,\n\t\t\t},\n\t\t\t{ name: 'ECDH', namedCurve: 'P-256' },\n\t\t\tfalse,\n\t\t\t['deriveBits'],\n\t\t)\n\t\tconst ephemeral = await importPoint(recovery.ephemeralPublicKey)\n\t\tconst key = await recoveryWrappingKey(\n\t\t\tprivateKey,\n\t\t\tephemeral,\n\t\t\trecovery.ephemeralPublicKey,\n\t\t\trecovery.publicKey,\n\t\t\t'decrypt',\n\t\t)\n\t\tconst raw = await subtle().decrypt(\n\t\t\t{\n\t\t\t\tname: 'AES-GCM',\n\t\t\t\tiv: buffer(fromBase64(recovery.iv)),\n\t\t\t\tadditionalData: buffer(\n\t\t\t\t\tmasterWrapAdditionalData(keyring, ringId, 'recovery', recovery.publicKey),\n\t\t\t\t),\n\t\t\t},\n\t\t\tkey,\n\t\t\tbuffer(fromBase64(recovery.wrappedKey)),\n\t\t)\n\t\treturn new Uint8Array(raw)\n\t} catch (cause) {\n\t\tthrow new KeyUnwrapError(`The recovery key does not open keyring \"${keyring}\".`, {\n\t\t\tcode: 'WRONG_RECOVERY_KEY',\n\t\t\tcause: cause instanceof Error ? cause.message : String(cause),\n\t\t})\n\t}\n}\n\n/** A new random data key, extractable so it can be wrapped (then discarded). */\nexport async function generateDataKey(): Promise<CryptoKey> {\n\treturn subtle().generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt'])\n}\n\n/**\n * Re-import an extractable data key as a non-extractable one (the copy a device\n * keeps). Exports the raw bytes transiently, inside this call only.\n */\nexport async function toNonExtractable(dataKey: CryptoKey): Promise<CryptoKey> {\n\tif (!dataKey.extractable) return dataKey\n\tconst raw = await subtle().exportKey('raw', dataKey)\n\ttry {\n\t\treturn await subtle().importKey('raw', raw, { name: 'AES-GCM', length: 256 }, false, [\n\t\t\t'encrypt',\n\t\t\t'decrypt',\n\t\t])\n\t} finally {\n\t\tnew Uint8Array(raw).fill(0)\n\t}\n}\n\nasync function importPoint(point: { x: string; y: string }): Promise<CryptoKey> {\n\treturn subtle().importKey(\n\t\t'jwk',\n\t\t{ kty: 'EC', crv: 'P-256', x: point.x, y: point.y, ext: true },\n\t\t{ name: 'ECDH', namedCurve: 'P-256' },\n\t\ttrue,\n\t\t[],\n\t)\n}\n\n/** Standard base64 of bytes. */\nexport function toBase64(bytes: Uint8Array): string {\n\tlet binary = ''\n\tfor (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i] as number)\n\treturn btoa(binary)\n}\n\nfunction toBase64Url(bytes: Uint8Array): string {\n\treturn toBase64(bytes).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction fromBase64Url(value: string): Uint8Array {\n\tconst padded = value.replace(/-/g, '+').replace(/_/g, '/')\n\treturn fromBase64(padded + '='.repeat((4 - (padded.length % 4)) % 4))\n}\n\n/** Bytes of standard base64. */\nexport function fromBase64(value: string): Uint8Array {\n\tconst binary = atob(value)\n\tconst bytes = new Uint8Array(binary.length)\n\tfor (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)\n\treturn bytes\n}\n","import { SyncError } from '@korajs/core'\nimport type {\n\tAtomicOp,\n\tEncryptedEnvelopeField,\n\tEncryptedOperationEnvelope,\n\tHLCTimestamp,\n\tOperation,\n\tOperationType,\n} from '@korajs/core'\nimport { canonicalize } from '@korajs/core/internal'\nimport type { SerializedOperation } from '../protocol/messages'\nimport { deriveVersionedKey } from './key-derivation'\nimport type { SyncEncryptionConfig, VersionedKey } from './types'\n\n/**\n * Thrown when encryption of operation data fails.\n */\nexport class EncryptionError extends SyncError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, { ...context, errorType: 'ENCRYPTION_ERROR' })\n\t\tthis.name = 'EncryptionError'\n\t}\n}\n\n/**\n * Thrown when decryption of operation data fails.\n * This typically indicates a wrong key, tampered ciphertext, or corrupted data.\n */\nexport class DecryptionError extends SyncError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, { ...context, errorType: 'DECRYPTION_ERROR' })\n\t\tthis.name = 'DecryptionError'\n\t}\n}\n\n/** AES-GCM initialization vector length in bytes (96 bits). NIST recommended. */\nconst IV_LENGTH = 12\n\n/** Marker of the protocol-1 in-`data` payload (no integrity binding; no longer accepted). */\nconst LEGACY_ENCRYPTED_MARKER = '__kora_e2e_encrypted' as const\n\n/** The members of an operation the envelope protects. */\ntype EnvelopeMember = 'data' | 'previousData' | 'atomicOps'\n\n/**\n * The operation shape the encryptor works on: a domain {@link Operation} or its wire\n * form {@link SerializedOperation} (same fields).\n */\ninterface EncryptableOperation {\n\tid: string\n\tnodeId: string\n\ttype: OperationType\n\tcollection: string\n\trecordId: string\n\tdata: Record<string, unknown> | null\n\tpreviousData: Record<string, unknown> | null\n\ttimestamp: HLCTimestamp\n\tsequenceNumber: number\n\tschemaVersion: number\n\tatomicOps?: Record<string, AtomicOp>\n\thashVersion?: 1 | 2\n\tencrypted?: EncryptedOperationEnvelope\n}\n\n/**\n * End-to-end encryption of operations, envelope v2 (protocol v2; ENC-3, NEW-ENC-1).\n *\n * **Envelope.** The plaintext `data`, `previousData` and `atomicOps` move into\n * `op.encrypted` ({@link EncryptedOperationEnvelope}: alg, keyId, keyVersion and one\n * AES-256-GCM ciphertext per member). On the wire `data` is null, or holds only the\n * collection's documented cleartext scope fields (`cleartextFields`), so a schema-aware\n * server stores the operation opaquely and can still evaluate scopes. Metadata (id,\n * nodeId, collection, recordId, timestamp, sequence, causalDeps, schemaVersion) stays in\n * cleartext so the server can route, deduplicate and order.\n *\n * **Binding (ENC-3).** Every ciphertext is authenticated with AES-GCM additional data\n * canonical(nodeId, collection, recordId, type, timestamp, sequenceNumber, field,\n * keyVersion, hashVersion): a ciphertext moved to another operation, record or member,\n * or an envelope whose metadata was rewritten, fails authentication. `data` and\n * `previousData` are always encrypted, even when null, so a delete is authenticated too.\n * The operation id (the version-2 content hash of the PLAINTEXT) is verified after\n * decryption by the sync engine (`verify-inbound.ts`).\n *\n * **Plaintext.** With encryption enabled an operation without an envelope is refused\n * (a server must not be able to inject unauthenticated writes), unless the config opens\n * a migration window (`allowPlaintextMigration`). Protocol-1 payloads (ciphertext inside\n * `data`, no binding) are refused the same way.\n *\n * **Key id.** The envelope names its key material (`keyId`: the keyring's random key\n * id, ENC-1), so a device holding different material reports a diagnosable\n * `KEY_ID_MISMATCH` rather than a bare authentication failure.\n *\n * @example\n * ```typescript\n * const encryptor = SyncEncryptor.fromKeys([{ version: 1, key: dataKey, keyId }])\n * const sealed = await encryptor.encryptOperation(operation)\n * const opened = await encryptor.decryptOperation(sealed)\n * ```\n */\nexport class SyncEncryptor {\n\t/**\n\t * Map of key version -> VersionedKey. The current (latest) version is used\n\t * for encryption. All versions are available for decryption (key rotation).\n\t */\n\tprivate readonly keys: Map<number, VersionedKey>\n\t/** The current key version used for encryption. */\n\tprivate currentVersion: number\n\t/** Fingerprint of each key version's material (cached). */\n\tprivate readonly keyIds = new Map<number, Promise<string>>()\n\tprivate readonly cleartextFields: Readonly<Record<string, readonly string[]>>\n\tprivate readonly allowPlaintextMigration: boolean\n\n\tprivate constructor(\n\t\tkeys: Map<number, VersionedKey>,\n\t\tcurrentVersion: number,\n\t\toptions: EncryptorOptions = {},\n\t) {\n\t\tthis.keys = keys\n\t\tthis.currentVersion = currentVersion\n\t\tthis.cleartextFields = options.cleartextFields ?? {}\n\t\tthis.allowPlaintextMigration = options.allowPlaintextMigration === true\n\t}\n\n\t/**\n\t * Creates a SyncEncryptor from a passphrase and an explicit, shared salt.\n\t *\n\t * Low-level: apps use the keyring (`createApp` with `sync.encryption`), which\n\t * distributes random data keys wrapped by the passphrase. This derives the data key\n\t * directly from the passphrase, so every device must pass the same salt; there is\n\t * no random-salt default any more (ENC-1: a per-process random salt made every\n\t * device derive a different key).\n\t *\n\t * @param config - Encryption configuration with the passphrase in `key`\n\t * @param salt - The key-derivation salt, shared by every device (16 bytes or more)\n\t * @param iterations - Optional PBKDF2 iteration count. Lower it only in tests.\n\t * @returns A configured SyncEncryptor instance\n\t * @throws {EncryptionError} If configuration is invalid or the salt is missing\n\t * @throws {KeyDerivationError} If key derivation fails\n\t */\n\tstatic async create(\n\t\tconfig: SyncEncryptionConfig,\n\t\tsalt: Uint8Array,\n\t\titerations?: number,\n\t): Promise<SyncEncryptor> {\n\t\tif (!config.enabled) {\n\t\t\tthrow new EncryptionError(\n\t\t\t\t'Cannot create SyncEncryptor with encryption disabled. ' +\n\t\t\t\t\t'Set enabled: true in the encryption config.',\n\t\t\t)\n\t\t}\n\t\tif (!(salt instanceof Uint8Array) || salt.length < 16) {\n\t\t\tthrow new EncryptionError(\n\t\t\t\t'SyncEncryptor.create needs the shared key-derivation salt (16 bytes or more). Devices that derive with different salts cannot read each other; use the keyring (sync.encryption in createApp) for shared keys.',\n\t\t\t\t{ code: 'SALT_REQUIRED' },\n\t\t\t)\n\t\t}\n\n\t\tconst passphrase = typeof config.key === 'function' ? await config.key() : (config.key ?? '')\n\n\t\tif (passphrase.length === 0) {\n\t\t\tthrow new EncryptionError(\n\t\t\t\t'Encryption key/passphrase must not be empty. ' +\n\t\t\t\t\t'Provide a non-empty string or key provider function.',\n\t\t\t)\n\t\t}\n\n\t\tconst versionedKey = await deriveVersionedKey(passphrase, 1, salt, iterations)\n\t\tconst keys = new Map<number, VersionedKey>()\n\t\tkeys.set(1, versionedKey)\n\n\t\treturn new SyncEncryptor(keys, 1, {\n\t\t\t...(config.cleartextFields ? { cleartextFields: config.cleartextFields } : {}),\n\t\t\t...(config.allowPlaintextMigration ? { allowPlaintextMigration: true } : {}),\n\t\t})\n\t}\n\n\t/**\n\t * Creates a SyncEncryptor from pre-derived versioned keys.\n\t *\n\t * Use this when you need to support multiple key versions for key rotation,\n\t * or when you have already derived the keys externally.\n\t *\n\t * @param versionedKeys - Array of versioned keys. The highest version is used for encryption.\n\t * @param options - Cleartext scope fields and the plaintext migration window\n\t * @returns A configured SyncEncryptor instance\n\t * @throws {EncryptionError} If no keys are provided\n\t */\n\tstatic fromKeys(versionedKeys: VersionedKey[], options: EncryptorOptions = {}): SyncEncryptor {\n\t\tif (versionedKeys.length === 0) {\n\t\t\tthrow new EncryptionError('At least one versioned key must be provided.')\n\t\t}\n\n\t\tconst keys = new Map<number, VersionedKey>()\n\t\tlet maxVersion = 0\n\n\t\tfor (const vk of versionedKeys) {\n\t\t\tkeys.set(vk.version, vk)\n\t\t\tif (vk.version > maxVersion) {\n\t\t\t\tmaxVersion = vk.version\n\t\t\t}\n\t\t}\n\n\t\treturn new SyncEncryptor(keys, maxVersion, options)\n\t}\n\n\t/**\n\t * Add a new key version for key rotation.\n\t *\n\t * After adding, the new key becomes the current version used for encryption\n\t * if its version number is higher than the current version. Previously-versioned\n\t * keys remain available for decrypting older operations.\n\t *\n\t * @param key - The new versioned key to add\n\t * @throws {EncryptionError} If the key version already exists\n\t */\n\taddKey(key: VersionedKey): void {\n\t\tif (this.keys.has(key.version)) {\n\t\t\tthrow new EncryptionError(\n\t\t\t\t`Key version ${key.version} already exists. Use a higher version number for rotation.`,\n\t\t\t\t{ existingVersion: key.version },\n\t\t\t)\n\t\t}\n\n\t\tthis.keys.set(key.version, key)\n\t\tif (key.version > this.currentVersion) {\n\t\t\tthis.currentVersion = key.version\n\t\t}\n\t}\n\n\t/** The key registered under an explicit key id (keyring keys), if any. */\n\tprivate keyById(keyId: string): VersionedKey | undefined {\n\t\tfor (const key of this.keys.values()) {\n\t\t\tif (key.keyId === keyId) return key\n\t\t}\n\t\treturn undefined\n\t}\n\n\t/**\n\t * Get the current encryption key version number.\n\t */\n\tgetCurrentKeyVersion(): number {\n\t\treturn this.currentVersion\n\t}\n\n\t/**\n\t * The key id written into envelopes for a key version: the keyring's random key id,\n\t * or for a passphrase-derived key a fingerprint of its salt (never the key itself).\n\t *\n\t * @param version - Key version (defaults to the current one)\n\t */\n\tasync getKeyId(version: number = this.currentVersion): Promise<string> {\n\t\tconst key = this.keys.get(version)\n\t\tif (!key) {\n\t\t\tthrow new EncryptionError(`Encryption key version ${version} not found.`, { version })\n\t\t}\n\t\tif (key.keyId !== undefined) return key.keyId\n\t\tlet cached = this.keyIds.get(version)\n\t\tif (!cached) {\n\t\t\tif (!key.salt) {\n\t\t\t\tthrow new EncryptionError(\n\t\t\t\t\t`Encryption key version ${version} has neither a key id nor a salt to name it.`,\n\t\t\t\t\t{ version },\n\t\t\t\t)\n\t\t\t}\n\t\t\tcached = keyFingerprint(key.salt)\n\t\t\tthis.keyIds.set(version, cached)\n\t\t}\n\t\treturn cached\n\t}\n\n\t/**\n\t * Seal an operation into an envelope v2.\n\t *\n\t * Returns a new operation (operations are immutable): `encrypted` holds the\n\t * ciphertexts, `data` is null or the collection's cleartext scope fields, and\n\t * `previousData`/`atomicOps` are removed. An operation that already carries an\n\t * envelope is returned unchanged.\n\t *\n\t * @param operation - The plaintext operation\n\t * @returns The sealed operation\n\t * @throws {EncryptionError} If encryption fails\n\t */\n\tasync encryptOperation(operation: Operation): Promise<Operation> {\n\t\treturn this.seal(operation)\n\t}\n\n\t/**\n\t * Open an envelope v2 and restore the plaintext `data`, `previousData` and\n\t * `atomicOps`. The returned operation has no `encrypted` member.\n\t *\n\t * @param operation - The sealed operation\n\t * @returns The plaintext operation (its id is NOT verified here; the caller does)\n\t * @throws {DecryptionError} On a plaintext operation (unless the migration window\n\t * is open), a protocol-1 payload, an unknown key, a key-id mismatch, or a\n\t * ciphertext that fails authentication (tampered, transplanted, wrong key)\n\t */\n\tasync decryptOperation(operation: Operation): Promise<Operation> {\n\t\treturn this.open(operation)\n\t}\n\n\t/**\n\t * Same as {@link encryptOperation} for the wire form.\n\t *\n\t * @param serialized - The serialized operation to encrypt\n\t * @returns The sealed serialized operation\n\t */\n\tasync encryptSerializedOperation(serialized: SerializedOperation): Promise<SerializedOperation> {\n\t\treturn this.seal(serialized)\n\t}\n\n\t/**\n\t * Same as {@link decryptOperation} for the wire form.\n\t *\n\t * @param serialized - The serialized operation to decrypt\n\t * @returns The plaintext serialized operation\n\t */\n\tasync decryptSerializedOperation(serialized: SerializedOperation): Promise<SerializedOperation> {\n\t\treturn this.open(serialized)\n\t}\n\n\t/**\n\t * Encrypt a batch of operations in parallel.\n\t *\n\t * @param operations - Operations to encrypt\n\t * @returns New operations with encrypted data fields\n\t */\n\tasync encryptBatch(operations: Operation[]): Promise<Operation[]> {\n\t\treturn Promise.all(operations.map((op) => this.encryptOperation(op)))\n\t}\n\n\t/**\n\t * Decrypt a batch of operations in parallel.\n\t *\n\t * @param operations - Operations to decrypt\n\t * @returns New operations with decrypted data fields\n\t */\n\tasync decryptBatch(operations: Operation[]): Promise<Operation[]> {\n\t\treturn Promise.all(operations.map((op) => this.decryptOperation(op)))\n\t}\n\n\t/**\n\t * Check if an operation `data` field holds a protocol-1 encrypted payload (the\n\t * pre-envelope format, refused since protocol v2).\n\t *\n\t * @param field - An operation's `data` or `previousData` field\n\t */\n\tstatic isEncryptedPayload(field: Record<string, unknown> | null): boolean {\n\t\treturn isEncryptedPayload(field)\n\t}\n\n\t/**\n\t * Whether a plaintext operation touches only data the server can already read: its\n\t * `data` (and, except for a delete, its `previousData`) names only the collection's\n\t * cleartext scope fields, and it has no atomic ops. The sync engine accepts such an\n\t * operation from a server-authored node (a cascade, a set-null of a cleartext\n\t * reference, a constraint correction of a cleartext field) under end-to-end\n\t * encryption: the server cannot seal it (it has no key), and it reveals or forges\n\t * nothing the server does not already hold in cleartext. Any sealed field makes it\n\t * false, so the operation is refused as plaintext.\n\t *\n\t * @param operation - A delivered operation without an envelope\n\t */\n\tisCleartextOnly(operation: Operation): boolean {\n\t\tif (operation.encrypted !== undefined) return false\n\t\tif (operation.atomicOps !== undefined && Object.keys(operation.atomicOps).length > 0) {\n\t\t\treturn false\n\t\t}\n\t\tconst allowed = new Set(this.cleartextFields[operation.collection] ?? [])\n\t\tconst within = (value: Record<string, unknown> | null): boolean =>\n\t\t\tvalue === null || Object.keys(value).every((field) => allowed.has(field))\n\t\tif (!within(operation.data)) return false\n\t\t// A delete's previousData is informational: the fold never reads it.\n\t\treturn operation.type === 'delete' || within(operation.previousData)\n\t}\n\n\t/** Whether an operation carries an envelope v2. */\n\tstatic isEncryptedOperation(operation: { encrypted?: unknown }): boolean {\n\t\treturn operation.encrypted !== undefined && operation.encrypted !== null\n\t}\n\n\t// --- Private helpers ---\n\n\tprivate async seal<T extends EncryptableOperation>(operation: T): Promise<T> {\n\t\tif (operation.encrypted !== undefined) return operation\n\t\tconst keyVersion = this.currentVersion\n\t\tconst key = this.keys.get(keyVersion)\n\t\tif (!key) {\n\t\t\tthrow new EncryptionError(`Current encryption key version ${keyVersion} not found.`, {\n\t\t\t\toperationId: operation.id,\n\t\t\t})\n\t\t}\n\t\tconst keyId = await this.getKeyId(keyVersion)\n\t\tconst hasAtomicOps =\n\t\t\toperation.atomicOps !== undefined && Object.keys(operation.atomicOps).length > 0\n\t\tconst [data, previousData, atomicOps] = await Promise.all([\n\t\t\tthis.encryptMember(operation, 'data', operation.data, key, keyVersion),\n\t\t\tthis.encryptMember(operation, 'previousData', operation.previousData, key, keyVersion),\n\t\t\thasAtomicOps\n\t\t\t\t? this.encryptMember(operation, 'atomicOps', operation.atomicOps ?? null, key, keyVersion)\n\t\t\t\t: Promise.resolve(null),\n\t\t])\n\t\tconst envelope: EncryptedOperationEnvelope = {\n\t\t\tv: 2,\n\t\t\talg: 'aes-256-gcm',\n\t\t\tkeyId,\n\t\t\tkeyVersion,\n\t\t\tdata,\n\t\t\tpreviousData,\n\t\t\t...(atomicOps !== null ? { atomicOps } : {}),\n\t\t}\n\t\tconst { atomicOps: _plainAtomic, ...rest } = operation\n\t\treturn {\n\t\t\t...rest,\n\t\t\tdata: this.cleartextScope(operation),\n\t\t\tpreviousData: null,\n\t\t\tencrypted: envelope,\n\t\t} as T\n\t}\n\n\tprivate async open<T extends EncryptableOperation>(operation: T): Promise<T> {\n\t\tconst envelope = operation.encrypted\n\t\tif (envelope === undefined || envelope === null) {\n\t\t\tif (isEncryptedPayload(operation.data) || isEncryptedPayload(operation.previousData)) {\n\t\t\t\tthrow new DecryptionError(\n\t\t\t\t\t`Operation ${operation.id} carries a protocol-1 encrypted payload (ciphertext inside data, not bound to the operation). Protocol v2 accepts only the encryption envelope; re-sync from an upgraded client.`,\n\t\t\t\t\t{ operationId: operation.id, code: 'LEGACY_ENCRYPTED_PAYLOAD' },\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (this.allowPlaintextMigration) return operation\n\t\t\tthrow new DecryptionError(\n\t\t\t\t`Operation ${operation.id} is not encrypted, but end-to-end encryption is enabled: a plaintext operation could have been written by anyone who can reach the sync server. It is refused. During a migration from plaintext sync, set encryption.allowPlaintextMigration.`,\n\t\t\t\t{ operationId: operation.id, code: 'PLAINTEXT_REJECTED' },\n\t\t\t)\n\t\t}\n\t\tif (envelope.v !== 2 || envelope.alg !== 'aes-256-gcm') {\n\t\t\tthrow new DecryptionError(\n\t\t\t\t`Unsupported encryption envelope (v${String(envelope.v)}, ${String(envelope.alg)}) on operation ${operation.id}. Update @korajs/sync.`,\n\t\t\t\t{ operationId: operation.id },\n\t\t\t)\n\t\t}\n\t\t// Keyring keys are found by key id: after two forked rings are merged (RT-104) one\n\t\t// ring holds keys that sealed operations under another ring's version numbers. The\n\t\t// envelope's own keyVersion is still what the AAD binds.\n\t\tconst key = this.keyById(envelope.keyId) ?? this.keys.get(envelope.keyVersion)\n\t\tif (!key) {\n\t\t\tthrow new DecryptionError(\n\t\t\t\t`No encryption key available for version ${envelope.keyVersion} (key id ${envelope.keyId}). This operation was encrypted with a key that is not registered. If you rotated keys, ensure all previous key versions are provided.`,\n\t\t\t\t{\n\t\t\t\t\toperationId: operation.id,\n\t\t\t\t\tkeyVersion: envelope.keyVersion,\n\t\t\t\t\tkeyId: envelope.keyId,\n\t\t\t\t\tavailableVersions: [...this.keys.keys()],\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t\tconst localKeyId = key.keyId ?? (await this.getKeyId(envelope.keyVersion))\n\t\tif (localKeyId !== envelope.keyId) {\n\t\t\tthrow new DecryptionError(\n\t\t\t\t`Operation ${operation.id} was encrypted with key material \"${envelope.keyId}\" (version ${envelope.keyVersion}), but this device holds \"${localKeyId}\". The devices hold different keys: they must open the same keyring (the same user and keyring name) on the same sync server.`,\n\t\t\t\t{\n\t\t\t\t\toperationId: operation.id,\n\t\t\t\t\tcode: 'KEY_ID_MISMATCH',\n\t\t\t\t\tkeyId: envelope.keyId,\n\t\t\t\t\tlocalKeyId,\n\t\t\t\t\tkeyVersion: envelope.keyVersion,\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t\tif (envelope.data === null || envelope.previousData === null) {\n\t\t\tthrow new DecryptionError(\n\t\t\t\t`Operation ${operation.id} has an incomplete encryption envelope (data and previousData must both be sealed).`,\n\t\t\t\t{ operationId: operation.id },\n\t\t\t)\n\t\t}\n\t\tconst [data, previousData, atomicOps] = await Promise.all([\n\t\t\tthis.decryptMember(operation, 'data', envelope.data, key, envelope.keyVersion),\n\t\t\tthis.decryptMember(\n\t\t\t\toperation,\n\t\t\t\t'previousData',\n\t\t\t\tenvelope.previousData,\n\t\t\t\tkey,\n\t\t\t\tenvelope.keyVersion,\n\t\t\t),\n\t\t\tenvelope.atomicOps\n\t\t\t\t? this.decryptMember(operation, 'atomicOps', envelope.atomicOps, key, envelope.keyVersion)\n\t\t\t\t: Promise.resolve(null),\n\t\t])\n\t\tconst { encrypted: _sealed, atomicOps: _unsealedAtomic, ...rest } = operation\n\t\treturn {\n\t\t\t...rest,\n\t\t\tdata,\n\t\t\tpreviousData,\n\t\t\t...(atomicOps !== null ? { atomicOps: atomicOps as Record<string, AtomicOp> } : {}),\n\t\t} as T\n\t}\n\n\t/** The collection's documented cleartext scope fields, or null when none. */\n\tprivate cleartextScope(operation: EncryptableOperation): Record<string, unknown> | null {\n\t\tconst fields = this.cleartextFields[operation.collection]\n\t\tif (!fields || fields.length === 0 || operation.data === null) return null\n\t\tconst scope: Record<string, unknown> = {}\n\t\tfor (const field of fields) {\n\t\t\tif (field in operation.data) scope[field] = operation.data[field]\n\t\t}\n\t\treturn Object.keys(scope).length > 0 ? scope : null\n\t}\n\n\tprivate async encryptMember(\n\t\toperation: EncryptableOperation,\n\t\tmember: EnvelopeMember,\n\t\tvalue: Record<string, unknown> | null,\n\t\tkey: VersionedKey,\n\t\tkeyVersion: number,\n\t): Promise<EncryptedEnvelopeField> {\n\t\ttry {\n\t\t\tconst plaintext = new TextEncoder().encode(JSON.stringify(value))\n\t\t\t// A fresh random IV per member encryption (NIST SP 800-38D, 96-bit IV).\n\t\t\tconst iv = globalThis.crypto.getRandomValues(new Uint8Array(IV_LENGTH))\n\t\t\tconst ciphertext = await globalThis.crypto.subtle.encrypt(\n\t\t\t\t{\n\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\tiv: iv as unknown as ArrayBuffer,\n\t\t\t\t\tadditionalData: additionalData(operation, member, keyVersion) as unknown as ArrayBuffer,\n\t\t\t\t},\n\t\t\t\tkey.key,\n\t\t\t\tplaintext as unknown as ArrayBuffer,\n\t\t\t)\n\t\t\treturn { iv: toBase64(iv), ct: toBase64(new Uint8Array(ciphertext)) }\n\t\t} catch (cause) {\n\t\t\tthrow new EncryptionError(\n\t\t\t\t`Failed to encrypt operation ${member}. Ensure the encryption key is valid and crypto.subtle is available.`,\n\t\t\t\t{\n\t\t\t\t\toperationId: operation.id,\n\t\t\t\t\tfieldName: member,\n\t\t\t\t\tcause: cause instanceof Error ? cause.message : String(cause),\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate async decryptMember(\n\t\toperation: EncryptableOperation,\n\t\tmember: EnvelopeMember,\n\t\tsealed: EncryptedEnvelopeField,\n\t\tkey: VersionedKey,\n\t\tkeyVersion: number,\n\t): Promise<Record<string, unknown> | null> {\n\t\tlet parsed: unknown\n\t\ttry {\n\t\t\tconst plaintext = await globalThis.crypto.subtle.decrypt(\n\t\t\t\t{\n\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\tiv: fromBase64(sealed.iv) as unknown as ArrayBuffer,\n\t\t\t\t\tadditionalData: additionalData(operation, member, keyVersion) as unknown as ArrayBuffer,\n\t\t\t\t},\n\t\t\t\tkey.key,\n\t\t\t\tfromBase64(sealed.ct) as unknown as ArrayBuffer,\n\t\t\t)\n\t\t\tparsed = JSON.parse(new TextDecoder().decode(plaintext))\n\t\t} catch (cause) {\n\t\t\tthrow new DecryptionError(\n\t\t\t\t`Failed to decrypt operation ${member}: the ciphertext does not authenticate against this operation. It was tampered with, moved from another operation, record or field, or encrypted with a different key.`,\n\t\t\t\t{\n\t\t\t\t\toperationId: operation.id,\n\t\t\t\t\tfieldName: member,\n\t\t\t\t\tkeyVersion,\n\t\t\t\t\tcause: cause instanceof Error ? cause.message : String(cause),\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t\tif (parsed === null) return null\n\t\tif (typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\tthrow new DecryptionError(`Decrypted ${member} is not a valid record object.`, {\n\t\t\t\toperationId: operation.id,\n\t\t\t\tfieldName: member,\n\t\t\t})\n\t\t}\n\t\treturn parsed as Record<string, unknown>\n\t}\n}\n\n/** Options of {@link SyncEncryptor.fromKeys}. */\nexport interface EncryptorOptions {\n\t/** Per collection, the data fields kept in cleartext beside the envelope (for scopes). */\n\tcleartextFields?: Readonly<Record<string, readonly string[]>>\n\t/** Accept plaintext (envelope-less) inbound operations during a migration window. */\n\tallowPlaintextMigration?: boolean\n}\n\n/**\n * AES-GCM additional authenticated data of one envelope member (ENC-3): canonical\n * JSON of (nodeId, collection, recordId, type, timestamp, sequenceNumber, field,\n * keyVersion, hashVersion). The declared hash version is bound too, so an envelope's\n * id cannot be downgraded to the weaker version-1 hash in transit.\n */\nfunction additionalData(\n\toperation: EncryptableOperation,\n\tmember: EnvelopeMember,\n\tkeyVersion: number,\n): Uint8Array {\n\treturn new TextEncoder().encode(\n\t\tcanonicalize({\n\t\t\tnodeId: operation.nodeId,\n\t\t\tcollection: operation.collection,\n\t\t\trecordId: operation.recordId,\n\t\t\ttype: operation.type,\n\t\t\ttimestamp: {\n\t\t\t\twallTime: operation.timestamp.wallTime,\n\t\t\t\tlogical: operation.timestamp.logical,\n\t\t\t\tnodeId: operation.timestamp.nodeId,\n\t\t\t},\n\t\t\tsequenceNumber: operation.sequenceNumber,\n\t\t\tfield: member,\n\t\t\tkeyVersion,\n\t\t\thashVersion: operation.hashVersion ?? 1,\n\t\t}),\n\t)\n}\n\n/** First 16 hex chars of SHA-256(\"kora-key-id\" || salt): names key material, reveals nothing. */\nasync function keyFingerprint(salt: Uint8Array): Promise<string> {\n\tconst prefix = new TextEncoder().encode('kora-key-id\\u0000')\n\tconst input = new Uint8Array(prefix.length + salt.length)\n\tinput.set(prefix, 0)\n\tinput.set(salt, prefix.length)\n\tconst digest = new Uint8Array(\n\t\tawait globalThis.crypto.subtle.digest('SHA-256', input as unknown as ArrayBuffer),\n\t)\n\treturn `k1-${Array.from(digest.slice(0, 8), (b) => b.toString(16).padStart(2, '0')).join('')}`\n}\n\n// --- Utility functions ---\n\n/**\n * Check if a field value contains a protocol-1 encrypted payload (ciphertext inside\n * `data`). Protocol v2 refuses such payloads; the envelope lives in `op.encrypted`.\n *\n * @param field - An operation's `data` or `previousData` field\n * @returns true if the field contains a protocol-1 encrypted payload\n */\nexport function isEncryptedPayload(field: Record<string, unknown> | null): boolean {\n\tif (field === null || typeof field !== 'object') {\n\t\treturn false\n\t}\n\treturn (\n\t\tfield[LEGACY_ENCRYPTED_MARKER] === true &&\n\t\ttypeof field.v === 'number' &&\n\t\ttypeof field.iv === 'string' &&\n\t\ttypeof field.ct === 'string' &&\n\t\ttypeof field.alg === 'string'\n\t)\n}\n\n// --- Base64 helpers ---\n\nfunction toBase64(bytes: Uint8Array): string {\n\tlet binary = ''\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\tbinary += String.fromCharCode(bytes[i] as number)\n\t}\n\treturn btoa(binary)\n}\n\nfunction fromBase64(str: string): Uint8Array {\n\tconst binary = atob(str)\n\tconst bytes = new Uint8Array(binary.length)\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tbytes[i] = binary.charCodeAt(i)\n\t}\n\treturn bytes\n}\n","import { generateUUIDv7 } from '@korajs/core'\nimport type { YjsDocUpdateMessage } from '../protocol/messages'\nimport { decodeYjsUpdate, encodeYjsUpdate, richtextDocKey } from './doc-channel-wire'\n\n/** Default snapshot size above which the doc channel is preferred. */\nexport const DEFAULT_RICHTEXT_DOC_CHANNEL_THRESHOLD = 4096\n\nexport type RichtextDocListener = (update: Uint8Array) => void\n\nexport interface RichtextDocChannelOptions {\n\t/** Snapshot byte length at which incremental channel sync is recommended. */\n\tlargeDocThreshold?: number\n\t/** Called when a local update should be sent on the wire. */\n\tonSend?: (message: YjsDocUpdateMessage) => void\n}\n\n/**\n * Side channel for incremental Yjs updates on large richtext fields.\n * Ephemeral — not persisted in the operation log (durable state still flows via ops).\n */\nexport class RichtextDocChannel {\n\tprivate readonly threshold: number\n\tprivate readonly onSend: ((message: YjsDocUpdateMessage) => void) | null\n\tprivate readonly listeners = new Map<string, Set<RichtextDocListener>>()\n\n\tconstructor(options?: RichtextDocChannelOptions) {\n\t\tthis.threshold = options?.largeDocThreshold ?? DEFAULT_RICHTEXT_DOC_CHANNEL_THRESHOLD\n\t\tthis.onSend = options?.onSend ?? null\n\t}\n\n\t/**\n\t * Whether the doc channel should be used for a field.\n\t * @param preference - `true` forces on, `false` forces off, `undefined` uses size threshold\n\t */\n\tshouldUseChannel(snapshotByteLength: number, preference?: boolean): boolean {\n\t\tif (preference === false) {\n\t\t\treturn false\n\t\t}\n\t\tif (preference === true) {\n\t\t\treturn true\n\t\t}\n\t\treturn snapshotByteLength >= this.threshold\n\t}\n\n\tgetThreshold(): number {\n\t\treturn this.threshold\n\t}\n\n\t/**\n\t * Subscribe to incremental updates for a document key.\n\t */\n\tsubscribe(\n\t\tcollection: string,\n\t\trecordId: string,\n\t\tfield: string,\n\t\tlistener: RichtextDocListener,\n\t): () => void {\n\t\tconst key = richtextDocKey(collection, recordId, field)\n\t\tlet set = this.listeners.get(key)\n\t\tif (!set) {\n\t\t\tset = new Set()\n\t\t\tthis.listeners.set(key, set)\n\t\t}\n\t\tset.add(listener)\n\n\t\treturn () => {\n\t\t\tconst current = this.listeners.get(key)\n\t\t\tif (!current) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcurrent.delete(listener)\n\t\t\tif (current.size === 0) {\n\t\t\t\tthis.listeners.delete(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Publish a local incremental update to peers.\n\t */\n\tsend(collection: string, recordId: string, field: string, update: Uint8Array): void {\n\t\tif (!this.onSend || update.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tthis.onSend({\n\t\t\ttype: 'yjs-doc-update',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tcollection,\n\t\t\trecordId,\n\t\t\tfield,\n\t\t\tupdate: encodeYjsUpdate(update),\n\t\t})\n\t}\n\n\t/**\n\t * Deliver a remote incremental update to local subscribers.\n\t */\n\tdeliver(message: YjsDocUpdateMessage): void {\n\t\tconst key = richtextDocKey(message.collection, message.recordId, message.field)\n\t\tconst set = this.listeners.get(key)\n\t\tif (!set || set.size === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst update = decodeYjsUpdate(message.update)\n\t\tfor (const listener of set) {\n\t\t\tlistener(update)\n\t\t}\n\t}\n}\n","const WIRE_BYTES_KEY = '__kora_bytes__'\n\n/**\n * Encode a Yjs update for the doc channel wire (base64).\n */\nexport function encodeYjsUpdate(update: Uint8Array): string {\n\tlet binary = ''\n\tfor (let i = 0; i < update.length; i++) {\n\t\tbinary += String.fromCharCode(update[i] as number)\n\t}\n\treturn btoa(binary)\n}\n\n/**\n * Decode a base64 Yjs update from the doc channel wire.\n */\nexport function decodeYjsUpdate(encoded: string): Uint8Array {\n\tconst binary = atob(encoded)\n\tconst bytes = new Uint8Array(binary.length)\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tbytes[i] = binary.charCodeAt(i)\n\t}\n\treturn bytes\n}\n\n/**\n * Build a stable key for a richtext field document.\n */\nexport function richtextDocKey(collection: string, recordId: string, field: string): string {\n\treturn `${collection}:${recordId}:${field}`\n}\n\nexport { WIRE_BYTES_KEY }\n","/**\n * Canonical key of a downlink scope map (SYNC-11). The client keys its per-view\n * delivery watermark by the scope the server ACCEPTED and reports that scope's key at\n * the next handshake; the server computes the key of the scope it resolves now and\n * resumes the delivery stream from the reported watermark only when the two match.\n * Both sides must therefore compute it the same way, from scopes that may have been\n * re-ordered or round-tripped through JSON: object keys are sorted, `undefined` entries\n * dropped, and `$in` lists de-duplicated and sorted. No scope (undefined or null) is\n * the empty string.\n */\nexport function scopeViewKey(\n\tscope: Record<string, Record<string, unknown>> | null | undefined,\n): string {\n\tif (scope === null || scope === undefined) return ''\n\treturn canonicalJson(normalizeValue(scope))\n}\n\nfunction normalizeValue(value: unknown): unknown {\n\tif (Array.isArray(value)) return value.map(normalizeValue)\n\tif (value === null || typeof value !== 'object') return value\n\tconst record = value as Record<string, unknown>\n\tconst out: Record<string, unknown> = {}\n\tfor (const key of Object.keys(record)) {\n\t\tconst entry = record[key]\n\t\tif (entry === undefined) continue\n\t\tif (key === '$in' && Array.isArray(entry)) {\n\t\t\tconst byKey = new Map<string, unknown>()\n\t\t\tfor (const item of entry) {\n\t\t\t\tconst normalized = normalizeValue(item)\n\t\t\t\tbyKey.set(canonicalJson(normalized), normalized)\n\t\t\t}\n\t\t\tout[key] = [...byKey.keys()].sort().map((k) => byKey.get(k))\n\t\t\tcontinue\n\t\t}\n\t\tout[key] = normalizeValue(entry)\n\t}\n\treturn out\n}\n\nfunction canonicalJson(value: unknown): string {\n\tif (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null'\n\tif (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`\n\tconst record = value as Record<string, unknown>\n\tconst entries = Object.keys(record)\n\t\t.sort()\n\t\t.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)\n\treturn `{${entries.join(',')}}`\n}\n","import type { Operation } from '@korajs/core'\nimport type { QueueStorage } from '../types'\n\n/**\n * In-memory QueueStorage implementation for testing.\n * Operations are lost on process restart — production should use\n * IndexedDB or OPFS-backed storage.\n */\nexport class MemoryQueueStorage implements QueueStorage {\n\tprivate operations: Map<string, Operation> = new Map()\n\tprivate readonly sent = new Set<string>()\n\n\tasync load(): Promise<Operation[]> {\n\t\treturn [...this.operations.values()]\n\t}\n\n\tasync enqueue(op: Operation): Promise<void> {\n\t\tthis.operations.set(op.id, op)\n\t}\n\n\tasync dequeue(ids: string[]): Promise<void> {\n\t\tfor (const id of ids) {\n\t\t\tthis.operations.delete(id)\n\t\t\tthis.sent.delete(id)\n\t\t}\n\t}\n\n\tasync markSent(ops: Operation[]): Promise<void> {\n\t\tfor (const op of ops) {\n\t\t\tif (this.operations.has(op.id)) this.sent.add(op.id)\n\t\t}\n\t}\n\n\tasync loadSentIds(): Promise<string[]> {\n\t\treturn [...this.sent]\n\t}\n\n\tasync count(): Promise<number> {\n\t\treturn this.operations.size\n\t}\n}\n","import type { Operation } from '@korajs/core'\nimport { topologicalSort } from '@korajs/core/internal'\nimport type { QueueStorage } from '../types'\n\n/**\n * A batch of operations taken from the queue for sending.\n */\nexport interface OutboundBatch {\n\t/** Unique identifier for this batch */\n\tbatchId: string\n\t/** Operations in this batch, in causal order */\n\toperations: Operation[]\n}\n\n/**\n * Outbound operation queue with pluggable persistence.\n * Manages operations waiting to be sent to the sync server.\n *\n * Operations are deduplicated by ID (content-addressed) and maintained\n * in causal order via topological sort.\n */\nexport class OutboundQueue {\n\tprivate queue: Operation[] = []\n\tprivate readonly seen: Set<string> = new Set()\n\tprivate readonly inFlight: Map<string, Operation[]> = new Map()\n\t/**\n\t * Ids of queued operations that were put on the wire at least once. Such an op may\n\t * already be stored on the server (its ack can be lost), so a clock rebase must never\n\t * re-stamp it (W3 step 4). Persisted through the storage when it supports it.\n\t */\n\tprivate readonly sent: Set<string> = new Set()\n\tprivate nextBatchId = 0\n\tprivate initialized = false\n\n\tconstructor(private readonly storage: QueueStorage) {}\n\n\t/**\n\t * Load persisted operations from storage.\n\t * Must be called before using the queue.\n\t */\n\tasync initialize(): Promise<void> {\n\t\tconst stored = await this.storage.load()\n\t\tfor (const op of stored) {\n\t\t\tif (!this.seen.has(op.id)) {\n\t\t\t\tthis.seen.add(op.id)\n\t\t\t\tthis.queue.push(op)\n\t\t\t}\n\t\t}\n\t\t// Ensure causal order\n\t\tif (this.queue.length > 1) {\n\t\t\tthis.queue = topologicalSort(this.queue)\n\t\t}\n\t\tif (this.storage.loadSentIds) {\n\t\t\tfor (const id of await this.storage.loadSentIds()) {\n\t\t\t\tif (this.seen.has(id)) this.sent.add(id)\n\t\t\t}\n\t\t}\n\t\tthis.initialized = true\n\t}\n\n\t/**\n\t * Add an operation to the outbound queue.\n\t * Deduplicates by operation ID. Persists to storage.\n\t */\n\tasync enqueue(op: Operation): Promise<void> {\n\t\tif (this.seen.has(op.id)) return\n\n\t\tthis.seen.add(op.id)\n\t\tthis.queue.push(op)\n\t\tawait this.storage.enqueue(op)\n\n\t\t// Re-sort to maintain causal order when new ops arrive\n\t\tif (this.queue.length > 1) {\n\t\t\tthis.queue = topologicalSort(this.queue)\n\t\t}\n\t}\n\n\t/**\n\t * Add several operations at once: deduplicated by id, persisted, and causally\n\t * re-sorted once (rather than once per operation).\n\t */\n\tasync enqueueMany(ops: Operation[]): Promise<void> {\n\t\tlet added = false\n\t\tfor (const op of ops) {\n\t\t\tif (this.seen.has(op.id)) continue\n\t\t\tthis.seen.add(op.id)\n\t\t\tthis.queue.push(op)\n\t\t\tawait this.storage.enqueue(op)\n\t\t\tadded = true\n\t\t}\n\t\tif (added && this.queue.length > 1) {\n\t\t\tthis.queue = topologicalSort(this.queue)\n\t\t}\n\t}\n\n\t/**\n\t * Take a batch of operations from the front of the queue.\n\t * Moves them to in-flight status. Returns null if queue is empty.\n\t *\n\t * @param batchSize - Maximum number of operations in the batch\n\t * @param accept - Only operations it accepts are taken (in queue order); the others\n\t * stay queued. The engine passes \"authored under this session's node id\": a session\n\t * may only upload its own node's operations (RT-38, RT-40).\n\t */\n\ttakeBatch(batchSize: number, accept?: (op: Operation) => boolean): OutboundBatch | null {\n\t\tif (this.queue.length === 0) return null\n\n\t\tlet ops: Operation[]\n\t\tif (accept) {\n\t\t\tops = []\n\t\t\tconst kept: Operation[] = []\n\t\t\tfor (const op of this.queue) {\n\t\t\t\tif (ops.length < batchSize && accept(op)) ops.push(op)\n\t\t\t\telse kept.push(op)\n\t\t\t}\n\t\t\tif (ops.length === 0) return null\n\t\t\tthis.queue = kept\n\t\t} else {\n\t\t\tops = this.queue.splice(0, batchSize)\n\t\t}\n\t\tconst batchId = `batch-${this.nextBatchId++}`\n\t\tthis.inFlight.set(batchId, ops)\n\n\t\treturn { batchId, operations: ops }\n\t}\n\n\t/** Whether any queued (not in-flight) operation satisfies `accept`. */\n\thasOperationsMatching(accept: (op: Operation) => boolean): boolean {\n\t\treturn this.queue.some(accept)\n\t}\n\n\t/** Number of queued (not in-flight) operations that satisfy `accept`. */\n\tcountMatching(accept: (op: Operation) => boolean): number {\n\t\tlet count = 0\n\t\tfor (const op of this.queue) if (accept(op)) count++\n\t\treturn count\n\t}\n\n\t/**\n\t * Acknowledge a batch, removing its operations permanently.\n\t */\n\tasync acknowledge(batchId: string): Promise<void> {\n\t\tconst ops = this.inFlight.get(batchId)\n\t\tif (!ops) return\n\n\t\tthis.inFlight.delete(batchId)\n\t\tconst ids = ops.map((op) => op.id)\n\t\t// Release the ids (SYNC-6): the seen set must only hold ops still owned by the\n\t\t// queue, or it grows forever and an acked op could never be re-enqueued (for\n\t\t// example when a restored server needs it again).\n\t\tfor (const id of ids) {\n\t\t\tthis.seen.delete(id)\n\t\t\tthis.sent.delete(id)\n\t\t}\n\t\tawait this.storage.dequeue(ids)\n\t}\n\n\t/**\n\t * Acknowledge only operations in an in-flight batch whose local sequence number is\n\t * covered by the server's ack. Any later operations return to the front of the\n\t * queue for retry. This is required when the server accepts a prefix but rejects a\n\t * later operation retriably (for example a stale-scope push after auth changed).\n\t */\n\tasync acknowledgeThrough(\n\t\tbatchId: string,\n\t\tlastSequenceNumber: number,\n\t): Promise<{ acknowledged: Operation[]; returned: Operation[] }> {\n\t\tconst ops = this.inFlight.get(batchId)\n\t\tif (!ops) return { acknowledged: [], returned: [] }\n\n\t\tthis.inFlight.delete(batchId)\n\t\tconst acknowledged = ops.filter((op) => op.sequenceNumber <= lastSequenceNumber)\n\t\tconst retry = ops.filter((op) => op.sequenceNumber > lastSequenceNumber)\n\n\t\tif (retry.length > 0) {\n\t\t\tthis.queue.unshift(...retry)\n\t\t\tif (this.queue.length > 1) {\n\t\t\t\tthis.queue = topologicalSort(this.queue)\n\t\t\t}\n\t\t}\n\n\t\tif (acknowledged.length > 0) {\n\t\t\tconst ids = acknowledged.map((op) => op.id)\n\t\t\tfor (const id of ids) {\n\t\t\t\tthis.seen.delete(id)\n\t\t\t\tthis.sent.delete(id)\n\t\t\t}\n\t\t\tawait this.storage.dequeue(ids)\n\t\t}\n\t\treturn { acknowledged, returned: retry }\n\t}\n\n\t/**\n\t * Record that a taken batch was put on the wire. Its operations may now be stored on\n\t * the server even if no ack ever arrives, so they are excluded from clock rebases.\n\t */\n\tasync markSent(batchId: string): Promise<void> {\n\t\tconst ops = this.inFlight.get(batchId)\n\t\tif (!ops) return\n\t\tconst fresh = ops.filter((op) => !this.sent.has(op.id))\n\t\tif (fresh.length === 0) return\n\t\tfor (const op of fresh) this.sent.add(op.id)\n\t\tawait this.storage.markSent?.(fresh)\n\t}\n\n\t/** Whether this queued operation was ever put on the wire. */\n\twasSent(opId: string): boolean {\n\t\treturn this.sent.has(opId)\n\t}\n\n\t/**\n\t * Forget the sent flag of operations the server explicitly refused without storing\n\t * (for example a batch refused with INVALID_TIMESTAMP), so a clock rebase may re-stamp\n\t * them. In memory only: after a restart they are conservatively treated as sent.\n\t */\n\tclearSent(opIds: string[]): void {\n\t\tfor (const id of opIds) this.sent.delete(id)\n\t}\n\n\t/** Whether the queue owns this operation (queued or in flight). */\n\thas(opId: string): boolean {\n\t\treturn this.seen.has(opId)\n\t}\n\n\t/** Every operation currently in flight, across batches. */\n\tgetInFlight(): Operation[] {\n\t\treturn [...this.inFlight.values()].flat()\n\t}\n\n\t/**\n\t * Replace some queued (not in-flight) operations by rewritten ones, for example after\n\t * a clock rebase re-stamped never-sent operations under new content-addressed ids.\n\t * Operations not named in `oldIds` are untouched.\n\t */\n\tasync replace(oldIds: string[], newOps: Operation[]): Promise<void> {\n\t\tconst remove = new Set(oldIds)\n\t\tthis.queue = this.queue.filter((op) => !remove.has(op.id))\n\t\tfor (const id of oldIds) {\n\t\t\tthis.seen.delete(id)\n\t\t\tthis.sent.delete(id)\n\t\t}\n\t\tif (oldIds.length > 0) {\n\t\t\tawait this.storage.dequeue(oldIds)\n\t\t}\n\t\tfor (const op of newOps) {\n\t\t\tif (this.seen.has(op.id)) continue\n\t\t\tthis.seen.add(op.id)\n\t\t\tthis.queue.push(op)\n\t\t\tawait this.storage.enqueue(op)\n\t\t}\n\t\tif (this.queue.length > 1) {\n\t\t\tthis.queue = topologicalSort(this.queue)\n\t\t}\n\t}\n\n\t/**\n\t * Return a failed batch to the front of the queue for retry.\n\t * Prepends the operations to maintain priority.\n\t */\n\treturnBatch(batchId: string): void {\n\t\tconst ops = this.inFlight.get(batchId)\n\t\tif (!ops) return\n\n\t\tthis.inFlight.delete(batchId)\n\t\t// Prepend returned ops, then re-sort for causal order\n\t\tthis.queue.unshift(...ops)\n\t\tif (this.queue.length > 1) {\n\t\t\tthis.queue = topologicalSort(this.queue)\n\t\t}\n\t}\n\n\t/**\n\t * Number of operations waiting in the queue (not counting in-flight).\n\t */\n\tget size(): number {\n\t\treturn this.queue.length\n\t}\n\n\t/**\n\t * Total operations including in-flight.\n\t */\n\tget totalPending(): number {\n\t\tlet inFlightCount = 0\n\t\tfor (const ops of this.inFlight.values()) {\n\t\t\tinFlightCount += ops.length\n\t\t}\n\t\treturn this.queue.length + inFlightCount\n\t}\n\n\t/**\n\t * Whether the queue has any operations to send.\n\t */\n\tget hasOperations(): boolean {\n\t\treturn this.queue.length > 0\n\t}\n\n\t/**\n\t * Peek at the first `count` operations without removing them.\n\t */\n\tpeek(count: number): Operation[] {\n\t\treturn this.queue.slice(0, count)\n\t}\n\n\t/**\n\t * All queued operations (not counting in-flight), in causal order.\n\t */\n\tgetAll(): Operation[] {\n\t\treturn [...this.queue]\n\t}\n\n\t/**\n\t * Atomically replace the entire queue with a new set of operations.\n\t *\n\t * Used after a timestamp rebase rewrote the queued operations under new\n\t * content-addressed ids: the old entries must vanish from memory AND from\n\t * persistent storage in one step, or a page refresh could resurrect\n\t * stale-stamped ops the server would reject. Resets the seen set to exactly\n\t * the new ids so the rewritten ops are not treated as duplicates.\n\t */\n\tasync replaceAll(ops: Operation[]): Promise<void> {\n\t\tconst removeIds: string[] = this.queue.map((op) => op.id)\n\t\tfor (const batch of this.inFlight.values()) {\n\t\t\tfor (const op of batch) {\n\t\t\t\tremoveIds.push(op.id)\n\t\t\t}\n\t\t}\n\t\tthis.inFlight.clear()\n\n\t\tthis.queue = ops.length > 1 ? topologicalSort([...ops]) : [...ops]\n\t\tthis.seen.clear()\n\t\tthis.sent.clear()\n\t\tfor (const op of this.queue) {\n\t\t\tthis.seen.add(op.id)\n\t\t}\n\n\t\tif (removeIds.length > 0) {\n\t\t\tawait this.storage.dequeue(removeIds)\n\t\t}\n\t\tfor (const op of this.queue) {\n\t\t\tawait this.storage.enqueue(op)\n\t\t}\n\t}\n\n\t/**\n\t * Whether initialize() has been called.\n\t */\n\tget isInitialized(): boolean {\n\t\treturn this.initialized\n\t}\n\n\t/**\n\t * Remove a single operation the server permanently rejected — from the queue,\n\t * from any in-flight batch, and from durable storage — so a later batch ack or\n\t * a reconnect `returnBatch` can never resend or resurrect it. Returns the\n\t * removed operation so the caller can record it in a durable rejected store,\n\t * or null if it was not present (already acknowledged and removed).\n\t */\n\tasync reject(opId: string): Promise<Operation | null> {\n\t\tlet removed: Operation | null = null\n\n\t\t// Common case: the op was just sent and sits in an in-flight batch.\n\t\tfor (const [batchId, ops] of this.inFlight) {\n\t\t\tconst idx = ops.findIndex((op) => op.id === opId)\n\t\t\tif (idx !== -1) {\n\t\t\t\tremoved = ops[idx] ?? null\n\t\t\t\tops.splice(idx, 1)\n\t\t\t\tif (ops.length === 0) {\n\t\t\t\t\tthis.inFlight.delete(batchId)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\t// Otherwise it may still be queued (not yet sent).\n\t\tif (!removed) {\n\t\t\tconst idx = this.queue.findIndex((op) => op.id === opId)\n\t\t\tif (idx !== -1) {\n\t\t\t\tremoved = this.queue[idx] ?? null\n\t\t\t\tthis.queue.splice(idx, 1)\n\t\t\t}\n\t\t}\n\n\t\tthis.seen.delete(opId)\n\t\tthis.sent.delete(opId)\n\t\tawait this.storage.dequeue([opId])\n\t\treturn removed\n\t}\n\n\t/** Remove every pending/in-flight operation for a record so authorization loss cannot upload it. */\n\tasync rejectRecord(collection: string, recordId: string): Promise<Operation[]> {\n\t\t// An operation already put on the wire may be stored by the server whatever this\n\t\t// device decides now: it stays in flight and its ack (stored) or rejection\n\t\t// resolves it. Quarantining it here would exclude from this device's fold a write\n\t\t// every other replica folds (merge breakage found in Phase 3: RT-27 retract case).\n\t\tconst matching = [...this.queue, ...[...this.inFlight.values()].flat()].filter(\n\t\t\t(op) => op.collection === collection && op.recordId === recordId && !this.sent.has(op.id),\n\t\t)\n\t\tconst removed: Operation[] = []\n\t\tfor (const operation of matching) {\n\t\t\tconst rejected = await this.reject(operation.id)\n\t\t\tif (rejected) removed.push(rejected)\n\t\t}\n\t\treturn removed\n\t}\n\n\t/**\n\t * Remove operations by id from queue and persistent storage.\n\t * Used when ops were already sent during handshake delta exchange.\n\t */\n\tasync removeByIds(ids: string[]): Promise<void> {\n\t\tif (ids.length === 0) return\n\t\tconst idSet = new Set(ids)\n\t\tthis.queue = this.queue.filter((op) => !idSet.has(op.id))\n\t\tfor (const id of ids) {\n\t\t\tthis.seen.delete(id)\n\t\t\tthis.sent.delete(id)\n\t\t}\n\t\tawait this.storage.dequeue(ids)\n\t}\n}\n","import {\n\tbase64ToBytes,\n\tbytesToBase64,\n\tcanonicalizeLegacyOperation,\n\tisKoraBytesValue,\n\tverifyOperationId,\n} from '@korajs/core'\nimport type { FieldDescriptor, Operation, SchemaDefinition } from '@korajs/core'\nimport { INVALID_OPERATION_ID } from '../protocol/protocol-version'\n\n/** Prefix of node ids reserved for Kora itself (server-authored and server-synthesized). */\nconst RESERVED_NODE_PREFIX = 'kora:'\n\n/** Node of server-synthesized scope-entry operations (ids derived from the trigger op). */\nconst SCOPE_ENTRY_NODE_ID = 'kora:scope-entry'\n\n/**\n * Whether an operation's id is exempt from content verification: it is not a content\n * hash by construction. Only two kinds of operation qualify, both on Kora's reserved\n * nodes, which no device can claim:\n * - scope entries (`kora:scope-entry`): the id names the triggering operation, and the\n * data is the record's current row;\n * - server-derived operations (cascades, set-nulls, constraint corrections) of a\n * `kora:server:` node: the id is a keyed HMAC (RT-64) and they declare no hash version.\n *\n * A reserved-node operation that declares a hash version (a server route write, built\n * with `createOperation`) claims a content-addressed id and is verified like any other.\n */\nfunction isExemptFromIdVerification(op: Operation): boolean {\n\tif (!op.nodeId.startsWith(RESERVED_NODE_PREFIX)) return false\n\treturn op.nodeId === SCOPE_ENTRY_NODE_ID || op.hashVersion === undefined\n}\n\n/** Outcome of {@link verifyInboundOperation}. */\nexport type InboundVerification =\n\t| {\n\t\t\tok: true\n\t\t\t/** Whether the id was checked (false: not content-addressed, or not declared). */\n\t\t\tverified: boolean\n\t\t\t/**\n\t\t\t * The hash version the id matched, when it declared none (`'verify-ids'`): 1, or 2\n\t\t\t * for a version-2 id whose declaration was lost on the way.\n\t\t\t */\n\t\t\tmatchedVersion?: 1 | 2\n\t\t\t/**\n\t\t\t * False when the id matched only through the schema-dependent rebuild of a\n\t\t\t * beta.12 hash (declared nested object members that were `undefined`): a\n\t\t\t * receiver without the schema cannot repeat that check, so the server must not\n\t\t\t * declare the matched version on the stored copy. Absent: true.\n\t\t\t */\n\t\t\tdeclarable?: boolean\n\t\t\t/**\n\t\t\t * The update's data with each top-level member beta.12 hashed as `undefined`\n\t\t\t * restored as `null` (RT-71): beta.12 cleared those fields locally, and `null`\n\t\t\t * hashes identically under version 1, so the stored copy carries the write the\n\t\t\t * beta.12 client made, under the same verified id. Present only when the id\n\t\t\t * matched through that rebuild.\n\t\t\t */\n\t\t\trestoredData?: Operation['data']\n\t }\n\t| { ok: false; code: typeof INVALID_OPERATION_ID; message: string }\n\n/** How {@link verifyInboundOperation} treats an operation that declares no hash version. */\nexport type AbsentHashVersionPolicy =\n\t/**\n\t * Not checked (clients). The server declares the version of every id it verified\n\t * (1 or 2) when it stores the operation; an absent version marks an id the server\n\t * could not verify: a log row from before beta.13, a server-side schema transform\n\t * (which rewrites data under the original id), or a protocol-1 encrypted payload.\n\t */\n\t| 'skip'\n\t/**\n\t * Checked (the server, on upload: omitting the version skips nothing): the id must be\n\t * the operation's version-1 or version-2 content hash.\n\t */\n\t| 'verify-ids'\n\n/**\n * Verify a delivered (or uploaded) operation's content-addressed id (CORE-1, RT-64).\n * Runs AFTER decryption, on the plaintext the id was computed over, and before any\n * transform or apply. A mismatch means the operation was altered after it was created\n * (in transit, by a relay, or a ciphertext swap that kept its id), or that its id was\n * chosen rather than computed (squatting an id the server derives).\n *\n * Rules:\n * - Scope entries and server-derived operations (reserved `kora:` nodes, no declared hash\n * version: keyed HMAC ids) are not checked. A reserved-node operation that declares a\n * hash version (a server route write) is checked like any other.\n * - An operation that arrived in an encryption envelope is always checked, against\n * the hash version it declares (bound into the envelope's authenticated data).\n * - A plaintext operation declaring hash version 2 or 1 is checked against that version.\n * Version 1 covers type, collection, recordId, data, timestamp, nodeId and atomicOps;\n * binary values are accepted in either of their two forms (bytes, or the canonical\n * `{ $koraBytes }` form a JSON round trip produces), since a version-1 hash was\n * computed over whichever form the writer held.\n * - A plaintext operation declaring no version follows `absentVersion`: skipped, or\n * accepted when its id is its version-1 or version-2 content hash.\n * - An unknown declared version fails closed.\n *\n * @param op - The operation, decrypted\n * @param context - Whether it arrived in an encryption envelope, and the policy for an\n * operation that declares no hash version (default `'skip'`)\n * @returns ok, or the refusal with a code and an explanation\n */\nexport async function verifyInboundOperation(\n\top: Operation,\n\tcontext: {\n\t\tencrypted: boolean\n\t\tabsentVersion?: AbsentHashVersionPolicy\n\t\t/** The schema, for rebuilding beta.12 hashes of `undefined` nested members (RT-71). */\n\t\tschema?: SchemaDefinition | null\n\t},\n): Promise<InboundVerification> {\n\tif (isExemptFromIdVerification(op)) return { ok: true, verified: false }\n\tconst declared = op.hashVersion\n\tif (declared !== undefined && declared !== 1 && declared !== 2) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tcode: INVALID_OPERATION_ID,\n\t\t\tmessage: `Operation \"${op.id}\" declares unknown content-hash version ${String(declared)}; it cannot be verified and is not applied. Upgrade this client.`,\n\t\t}\n\t}\n\tif (\n\t\t!context.encrypted &&\n\t\tdeclared === undefined &&\n\t\t(context.absentVersion ?? 'skip') === 'skip'\n\t) {\n\t\treturn { ok: true, verified: false }\n\t}\n\tif (declared === undefined && !context.encrypted) {\n\t\tif (await verifyOperationId(op)) return { ok: true, verified: true, matchedVersion: 1 }\n\t\tif (await verifyOperationId({ ...op, hashVersion: 2 })) {\n\t\t\treturn { ok: true, verified: true, matchedVersion: 2 }\n\t\t}\n\t\tconst match = await matchOperationId(op, context.schema ?? null)\n\t\tif (match !== null) {\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tverified: true,\n\t\t\t\tmatchedVersion: 1,\n\t\t\t\t// Not declarable: a receiver without the schema cannot repeat a nested rebuild,\n\t\t\t\t// and a Date-form match never bound the Date's value (RT-90), so the stored\n\t\t\t\t// copy must not claim a verified version.\n\t\t\t\tdeclarable: !match.usedSchema && match.usedDateForm !== true,\n\t\t\t\t...(match.usedPrevious ? { restoredData: restoreUndefinedFromPrevious(op) } : {}),\n\t\t\t}\n\t\t}\n\t} else if ((await matchOperationId(op, context.schema ?? null)) !== null) {\n\t\treturn { ok: true, verified: true }\n\t}\n\treturn {\n\t\tok: false,\n\t\tcode: INVALID_OPERATION_ID,\n\t\tmessage: `Operation \"${op.id}\" from node \"${op.nodeId}\" does not match its content hash (hash version ${String(declared ?? 1)}${context.encrypted ? ', after decryption' : ''}): it was altered after it was created, or its id was not computed from its content. It is refused, not applied.`,\n\t}\n}\n\n/**\n * Whether the operation's id is its content hash under its declared version (absent:\n * 1). A version-1 id is also accepted over the other form of its binary values, and\n * over the beta.12 form of `undefined` members (see {@link matchOperationId}).\n *\n * @param op - The operation (plaintext)\n * @param schema - Optional schema, to rebuild declared nested members\n * @returns true when the id is the content hash\n */\nexport async function operationIdMatches(\n\top: Operation,\n\tschema: SchemaDefinition | null = null,\n): Promise<boolean> {\n\treturn (await matchOperationId(op, schema)) !== null\n}\n\n/** How an id matched: which beta.12 rebuilds (if any) its content needed. */\nexport interface OperationIdMatch {\n\t/** Top-level members restored from an update's `previousData` keys. */\n\tusedPrevious: boolean\n\t/** Declared nested members restored (needs the schema). */\n\tusedSchema: boolean\n\t/**\n\t * Strings in `Date#toISOString()` form hashed as `{}` (RT-90): beta.12 hashed a Date as\n\t * `{}`, so such an id never covered the Date's value.\n\t */\n\tusedDateForm?: boolean\n}\n\n/**\n * Match an operation's id against its content hash (RT-64, RT-71).\n *\n * Version 2 is checked as is: its canonical form is the JSON form (an `undefined`\n * member is absent, RT-72), so what was hashed is what arrives.\n *\n * Version 1 (beta.12 and earlier) hashed the in-memory value with `canonicalize`,\n * which writes an `undefined` object member as `\"key\":null`; the op log and the wire\n * are JSON, so the member is gone on arrival. The forms beta.12 actually produced are\n * rebuilt where they are recoverable:\n * - an update: beta.12 writes `previousData[key]` for every key it validated, so every\n * `previousData` key absent from `data` was an `undefined` value in `data`\n * (`update(id, { assignee: undefined })`);\n * - an object field (needs the schema): declared nested members absent from the value\n * (`{ a: 1, b: undefined }`), recursively for declared nested objects;\n * - a `Date` inside a json value (RT-90): `canonicalize` walks `Object.keys`, so it\n * hashed as `{}`, while the JSON log and wire hold its `toISOString()` string. Strings\n * in exactly that form are tried as `{}` (see {@link dateFormCandidates}).\n * Each form is also tried with the other form of its binary values (bytes, or the\n * canonical `{ $koraBytes }` form a JSON round trip produces).\n *\n * @param op - The operation (plaintext)\n * @param schema - The schema, or null (no nested rebuild)\n * @returns how the id matched, or null when it is not the content hash\n */\nexport async function matchOperationId(\n\top: Operation,\n\tschema: SchemaDefinition | null,\n): Promise<OperationIdMatch | null> {\n\tif (await verifyOperationId(op)) return { usedPrevious: false, usedSchema: false }\n\tif ((op.hashVersion ?? 1) !== 1) return null\n\tconst candidates: Array<{ data: Operation['data']; kind: OperationIdMatch }> = [\n\t\t{ data: op.data, kind: { usedPrevious: false, usedSchema: false } },\n\t]\n\tconst filled = restoreUndefinedFromPrevious(op)\n\tif (filled !== op.data) {\n\t\tcandidates.push({ data: filled, kind: { usedPrevious: true, usedSchema: false } })\n\t}\n\tif (schema) {\n\t\tfor (const candidate of [...candidates]) {\n\t\t\tconst nested = fillDeclaredNestedMembers(candidate.data, op.collection, schema)\n\t\t\tif (nested !== null) {\n\t\t\t\tcandidates.push({ data: nested, kind: { ...candidate.kind, usedSchema: true } })\n\t\t\t}\n\t\t}\n\t}\n\t// beta.12 (and older) hashed a Date inside a json value as `{}` (RT-90).\n\tfor (const candidate of [...candidates]) {\n\t\tfor (const data of dateFormCandidates(candidate.data)) {\n\t\t\tcandidates.push({ data, kind: { ...candidate.kind, usedDateForm: true } })\n\t\t}\n\t}\n\tfor (const candidate of candidates) {\n\t\tif (candidate.data !== op.data && (await verifyOperationId({ ...op, data: candidate.data }))) {\n\t\t\treturn candidate.kind\n\t\t}\n\t\tfor (const convert of [toBytesForm, toKoraBytesForm]) {\n\t\t\tconst data = convert(candidate.data)\n\t\t\tconst atomicOps = op.atomicOps === undefined ? undefined : convert(op.atomicOps)\n\t\t\tif (data.changed || atomicOps?.changed) {\n\t\t\t\tconst variant: Operation = {\n\t\t\t\t\t...op,\n\t\t\t\t\tdata: data.value as Operation['data'],\n\t\t\t\t\t...(atomicOps ? { atomicOps: atomicOps.value as Operation['atomicOps'] } : {}),\n\t\t\t\t}\n\t\t\t\tif (await verifyOperationId(variant)) return candidate.kind\n\t\t\t}\n\t\t}\n\t}\n\treturn null\n}\n\n/**\n * A beta.12 update's data with every `previousData` key it lacks restored as `null`\n * (RT-71). beta.12 writes `previousData[key]` for every key it validated, so such a key\n * held `undefined` in `data`: hashed as `null`, applied locally as a cleared field, and\n * dropped by the JSON upload. Returns `op.data` itself when there is none to restore.\n *\n * @param op - A plaintext operation\n * @returns The restored data, or `op.data` unchanged\n */\nexport function restoreUndefinedFromPrevious(op: Operation): Operation['data'] {\n\t// One rule for every replica: core's legacy canonical body (RT-71, RT-83).\n\treturn canonicalizeLegacyOperation({ ...op, hashVersion: undefined }).data\n}\n\n/**\n * The data with each declared nested member an object value lacks restored as `null`\n * (recursively), or null when nothing was restored.\n */\nfunction fillDeclaredNestedMembers(\n\tdata: Operation['data'],\n\tcollection: string,\n\tschema: SchemaDefinition,\n): Operation['data'] {\n\tconst fields = schema.collections[collection]?.fields\n\tif (data === null || fields === undefined) return null\n\tlet changed = false\n\tconst out: Record<string, unknown> = { ...data }\n\tfor (const [name, value] of Object.entries(data)) {\n\t\tconst descriptor = fields[name]\n\t\tif (descriptor === undefined) continue\n\t\tconst filled = fillNested(value, descriptor)\n\t\tif (filled !== value) {\n\t\t\tout[name] = filled\n\t\t\tchanged = true\n\t\t}\n\t}\n\treturn changed ? out : null\n}\n\nfunction fillNested(value: unknown, descriptor: FieldDescriptor): unknown {\n\tconst nested = descriptor.nestedFields\n\tif (descriptor.kind !== 'object' || !nested) return value\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return value\n\tconst record = value as Record<string, unknown>\n\tlet changed = false\n\tconst out: Record<string, unknown> = { ...record }\n\tfor (const [name, child] of Object.entries(nested)) {\n\t\tif (!(name in record)) {\n\t\t\tout[name] = null\n\t\t\tchanged = true\n\t\t\tcontinue\n\t\t}\n\t\tconst filled = fillNested(record[name], child)\n\t\tif (filled !== record[name]) {\n\t\t\tout[name] = filled\n\t\t\tchanged = true\n\t\t}\n\t}\n\treturn changed ? out : value\n}\n\n/** `Date#toISOString()` output, including its extended-year form. */\nconst ISO_DATE_STRING = /^(?:\\d{4}|[+-]\\d{6})-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$/\n\n/**\n * Most ISO strings per operation whose every Date/string combination is tried; above\n * it only \"all of them were Dates\" is tried (bounds the hashing work to 2^6 - 1).\n */\nconst MAX_DATE_FORM_STRINGS = 6\n\n/**\n * The data with strings in `Date#toISOString()` form replaced by `{}`: how beta.12 (and\n * older) hashed a `Date` held in a json value (RT-90). Which of the strings were Dates\n * is unknown, so every non-empty subset is returned (all of them only, above\n * {@link MAX_DATE_FORM_STRINGS}). Empty when the data holds no such string.\n *\n * The stored data keeps the string, which is what beta.12 wrote to its row and applied.\n * Such an id never covered the Date's value (beta.12's hash did not either), so a match\n * through this rebuild is not declarable: the server stores the operation without a\n * declared hash version, as it stored every unverifiable protocol-1 id.\n */\nfunction dateFormCandidates(data: Operation['data']): Array<Operation['data']> {\n\tif (data === null) return []\n\tconst paths: Array<Array<string | number>> = []\n\tconst collect = (value: unknown, path: Array<string | number>): void => {\n\t\tif (typeof value === 'string') {\n\t\t\tif (ISO_DATE_STRING.test(value)) paths.push(path)\n\t\t\treturn\n\t\t}\n\t\tif (Array.isArray(value)) {\n\t\t\tvalue.forEach((item, index) => collect(item, [...path, index]))\n\t\t\treturn\n\t\t}\n\t\tif (value !== null && typeof value === 'object' && !(value instanceof Uint8Array)) {\n\t\t\tfor (const [key, item] of Object.entries(value)) collect(item, [...path, key])\n\t\t}\n\t}\n\tfor (const [field, value] of Object.entries(data)) collect(value, [field])\n\tif (paths.length === 0) return []\n\tconst subsets: Array<Array<Array<string | number>>> = []\n\tif (paths.length > MAX_DATE_FORM_STRINGS) {\n\t\tsubsets.push(paths)\n\t} else {\n\t\tfor (let mask = 1; mask < 1 << paths.length; mask++) {\n\t\t\tsubsets.push(paths.filter((_path, index) => (mask & (1 << index)) !== 0))\n\t\t}\n\t}\n\treturn subsets.map((subset) => {\n\t\tlet out: unknown = data\n\t\tfor (const path of subset) out = replaceAt(out, path, {})\n\t\treturn out as Operation['data']\n\t})\n}\n\n/** A copy of `value` with the member at `path` replaced (copy on write). */\nfunction replaceAt(\n\tvalue: unknown,\n\tpath: ReadonlyArray<string | number>,\n\treplacement: unknown,\n): unknown {\n\tconst [head, ...rest] = path\n\tif (head === undefined) return replacement\n\tif (Array.isArray(value)) {\n\t\tconst copy = [...value]\n\t\tcopy[head as number] = replaceAt(copy[head as number], rest, replacement)\n\t\treturn copy\n\t}\n\tconst record = value as Record<string, unknown>\n\treturn { ...record, [head]: replaceAt(record[head as string], rest, replacement) }\n}\n\ninterface Converted {\n\tvalue: unknown\n\tchanged: boolean\n}\n\n/** `{ $koraBytes }` values replaced by the bytes they encode. */\nfunction toBytesForm(value: unknown): Converted {\n\tif (isKoraBytesValue(value)) {\n\t\ttry {\n\t\t\treturn { value: base64ToBytes(value.$koraBytes), changed: true }\n\t\t} catch {\n\t\t\treturn { value, changed: false }\n\t\t}\n\t}\n\treturn mapMembers(value, toBytesForm)\n}\n\n/** Byte values replaced by their canonical `{ $koraBytes }` form. */\nfunction toKoraBytesForm(value: unknown): Converted {\n\tif (value instanceof Uint8Array)\n\t\treturn { value: { $koraBytes: bytesToBase64(value) }, changed: true }\n\treturn mapMembers(value, toKoraBytesForm)\n}\n\nfunction mapMembers(value: unknown, convert: (member: unknown) => Converted): Converted {\n\tif (Array.isArray(value)) {\n\t\tlet changed = false\n\t\tconst out = value.map((member) => {\n\t\t\tconst result = convert(member)\n\t\t\tchanged ||= result.changed\n\t\t\treturn result.value\n\t\t})\n\t\treturn changed ? { value: out, changed } : { value, changed: false }\n\t}\n\tif (value !== null && typeof value === 'object' && !(value instanceof Uint8Array)) {\n\t\tlet changed = false\n\t\tconst out: Record<string, unknown> = {}\n\t\tfor (const [key, member] of Object.entries(value)) {\n\t\t\tconst result = convert(member)\n\t\t\tchanged ||= result.changed\n\t\t\tout[key] = result.value\n\t\t}\n\t\treturn changed ? { value: out, changed } : { value, changed: false }\n\t}\n\treturn { value, changed: false }\n}\n","import type { ConnectionQuality, TimeSource } from '@korajs/core'\n\n/**\n * Configuration for the connection monitor.\n */\nexport interface ConnectionMonitorConfig {\n\t/** Number of latency samples to keep in the rolling window. Defaults to 20. */\n\twindowSize?: number\n\t/** Time in ms after which the connection is considered stale. Defaults to 30000. */\n\tstaleThreshold?: number\n\t/** Injectable time source for deterministic testing */\n\ttimeSource?: TimeSource\n}\n\n/**\n * Monitors connection quality based on RTT latency samples,\n * missed acknowledgments, and activity timestamps.\n */\nexport class ConnectionMonitor {\n\tprivate readonly windowSize: number\n\tprivate readonly staleThreshold: number\n\tprivate readonly timeSource: TimeSource\n\n\tprivate latencies: number[] = []\n\tprivate missedAcks = 0\n\tprivate lastActivityTime: number\n\n\tconstructor(config?: ConnectionMonitorConfig) {\n\t\tthis.windowSize = config?.windowSize ?? 20\n\t\tthis.staleThreshold = config?.staleThreshold ?? 30000\n\t\tthis.timeSource = config?.timeSource ?? { now: () => Date.now() }\n\t\tthis.lastActivityTime = this.timeSource.now()\n\t}\n\n\t/**\n\t * Record a round-trip time sample.\n\t */\n\trecordLatency(ms: number): void {\n\t\tthis.latencies.push(ms)\n\t\tif (this.latencies.length > this.windowSize) {\n\t\t\tthis.latencies.shift()\n\t\t}\n\t\tthis.lastActivityTime = this.timeSource.now()\n\t\t// Successful response resets missed acks\n\t\tthis.missedAcks = 0\n\t}\n\n\t/**\n\t * Record a missed acknowledgment (message sent but no response received).\n\t */\n\trecordMissedAck(): void {\n\t\tthis.missedAcks++\n\t}\n\n\t/**\n\t * Record any activity (message sent or received).\n\t */\n\trecordActivity(): void {\n\t\tthis.lastActivityTime = this.timeSource.now()\n\t}\n\n\t/**\n\t * Assess current connection quality based on collected metrics.\n\t *\n\t * Quality thresholds (average RTT):\n\t * - excellent: < 100ms, 0 missed acks\n\t * - good: < 300ms, ≤ 1 missed ack\n\t * - fair: < 1000ms, ≤ 3 missed acks\n\t * - poor: < 5000ms or > 3 missed acks\n\t * - offline: no activity for staleThreshold ms\n\t */\n\tgetQuality(): ConnectionQuality {\n\t\tconst elapsed = this.timeSource.now() - this.lastActivityTime\n\t\tif (elapsed > this.staleThreshold) return 'offline'\n\n\t\tif (this.latencies.length === 0) {\n\t\t\t// No data yet — assume good until proven otherwise\n\t\t\treturn this.missedAcks > 3 ? 'poor' : 'good'\n\t\t}\n\n\t\tconst avgLatency = this.latencies.reduce((sum, l) => sum + l, 0) / this.latencies.length\n\n\t\tif (this.missedAcks > 3) return 'poor'\n\t\tif (avgLatency < 100 && this.missedAcks === 0) return 'excellent'\n\t\tif (avgLatency < 300 && this.missedAcks <= 1) return 'good'\n\t\tif (avgLatency < 1000 && this.missedAcks <= 3) return 'fair'\n\t\treturn 'poor'\n\t}\n\n\t/**\n\t * Reset all metrics. Call on disconnect.\n\t */\n\treset(): void {\n\t\tthis.latencies = []\n\t\tthis.missedAcks = 0\n\t\tthis.lastActivityTime = this.timeSource.now()\n\t}\n\n\t/**\n\t * Get the current average latency in ms. Returns null if no samples.\n\t */\n\tgetAverageLatency(): number | null {\n\t\tif (this.latencies.length === 0) return null\n\t\treturn this.latencies.reduce((sum, l) => sum + l, 0) / this.latencies.length\n\t}\n\n\t/**\n\t * Get the number of missed acks.\n\t */\n\tgetMissedAcks(): number {\n\t\treturn this.missedAcks\n\t}\n}\n","import type { TimeSource } from '@korajs/core'\n\n/**\n * Configuration for the reconnection manager.\n */\nexport interface ReconnectionConfig {\n\t/** Initial delay in ms before first reconnection attempt. Defaults to 1000. */\n\tinitialDelay?: number\n\t/** Maximum delay in ms between attempts. Defaults to 30000. */\n\tmaxDelay?: number\n\t/** Multiplier for exponential backoff. Defaults to 2. */\n\tmultiplier?: number\n\t/** Maximum number of reconnection attempts. 0 means unlimited. Defaults to 0. */\n\tmaxAttempts?: number\n\t/** Jitter factor (0-1). Random variation applied to delay. Defaults to 0.25. */\n\tjitter?: number\n\t/**\n\t * How long a connection must stay up (see {@link ReconnectionManager.reportConnected})\n\t * before the backoff resets to the initial delay, in ms (SYNC-8). A server that\n\t * accepts sessions and drops them right away therefore keeps backing off instead of\n\t * being retried at the initial interval forever. Defaults to 10000.\n\t */\n\tstableAfterMs?: number\n\t/** Injectable time source for deterministic testing. */\n\ttimeSource?: TimeSource\n\t/** Injectable random source for deterministic jitter. Returns value in [0, 1). */\n\trandomSource?: () => number\n}\n\n/**\n * Manages reconnection attempts with exponential backoff and jitter.\n *\n * Formula: min(initialDelay * multiplier^attempt, maxDelay) * (1 + jitter * (random - 0.5) * 2)\n *\n * The attempt counter belongs to the manager, not to one `start()` run (SYNC-8): it\n * keeps growing across runs and resets only once a connection stayed up for\n * `stableAfterMs` ({@link reportConnected}), or on an explicit {@link reset}.\n */\nexport class ReconnectionManager {\n\tprivate readonly initialDelay: number\n\tprivate readonly maxDelay: number\n\tprivate readonly multiplier: number\n\tprivate readonly maxAttempts: number\n\tprivate readonly jitter: number\n\tprivate readonly stableAfterMs: number\n\tprivate readonly random: () => number\n\n\tprivate attempt = 0\n\tprivate timer: ReturnType<typeof setTimeout> | null = null\n\tprivate stableTimer: ReturnType<typeof setTimeout> | null = null\n\tprivate stopped = false\n\tprivate running = false\n\t/** A disconnect was reported while an attempt was in flight (NEW-SYNC-2). */\n\tprivate pendingRetry = false\n\tprivate waitResolve: (() => void) | null = null\n\n\tconstructor(config?: ReconnectionConfig) {\n\t\tthis.initialDelay = config?.initialDelay ?? 1000\n\t\tthis.maxDelay = config?.maxDelay ?? 30000\n\t\tthis.multiplier = config?.multiplier ?? 2\n\t\tthis.maxAttempts = config?.maxAttempts ?? 0\n\t\tthis.jitter = config?.jitter ?? 0.25\n\t\tthis.stableAfterMs = config?.stableAfterMs ?? 10_000\n\t\tthis.random = config?.randomSource ?? Math.random\n\t}\n\n\t/**\n\t * Start reconnection attempts. Calls `onReconnect` with exponential backoff.\n\t *\n\t * An attempt counts as successful only when `onReconnect` returns true AND no\n\t * disconnect was reported ({@link requestRetry}) while it ran: a session that\n\t * drops before the attempt returns is retried, never silently lost (NEW-SYNC-2).\n\t *\n\t * @param onReconnect - Called on each attempt. Return `true` if reconnection succeeded\n\t * (the caller decides what success means; Kora waits for the `streaming` state).\n\t * @returns Promise that resolves when reconnection succeeds or maxAttempts reached.\n\t */\n\tasync start(onReconnect: () => Promise<boolean>): Promise<boolean> {\n\t\tthis.stopped = false\n\t\tthis.running = true\n\t\tthis.pendingRetry = false\n\t\tthis.clearStableTimer()\n\n\t\ttry {\n\t\t\twhile (!this.stopped) {\n\t\t\t\tif (this.maxAttempts > 0 && this.attempt >= this.maxAttempts) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\n\t\t\t\tconst delay = this.getNextDelay()\n\t\t\t\tthis.attempt++\n\n\t\t\t\tawait this.wait(delay)\n\n\t\t\t\tif (this.stopped) return false\n\n\t\t\t\tthis.pendingRetry = false\n\t\t\t\ttry {\n\t\t\t\t\tconst success = await onReconnect()\n\t\t\t\t\tif (this.stopped) return false\n\t\t\t\t\tif (success && !this.pendingRetry) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Continue retrying on failure\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false\n\t\t} finally {\n\t\t\tthis.running = false\n\t\t\tthis.pendingRetry = false\n\t\t}\n\t}\n\n\t/**\n\t * Report a disconnect. While a run is in flight it makes the current attempt count\n\t * as failed, so the loop retries (with backoff) instead of exiting on a session\n\t * that already dropped. Returns true when a running loop took the request; false\n\t * means the caller should start a new run.\n\t */\n\trequestRetry(): boolean {\n\t\tthis.clearStableTimer()\n\t\tif (!this.running) return false\n\t\tthis.pendingRetry = true\n\t\treturn true\n\t}\n\n\t/**\n\t * Report that a connection is up (it reached streaming). If it is still up after\n\t * `stableAfterMs`, the backoff resets to the initial delay. A disconnect reported\n\t * before then ({@link requestRetry} or {@link reportDisconnected}) cancels the reset.\n\t */\n\treportConnected(): void {\n\t\tthis.clearStableTimer()\n\t\tif (this.stableAfterMs <= 0) {\n\t\t\tthis.attempt = 0\n\t\t\treturn\n\t\t}\n\t\tthis.stableTimer = setTimeout(() => {\n\t\t\tthis.stableTimer = null\n\t\t\tthis.attempt = 0\n\t\t}, this.stableAfterMs)\n\t\tconst timer = this.stableTimer as { unref?: () => void }\n\t\ttimer.unref?.()\n\t}\n\n\t/** Report that the connection dropped: a pending backoff reset is cancelled. */\n\treportDisconnected(): void {\n\t\tthis.clearStableTimer()\n\t}\n\n\t/**\n\t * Cancel the current backoff wait and proceed to the next reconnect attempt immediately.\n\t * No-op if not waiting.\n\t */\n\twake(): void {\n\t\tif (this.timer !== null) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tthis.timer = null\n\t\t}\n\t\tif (this.waitResolve) {\n\t\t\tthis.waitResolve()\n\t\t\tthis.waitResolve = null\n\t\t}\n\t}\n\n\t/**\n\t * Stop any pending reconnection attempt.\n\t */\n\tstop(): void {\n\t\tthis.stopped = true\n\t\tthis.clearStableTimer()\n\t\tif (this.timer !== null) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tthis.timer = null\n\t\t}\n\t\t// Resolve the pending wait promise so start() loop can exit\n\t\tif (this.waitResolve) {\n\t\t\tthis.waitResolve()\n\t\t\tthis.waitResolve = null\n\t\t}\n\t}\n\n\t/**\n\t * Reset the attempt counter to the initial delay. Call after a deliberate\n\t * (user-initiated) reconnection. It never clears a {@link stop}: a stopped loop\n\t * stays stopped, so a stop racing a reset cannot resurrect it (SYNC-8).\n\t */\n\treset(): void {\n\t\tthis.attempt = 0\n\t}\n\n\t/**\n\t * Compute the next delay for the current attempt.\n\t * Exposed for testing purposes.\n\t */\n\tgetNextDelay(): number {\n\t\tconst baseDelay = Math.min(this.initialDelay * this.multiplier ** this.attempt, this.maxDelay)\n\n\t\t// Apply jitter: varies the delay by ±jitter factor\n\t\tconst jitterRange = baseDelay * this.jitter\n\t\tconst jitterOffset = (this.random() - 0.5) * 2 * jitterRange\n\t\treturn Math.max(0, Math.round(baseDelay + jitterOffset))\n\t}\n\n\t/**\n\t * Whether the reconnection loop is currently running.\n\t */\n\tisRunning(): boolean {\n\t\treturn this.running\n\t}\n\n\t/**\n\t * Current attempt number (for testing).\n\t */\n\tgetAttemptCount(): number {\n\t\treturn this.attempt\n\t}\n\n\tprivate clearStableTimer(): void {\n\t\tif (this.stableTimer !== null) {\n\t\t\tclearTimeout(this.stableTimer)\n\t\t\tthis.stableTimer = null\n\t\t}\n\t}\n\n\tprivate wait(ms: number): Promise<void> {\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.waitResolve = resolve\n\t\t\tthis.timer = setTimeout(() => {\n\t\t\t\tthis.timer = null\n\t\t\t\tthis.waitResolve = null\n\t\t\t\tresolve()\n\t\t\t}, ms)\n\t\t})\n\t}\n}\n","import { KoraError } from '@korajs/core'\nimport type { SchemaDefinition } from '@korajs/core'\n\n/** The part of the sync encryption config that decides what the server can read. */\nexport interface EncryptedRelationConfig {\n\tenabled?: boolean\n\tcleartextFields?: Readonly<Record<string, readonly string[]>>\n}\n\n/**\n * Thrown at app initialization when end-to-end encryption would seal the foreign-key\n * field of a relation with a referential `onDelete` policy.\n */\nexport class SealedRelationFieldError extends KoraError {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly relation: string,\n\t\tpublic readonly collection: string,\n\t\tpublic readonly field: string,\n\t) {\n\t\tsuper(message, 'SEALED_RELATION_FIELD', {\n\t\t\trelation,\n\t\t\tcollection,\n\t\t\tfield,\n\t\t\tfix: `Add \"${field}\" to sync.encryption.cleartextFields.${collection}.`,\n\t\t})\n\t\tthis.name = 'SealedRelationFieldError'\n\t}\n}\n\n/**\n * Refuse a configuration where end-to-end encryption seals the foreign key of a\n * relation whose `onDelete` is `cascade`, `set-null` or `restrict`.\n *\n * Referential policies are enforced by the sync server, the one replica that sees every\n * child: it cascades (or nulls, or refuses) for children no single device knows about,\n * and relays the result. It can only do so when it can read the foreign key. With the\n * key sealed, no replica can enforce the policy for everyone, and every device-side\n * substitute either loses a cascade or deletes a child another device moved away\n * (RT-74, RT-78, RT-82). So the foreign key of such a relation must travel in cleartext:\n * it reveals which parent a child belongs to, never the child's other fields.\n *\n * Relations with `onDelete: 'no-action'` (or none) are not affected: nothing is\n * enforced on delete, so their foreign key may stay sealed.\n *\n * @param schema - The app schema\n * @param encryption - The sync encryption config (none, or disabled: nothing to check)\n * @throws {SealedRelationFieldError} Naming the relation, the field and the fix\n */\nexport function validateEncryptedRelations(\n\tschema: SchemaDefinition,\n\tencryption: EncryptedRelationConfig | null | undefined,\n): void {\n\tif (!encryption || encryption.enabled !== true) return\n\tfor (const [name, relation] of Object.entries(schema.relations ?? {})) {\n\t\tconst policy = relation.onDelete\n\t\tif (policy !== 'cascade' && policy !== 'set-null' && policy !== 'restrict') continue\n\t\tconst cleartext = encryption.cleartextFields?.[relation.from] ?? []\n\t\tif (cleartext.includes(relation.field)) continue\n\t\tthrow new SealedRelationFieldError(\n\t\t\t`Relation \"${name}\" (${relation.from}.${relation.field} -> ${relation.to}, onDelete: '${policy}') needs its foreign key in cleartext under end-to-end sync encryption: the sync server enforces '${policy}' for every device and cannot read a sealed \"${relation.field}\". Add it to sync.encryption.cleartextFields: { ${relation.from}: ['${relation.field}'] } (the server then sees which ${relation.to} record each ${relation.from} record belongs to, nothing else), or set onDelete: 'no-action'.`,\n\t\t\tname,\n\t\t\trelation.from,\n\t\t\trelation.field,\n\t\t)\n\t}\n}\n","import type { KoraEventEmitter, KoraEventType } from '@korajs/core'\nimport type { HeldNodeInfo, SyncStatusInfo } from '../types'\n\nconst NO_HELD_NODES: HeldNodeInfo[] = Object.freeze([]) as unknown as HeldNodeInfo[]\n\n/** Default status when sync is not configured or the engine is unavailable. */\nexport const OFFLINE_SYNC_STATUS: SyncStatusInfo = Object.freeze({\n\tstatus: 'offline',\n\tphase: 'offline',\n\treconnecting: false,\n\tpendingOperations: 0,\n\tlastSyncedAt: null,\n\tlastSuccessfulPush: null,\n\tlastSuccessfulPull: null,\n\tconflicts: 0,\n\theldOperations: 0,\n\theldNodes: NO_HELD_NODES,\n\tlocalDurability: 'durable',\n\tserverProtocolVersion: null,\n\tprotocolDeprecated: false,\n\tclockSkewMs: null,\n\tinFlightUploadOperations: 0,\n\thasInFlightDeliveryBatch: false,\n\tactiveViewId: '',\n\tactiveViewComplete: false,\n\tinitialSync: { complete: false, receivedBatches: 0, totalBatches: null, progress: null },\n\tdeliveryWatermark: 0,\n\tserverFrontier: null,\n\tblockedFailure: null,\n})\n\n/**\n * Returns `next` with every value unchanged since `previous` carried over by\n * reference, and the optional Phase 2/3 fields defaulted, so UI bindings see the\n * same object (and the same nested `heldNodes`, `initialSync`, `blockedFailure`)\n * until something actually changes (DX-5). Returns `previous` itself when nothing did.\n */\nexport function stabilizeSyncStatus(\n\tnext: SyncStatusInfo,\n\tprevious: SyncStatusInfo | null,\n): SyncStatusInfo {\n\tconst normalized: SyncStatusInfo = {\n\t\t...next,\n\t\theldOperations: next.heldOperations ?? 0,\n\t\theldNodes: next.heldNodes && next.heldNodes.length > 0 ? next.heldNodes : NO_HELD_NODES,\n\t\tlocalDurability: next.localDurability ?? 'durable',\n\t\tserverProtocolVersion: next.serverProtocolVersion ?? null,\n\t\tprotocolDeprecated: next.protocolDeprecated ?? false,\n\t}\n\tif (previous === null) return Object.freeze(normalized)\n\n\tconst nextRecord = normalized as unknown as Record<string, unknown>\n\tconst prevRecord = previous as unknown as Record<string, unknown>\n\tlet changed = Object.keys(prevRecord).some((key) => !(key in nextRecord))\n\tconst merged: Record<string, unknown> = {}\n\tfor (const [key, value] of Object.entries(nextRecord)) {\n\t\tconst before = prevRecord[key]\n\t\tif (key in prevRecord && (value === before || sameJson(value, before))) {\n\t\t\tmerged[key] = before\n\t\t} else {\n\t\t\tmerged[key] = value\n\t\t\tchanged = true\n\t\t}\n\t}\n\treturn changed ? (Object.freeze(merged) as unknown as SyncStatusInfo) : previous\n}\n\nfunction sameJson(a: unknown, b: unknown): boolean {\n\tif (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {\n\t\treturn false\n\t}\n\treturn JSON.stringify(a) === JSON.stringify(b)\n}\n\nconst SYNC_STATUS_EVENT_TYPES = [\n\t'sync:connected',\n\t'sync:clock-skew',\n\t'sync:disconnected',\n\t'sync:schema-mismatch',\n\t'sync:auth-failed',\n\t'sync:sent',\n\t'sync:received',\n\t'sync:acknowledged',\n\t'sync:apply-failed',\n\t'sync:suspended',\n\t'sync:apply-blocked',\n\t'sync:apply-retrying',\n\t'sync:apply-recovered',\n\t'sync:delivery-gap',\n\t'sync:diagnostics',\n\t'sync:initial-sync-progress',\n\t'sync:durability-degraded',\n\t'sync:durability-restored',\n\t'sync:local-node',\n] as const satisfies readonly KoraEventType[]\n\nexport interface SyncStatusControllerOptions {\n\tsyncEngine?: { getStatus(): SyncStatusInfo } | null\n\tgetSyncEngine?: () => { getStatus(): SyncStatusInfo } | null\n\tsubscribeSyncStatus: ((listener: (status: SyncStatusInfo) => void) => () => void) | null\n\tevents: KoraEventEmitter | null\n}\n\nexport interface SyncStatusController {\n\tgetSnapshot(): SyncStatusInfo\n\tsubscribe(listener: () => void): () => void\n\trefresh(): void\n\tdestroy(): void\n}\n\n/**\n * Framework-agnostic sync status subscription with live reads when no bridge exists.\n */\nexport function createSyncStatusController(\n\toptions: SyncStatusControllerOptions,\n): SyncStatusController {\n\tconst useLiveSnapshot = options.subscribeSyncStatus === null && options.events === null\n\tlet snapshot = OFFLINE_SYNC_STATUS\n\tconst listeners = new Set<() => void>()\n\tlet cleanup: (() => void) | null = null\n\n\tconst resolveEngine = (): { getStatus(): SyncStatusInfo } | null => {\n\t\treturn options.getSyncEngine?.() ?? options.syncEngine ?? null\n\t}\n\n\tconst notify = (): void => {\n\t\tfor (const listener of listeners) {\n\t\t\tlistener()\n\t\t}\n\t}\n\n\tconst setSnapshot = (next: SyncStatusInfo): void => {\n\t\tconst stable = stabilizeSyncStatus(next, snapshot)\n\t\tif (stable === snapshot) {\n\t\t\treturn\n\t\t}\n\t\tsnapshot = stable\n\t\tnotify()\n\t}\n\n\tconst refresh = (): void => {\n\t\tconst engine = resolveEngine()\n\t\tconst next = engine ? engine.getStatus() : OFFLINE_SYNC_STATUS\n\t\tsetSnapshot(next)\n\t}\n\n\tconst attach = (): void => {\n\t\tcleanup?.()\n\n\t\tif (options.subscribeSyncStatus) {\n\t\t\tcleanup = options.subscribeSyncStatus(setSnapshot)\n\t\t\treturn\n\t\t}\n\n\t\tif (!resolveEngine()) {\n\t\t\tsetSnapshot(OFFLINE_SYNC_STATUS)\n\t\t\tcleanup = () => {}\n\t\t\treturn\n\t\t}\n\n\t\tif (options.events) {\n\t\t\tconst unsubs = SYNC_STATUS_EVENT_TYPES.map((type) => options.events?.on(type, refresh))\n\t\t\trefresh()\n\t\t\tcleanup = () => {\n\t\t\t\tfor (const unsub of unsubs) {\n\t\t\t\t\tunsub?.()\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\trefresh()\n\t\tcleanup = () => {}\n\t}\n\n\tattach()\n\n\treturn {\n\t\tgetSnapshot(): SyncStatusInfo {\n\t\t\tif (useLiveSnapshot) {\n\t\t\t\t// The engine builds a new object per call: keep the previous one while equal,\n\t\t\t\t// or useSyncExternalStore would see a change on every read and loop.\n\t\t\t\tconst engine = resolveEngine()\n\t\t\t\tsnapshot = stabilizeSyncStatus(engine ? engine.getStatus() : OFFLINE_SYNC_STATUS, snapshot)\n\t\t\t}\n\t\t\treturn snapshot\n\t\t},\n\t\tsubscribe(listener: () => void): () => void {\n\t\t\tlisteners.add(listener)\n\t\t\tlistener()\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener)\n\t\t\t}\n\t\t},\n\t\trefresh,\n\t\tdestroy(): void {\n\t\t\tcleanup?.()\n\t\t\tcleanup = null\n\t\t\tlisteners.clear()\n\t\t},\n\t}\n}\n","import type { AwarenessManager } from '../awareness/awareness-manager'\nimport type { AwarenessState } from '../awareness/types'\n\nconst EMPTY_COLLABORATORS: AwarenessState[] = []\n\nfunction awarenessStatesEqual(left: AwarenessState[], right: AwarenessState[]): boolean {\n\tif (left.length !== right.length) {\n\t\treturn false\n\t}\n\n\tfor (let index = 0; index < left.length; index++) {\n\t\tconst a = left[index]\n\t\tconst b = right[index]\n\t\tif (!a || !b) {\n\t\t\treturn false\n\t\t}\n\t\tif (a.user.name !== b.user.name || a.user.color !== b.user.color) {\n\t\t\treturn false\n\t\t}\n\t\tconst aCursor = a.cursor\n\t\tconst bCursor = b.cursor\n\t\tif (aCursor === undefined && bCursor === undefined) {\n\t\t\tcontinue\n\t\t}\n\t\tif (!aCursor || !bCursor) {\n\t\t\treturn false\n\t\t}\n\t\tif (\n\t\t\taCursor.collection !== bCursor.collection ||\n\t\t\taCursor.recordId !== bCursor.recordId ||\n\t\t\taCursor.field !== bCursor.field ||\n\t\t\taCursor.anchor !== bCursor.anchor ||\n\t\t\taCursor.head !== bCursor.head\n\t\t) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\n/**\n * Returns remote collaborators' awareness states (excludes the local client).\n */\nexport function getRemoteAwarenessStates(awareness: AwarenessManager): AwarenessState[] {\n\tconst localClientId = awareness.clientId\n\tconst remoteStates: AwarenessState[] = []\n\n\tfor (const [clientId, state] of awareness.getStates()) {\n\t\tif (clientId === localClientId) continue\n\t\tremoteStates.push(state)\n\t}\n\n\treturn remoteStates\n}\n\n/**\n * Subscribes to awareness changes and invokes `listener` only when the remote\n * collaborator set changes.\n */\nexport function subscribeRemoteAwarenessStates(\n\tawareness: AwarenessManager,\n\tlistener: (states: AwarenessState[]) => void,\n): () => void {\n\tlet snapshot = EMPTY_COLLABORATORS\n\n\tconst emitIfChanged = (): void => {\n\t\tconst next = getRemoteAwarenessStates(awareness)\n\t\tif (awarenessStatesEqual(snapshot, next)) {\n\t\t\treturn\n\t\t}\n\t\tsnapshot = next\n\t\tlistener(snapshot)\n\t}\n\n\temitIfChanged()\n\treturn awareness.on('change', emitIfChanged)\n}\n"],"mappings":"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","names":["import_core","protobuf","import_core","import_core","buffer","buffer","import_core","import_core","import_core","import_core","import_core","toBase64","fromBase64","toBase64","fromBase64","import_core","toBase64","fromBase64","outcome","toBase64","fromBase64","import_core","canonicalJson","import_internal","import_core","stableStringify","op","import_core"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/scopes/scope-snapshot.ts","../src/scopes/scope-filter.ts","../src/scopes/query-subset.ts","../src/delta/delta-cursor.ts","../src/protocol/schema-version.ts","../src/protocol/protocol-version.ts","../src/encryption/key-messages.ts","../src/protocol/messages.ts","../src/protocol/serializer.ts","../src/transport/websocket-transport.ts","../src/transport/http-long-polling-transport.ts","../src/transport/chaos-transport.ts","../src/engine/sync-engine.ts","../src/awareness/awareness-manager.ts","../src/blob/blob-chunk-channel.ts","../src/diagnostics/bandwidth-estimator.ts","../src/diagnostics/percentile.ts","../src/diagnostics/metrics-collector.ts","../src/encryption/keyring.ts","../src/encryption/key-cache.ts","../src/encryption/key-derivation.ts","../src/encryption/key-record.ts","../src/encryption/keyring-crypto.ts","../src/encryption/sync-encryptor.ts","../src/richtext/richtext-doc-channel.ts","../src/richtext/doc-channel-wire.ts","../src/scopes/scope-view-key.ts","../src/engine/memory-queue-storage.ts","../src/engine/outbound-queue.ts","../src/engine/verify-inbound.ts","../src/engine/connection-monitor.ts","../src/engine/reconnection-manager.ts","../src/encryption/encrypted-relations.ts","../src/reactivity/sync-status-controller.ts","../src/reactivity/collaborators-snapshot.ts"],"sourcesContent":["// @korajs/sync — public API\n// Every export here is a public API commitment. Be explicit.\n\n// === Types ===\nexport type {\n\tQueueStorage,\n\tQuarantinedOperation,\n\tRejectedOperation,\n\tRejectedOperationStorage,\n\tSyncStatePersistence,\n\tSyncAuthRequest,\n\tSyncConfig,\n\tSyncEncryptionConfig,\n\tSyncScopeContext,\n\tSyncScopeMap,\n\tSyncState,\n\tSyncStatus,\n\tSyncStatusInfo,\n\tSyncPhase,\n\tSyncSettlementOptions,\n\tSyncSettlementResult,\n\tActiveApplyFailure,\n\tDeltaCursor,\n\tLocalNodeInfo,\n\tHeldNodeInfo,\n\tHeldReason,\n\tAdoptionScheduleInfo,\n\tTerminalRejectionRecord,\n} from './types'\n\nexport {\n\tInvalidScopeError,\n\tMemoryRejectedOperationStorage,\n\tScopeViolationError,\n\tSYNC_STATES,\n\tSYNC_STATUSES,\n} from './types'\n\n// === Scope Filtering ===\nexport { filterOperationsByScope, operationMatchesScope } from './scopes/scope-filter'\nexport type { ScopeSnapshotOptions } from './scopes/scope-snapshot'\nexport {\n\tdedupeQuerySubsets,\n\tquerySubsetContains,\n\toperationMatchesQuerySubsets,\n\ttype SyncQuerySubset,\n} from './scopes/query-subset'\n\n// === Delta Cursor ===\nexport {\n\tcreateDeltaCursorFromBatch,\n\tdecodeDeltaCursor,\n\tencodeDeltaCursor,\n\tsliceOperationsAfterCursor,\n} from './delta/delta-cursor'\n\n// === SyncStore Interface ===\nexport type { ApplyFailureReason, ApplyResult, SyncStore } from './engine/sync-store'\n\n// === Protocol Messages ===\nexport type {\n\tAcknowledgmentMessage,\n\tErrorMessage,\n\tHandshakeMessage,\n\tHeartbeatMessage,\n\tHandshakeResponseMessage,\n\tOperationBatchMessage,\n\tOperationRejectedMessage,\n\tSerializedOperation,\n\tSyncMessage,\n\tWireFormat,\n} from './protocol/messages'\n\nexport {\n\tisClientSchemaVersionSupported,\n\tisSchemaMismatchReject,\n\tSCHEMA_MISMATCH_PREFIX,\n} from './protocol/schema-version'\n\nexport {\n\tdeclaredProtocolVersion,\n\tINVALID_OPERATION_ID,\n\tLEGACY_SYNC_PROTOCOL_VERSION,\n\tPLAINTEXT_REJECTED,\n\tPROTOCOL_V1_DEPRECATED,\n\tSYNC_PROTOCOL_VERSION,\n} from './protocol/protocol-version'\n\nexport {\n\tisAcknowledgmentMessage,\n\tisErrorMessage,\n\tisHandshakeMessage,\n\tisHandshakeResponseMessage,\n\tisOperationBatchMessage,\n\tisOperationRejectedMessage,\n\tisSyncMessage,\n} from './protocol/messages'\n\n// === Serialization ===\nexport type { MessageSerializer } from './protocol/serializer'\n\nexport {\n\tJsonMessageSerializer,\n\tNegotiatedMessageSerializer,\n\tProtobufMessageSerializer,\n\tversionVectorToWire,\n\twireToVersionVector,\n} from './protocol/serializer'\n\n// === Transport ===\nexport type {\n\tSyncTransport,\n\tTransportCloseHandler,\n\tTransportErrorHandler,\n\tTransportMessageHandler,\n\tTransportOptions,\n} from './transport/transport'\n\nexport type {\n\tWebSocketConstructor,\n\tWebSocketLike,\n\tWebSocketTransportOptions,\n} from './transport/websocket-transport'\n\nexport type { HttpLongPollingTransportOptions } from './transport/http-long-polling-transport'\n\nexport { WebSocketTransport } from './transport/websocket-transport'\nexport {\n\tHTTP_SYNC_SESSION_HEADER,\n\tHttpLongPollingTransport,\n} from './transport/http-long-polling-transport'\n\nexport type { ChaosConfig } from './transport/chaos-transport'\n\nexport { ChaosTransport } from './transport/chaos-transport'\n\n// === Engine ===\nexport type { SyncDiagnostics, SyncEngineOptions } from './engine/sync-engine'\n\nexport { SyncEngine } from './engine/sync-engine'\n\nexport type { OutboundBatch } from './engine/outbound-queue'\n\nexport { OutboundQueue } from './engine/outbound-queue'\n\nexport type { ConnectionMonitorConfig } from './engine/connection-monitor'\n\nexport { ConnectionMonitor } from './engine/connection-monitor'\n\nexport type { ReconnectionConfig } from './engine/reconnection-manager'\n\nexport { ReconnectionManager } from './engine/reconnection-manager'\n\n// === Awareness ===\nexport type {\n\tAwarenessChange,\n\tAwarenessCursor,\n\tAwarenessMessage,\n\tAwarenessState,\n\tAwarenessUser,\n\tCursorInfo,\n} from './awareness/types'\n\nexport { AwarenessManager } from './awareness/awareness-manager'\n\n// === Awareness Protocol Messages ===\nexport type {\n\tAwarenessStateWire,\n\tAwarenessUpdateMessage,\n} from './protocol/messages'\n\nexport { isAwarenessUpdateMessage } from './protocol/messages'\n\n// === Richtext doc channel (large documents) ===\nexport type { YjsDocUpdateMessage } from './protocol/messages'\n\nexport { isYjsDocUpdateMessage } from './protocol/messages'\n\nexport {\n\tDEFAULT_RICHTEXT_DOC_CHANNEL_THRESHOLD,\n\tRichtextDocChannel,\n} from './richtext/richtext-doc-channel'\n\nexport type {\n\tRichtextDocChannelOptions,\n\tRichtextDocListener,\n} from './richtext/richtext-doc-channel'\n\nexport { decodeYjsUpdate, encodeYjsUpdate, richtextDocKey } from './richtext/doc-channel-wire'\n\n// === Blob chunk channel (out-of-band blob transfer) ===\nexport type {\n\tBlobChunkPushMessage,\n\tBlobChunkRequestMessage,\n\tBlobChunkResponseMessage,\n} from './protocol/messages'\n\nexport {\n\tisBlobChunkPushMessage,\n\tisBlobChunkRequestMessage,\n\tisBlobChunkResponseMessage,\n} from './protocol/messages'\n\nexport {\n\tBlobChunkChannel,\n\tdecodeBlobChunkBytes,\n\tencodeBlobChunkBytes,\n} from './blob/blob-chunk-channel'\n\nexport type {\n\tBlobChunkChannelMessage,\n\tBlobChunkChannelOptions,\n} from './blob/blob-chunk-channel'\n\n// === Encryption ===\nexport type {\n\tEncryptedPayload,\n\tSyncEncryptionAlgorithm,\n\tVersionedKey,\n} from './encryption/types'\n\nexport {\n\tSyncEncryptor,\n\tEncryptionError,\n\tDecryptionError,\n\tisEncryptedPayload,\n} from './encryption/sync-encryptor'\n\nexport {\n\tSealedRelationFieldError,\n\tvalidateEncryptedRelations,\n} from './encryption/encrypted-relations'\nexport type { EncryptedRelationConfig } from './encryption/encrypted-relations'\n\nexport {\n\tKeyDerivationError,\n\tderiveKey,\n\tderiveVersionedKey,\n\tgenerateSalt,\n} from './encryption/key-derivation'\n\n// Shared key material (ENC-1): the server-stored, passphrase-wrapped keyring.\nexport { EncryptionKeyring, EncryptionKeyError } from './encryption/keyring'\nexport type {\n\tEncryptionKeyringOptions,\n\tEncryptionLockState,\n\tEncryptionStatus,\n\tEncryptionStatusCode,\n\tKeyServiceChannel,\n\tKeyServiceReply,\n} from './encryption/keyring'\nexport {\n\tIndexedDbKeyCache,\n\tMemoryKeyCache,\n\tNoKeyCache,\n\tcreateKeyCache,\n} from './encryption/key-cache'\nexport type { CachedKeyring, KeyCache } from './encryption/key-cache'\nexport {\n\tDEFAULT_KEYRING,\n\tKEY_RECORD_FORMAT,\n\tMAX_KDF_ITERATIONS,\n\tMAX_KEY_RECORD_BYTES,\n\tisKeyId,\n\tisKeyRecordSuccessor,\n\tisValidKeyringName,\n\tvalidateKeyRecord,\n} from './encryption/key-record'\nexport type {\n\tKeyRecordKdf,\n\tKeyRecordRecovery,\n\tKeyRecordValidation,\n\tWrappedDataKey,\n\tWrappedKeyRecord,\n\tWrappedMasterKey,\n} from './encryption/key-record'\nexport { KeyUnwrapError } from './encryption/keyring-crypto'\nexport { isEncryptionKeyMessage } from './encryption/key-messages'\nexport type {\n\tEncryptionKeyMessage,\n\tEncryptionKeyPutMessage,\n\tEncryptionKeyRequestMessage,\n\tEncryptionKeyResponseMessage,\n\tEncryptionKeyResponseStatus,\n} from './encryption/key-messages'\n\nexport {\n\tcreateSyncStatusController,\n\tOFFLINE_SYNC_STATUS,\n} from './reactivity/sync-status-controller'\nexport type {\n\tSyncStatusController,\n\tSyncStatusControllerOptions,\n} from './reactivity/sync-status-controller'\n\nexport {\n\tgetRemoteAwarenessStates,\n\tsubscribeRemoteAwarenessStates,\n} from './reactivity/collaborators-snapshot'\n","import type { Operation, VersionVector } from '@korajs/core'\nimport { KoraError } from '@korajs/core'\nimport type { SyncEncryptionConfig } from './encryption/types'\n\n// Re-export for convenience — consumers can import from '@korajs/sync' types\nexport type { SyncEncryptionConfig }\n\n/**\n * Internal sync engine states. Used for state machine transitions.\n */\nexport const SYNC_STATES = [\n\t'disconnected',\n\t'connecting',\n\t'handshaking',\n\t'syncing',\n\t'streaming',\n\t'error',\n] as const\nexport type SyncState = (typeof SYNC_STATES)[number]\n\n/**\n * Developer-facing sync status. Simplified view of the internal state.\n */\nexport const SYNC_STATUSES = [\n\t'connected',\n\t'reconnecting',\n\t'syncing',\n\t'synced',\n\t'offline',\n\t'clock-error',\n\t'error',\n\t'schema-mismatch',\n\t'auth-required',\n\t// End-to-end encryption is on and this device's keyring is locked (ENC-1): sync is\n\t// paused until `app.encryption.unlock(passphrase)`. Local reads and writes go on.\n\t'encryption-locked',\n] as const\nexport type SyncStatus = (typeof SYNC_STATUSES)[number]\n\nexport type SyncPhase =\n\t| 'suspended'\n\t| 'offline'\n\t| 'connecting'\n\t| 'authenticating'\n\t| 'handshaking'\n\t| 'uploading'\n\t| 'receiving'\n\t| 'applying'\n\t| 'streaming'\n\t| 'blocked'\n\nexport interface ActiveApplyFailure {\n\toperationId: string\n\tcollection: string\n\trecordId: string\n\tcode: string\n\tmessage: string\n\tretriable: boolean\n\tfirstSeenAt: number\n\tretryCount: number\n}\n\n/** Why a local node's unsynced writes are held (RT-38, RT-50). */\nexport type HeldReason = 'other-user' | 'unassigned'\n\n/** A local node whose unsynced writes are held (see {@link SyncStatusInfo.heldNodes}). */\nexport interface HeldNodeInfo {\n\tnodeId: string\n\t/** Unsynced writes the node holds. */\n\toperationCount: number\n\treason: HeldReason\n\t/** The user the node is bound to, or null when nobody knows (unassigned). */\n\tprincipal: string | null\n}\n\n/**\n * Sync status information exposed to developers.\n */\nexport interface SyncStatusInfo {\n\t/** Current developer-facing status */\n\tstatus: SyncStatus\n\tphase?: SyncPhase\n\treason?: string\n\t/** True when the engine is actively trying to re-establish a transport session. */\n\treconnecting: boolean\n\t/** Number of operations waiting to be sent */\n\tpendingOperations: number\n\t/** Timestamp of last successful sync (null if never synced) */\n\tlastSyncedAt: number | null\n\t/** Timestamp of last successful push to the server (null if never pushed) */\n\tlastSuccessfulPush: number | null\n\t/** Timestamp of last successful pull from the server (null if never pulled) */\n\tlastSuccessfulPull: number | null\n\t/** Number of merge conflicts encountered during this session */\n\tconflicts: number\n\t/**\n\t * Unsynced writes of another user who shared this local database (RT-38): the server\n\t * refused their node for the signed-in principal, so they wait, not counted in\n\t * `pendingOperations`, until that user signs in again on this device. Use\n\t * `store.namespaceByAuthUser` to give each user their own database instead.\n\t */\n\theldOperations?: number\n\t/**\n\t * The local nodes whose unsynced writes are held (RT-38, RT-50), with how many each\n\t * holds and why: `other-user` (they belong to another user, or the server refused\n\t * the node for the signed-in one: they upload when their user signs in) or\n\t * `unassigned` (written before the app knew who was signed in, on a node that never\n\t * synced: nobody can tell whose they are, so the app decides with\n\t * `app.sync.assignHeld` or `app.sync.discardHeld`). As of the last session start.\n\t */\n\theldNodes?: HeldNodeInfo[]\n\t/**\n\t * `degraded` when this device's local database could not be made durable several times\n\t * in a row (storage quota exceeded, IndexedDB broken; RT-49). Uploads continue so the\n\t * server keeps a durable copy, and a later reload recovers its writes from the server;\n\t * writes made while offline in this state may be lost if the page closes. Show the user\n\t * a warning (free up storage, stay online). `durable` otherwise.\n\t */\n\tlocalDurability?: 'durable' | 'degraded'\n\t/**\n\t * Sync protocol version the server answered with in the current (or last) session,\n\t * or null before any server answered.\n\t */\n\tserverProtocolVersion?: number | null\n\t/**\n\t * True when the server speaks an older sync protocol than this client (a pre-beta.13\n\t * server). It still syncs, but that protocol is deprecated and a later release\n\t * refuses it: upgrade the sync server.\n\t */\n\tprotocolDeprecated?: boolean\n\t/** serverTime - localTime in ms measured at the last handshake, or null before first connect. Negative = this device's clock is fast. */\n\tclockSkewMs: number | null\n\tinFlightUploadOperations?: number\n\thasInFlightDeliveryBatch?: boolean\n\tactiveViewId?: string\n\tactiveViewComplete?: boolean\n\tinitialSync?: {\n\t\tcomplete: boolean\n\t\treceivedBatches: number\n\t\ttotalBatches: number | null\n\t\tprogress: number | null\n\t}\n\tdeliveryWatermark?: number\n\tserverFrontier?: number | null\n\tblockedFailure?: ActiveApplyFailure | null\n}\n\nexport interface SyncSettlementOptions {\n\tupload?: boolean\n\tdownload?: 'active-view' | false\n\ttimeoutMs?: number\n\tsignal?: AbortSignal\n}\n\nexport type SyncSettlementResult =\n\t| { outcome: 'settled'; status: SyncStatusInfo }\n\t| { outcome: 'offline'; status: SyncStatusInfo }\n\t| { outcome: 'suspended'; reason: string; status: SyncStatusInfo }\n\t| { outcome: 'blocked'; failure: ActiveApplyFailure; status: SyncStatusInfo }\n\t| { outcome: 'timeout'; status: SyncStatusInfo }\n\t| { outcome: 'aborted'; status: SyncStatusInfo }\n\n/**\n * Per-collection sync scope map. Maps collection names to field-value filters.\n * Empty filter `{}` means no restriction (all records visible).\n * Missing collection means hidden (no records visible for that collection).\n */\nexport type SyncScopeMap = Record<string, Record<string, unknown>>\n\n/** Options passed to {@link SyncConfig.auth}. */\nexport interface SyncAuthRequest {\n\t/**\n\t * True when the server ended the previous session because its credential\n\t * expired or was revoked. A cached token must not be reused.\n\t */\n\tforceRefresh?: boolean\n}\n\n/**\n * Sync configuration provided by the developer.\n */\nexport interface SyncConfig {\n\t/** WebSocket or HTTP URL for the sync server */\n\turl: string\n\t/** Transport type to use. Defaults to 'websocket'. */\n\ttransport?: 'websocket' | 'http'\n\t/**\n\t * Auth provider function. Called before each connection attempt. After the\n\t * server ends a session with `AUTH_EXPIRED` or `AUTH_REVOKED` it is called\n\t * with `{ forceRefresh: true }`: return a freshly refreshed token, never the\n\t * cached one the server just refused.\n\t */\n\tauth?: (options?: SyncAuthRequest) => Promise<{ token: string }>\n\t/**\n\t * The signed-in user whose writes this device makes (RT-42): a user id, or null /\n\t * undefined when nobody is known to be signed in. Called before each session. With\n\t * it, the engine binds the store's node to that user (the store moves to the user's\n\t * own node when another user's is in use) and never uploads, or adopts, a node that\n\t * belongs to another user: those writes wait for their user (`heldOperations`).\n\t * `createApp` derives it from `sync.authClient`.\n\t */\n\tprincipal?: () => Promise<string | null | undefined>\n\t/** Auth readiness gate. A suspended result prevents transport creation and retries. */\n\tauthState?: () => Promise<{\n\t\tstate: 'loading' | 'signed-out' | 'anonymous' | 'authenticated'\n\t\tmayConnectAnonymously?: boolean\n\t}>\n\tquerySubsets?: { mode?: 'reactive' | 'static' | 'disabled' }\n\t/** Client-local behavior for records leaving the accepted downlink scope. Defaults to `retain`. */\n\tscopeExit?: 'retain' | 'retract'\n\t/** Sync scopes per collection. Limits which records sync to this client. */\n\tscopes?: Record<string, (ctx: SyncScopeContext) => Record<string, unknown>>\n\t/**\n\t * Pre-computed per-collection sync scope map. Sent to the server in the handshake.\n\t * Built automatically by createApp from schema scope declarations + flat scope values.\n\t */\n\tscopeMap?: SyncScopeMap\n\t/**\n\t * Collections the app's schema syncs (all of them, or the sync-scoped ones when the\n\t * schema declares partial sync rules). Set by createApp. When no `scopeMap` is\n\t * configured, a write on one of these that the server's accepted upload scope omits\n\t * is surfaced as `sync:operation-rejected` (`OUT_OF_UPLINK_SCOPE`) instead of being\n\t * treated as local-only (RT-89). Omitted: such writes stay local without a rejection.\n\t */\n\tsyncedCollections?: readonly string[]\n\t/** Number of operations per batch. Defaults to 100. */\n\tbatchSize?: number\n\t/**\n\t * Maximum time to wait for the server to acknowledge an outbound batch before\n\t * treating the connection as stalled. The batch is returned to the durable\n\t * outbound queue before disconnecting so it can be retried on reconnect.\n\t * Defaults to 30000ms. Set to 0 to disable the watchdog.\n\t */\n\toutboundAckTimeoutMs?: number\n\t/** Initial delay before resending a transiently rejected outbound operation. Defaults to 250ms. */\n\toutboundRetryBaseDelayMs?: number\n\t/** Maximum transient-operation retry delay. Defaults to 30000ms. */\n\toutboundRetryMaxDelayMs?: number\n\t/** Initial reconnection delay in ms. Defaults to 1000. */\n\treconnectInterval?: number\n\t/** Maximum reconnection delay in ms. Defaults to 30000. */\n\tmaxReconnectInterval?: number\n\t/** Schema version of this client. */\n\tschemaVersion?: number\n\t/** Start sync automatically when the engine is created. Defaults to false. */\n\tautoConnect?: boolean\n\t/**\n\t * When true, wait for server ACKs on all outbound handshake delta batches before\n\t * entering streaming. Improves backpressure for large initial syncs.\n\t */\n\tstrictHandshake?: boolean\n\t/**\n\t * Optional schema transforms for cross-schema-version sync. They run at fold time\n\t * (RT-84): delivered operations are stored exactly as received and the local store\n\t * folds their transformed view, so the store must be given the same list (`createApp`\n\t * does). An operation with no transform path to this schema version is kept aside\n\t * and replayed after an upgrade.\n\t */\n\toperationTransforms?: import('@korajs/core').OperationTransform[]\n\t/**\n\t * Richtext snapshot size (bytes) at which the optional Yjs doc channel is used.\n\t * Defaults to 4096.\n\t */\n\trichtextDocChannelThreshold?: number\n\t/**\n\t * End-to-end encryption configuration.\n\t * When enabled, `data` and `previousData` fields are encrypted before sending\n\t * over the wire. The server never sees plaintext user data.\n\t */\n\tencryption?: SyncEncryptionConfig\n}\n\n/**\n * Context passed to sync scope functions.\n */\nexport interface SyncScopeContext {\n\tuserId?: string\n\t[key: string]: unknown\n}\n\n/**\n * Persists last-acked server version vector and computes unsynced operations from the op log.\n */\nexport interface DeltaCursor {\n\t/** ID of the last fully applied operation in the previous delta stream */\n\tlastOperationId: string\n\t/** Zero-based batch index where the cursor was recorded */\n\tbatchIndex: number\n}\n\nexport interface SyncStatePersistence {\n\tloadLastAckedServerVector(): Promise<VersionVector>\n\tsaveLastAckedServerVector(vector: VersionVector): Promise<void>\n\tmergeServerVectors(a: VersionVector, b: VersionVector): VersionVector\n\tcountUnsyncedOperations(serverVector: VersionVector): Promise<number>\n\tgetUnsyncedOperations(serverVector: VersionVector): Promise<Operation[]>\n\t/** Resume position for paginated initial sync (optional). */\n\tloadDeltaCursor?(): Promise<DeltaCursor | null>\n\tsaveDeltaCursor?(cursor: DeltaCursor | null): Promise<void>\n\t/**\n\t * The delivery watermark for a sync view, keyed by an opaque view signature (the auth\n\t * scope plus active query subscriptions). Persisted per view so a restarted client\n\t * resumes each view's gap-free stream from the right point, and switching views does not\n\t * re-sync a view already synced. The empty-string signature is the default, unfiltered\n\t * view. Optional: a persistence layer that omits these falls back to re-sync on restart.\n\t */\n\tloadDeliveryWatermark?(signature: string): Promise<number>\n\tsaveDeliveryWatermark?(signature: string, watermark: number): Promise<void>\n\t/** Load every persisted view watermark, keyed by signature. */\n\tloadAllDeliveryWatermarks?(): Promise<Record<string, number>>\n\t/**\n\t * Delete a persisted view watermark. Used to bound the number of stored view watermarks\n\t * (a cold view is evicted from the client's cache and its persisted row removed). Safe:\n\t * an evicted view back-fills from 0 (deduplicated) when next visited.\n\t */\n\tdeleteDeliveryWatermark?(signature: string): Promise<void>\n\t/**\n\t * The per-device node token the server issued at this node id's first anonymous\n\t * claim (RT-12), stored next to the node id so the device can reconnect with it.\n\t * Optional: without it the token lives only as long as the sync engine.\n\t */\n\tloadNodeToken?(nodeId?: string): Promise<string | null>\n\t/**\n\t * `nodeId` names the node the token was issued for: a database that authors under\n\t * several node ids (a rotated identity, per-tab isolation) keeps one token per node.\n\t */\n\tsaveNodeToken?(token: string, nodeId?: string): Promise<void>\n\t/**\n\t * The node ids the server named authoritative in its last accepted handshake response\n\t * (`HandshakeResponseMessage.authoritativeNodeIds`, protocol v2): operations from\n\t * these nodes are server-authored and may carry `fieldVersions` / `foldState`.\n\t * Returns null when no protocol-2 server ever answered. Optional.\n\t */\n\tloadAuthoritativeNodeIds?(): Promise<string[] | null>\n\tsaveAuthoritativeNodeIds?(nodeIds: string[]): Promise<void>\n\t/**\n\t * The contiguous acknowledged prefix of this device's own operations (W3): the highest\n\t * sequence s such that every own operation with sequence <= s is stored on the server,\n\t * terminally rejected and recorded, or was never upload-eligible. Keyed by node id, so\n\t * a rotated node starts from 0. Returns null when nothing was ever recorded under this\n\t * contract (a device upgrading from a release that persisted a max, not a prefix): the\n\t * engine then re-uploads the device's own history once, from 0; the server dedups by id.\n\t * Optional: without it the engine trusts the own entry of the last acked server vector.\n\t */\n\tloadOwnAckedThrough?(nodeId: string): Promise<number | null>\n\tsaveOwnAckedThrough?(nodeId: string, sequence: number): Promise<void>\n\t/**\n\t * Durable inbound quarantine (W4): delivered operations the client deliberately did\n\t * not apply (unknown collection, transform unavailable, deferred or rejected apply,\n\t * far-future timestamp, undecryptable payload). When `watermark` is given, the rows and\n\t * the delivery watermark advance MUST be written in one transaction, so the watermark\n\t * never passes an operation that is neither applied nor recorded here. Optional: a\n\t * persistence layer without it keeps the old behaviour (the watermark stalls instead).\n\t */\n\tsaveQuarantine?(\n\t\tentries: QuarantinedOperation[],\n\t\twatermark?: { signature: string; watermark: number },\n\t): Promise<void>\n\t/** Every quarantined operation, oldest delivery first. */\n\tloadQuarantine?(): Promise<QuarantinedOperation[]>\n\t/** Remove quarantined operations once applied (replay) or reconciled. */\n\tremoveQuarantine?(operationIds: string[]): Promise<void>\n\t/**\n\t * The downlink scope the server last accepted for this device (SYNC-11). The next\n\t * handshake reports its canonical key and that view's delivery watermark next to the\n\t * requested view's, so a server that resolves the same scope again resumes the\n\t * stream instead of restarting it from 0. Never sent as the requested scope.\n\t * Optional; without it every handshake with a server-chosen scope rescans from 0.\n\t */\n\tloadAcceptedDownlinkScope?(): Promise<SyncScopeMap | null>\n\tsaveAcceptedDownlinkScope?(scope: SyncScopeMap | null): Promise<void>\n\t/**\n\t * Durable terminal-rejection markers (RT-36): operations the server refused with a\n\t * non-retriable rejection. Never cleared (unlike the app's rejected list), so a\n\t * rescan of the device's own history never submits a refused operation again.\n\t * Optional: without them the engine remembers refusals for its own lifetime only.\n\t */\n\trecordTerminalRejections?(entries: TerminalRejectionRecord[]): Promise<void>\n\t/** Which of these operation ids carry a terminal-rejection marker. */\n\tfindTerminalRejections?(operationIds: string[]): Promise<Set<string>>\n\t/**\n\t * The node ids this database authored operations under (RT-38, RT-40), with what the\n\t * sync server said about each. Optional: without it the engine tracks only the\n\t * current node and keeps the pre-Phase-2 behaviour for refused nodes.\n\t */\n\tlistLocalNodes?(): Promise<LocalNodeInfo[]>\n\t/** Record an accepted handshake as `nodeId`; starts a new refusal cycle. */\n\tmarkLocalNodeAccepted?(nodeId: string): Promise<void>\n\t/** Record that the server refused `nodeId`; `held` holds its unsynced writes. */\n\tmarkLocalNodeRefused?(nodeId: string, held: boolean): Promise<void>\n\t/**\n\t * A handshake as `nodeId` was accepted for `principal` (RT-50): bind an unbound node\n\t * (or replace a guessed binding) from the server's answer.\n\t */\n\tconfirmLocalNodePrincipal?(nodeId: string, principal: string): Promise<void>\n\t/**\n\t * The server refused `nodeId` for `principal` (RT-50): clear a guessed binding, and\n\t * remember that an unbound node is not theirs.\n\t */\n\trecordLocalNodeRefusedFor?(nodeId: string, principal: string): Promise<void>\n\t/**\n\t * Assign a held, unbound node's writes to `principal` (RT-50, the app's decision).\n\t * @returns false when the node is not unbound, or was refused for `principal`\n\t */\n\tassignLocalNodePrincipal?(nodeId: string, principal: string): Promise<boolean>\n\t/** Forget a non-current local node whatever it holds (its held writes were discarded). */\n\tdropLocalNode?(nodeId: string): Promise<void>\n\t/** The current refusal cycle (count of accepted handshakes). */\n\tloadAcceptedCycle?(): Promise<number>\n\t/** Forget a non-current local node with nothing left to upload (bounds the registry). */\n\tforgetLocalNode?(nodeId: string): Promise<void>\n\t/**\n\t * Which adoptions of other local nodes are parked (RT-46), and an upload-progress\n\t * counter. Optional: without it the schedule lives for the engine's lifetime.\n\t */\n\tloadAdoptionSchedule?(): Promise<AdoptionScheduleInfo>\n\tsaveAdoptionSchedule?(schedule: AdoptionScheduleInfo): Promise<void>\n}\n\n/**\n * Upload scheduling across a database's local nodes (RT-46). A node whose adoption made\n * no progress is parked: it is retried once anything else uploaded (`progress` moved past\n * `progressMark`) or after its backoff (`untilMs`).\n */\nexport interface AdoptionScheduleInfo {\n\tprogress: number\n\tparked: Record<\n\t\tstring,\n\t\t{ progressMark: number; untilMs: number; count: number; parkedAtMs: number }\n\t>\n}\n\n/** A terminally rejected operation, as recorded in the durable markers (RT-36). */\nexport interface TerminalRejectionRecord {\n\toperationId: string\n\tnodeId: string | null\n\tsequenceNumber: number | null\n\tcode: string\n\trejectedAt: number\n}\n\n/** A node id this database authored operations under (RT-38, RT-40). */\nexport interface LocalNodeInfo {\n\tnodeId: string\n\t/** A handshake as this node was accepted at least once. */\n\taccepted: boolean\n\t/** Refused after acceptance: its unsynced writes belong to a principal not signed in. */\n\theld: boolean\n\t/** Refusal cycle in which the server last refused it, or null. */\n\trefusedCycle: number | null\n\t/**\n\t * The user the node's writes belong to (RT-42), or null when unknown. A node bound to\n\t * another user than the signed-in one is never uploaded or adopted.\n\t */\n\tprincipal?: string | null\n\t/**\n\t * How `principal` was learned (RT-50): `fresh` (bound before its first write),\n\t * `server` (an accepted handshake), `app` (assigned by the app: a guess the server can\n\t * overrule), or null (unbound, or a guess by an earlier release).\n\t */\n\tbinding?: 'fresh' | 'server' | 'app' | null\n\t/** Users the server refused this unbound node for (RT-50): never tried for them again. */\n\trefusedPrincipals?: string[]\n}\n\n/**\n * A delivered operation the client deliberately did not apply, kept durably so it is\n * never silently lost and can be replayed (on start, after a schema upgrade, or on demand).\n */\nexport interface QuarantinedOperation {\n\t/** The operation as delivered (still encrypted when decryption failed). */\n\toperation: Operation\n\t/** Delivery sequence of the batch that carried it, or null for a legacy batch. */\n\tdeliverySequence: number | null\n\t/** Machine-readable reason (for example `APPLY_SKIPPED`, `REMOTE_CLOCK_DRIFT`). */\n\tcode: string\n\t/** Human-readable explanation. */\n\tmessage: string\n\t/** Wall-clock time (ms) it was quarantined. Display only. */\n\tquarantinedAt: number\n}\n\n/**\n * Interface for persisting the outbound operation queue.\n * Operations must survive page refreshes and be sent when connection is re-established.\n */\nexport interface QueueStorage {\n\t/** Load all queued operations from persistent storage */\n\tload(): Promise<Operation[]>\n\t/** Persist an operation to the queue */\n\tenqueue(op: Operation): Promise<void>\n\t/** Remove acknowledged operations by their IDs */\n\tdequeue(ids: string[]): Promise<void>\n\t/** Return number of operations in storage */\n\tcount(): Promise<number>\n\t/**\n\t * Record that these queued operations were put on the wire at least once. A sent\n\t * operation may already be stored on the server (its ack can be lost), so it must\n\t * never be re-stamped by a clock rebase (W3 step 4). Optional: without it the flag\n\t * lives for the engine's lifetime only.\n\t */\n\tmarkSent?(ops: Operation[]): Promise<void>\n\t/** Ids of queued operations recorded by {@link markSent}. */\n\tloadSentIds?(): Promise<string[]>\n}\n\n/**\n * A record of one outbound operation the server permanently refused. Preserved so\n * a rejected op is explainable and reconcilable rather than silently lost.\n */\nexport interface RejectedOperation {\n\t/** Content-addressed id of the rejected operation. */\n\toperationId: string\n\t/** Collection the operation targeted. */\n\tcollection: string\n\t/** Record the operation targeted. */\n\trecordId: string\n\t/** Stable, machine-readable reason code from the server. */\n\tcode: string\n\t/** Human-readable explanation. */\n\tmessage: string\n\t/** Whether resubmitting the identical operation may later succeed. */\n\tretriable: boolean\n\t/** Wall-clock time (ms since epoch) the rejection was recorded. Display only. */\n\trejectedAt: number\n}\n\n/**\n * Interface for durably persisting operations the server rejected, so the record\n * survives a page refresh. Keyed by operation id (idempotent on re-rejection).\n */\nexport interface RejectedOperationStorage {\n\t/** Record (or overwrite) a rejected operation. */\n\trecord(rejected: RejectedOperation): Promise<void>\n\t/** List all recorded rejected operations. */\n\tlist(): Promise<RejectedOperation[]>\n\t/** Remove rejected operations by their operation ids (after the app reconciles them). */\n\tremove(operationIds: string[]): Promise<void>\n}\n\n/**\n * In-memory {@link RejectedOperationStorage}. The default when no durable store is\n * provided; suitable for tests and ephemeral sessions.\n */\nexport class MemoryRejectedOperationStorage implements RejectedOperationStorage {\n\tprivate readonly rejected = new Map<string, RejectedOperation>()\n\n\tasync record(rejected: RejectedOperation): Promise<void> {\n\t\tthis.rejected.set(rejected.operationId, rejected)\n\t}\n\n\tasync list(): Promise<RejectedOperation[]> {\n\t\treturn [...this.rejected.values()]\n\t}\n\n\tasync remove(operationIds: string[]): Promise<void> {\n\t\tfor (const id of operationIds) {\n\t\t\tthis.rejected.delete(id)\n\t\t}\n\t}\n}\n\n/**\n * Thrown when an operation violates sync scope constraints.\n * This can happen when:\n * - A client tries to push an operation outside its configured scope\n * - The server rejects an operation because it falls outside the client's scope\n */\nexport class ScopeViolationError extends KoraError {\n\tconstructor(\n\t\tpublic readonly operationId: string,\n\t\tpublic readonly collection: string,\n\t\tpublic readonly scope: Record<string, unknown>,\n\t\tmessage?: string,\n\t) {\n\t\tsuper(\n\t\t\tmessage ?? `Operation \"${operationId}\" in collection \"${collection}\" violates sync scope`,\n\t\t\t'SCOPE_VIOLATION',\n\t\t\t{ operationId, collection, scope },\n\t\t)\n\t\tthis.name = 'ScopeViolationError'\n\t}\n}\n\n/**\n * Thrown when a sync scope configuration is invalid.\n */\nexport class InvalidScopeError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'INVALID_SCOPE', context)\n\t\tthis.name = 'InvalidScopeError'\n\t}\n}\n","import type { Operation } from '@korajs/core'\n\n/** Options for {@link buildScopeSnapshot}. */\nexport interface ScopeSnapshotOptions {\n\t/**\n\t * Layer the writer's `previousData` between the stored record and `op.data`.\n\t * Off by default: `previousData` is supplied by the writer and is never checked\n\t * against the stored row, so a server visibility decision built on it lets one\n\t * tenant steer its operations into another tenant's log (RT-3). Opt in only\n\t * where the writer is trusted for the decision, such as a client filtering its\n\t * own local view.\n\t * @default false\n\t */\n\tincludePreviousData?: boolean\n}\n\n/**\n * Build the record snapshot a scope or query-subset predicate is evaluated against\n * for an operation.\n *\n * Layering is `fullRecord < data` (or `fullRecord < previousData < data` with\n * `includePreviousData`), so the snapshot approximates the record after the\n * operation. The record identity is NOT taken from any of those\n * layers: `id` is always `op.recordId`, assigned last. An operation (or a stale\n * stored row) can therefore never claim to be a different record than the one it\n * actually targets, which is what made `previousData: { id: '<allowed>' }` a write\n * bypass under id-scoped predicates.\n *\n * This is the single snapshot builder shared by the client scope filter, the client\n * query-subset filter and the server scope filter. It is used for visibility\n * decisions only; upload authorization never reads `previousData` (see the server's\n * `authorizeUplinkWrite`).\n *\n * @param op - The operation being judged\n * @param fullRecord - Optional stored record state used to fill fields the op omits\n * @param options - Whether to trust the writer's `previousData` (default: no)\n * @returns The snapshot, always carrying `id === op.recordId`\n */\nexport function buildScopeSnapshot(\n\top: Operation,\n\tfullRecord?: Record<string, unknown> | null,\n\toptions: ScopeSnapshotOptions = {},\n): Record<string, unknown> {\n\treturn {\n\t\t...(fullRecord ?? {}),\n\t\t...(options.includePreviousData ? (asPlainRecord(op.previousData) ?? {}) : {}),\n\t\t...(asPlainRecord(op.data) ?? {}),\n\t\tid: op.recordId,\n\t}\n}\n\n/**\n * True when `actual` satisfies a scope predicate value: either an exact value\n * (compared with `Object.is`) or a bounded `{ $in: [...] }` set. An `undefined` or\n * `null` predicate value (or `$in` member) never matches.\n *\n * @param actual - The record's field value\n * @param expected - The scope predicate for that field\n * @returns Whether the value is inside the predicate\n */\nexport function matchesScopePredicate(actual: unknown, expected: unknown): boolean {\n\t// Fail closed (RT-8): an undefined/null predicate value would otherwise match\n\t// every record that lacks the field. The server refuses such grants outright;\n\t// this keeps any other caller from widening by accident.\n\tif (expected === undefined || expected === null) return false\n\tif (typeof expected === 'object' && !Array.isArray(expected) && '$in' in expected) {\n\t\tconst values = (expected as { $in?: unknown }).$in\n\t\tif (!Array.isArray(values)) return false\n\t\tconst set = membershipSet(values)\n\t\tif (set !== null && !needsObjectIsScan(actual)) {\n\t\t\treturn actual !== undefined && actual !== null && set.has(actual)\n\t\t}\n\t\treturn values.some((value) => value !== undefined && value !== null && Object.is(actual, value))\n\t}\n\treturn Object.is(actual, expected)\n}\n\n/** `$in` lists at least this long are looked up through a cached set (F17). */\nconst MEMBERSHIP_SET_MIN_VALUES = 32\nconst membershipSets = new WeakMap<readonly unknown[], Set<unknown>>()\n\n/**\n * A set of a large `$in` list, so a membership test costs O(1) instead of a scan of\n * every value (a grant can hold thousands, and every delivered operation is tested).\n * Only for frozen lists, which the server's scope normalization produces: a list that\n * can still change would make a cached set stale.\n */\nfunction membershipSet(values: readonly unknown[]): Set<unknown> | null {\n\tif (values.length < MEMBERSHIP_SET_MIN_VALUES || !Object.isFrozen(values)) return null\n\tlet set = membershipSets.get(values)\n\tif (!set) {\n\t\tset = new Set(values)\n\t\tmembershipSets.set(values, set)\n\t}\n\treturn set\n}\n\n/**\n * A set compares with SameValueZero, `Object.is` does not: `0` and `-0` differ under\n * `Object.is`. Such a value is checked by the exact scan instead.\n */\nfunction needsObjectIsScan(actual: unknown): boolean {\n\treturn typeof actual === 'number' && actual === 0\n}\n\n/**\n * True when every field predicate in a collection scope matches the record.\n *\n * @param record - The record (or snapshot) to test\n * @param collectionScope - Field predicates for the record's collection\n * @returns Whether the record is inside the scope\n */\nexport function recordMatchesScopePredicates(\n\trecord: Record<string, unknown>,\n\tcollectionScope: Record<string, unknown>,\n): boolean {\n\tfor (const [field, expected] of Object.entries(collectionScope)) {\n\t\tif (!matchesScopePredicate(record[field], expected)) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunction asPlainRecord(value: unknown): Record<string, unknown> | null {\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) {\n\t\treturn null\n\t}\n\treturn value as Record<string, unknown>\n}\n","import type { Operation } from '@korajs/core'\nimport type { SyncScopeMap } from '../types'\nimport {\n\ttype ScopeSnapshotOptions,\n\tbuildScopeSnapshot,\n\trecordMatchesScopePredicates,\n} from './scope-snapshot'\n\n/**\n * Check whether an operation matches the given scope map.\n *\n * Rules:\n * - No scope map configured: operation is always in scope.\n * - Collection not present in scope map: operation is out of scope.\n * - Empty scope for a collection `{}`: no field restrictions, operation is in scope.\n * - Scope has field/value pairs: all must match in the operation's data snapshot.\n *\n * The snapshot is the operation's `data` (layered over `previousData` only with\n * `options.includePreviousData`), with `id` always taken from `op.recordId`,\n * which represents the record's state after the operation is applied. When an optional\n * `fullRecord` is provided (e.g., from the local store), its values fill in scope fields\n * that weren't included in the operation's data (critical for update operations where\n * `data` only contains changed fields).\n *\n * @param op - The operation to check\n * @param scopeMap - Per-collection scope filters, or undefined for no filtering\n * @param fullRecord - Optional full record state from the store, used to fill in scope\n * fields not present in the operation's partial data\n * @param options - Snapshot options; `includePreviousData` trusts the writer's\n * `previousData`, which is only safe for a client judging its own local view\n * @returns true if the operation is within scope\n */\nexport function operationMatchesScope(\n\top: Operation,\n\tscopeMap: SyncScopeMap | undefined,\n\tfullRecord?: Record<string, unknown> | null,\n\toptions: ScopeSnapshotOptions = {},\n): boolean {\n\tif (!scopeMap) return true\n\n\tconst collectionScope = scopeMap[op.collection]\n\t// Collection not present in scope map means it's out of scope\n\tif (!collectionScope) return false\n\n\t// Empty scope means no field restrictions\n\tif (Object.keys(collectionScope).length === 0) return true\n\n\t// The record identity is always op.recordId (assigned last by the shared\n\t// snapshot), so an op cannot claim an in-scope `id` through data/previousData.\n\treturn recordMatchesScopePredicates(buildScopeSnapshot(op, fullRecord, options), collectionScope)\n}\n\n/**\n * Filter operations to only those matching the given scope map.\n *\n * @param operations - Array of operations to filter\n * @param scopeMap - Per-collection scope filters\n * @returns Operations that match the scope\n */\nexport function filterOperationsByScope(\n\toperations: Operation[],\n\tscopeMap: SyncScopeMap | undefined,\n): Operation[] {\n\tif (!scopeMap) return operations\n\treturn operations.filter((op) => operationMatchesScope(op, scopeMap))\n}\n","import type { Operation } from '@korajs/core'\nimport { type ScopeSnapshotOptions, buildScopeSnapshot } from './scope-snapshot'\n\n/**\n * A live query filter that narrows which operations sync for a collection.\n */\nexport interface SyncQuerySubset {\n\tcollection: string\n\twhere: Record<string, unknown>\n}\n\nfunction recordMatchesWhere(\n\tsnapshot: Record<string, unknown>,\n\twhere: Record<string, unknown>,\n): boolean {\n\tfor (const [field, expected] of Object.entries(where)) {\n\t\tif (snapshot[field] !== expected) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n/**\n * Returns true when an operation matches at least one active query subset\n * for its collection. Collections without query subsets pass through.\n *\n * The writer's `previousData` is ignored unless `options.includePreviousData` is\n * set (see {@link ScopeSnapshotOptions}); the server never sets it.\n */\nexport function operationMatchesQuerySubsets(\n\top: Operation,\n\tsubsets: SyncQuerySubset[] | undefined,\n\tfullRecord?: Record<string, unknown> | null,\n\toptions: ScopeSnapshotOptions = {},\n): boolean {\n\tif (!subsets || subsets.length === 0) {\n\t\treturn true\n\t}\n\n\tconst collectionSubsets = subsets.filter((subset) => subset.collection === op.collection)\n\tif (collectionSubsets.length === 0) {\n\t\treturn true\n\t}\n\t// An empty predicate is collection-wide. It must also admit fieldless deletes,\n\t// which intentionally have no data snapshot to evaluate.\n\tif (collectionSubsets.some((subset) => Object.keys(subset.where).length === 0)) {\n\t\treturn true\n\t}\n\n\t// Shared snapshot: identity always comes from op.recordId, never from op fields.\n\tconst snapshot = buildScopeSnapshot(op, fullRecord, options)\n\treturn collectionSubsets.some((subset) => recordMatchesWhere(snapshot, subset.where))\n}\n\nfunction stableValue(value: unknown): unknown {\n\tif (Array.isArray(value)) return value.map(stableValue)\n\tif (value && typeof value === 'object') {\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(value as Record<string, unknown>)\n\t\t\t\t.filter(([, entry]) => entry !== undefined)\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([key, entry]) => [key, stableValue(entry)]),\n\t\t)\n\t}\n\treturn value\n}\n\nfunction whereContains(broad: Record<string, unknown>, narrow: Record<string, unknown>): boolean {\n\treturn Object.entries(broad).every(\n\t\t([field, expected]) =>\n\t\t\tJSON.stringify(stableValue(narrow[field])) === JSON.stringify(stableValue(expected)),\n\t)\n}\n\n/** Canonicalize equality subsets and remove subsets already covered by a broader one. */\nexport function dedupeQuerySubsets(subsets: SyncQuerySubset[]): SyncQuerySubset[] {\n\tconst normalized = subsets.map((subset) => ({\n\t\tcollection: subset.collection,\n\t\twhere: stableValue(subset.where) as Record<string, unknown>,\n\t}))\n\tconst result: SyncQuerySubset[] = []\n\tfor (const candidate of normalized) {\n\t\tif (\n\t\t\tnormalized.some(\n\t\t\t\t(other) =>\n\t\t\t\t\tother !== candidate &&\n\t\t\t\t\tother.collection === candidate.collection &&\n\t\t\t\t\twhereContains(other.where, candidate.where) &&\n\t\t\t\t\t(Object.keys(other.where).length < Object.keys(candidate.where).length ||\n\t\t\t\t\t\tJSON.stringify(other.where) === JSON.stringify(candidate.where)),\n\t\t\t)\n\t\t) {\n\t\t\tconst duplicateAlreadyKept = result.some(\n\t\t\t\t(item) =>\n\t\t\t\t\titem.collection === candidate.collection &&\n\t\t\t\t\tJSON.stringify(item.where) === JSON.stringify(candidate.where),\n\t\t\t)\n\t\t\tif (\n\t\t\t\tduplicateAlreadyKept ||\n\t\t\t\tnormalized.some(\n\t\t\t\t\t(other) =>\n\t\t\t\t\t\tother !== candidate &&\n\t\t\t\t\t\tother.collection === candidate.collection &&\n\t\t\t\t\t\tObject.keys(other.where).length < Object.keys(candidate.where).length &&\n\t\t\t\t\t\twhereContains(other.where, candidate.where),\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tcontinue\n\t\t}\n\t\tif (\n\t\t\t!result.some(\n\t\t\t\t(item) =>\n\t\t\t\t\titem.collection === candidate.collection &&\n\t\t\t\t\tJSON.stringify(item.where) === JSON.stringify(candidate.where),\n\t\t\t)\n\t\t)\n\t\t\tresult.push(candidate)\n\t}\n\treturn result.sort((a, b) =>\n\t\t`${a.collection}:${JSON.stringify(a.where)}`.localeCompare(\n\t\t\t`${b.collection}:${JSON.stringify(b.where)}`,\n\t\t),\n\t)\n}\n\n/** True when every record selected by `narrow` is also selected by `broad`. */\nexport function querySubsetContains(broad: SyncQuerySubset[], narrow: SyncQuerySubset[]): boolean {\n\tconst collections = new Set([\n\t\t...broad.map((subset) => subset.collection),\n\t\t...narrow.map((subset) => subset.collection),\n\t])\n\tfor (const collection of collections) {\n\t\tconst broadForCollection = broad.filter((subset) => subset.collection === collection)\n\t\tconst narrowForCollection = narrow.filter((subset) => subset.collection === collection)\n\t\t// No predicate for a collection means that collection is unrestricted.\n\t\tif (narrowForCollection.length === 0) {\n\t\t\tif (broadForCollection.length > 0) return false\n\t\t\tcontinue\n\t\t}\n\t\tif (broadForCollection.length === 0) continue\n\t\tif (\n\t\t\t!narrowForCollection.every((candidate) =>\n\t\t\t\tbroadForCollection.some((other) => whereContains(other.where, candidate.where)),\n\t\t\t)\n\t\t)\n\t\t\treturn false\n\t}\n\treturn true\n}\n","import type { DeltaCursor } from '../types'\n\nexport type { DeltaCursor }\n\nfunction bytesToBase64Url(bytes: Uint8Array): string {\n\tlet binary = ''\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte)\n\t}\n\treturn btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction base64UrlToBytes(value: string): Uint8Array {\n\tconst base64 = value.replace(/-/g, '+').replace(/_/g, '/')\n\tconst padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4)\n\tconst binary = atob(padded)\n\tconst bytes = new Uint8Array(binary.length)\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tbytes[i] = binary.charCodeAt(i)\n\t}\n\treturn bytes\n}\n\n/**\n * Encode a delta cursor for wire transfer or `_kora_meta` persistence.\n */\nexport function encodeDeltaCursor(cursor: DeltaCursor): string {\n\tconst json = JSON.stringify(cursor)\n\treturn bytesToBase64Url(new TextEncoder().encode(json))\n}\n\n/**\n * Decode a delta cursor string. Returns null when malformed.\n */\nexport function decodeDeltaCursor(value: string | undefined | null): DeltaCursor | null {\n\tif (!value) {\n\t\treturn null\n\t}\n\n\ttry {\n\t\tconst json = new TextDecoder().decode(base64UrlToBytes(value))\n\t\tconst parsed: unknown = JSON.parse(json)\n\t\tif (\n\t\t\ttypeof parsed !== 'object' ||\n\t\t\tparsed === null ||\n\t\t\ttypeof (parsed as DeltaCursor).lastOperationId !== 'string' ||\n\t\t\ttypeof (parsed as DeltaCursor).batchIndex !== 'number'\n\t\t) {\n\t\t\treturn null\n\t\t}\n\t\treturn parsed as DeltaCursor\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Slice a causally sorted operation list to resume after a cursor.\n */\nexport function sliceOperationsAfterCursor(\n\toperations: import('@korajs/core').Operation[],\n\tcursor: DeltaCursor | null,\n): import('@korajs/core').Operation[] {\n\tif (!cursor) {\n\t\treturn operations\n\t}\n\n\tconst index = operations.findIndex((op) => op.id === cursor.lastOperationId)\n\tif (index === -1) {\n\t\treturn operations\n\t}\n\n\treturn operations.slice(index + 1)\n}\n\n/**\n * Build a cursor from the last operation in a delta batch.\n */\nexport function createDeltaCursorFromBatch(\n\toperations: import('@korajs/core').Operation[],\n\tbatchIndex: number,\n): DeltaCursor | null {\n\tconst last = operations[operations.length - 1]\n\tif (!last) {\n\t\treturn null\n\t}\n\n\treturn {\n\t\tlastOperationId: last.id,\n\t\tbatchIndex,\n\t}\n}\n","/** Prefix for handshake rejection when client schema version is unsupported. */\nexport const SCHEMA_MISMATCH_PREFIX = 'SCHEMA_MISMATCH'\n\n/**\n * Returns true when a handshake `rejectReason` indicates unsupported schema version.\n */\nexport function isSchemaMismatchReject(rejectReason: string | undefined): boolean {\n\treturn rejectReason?.startsWith(SCHEMA_MISMATCH_PREFIX) === true\n}\n\n/**\n * Returns true when the client schema version is within the inclusive supported range.\n */\nexport function isClientSchemaVersionSupported(\n\tclientVersion: number,\n\tsupported: { min: number; max: number },\n): boolean {\n\treturn clientVersion >= supported.min && clientVersion <= supported.max\n}\n","/**\n * Sync protocol version this build speaks (decision D2: one bump carrying all wire\n * breaks). Version 2 adds:\n * - operation ids hashed with content-hash version 2 (CORE-1), with `hashVersion`\n * carried on the wire and verified on receive;\n * - the end-to-end encryption envelope v2 (`op.encrypted`, AES-GCM AAD, ENC-3 and\n * NEW-ENC-1);\n * - the sequence-reservation capability on every handshake (RT-37);\n * - `authoritativeNodeIds` and server-authored `foldState` (W7).\n *\n * A handshake without `protocolVersion` is version 1 (Kora <= beta.12). Servers accept\n * version-1 clients for one release (beta.13) with a deprecation warning.\n */\nexport const SYNC_PROTOCOL_VERSION = 2\n\n/** Protocol version assumed when a handshake or response does not declare one. */\nexport const LEGACY_SYNC_PROTOCOL_VERSION = 1\n\n/** Code of the server's deprecation warning for a version-1 client. */\nexport const PROTOCOL_V1_DEPRECATED = 'PROTOCOL_V1_DEPRECATED'\n\n/** Non-retriable rejection code for an operation whose id is not its content hash. */\nexport const INVALID_OPERATION_ID = 'INVALID_OPERATION_ID'\n\n/**\n * Non-retriable rejection code for a plaintext operation uploaded to a server that\n * requires the encryption envelope.\n */\nexport const PLAINTEXT_REJECTED = 'PLAINTEXT_REJECTED'\n\n/**\n * The protocol version a handshake (or handshake response) declares.\n *\n * @param declared - The message's `protocolVersion`\n * @returns The declared version, or 1 when absent or malformed\n */\nexport function declaredProtocolVersion(declared: unknown): number {\n\treturn typeof declared === 'number' && Number.isInteger(declared) && declared >= 1\n\t\t? declared\n\t\t: LEGACY_SYNC_PROTOCOL_VERSION\n}\n","import type { WrappedKeyRecord } from './key-record'\n\n/**\n * Key-distribution side channel of the sync protocol (ENC-1, D4b). Accepted only after\n * a successful handshake. The server answers for the session's own principal: a\n * message never names a user, so no session can read or write another user's record.\n * JSON on every wire format (the records are small and rare).\n */\n\n/** Client -> server: fetch the session principal's record of one keyring. */\nexport interface EncryptionKeyRequestMessage {\n\ttype: 'encryption-key-request'\n\tmessageId: string\n\t/** Correlates the response. */\n\trequestId: string\n\tkeyring: string\n}\n\n/** Client -> server: write the record with compare-and-set on its revision. */\nexport interface EncryptionKeyPutMessage {\n\ttype: 'encryption-key-put'\n\tmessageId: string\n\trequestId: string\n\tkeyring: string\n\t/** The full new record; its revision must be `expectedRevision + 1`. */\n\trecord: WrappedKeyRecord\n\t/** The revision being replaced (0: create). */\n\texpectedRevision: number\n}\n\n/** Status of a key-service response. */\nexport type EncryptionKeyResponseStatus =\n\t| 'ok'\n\t| 'conflict'\n\t| 'forbidden'\n\t| 'invalid'\n\t| 'unsupported'\n\t| 'throttled'\n\n/**\n * Server -> client: the answer to a request or put (with `requestId`), or an\n * unsolicited push of a record another device of the same principal wrote (without).\n */\nexport interface EncryptionKeyResponseMessage {\n\ttype: 'encryption-key-response'\n\tmessageId: string\n\trequestId?: string\n\tkeyring: string\n\tstatus: EncryptionKeyResponseStatus\n\t/** The current record (null when none exists, or on refusal). */\n\trecord: WrappedKeyRecord | null\n\t/** Human-readable reason for a refusal. */\n\tmessage?: string\n\t/**\n\t * With `record: null`: key ids the server's stored operations of this owner are\n\t * encrypted with (a sample, RT-104). Non-empty means encrypted history exists, so\n\t * the record was lost: a new device waits for one that holds the ring.\n\t */\n\tknownKeyIds?: string[]\n}\n\n/** Every key-distribution message. */\nexport type EncryptionKeyMessage =\n\t| EncryptionKeyRequestMessage\n\t| EncryptionKeyPutMessage\n\t| EncryptionKeyResponseMessage\n\nconst STATUSES: ReadonlySet<string> = new Set([\n\t'ok',\n\t'conflict',\n\t'forbidden',\n\t'invalid',\n\t'unsupported',\n\t'throttled',\n])\n\n/** Whether a message type belongs to the key-distribution channel. */\nexport function isEncryptionKeyMessageType(type: unknown): boolean {\n\treturn (\n\t\ttype === 'encryption-key-request' ||\n\t\ttype === 'encryption-key-put' ||\n\t\ttype === 'encryption-key-response'\n\t)\n}\n\n/**\n * Structural guard of the key-distribution messages (records are validated in full\n * by the receiver).\n */\nexport function isEncryptionKeyMessage(value: unknown): value is EncryptionKeyMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\tif (typeof msg.messageId !== 'string' || typeof msg.keyring !== 'string') return false\n\tswitch (msg.type) {\n\t\tcase 'encryption-key-request':\n\t\t\treturn typeof msg.requestId === 'string'\n\t\tcase 'encryption-key-put':\n\t\t\treturn (\n\t\t\t\ttypeof msg.requestId === 'string' &&\n\t\t\t\ttypeof msg.record === 'object' &&\n\t\t\t\tmsg.record !== null &&\n\t\t\t\ttypeof msg.expectedRevision === 'number'\n\t\t\t)\n\t\tcase 'encryption-key-response':\n\t\t\treturn (\n\t\t\t\t(msg.requestId === undefined || typeof msg.requestId === 'string') &&\n\t\t\t\ttypeof msg.status === 'string' &&\n\t\t\t\tSTATUSES.has(msg.status) &&\n\t\t\t\t(msg.knownKeyIds === undefined ||\n\t\t\t\t\t(Array.isArray(msg.knownKeyIds) &&\n\t\t\t\t\t\tmsg.knownKeyIds.every((keyId) => typeof keyId === 'string'))) &&\n\t\t\t\t(msg.record === null || (typeof msg.record === 'object' && msg.record !== undefined))\n\t\t\t)\n\t\tdefault:\n\t\t\treturn false\n\t}\n}\n","import type {\n\tAtomicOp,\n\tEncryptedOperationEnvelope,\n\tHLCTimestamp,\n\tOperationType,\n} from '@korajs/core'\nimport type { EncryptionKeyMessage } from '../encryption/key-messages'\nimport { isEncryptionKeyMessage } from '../encryption/key-messages'\nimport type { SyncQuerySubset } from '../scopes/query-subset'\n\nexport type WireFormat = 'json' | 'protobuf'\n\n/**\n * Wire-format operation. Plain object (no Map) for JSON serialization.\n * Maps 1:1 with Operation, but uses Record instead of Map for version vectors.\n */\nexport interface SerializedOperation {\n\tid: string\n\tnodeId: string\n\ttype: OperationType\n\tcollection: string\n\trecordId: string\n\tdata: Record<string, unknown> | null\n\tpreviousData: Record<string, unknown> | null\n\ttimestamp: HLCTimestamp\n\tsequenceNumber: number\n\tcausalDeps: string[]\n\tschemaVersion: number\n\t/** Atomic operation intents, present only when atomic ops were used. */\n\tatomicOps?: Record<string, AtomicOp>\n\t/** Groups this operation with others in an atomic transaction. */\n\ttransactionId?: string\n\t/** Human-readable name for the mutation group. For DevTools display. */\n\tmutationName?: string\n\t/** Per-field versions of a server scope-entry insert (RT-27). Server-authored only. */\n\tfieldVersions?: Record<string, HLCTimestamp>\n\t/**\n\t * Content-hash version of `id` (CORE-1, protocol v2). Absent means 1. Protobuf\n\t * operation field 14.\n\t */\n\thashVersion?: 1 | 2\n\t/**\n\t * Serialized fold state of a server scope-entry operation (W7). Server-authored\n\t * only: the server strips it from device uploads. Protobuf operation field 15.\n\t */\n\tfoldState?: string\n\t/**\n\t * End-to-end encryption envelope v2 (W9). `data` is then null or holds only the\n\t * documented cleartext scope fields. Protobuf operation field 16 (JSON).\n\t */\n\tencrypted?: EncryptedOperationEnvelope\n}\n\n/**\n * Handshake message sent by client to initiate sync.\n */\nexport interface HandshakeMessage {\n\ttype: 'handshake'\n\tmessageId: string\n\tnodeId: string\n\t/** Version vector as plain object (nodeId -> sequence number) */\n\tversionVector: Record<string, number>\n\tschemaVersion: number\n\tauthToken?: string\n\tsupportedWireFormats?: WireFormat[]\n\t/** Per-collection sync scope filters. Limits which records are synced to this client. */\n\tsyncScope?: Record<string, Record<string, unknown>>\n\t/** Base64url-encoded delta cursor for resuming paginated initial sync */\n\tdeltaCursor?: string\n\t/** Live query filters that further narrow synced data within auth/schema scope */\n\tsyncQueries?: SyncQuerySubset[]\n\t/**\n\t * The client's persisted delivery watermark: the highest server delivery sequence\n\t * up to which it has contiguously applied every in-scope operation. Omitted (or 0)\n\t * on first sync. When present, the server resumes the gap-free server->client\n\t * stream from just after it, superseding the version-vector delta and the resume\n\t * cursor for the server->client direction. Optional, so old servers ignore it.\n\t */\n\tlastDeliverySequence?: number\n\t/**\n\t * SYNC-11: canonical key (`scopeViewKey`) of the downlink scope the server accepted at\n\t * this client's last handshake, when it differs from `syncScope`. A server that\n\t * resolves a scope with the same key resumes the delivery stream from\n\t * `acceptedScopeWatermark` instead of restarting it from 0. Optional; old servers\n\t * ignore it.\n\t */\n\tacceptedScopeKey?: string\n\t/** The client's delivery watermark for the view named by `acceptedScopeKey`. */\n\tacceptedScopeWatermark?: number\n\t/** Opt in to client-local removal when records leave the accepted downlink view. */\n\tscopeExitPolicy?: 'retain' | 'retract'\n\t/**\n\t * The per-device secret the server issued at this node id's first claim\n\t * (`HandshakeResponseMessage.nodeToken`). An anonymous principal must present it\n\t * to reconnect with its node id, so another anonymous client cannot take the node\n\t * over (RT-12). Stored by the client next to its node id; never sent elsewhere.\n\t */\n\tnodeToken?: string\n\t/**\n\t * Set by a client transport that understands `heartbeat` messages (LMS #12). Only\n\t * then does the server send them, so an older client never receives a message type\n\t * it cannot decode. Old servers ignore it.\n\t */\n\tsupportsHeartbeat?: boolean\n\t/**\n\t * Capability (RT-37): this client reserves each operation's sequence number inside\n\t * the same local transaction that writes the operation, so it never puts two\n\t * different operations under one `(nodeId, sequenceNumber)`. Only for such a client\n\t * does the server refuse a second, different operation under a held sequence\n\t * (`SEQUENCE_CONFLICT`). A client that omits it (Kora <= beta.12, which could give\n\t * two concurrent transactions one number) is served as legacy: such a pair is\n\t * stored, both operations are delivered, and a warning is logged. Old servers\n\t * ignore it. Protobuf field 45.\n\t */\n\tsequenceReservation?: boolean\n\t/**\n\t * Sync protocol version the client speaks (`SYNC_PROTOCOL_VERSION`). Absent means 1\n\t * (Kora <= beta.12). A protocol-2 client sends hash-version-2 operation ids, the\n\t * encryption envelope v2 and always `sequenceReservation`. Protobuf field 47.\n\t */\n\tprotocolVersion?: number\n}\n\n/**\n * Application-level liveness message (LMS #12). A server sends it every\n * `HandshakeResponseMessage.heartbeatIntervalMs` to a client that set\n * `HandshakeMessage.supportsHeartbeat`. Browsers cannot see WebSocket ping frames, so\n * this is how a client notices a half-open connection: no inbound traffic for about\n * two intervals means the connection is dead. Carries no data; never acknowledged.\n */\nexport interface HeartbeatMessage {\n\ttype: 'heartbeat'\n\tmessageId: string\n}\n\n/**\n * Server response to a handshake.\n */\nexport interface HandshakeResponseMessage {\n\ttype: 'handshake-response'\n\tmessageId: string\n\tnodeId: string\n\tversionVector: Record<string, number>\n\tschemaVersion: number\n\taccepted: boolean\n\trejectReason?: string\n\t/** Inclusive minimum schema version the server accepts (present when rejected for schema mismatch). */\n\tsupportedSchemaMin?: number\n\t/** Inclusive maximum schema version the server accepts (present when rejected for schema mismatch). */\n\tsupportedSchemaMax?: number\n\tselectedWireFormat?: WireFormat\n\t/** The server-accepted per-collection sync scope. Confirms what data will be synced. */\n\tacceptedScope?: Record<string, Record<string, unknown>>\n\t/** Server-authoritative scope for records delivered to this client. */\n\tacceptedDownlinkScopes?: Record<string, Record<string, unknown>>\n\t/** Server-authoritative scope for operations uploaded by this client. */\n\tacceptedUplinkScopes?: Record<string, Record<string, unknown>>\n\t/** Server wall-clock time (ms since epoch) at response creation. Lets clients measure their own clock skew. */\n\tserverTime?: number\n\t/**\n\t * Whether the server persists blob bytes centrally. When true, a client\n\t * automatically uploads the bytes behind its `blob` fields so they remain\n\t * available to other devices even after the authoring device goes offline.\n\t */\n\tblobStorageEnabled?: boolean\n\t/**\n\t * The server's highest delivery sequence at handshake time. A client whose\n\t * persisted delivery watermark exceeds this (the server's operation log was rolled\n\t * back, for example by a backup restore) resets its watermark to 0 so it resyncs\n\t * from the beginning instead of sitting above a frontier that no longer exists.\n\t */\n\tserverMaxDeliverySequence?: number\n\t/**\n\t * A per-device secret issued when this handshake made the first claim of the node\n\t * id for an anonymous principal (RT-12). The client stores it next to its node id\n\t * and presents it in every later handshake (`HandshakeMessage.nodeToken`).\n\t *\n\t * The claim stays provisional until the client proves it saved the token, by\n\t * presenting it in a later handshake or by echoing it in an acknowledgment of this\n\t * response (`AcknowledgmentMessage.nodeToken`) right after persisting it (RT-21).\n\t */\n\tnodeToken?: string\n\t/**\n\t * Peer-relay servers (no central blob storage) set this to ask the client to push\n\t * the bytes behind its blob references anyway: the server verifies them against\n\t * their hash, records possession, and drops them (RT-23).\n\t */\n\tblobPossessionProof?: boolean\n\t/**\n\t * Interval of the server's `heartbeat` messages, in ms, present when the client set\n\t * `supportsHeartbeat` and the server sends them (LMS #12).\n\t */\n\theartbeatIntervalMs?: number\n\t/**\n\t * Sync protocol version the server speaks. Absent means 1 (a beta.12-era server).\n\t * Protobuf field 47.\n\t */\n\tprotocolVersion?: number\n\t/**\n\t * Explicit authoritative node ids (RT-75): legacy server node ids from before protocol\n\t * 2 and extras configured on the server store. Every `kora:server:` node id is\n\t * authoritative by its prefix and is not listed. Only operations of authoritative\n\t * nodes may carry server-authored metadata (`fieldVersions`, `foldState`); the client\n\t * keeps the union of every id it learns (`SyncStatePersistence.saveAuthoritativeNodeIds`).\n\t * Absent from older servers. Protobuf field 46 (repeated).\n\t */\n\tauthoritativeNodeIds?: string[]\n\t/**\n\t * Explicit authoritative node ids the deployment revoked (RT-81, server config\n\t * `revokedAuthoritativeNodeIds`). A client removes them from the authority union it\n\t * keeps, never learns them again, and re-folds the affected records; the server\n\t * never accepts them as device node ids. Absent when nothing was revoked, and from\n\t * older servers. Protobuf field 48 (repeated).\n\t */\n\trevokedAuthoritativeNodeIds?: string[]\n}\n\n/**\n * Batch of operations sent during delta exchange or streaming.\n */\nexport interface OperationBatchMessage {\n\ttype: 'operation-batch'\n\tmessageId: string\n\toperations: SerializedOperation[]\n\t/** Client-local authorization removals; these are never domain delete operations. */\n\tretractions?: ScopeRetraction[]\n\t/** True if this is the last batch in the delta exchange phase */\n\tisFinal: boolean\n\t/** Index of this batch (0-based) for ordering */\n\tbatchIndex: number\n\t/** Base64url-encoded cursor marking the last operation in this batch */\n\tcursor?: string\n\t/** Total batches in this delta exchange (for progress reporting) */\n\ttotalBatches?: number\n\t/**\n\t * Delivery-watermark chaining (server->client). `baseDeliverySequence` is the\n\t * delivery sequence the client must already hold for this batch to apply in order\n\t * (it equals the previous batch's `maxDeliverySequence`, or the client's reported\n\t * watermark for the first batch after a handshake). `maxDeliverySequence` is the\n\t * highest delivery sequence carried by this batch. The client applies the batch\n\t * only when its watermark equals `baseDeliverySequence`, then advances the\n\t * watermark to `maxDeliverySequence`. A dropped batch breaks the chain and stalls\n\t * the watermark, so the reconnect resend recovers it. Present only on batches the\n\t * server sends as part of the delivery stream; absent on legacy version-vector\n\t * batches.\n\t */\n\tbaseDeliverySequence?: number\n\tmaxDeliverySequence?: number\n}\n\nexport interface ScopeRetraction {\n\tcollection: string\n\trecordId: string\n}\n\n/**\n * Acknowledgment of a received message.\n */\nexport interface AcknowledgmentMessage {\n\ttype: 'acknowledgment'\n\tmessageId: string\n\tacknowledgedMessageId: string\n\tlastSequenceNumber: number\n\t/**\n\t * The acknowledged batch's `maxDeliverySequence`, echoed so the server can advance\n\t * this client's durable delivery watermark. Present only when acking a delivery-\n\t * stream batch. `lastSequenceNumber` stays for existing per-node behavior.\n\t */\n\tdeliverySequence?: number\n\t/**\n\t * Claim confirmation (RT-21): the node token from a handshake response, echoed\n\t * once the client has persisted it, so the server makes the provisional claim of\n\t * the node id permanent. Sent with `acknowledgedMessageId` set to the response id.\n\t */\n\tnodeToken?: string\n}\n\n/**\n * Error message from the server or client.\n */\nexport interface ErrorMessage {\n\ttype: 'error'\n\tmessageId: string\n\tcode: string\n\tmessage: string\n\tretriable: boolean\n\t/**\n\t * With `NODE_ID_CLAIMED` (beta.14 servers): `'other-principal'` when another user owns\n\t * the node id, `'unowned'` when it has history with no recorded owner (a node a beta.12\n\t * server accepted, or one an administrator released). Absent from older servers.\n\t */\n\tnodeOwnership?: 'other-principal' | 'unowned'\n}\n\n/**\n * Server-to-client rejection of ONE specific operation the client submitted.\n *\n * Unlike {@link ErrorMessage}, which is connection-level, this is tied to an\n * `operationId` so the client can divert exactly that operation out of its\n * pending outbound queue and into a durable rejected-operations store — kept and\n * explainable rather than silently retried forever or lost on the batch ack. The\n * operation never entered the server's authoritative log, so no other replica\n * ever sees it; the submitter is told so its optimistic local copy can be\n * reconciled (rolled back or resubmitted) by the app.\n */\nexport interface OperationRejectedMessage {\n\ttype: 'operation-rejected'\n\tmessageId: string\n\t/** Content-addressed id of the rejected operation. */\n\toperationId: string\n\t/** Collection the rejected operation targeted (for app routing / display). */\n\tcollection: string\n\t/** Record the rejected operation targeted. */\n\trecordId: string\n\t/** Stable, machine-readable reason code. */\n\tcode: string\n\t/** Human-readable explanation. */\n\tmessage: string\n\t/** Whether resubmitting the identical operation may later succeed. */\n\tretriable: boolean\n}\n\n/**\n * Awareness state for a single client (cursor position, user info).\n * Wire-format representation for JSON transport.\n * Ephemeral -- not persisted, only shared with connected peers.\n */\nexport interface AwarenessStateWire {\n\tuser: {\n\t\tname: string\n\t\tcolor: string\n\t\tavatar?: string\n\t}\n\tcursor?: {\n\t\tcollection: string\n\t\trecordId: string\n\t\tfield: string\n\t\tanchor: number\n\t\thead: number\n\t}\n}\n\n/**\n * Awareness update message. Carries ephemeral presence data (cursors, user info).\n * Processed separately from operation sync -- never persisted.\n */\nexport interface AwarenessUpdateMessage {\n\ttype: 'awareness-update'\n\tmessageId: string\n\t/** Client ID of the sender */\n\tclientId: number\n\t/** Map of clientId -> state (null means removal) */\n\tstates: Record<string, AwarenessStateWire | null>\n}\n\n/**\n * Incremental Yjs update for a richtext field. Ephemeral side channel for large documents.\n * JSON transport only; not persisted on the server operation log.\n */\nexport interface YjsDocUpdateMessage {\n\ttype: 'yjs-doc-update'\n\tmessageId: string\n\tcollection: string\n\trecordId: string\n\tfield: string\n\t/** Base64-encoded Yjs update binary */\n\tupdate: string\n}\n\n/**\n * Request for a single blob chunk by content hash. Ephemeral side channel for\n * out-of-band blob transfer over the sync connection; never persisted in the\n * operation log (durable state is the BlobRef inside a record's fields).\n *\n * Knowing a hash is not a capability: the server serves (from its central store)\n * or forwards a request only when a live record inside the requester's download\n * scope references the hash (as a blob, a manifest, or a chunk of one), forwards it\n * only to peers whose own scope references it, accepts the answer only from a peer\n * it asked and only when the bytes hash to the requested hash. Any other request\n * is answered `bytes: null`, exactly like an unknown hash.\n */\nexport interface BlobChunkRequestMessage {\n\ttype: 'blob-chunk-request'\n\tmessageId: string\n\t/** Correlates the response to this request. */\n\trequestId: string\n\t/** SHA-256 hash of the requested chunk. */\n\thash: string\n}\n\n/**\n * Response to a {@link BlobChunkRequestMessage}. Carries the chunk bytes, or\n * null when the responder does not hold that hash.\n */\nexport interface BlobChunkResponseMessage {\n\ttype: 'blob-chunk-response'\n\tmessageId: string\n\t/** Echoes the request's `requestId` for correlation. */\n\trequestId: string\n\t/** Base64-encoded chunk bytes, or null when the responder does not hold the hash. */\n\tbytes: string | null\n\t/**\n\t * True when the server refused the request for rate (RT-24), not because the\n\t * chunk is missing: retry after `retryAfterMs`. Absent (or false) otherwise.\n\t */\n\tthrottled?: boolean\n\t/** With `throttled`: milliseconds until the request may be retried. */\n\tretryAfterMs?: number\n}\n\n/**\n * Unsolicited upload of a blob chunk (or a blob manifest) from a client to the\n * server, so the server can persist it centrally and serve it to other devices\n * even after the authoring device disconnects. Content-addressed: the receiver\n * verifies the bytes hash to `hash`.\n */\nexport interface BlobChunkPushMessage {\n\ttype: 'blob-chunk-push'\n\tmessageId: string\n\t/** SHA-256 hash of the pushed bytes (a chunk hash or a manifest hash). */\n\thash: string\n\t/** Base64-encoded bytes. */\n\tbytes: string\n}\n\n/**\n * Union of all sync protocol messages.\n */\nexport type SyncMessage =\n\t| HandshakeMessage\n\t| HandshakeResponseMessage\n\t| OperationBatchMessage\n\t| AcknowledgmentMessage\n\t| ErrorMessage\n\t| OperationRejectedMessage\n\t| AwarenessUpdateMessage\n\t| YjsDocUpdateMessage\n\t| BlobChunkRequestMessage\n\t| BlobChunkResponseMessage\n\t| BlobChunkPushMessage\n\t| HeartbeatMessage\n\t| EncryptionKeyMessage\n\n// --- Type Guards ---\n\n/**\n * Check if an unknown value is a valid SyncMessage.\n */\nexport function isSyncMessage(value: unknown): value is SyncMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\tif (typeof msg.type !== 'string' || typeof msg.messageId !== 'string') return false\n\tswitch (msg.type) {\n\t\tcase 'handshake':\n\t\t\treturn isHandshakeMessage(value)\n\t\tcase 'handshake-response':\n\t\t\treturn isHandshakeResponseMessage(value)\n\t\tcase 'operation-batch':\n\t\t\treturn isOperationBatchMessage(value)\n\t\tcase 'acknowledgment':\n\t\t\treturn isAcknowledgmentMessage(value)\n\t\tcase 'error':\n\t\t\treturn isErrorMessage(value)\n\t\tcase 'operation-rejected':\n\t\t\treturn isOperationRejectedMessage(value)\n\t\tcase 'awareness-update':\n\t\t\treturn isAwarenessUpdateMessage(value)\n\t\tcase 'yjs-doc-update':\n\t\t\treturn isYjsDocUpdateMessage(value)\n\t\tcase 'blob-chunk-request':\n\t\t\treturn isBlobChunkRequestMessage(value)\n\t\tcase 'blob-chunk-response':\n\t\t\treturn isBlobChunkResponseMessage(value)\n\t\tcase 'blob-chunk-push':\n\t\t\treturn isBlobChunkPushMessage(value)\n\t\tcase 'heartbeat':\n\t\t\treturn true\n\t\tcase 'encryption-key-request':\n\t\tcase 'encryption-key-put':\n\t\tcase 'encryption-key-response':\n\t\t\treturn isEncryptionKeyMessage(value)\n\t\tdefault:\n\t\t\treturn false\n\t}\n}\n\n/**\n * Check if a value is a HandshakeMessage.\n */\nexport function isHandshakeMessage(value: unknown): value is HandshakeMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'handshake' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.nodeId === 'string' &&\n\t\ttypeof msg.versionVector === 'object' &&\n\t\tmsg.versionVector !== null &&\n\t\t!Array.isArray(msg.versionVector) &&\n\t\ttypeof msg.schemaVersion === 'number'\n\t)\n}\n\n/**\n * Check if a value is a HandshakeResponseMessage.\n */\nexport function isHandshakeResponseMessage(value: unknown): value is HandshakeResponseMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'handshake-response' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.nodeId === 'string' &&\n\t\ttypeof msg.versionVector === 'object' &&\n\t\tmsg.versionVector !== null &&\n\t\t!Array.isArray(msg.versionVector) &&\n\t\ttypeof msg.schemaVersion === 'number' &&\n\t\ttypeof msg.accepted === 'boolean'\n\t)\n}\n\n/**\n * Check if a value is an OperationBatchMessage.\n */\nexport function isOperationBatchMessage(value: unknown): value is OperationBatchMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'operation-batch' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\tArray.isArray(msg.operations) &&\n\t\ttypeof msg.isFinal === 'boolean' &&\n\t\ttypeof msg.batchIndex === 'number'\n\t)\n}\n\n/**\n * Check if a value is an AcknowledgmentMessage.\n */\nexport function isAcknowledgmentMessage(value: unknown): value is AcknowledgmentMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'acknowledgment' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.acknowledgedMessageId === 'string' &&\n\t\ttypeof msg.lastSequenceNumber === 'number'\n\t)\n}\n\n/**\n * Check if a value is an ErrorMessage.\n */\nexport function isErrorMessage(value: unknown): value is ErrorMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'error' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.code === 'string' &&\n\t\ttypeof msg.message === 'string' &&\n\t\ttypeof msg.retriable === 'boolean'\n\t)\n}\n\n/**\n * Check if a value is an OperationRejectedMessage.\n */\nexport function isOperationRejectedMessage(value: unknown): value is OperationRejectedMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'operation-rejected' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.operationId === 'string' &&\n\t\ttypeof msg.collection === 'string' &&\n\t\ttypeof msg.recordId === 'string' &&\n\t\ttypeof msg.code === 'string' &&\n\t\ttypeof msg.message === 'string' &&\n\t\ttypeof msg.retriable === 'boolean'\n\t)\n}\n\n/**\n * Check if a value is an AwarenessUpdateMessage.\n */\nexport function isAwarenessUpdateMessage(value: unknown): value is AwarenessUpdateMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'awareness-update' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.clientId === 'number' &&\n\t\ttypeof msg.states === 'object' &&\n\t\tmsg.states !== null &&\n\t\t!Array.isArray(msg.states)\n\t)\n}\n\n/**\n * Check if a value is a YjsDocUpdateMessage.\n */\nexport function isYjsDocUpdateMessage(value: unknown): value is YjsDocUpdateMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'yjs-doc-update' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.collection === 'string' &&\n\t\ttypeof msg.recordId === 'string' &&\n\t\ttypeof msg.field === 'string' &&\n\t\ttypeof msg.update === 'string'\n\t)\n}\n\n/**\n * Check if a value is a BlobChunkRequestMessage.\n */\nexport function isBlobChunkRequestMessage(value: unknown): value is BlobChunkRequestMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'blob-chunk-request' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.requestId === 'string' &&\n\t\ttypeof msg.hash === 'string'\n\t)\n}\n\n/**\n * Check if a value is a BlobChunkResponseMessage.\n */\nexport function isBlobChunkResponseMessage(value: unknown): value is BlobChunkResponseMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'blob-chunk-response' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.requestId === 'string' &&\n\t\t(typeof msg.bytes === 'string' || msg.bytes === null)\n\t)\n}\n\n/**\n * Check if a value is a BlobChunkPushMessage.\n */\nexport function isBlobChunkPushMessage(value: unknown): value is BlobChunkPushMessage {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst msg = value as Record<string, unknown>\n\treturn (\n\t\tmsg.type === 'blob-chunk-push' &&\n\t\ttypeof msg.messageId === 'string' &&\n\t\ttypeof msg.hash === 'string' &&\n\t\ttypeof msg.bytes === 'string'\n\t)\n}\n","import { SyncError } from '@korajs/core'\nimport type {\n\tEncryptedOperationEnvelope,\n\tHLCTimestamp,\n\tOperation,\n\tVersionVector,\n} from '@korajs/core'\n// protobufjs/minimal is CJS — named ESM imports fail in some runtimes (tsx, Node ESM).\n// Use a default import for the runtime values and type aliases for annotations.\n// The explicit .js extension is required: protobufjs has no \"exports\" map, so\n// Node ESM cannot resolve the extensionless \"protobufjs/minimal\" subpath\n// (bundlers tolerate it, which is why this only breaks in plain Node).\nimport protobuf from 'protobufjs/minimal.js'\n\ntype Reader = protobuf.Reader\ntype Writer = protobuf.Writer\nconst { Reader, Writer } = protobuf\nimport { isEncryptionKeyMessageType } from '../encryption/key-messages'\nimport type {\n\tAcknowledgmentMessage,\n\tErrorMessage,\n\tHandshakeMessage,\n\tHandshakeResponseMessage,\n\tOperationBatchMessage,\n\tSerializedOperation,\n\tSyncMessage,\n\tWireFormat,\n} from './messages'\nimport { isSyncMessage } from './messages'\n\nexport type EncodedMessage = string | Uint8Array\n\n/**\n * Interface for encoding/decoding sync protocol messages.\n */\nexport interface MessageSerializer {\n\tencode(message: SyncMessage): EncodedMessage\n\tdecode(data: string | Uint8Array | ArrayBuffer): SyncMessage\n\tencodeOperation(op: Operation): SerializedOperation\n\tdecodeOperation(serialized: SerializedOperation): Operation\n\tsetWireFormat?(format: WireFormat): void\n\tgetWireFormat?(): WireFormat\n}\n\n/**\n * Convert a VersionVector (Map) to a plain object for wire transmission.\n */\nexport function versionVectorToWire(vector: VersionVector): Record<string, number> {\n\tconst wire: Record<string, number> = {}\n\tfor (const [nodeId, seq] of vector) {\n\t\twire[nodeId] = seq\n\t}\n\treturn wire\n}\n\n/**\n * Convert a wire-format version vector (plain object) back to a VersionVector (Map).\n */\nexport function wireToVersionVector(wire: Record<string, number>): VersionVector {\n\treturn new Map(Object.entries(wire))\n}\n\nconst WIRE_BYTES_KEY = '__kora_bytes__'\n\nfunction toBase64(bytes: Uint8Array): string {\n\tlet binary = ''\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte)\n\t}\n\treturn btoa(binary)\n}\n\nfunction fromBase64(value: string): Uint8Array {\n\tconst binary = atob(value)\n\tconst bytes = new Uint8Array(binary.length)\n\tfor (let index = 0; index < binary.length; index++) {\n\t\tbytes[index] = binary.charCodeAt(index)\n\t}\n\treturn bytes\n}\n\nfunction serializeWireRecord(\n\trecord: Record<string, unknown> | null,\n): Record<string, unknown> | null {\n\tif (!record) {\n\t\treturn null\n\t}\n\n\tconst out: Record<string, unknown> = {}\n\tfor (const [key, value] of Object.entries(record)) {\n\t\tout[key] = serializeWireValue(value)\n\t}\n\treturn out\n}\n\nfunction serializeWireValue(value: unknown): unknown {\n\tif (value instanceof Uint8Array) {\n\t\treturn { [WIRE_BYTES_KEY]: toBase64(value) }\n\t}\n\tif (value instanceof ArrayBuffer) {\n\t\treturn { [WIRE_BYTES_KEY]: toBase64(new Uint8Array(value)) }\n\t}\n\treturn value\n}\n\nfunction deserializeWireRecord(\n\trecord: Record<string, unknown> | null,\n): Record<string, unknown> | null {\n\tif (!record) {\n\t\treturn null\n\t}\n\n\tconst out: Record<string, unknown> = {}\n\tfor (const [key, value] of Object.entries(record)) {\n\t\tout[key] = deserializeWireValue(value)\n\t}\n\treturn out\n}\n\n/**\n * A top-level op-data value in the wire's binary form: an object whose ONLY member is\n * `__kora_bytes__` (a base64 string), as `serializeWireValue` writes a `Uint8Array`\n * (beta.12 richtext). Any other object is data and is returned as is.\n *\n * Two earlier rules corrupted json and object values and are gone (value domain, RT-86):\n * an object that merely CONTAINED `__kora_bytes__` lost its other members, and an object\n * whose keys were all integers with number values (`{ \"1\": 2 }`, even `{ \" \": 0 }`) was\n * read as the indexed byte record of the 0.5 internal beta and became a `Uint8Array`.\n * The local API refuses a field value that is exactly the binary form, so nothing a\n * device writes can be mistaken for it.\n */\nfunction deserializeWireValue(value: unknown): unknown {\n\tif (isWireBytesValue(value)) {\n\t\treturn fromBase64(value[WIRE_BYTES_KEY])\n\t}\n\treturn value\n}\n\nfunction isWireBytesValue(value: unknown): value is Record<typeof WIRE_BYTES_KEY, string> {\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) return false\n\tconst keys = Object.keys(value)\n\treturn (\n\t\tkeys.length === 1 &&\n\t\tkeys[0] === WIRE_BYTES_KEY &&\n\t\ttypeof (value as Record<string, unknown>)[WIRE_BYTES_KEY] === 'string'\n\t)\n}\n\n/**\n * JSON-based message serializer.\n */\nexport class JsonMessageSerializer implements MessageSerializer {\n\tencode(message: SyncMessage): string {\n\t\treturn JSON.stringify(message)\n\t}\n\n\tdecode(data: string | Uint8Array | ArrayBuffer): SyncMessage {\n\t\tconst text = decodeTextPayload(data)\n\n\t\tlet parsed: unknown\n\t\ttry {\n\t\t\tparsed = JSON.parse(text)\n\t\t} catch {\n\t\t\tthrow new SyncError('Failed to decode sync message: invalid JSON', {\n\t\t\t\tdataLength: text.length,\n\t\t\t})\n\t\t}\n\n\t\tif (!isSyncMessage(parsed)) {\n\t\t\tthrow new SyncError('Failed to decode sync message: invalid message structure', {\n\t\t\t\treceivedType:\n\t\t\t\t\ttypeof parsed === 'object' && parsed !== null\n\t\t\t\t\t\t? (parsed as Record<string, unknown>).type\n\t\t\t\t\t\t: typeof parsed,\n\t\t\t})\n\t\t}\n\n\t\treturn parsed\n\t}\n\n\tencodeOperation(op: Operation): SerializedOperation {\n\t\treturn {\n\t\t\tid: op.id,\n\t\t\tnodeId: op.nodeId,\n\t\t\ttype: op.type,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tdata: serializeWireRecord(op.data),\n\t\t\tpreviousData: serializeWireRecord(op.previousData),\n\t\t\ttimestamp: {\n\t\t\t\twallTime: op.timestamp.wallTime,\n\t\t\t\tlogical: op.timestamp.logical,\n\t\t\t\tnodeId: op.timestamp.nodeId,\n\t\t\t},\n\t\t\tsequenceNumber: op.sequenceNumber,\n\t\t\tcausalDeps: [...op.causalDeps],\n\t\t\tschemaVersion: op.schemaVersion,\n\t\t\t...(op.atomicOps !== undefined ? { atomicOps: op.atomicOps } : {}),\n\t\t\t...(op.transactionId !== undefined ? { transactionId: op.transactionId } : {}),\n\t\t\t...(op.mutationName !== undefined ? { mutationName: op.mutationName } : {}),\n\t\t\t...(op.fieldVersions !== undefined\n\t\t\t\t? { fieldVersions: copyFieldVersions(op.fieldVersions) }\n\t\t\t\t: {}),\n\t\t\t...(op.hashVersion !== undefined ? { hashVersion: op.hashVersion } : {}),\n\t\t\t...(op.foldState !== undefined ? { foldState: op.foldState } : {}),\n\t\t\t...(op.encrypted !== undefined ? { encrypted: copyEnvelope(op.encrypted) } : {}),\n\t\t}\n\t}\n\n\tdecodeOperation(serialized: SerializedOperation): Operation {\n\t\treturn {\n\t\t\tid: serialized.id,\n\t\t\tnodeId: serialized.nodeId,\n\t\t\ttype: serialized.type,\n\t\t\tcollection: serialized.collection,\n\t\t\trecordId: serialized.recordId,\n\t\t\tdata: deserializeWireRecord(serialized.data),\n\t\t\tpreviousData: deserializeWireRecord(serialized.previousData),\n\t\t\ttimestamp: {\n\t\t\t\twallTime: serialized.timestamp.wallTime,\n\t\t\t\tlogical: serialized.timestamp.logical,\n\t\t\t\tnodeId: serialized.timestamp.nodeId,\n\t\t\t},\n\t\t\tsequenceNumber: serialized.sequenceNumber,\n\t\t\tcausalDeps: [...serialized.causalDeps],\n\t\t\tschemaVersion: serialized.schemaVersion,\n\t\t\t...(serialized.atomicOps !== undefined ? { atomicOps: serialized.atomicOps } : {}),\n\t\t\t...(serialized.transactionId !== undefined\n\t\t\t\t? { transactionId: serialized.transactionId }\n\t\t\t\t: {}),\n\t\t\t...(serialized.mutationName !== undefined ? { mutationName: serialized.mutationName } : {}),\n\t\t\t...withFieldVersions(serialized.fieldVersions),\n\t\t\t...withHashVersion(serialized.hashVersion),\n\t\t\t...(typeof serialized.foldState === 'string' ? { foldState: serialized.foldState } : {}),\n\t\t\t...withEnvelope(serialized.encrypted),\n\t\t}\n\t}\n}\n\n/**\n * Carry a wire hash version through. 1 and 2 are the defined versions. Any other\n * declared value (an unknown future version, garbage from an untrusted peer) is kept,\n * so id verification fails closed instead of treating the operation as version 1.\n */\nfunction withHashVersion(raw: unknown): { hashVersion?: 1 | 2 } {\n\tif (raw === undefined || raw === null) return {}\n\treturn { hashVersion: raw as 1 | 2 }\n}\n\n/**\n * Validate a wire encryption envelope (protocol v2). A malformed envelope from an\n * untrusted peer is dropped; the operation then has no envelope and fails decryption\n * (encryption enabled: plaintext is refused) or id verification. It never applies\n * as garbage.\n *\n * @param raw - The decoded `encrypted` value\n * @returns A validated copy, or undefined\n */\nexport function normalizeEnvelope(raw: unknown): EncryptedOperationEnvelope | undefined {\n\tif (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return undefined\n\tconst env = raw as Record<string, unknown>\n\tif (\n\t\tenv.v !== 2 ||\n\t\tenv.alg !== 'aes-256-gcm' ||\n\t\ttypeof env.keyId !== 'string' ||\n\t\ttypeof env.keyVersion !== 'number' ||\n\t\t!Number.isInteger(env.keyVersion)\n\t) {\n\t\treturn undefined\n\t}\n\tconst data = normalizeEnvelopeField(env.data)\n\tconst previousData = normalizeEnvelopeField(env.previousData)\n\tif (data === undefined || previousData === undefined) return undefined\n\tconst atomicOps = env.atomicOps === undefined ? null : normalizeEnvelopeField(env.atomicOps)\n\tif (atomicOps === undefined) return undefined\n\treturn {\n\t\tv: 2,\n\t\talg: 'aes-256-gcm',\n\t\tkeyId: env.keyId,\n\t\tkeyVersion: env.keyVersion,\n\t\tdata,\n\t\tpreviousData,\n\t\t...(atomicOps !== null ? { atomicOps } : {}),\n\t}\n}\n\nfunction normalizeEnvelopeField(raw: unknown): { iv: string; ct: string } | null | undefined {\n\tif (raw === null) return null\n\tif (typeof raw !== 'object' || raw === undefined) return undefined\n\tconst field = raw as Record<string, unknown>\n\tif (typeof field.iv !== 'string' || typeof field.ct !== 'string') return undefined\n\treturn { iv: field.iv, ct: field.ct }\n}\n\nfunction copyEnvelope(envelope: EncryptedOperationEnvelope): EncryptedOperationEnvelope {\n\treturn {\n\t\tv: envelope.v,\n\t\talg: envelope.alg,\n\t\tkeyId: envelope.keyId,\n\t\tkeyVersion: envelope.keyVersion,\n\t\tdata: envelope.data ? { ...envelope.data } : null,\n\t\tpreviousData: envelope.previousData ? { ...envelope.previousData } : null,\n\t\t...(envelope.atomicOps ? { atomicOps: { ...envelope.atomicOps } } : {}),\n\t}\n}\n\nfunction withEnvelope(raw: unknown): { encrypted?: EncryptedOperationEnvelope } {\n\tconst encrypted = raw === undefined ? undefined : normalizeEnvelope(raw)\n\treturn encrypted !== undefined ? { encrypted } : {}\n}\n\n/**\n * Validate wire per-field versions (RT-27): a map of field name to a well-formed HLC\n * timestamp. Anything else (from an untrusted peer) yields undefined: the operation\n * is then applied with its single timestamp, never with a malformed version.\n *\n * @param raw - The decoded `fieldVersions` value\n * @returns A fresh validated copy, or undefined\n */\nexport function normalizeFieldVersions(raw: unknown): Record<string, HLCTimestamp> | undefined {\n\tif (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return undefined\n\tconst result: Record<string, HLCTimestamp> = {}\n\tfor (const [field, value] of Object.entries(raw as Record<string, unknown>)) {\n\t\tif (value === null || typeof value !== 'object') return undefined\n\t\tconst ts = value as Record<string, unknown>\n\t\tif (\n\t\t\ttypeof ts.wallTime !== 'number' ||\n\t\t\t!Number.isFinite(ts.wallTime) ||\n\t\t\ttypeof ts.logical !== 'number' ||\n\t\t\t!Number.isFinite(ts.logical) ||\n\t\t\ttypeof ts.nodeId !== 'string'\n\t\t) {\n\t\t\treturn undefined\n\t\t}\n\t\tresult[field] = { wallTime: ts.wallTime, logical: ts.logical, nodeId: ts.nodeId }\n\t}\n\treturn result\n}\n\nfunction copyFieldVersions(versions: Record<string, HLCTimestamp>): Record<string, HLCTimestamp> {\n\tconst copy: Record<string, HLCTimestamp> = {}\n\tfor (const [field, ts] of Object.entries(versions)) {\n\t\tcopy[field] = { wallTime: ts.wallTime, logical: ts.logical, nodeId: ts.nodeId }\n\t}\n\treturn copy\n}\n\nfunction withFieldVersions(raw: unknown): { fieldVersions?: Record<string, HLCTimestamp> } {\n\tconst fieldVersions = raw === undefined ? undefined : normalizeFieldVersions(raw)\n\treturn fieldVersions !== undefined ? { fieldVersions } : {}\n}\n\n/**\n * Protobuf-based serializer for sync messages.\n */\n/**\n * Protobuf message serializer.\n *\n * Lossless by construction (SYNC-9): fields with a native protobuf slot are written there;\n * every other top-level member of the message (fields added after the native mapping,\n * and the bodies of the JSON-shaped awareness and Yjs messages) travels in the `extJson`\n * envelope field (49). Decoding therefore yields exactly what the JSON wire would, for\n * every message type, which a property test over every message type pins.\n *\n * It is not negotiated: the sync engine advertises `['json']` only and the server\n * reports the format its transport actually frames with. Use it explicitly, on both\n * ends of a transport, or not at all.\n */\nexport class ProtobufMessageSerializer implements MessageSerializer {\n\tencode(message: SyncMessage): Uint8Array {\n\t\tconst envelope = toProtoEnvelope(message)\n\t\tconst native = encodeEnvelope(envelope)\n\t\t// Decode what the native slots carried and ship everything they lost (or invented,\n\t\t// such as a defaulted field the message never had) in the extension field.\n\t\tconst ext = residualFields(message, fromProtoEnvelope(decodeEnvelope(native)))\n\t\tif (ext === undefined) return native\n\t\treturn encodeEnvelope({ ...envelope, extJson: JSON.stringify(ext) })\n\t}\n\n\tdecode(data: string | Uint8Array | ArrayBuffer): SyncMessage {\n\t\tconst bytes = toBytes(data)\n\t\tconst envelope = decodeEnvelope(bytes)\n\t\treturn applyResidualFields(fromProtoEnvelope(envelope), envelope.extJson)\n\t}\n\n\tencodeOperation(op: Operation): SerializedOperation {\n\t\treturn new JsonMessageSerializer().encodeOperation(op)\n\t}\n\n\tdecodeOperation(serialized: SerializedOperation): Operation {\n\t\treturn new JsonMessageSerializer().decodeOperation(serialized)\n\t}\n}\n\n/**\n * Negotiated serializer that supports runtime wire-format switching.\n */\nexport class NegotiatedMessageSerializer implements MessageSerializer {\n\tprivate readonly json = new JsonMessageSerializer()\n\tprivate readonly protobuf = new ProtobufMessageSerializer()\n\tprivate wireFormat: WireFormat\n\n\tconstructor(initialWireFormat: WireFormat = 'json') {\n\t\tthis.wireFormat = initialWireFormat\n\t}\n\n\tencode(message: SyncMessage): EncodedMessage {\n\t\tif (this.wireFormat === 'protobuf' && !isEncryptionKeyMessageType(message.type)) {\n\t\t\treturn this.protobuf.encode(message)\n\t\t}\n\n\t\treturn this.json.encode(message)\n\t}\n\n\tdecode(data: string | Uint8Array | ArrayBuffer): SyncMessage {\n\t\tif (typeof data === 'string') {\n\t\t\treturn this.json.decode(data)\n\t\t}\n\n\t\ttry {\n\t\t\treturn this.protobuf.decode(data)\n\t\t} catch {\n\t\t\treturn this.json.decode(data)\n\t\t}\n\t}\n\n\tencodeOperation(op: Operation): SerializedOperation {\n\t\treturn this.json.encodeOperation(op)\n\t}\n\n\tdecodeOperation(serialized: SerializedOperation): Operation {\n\t\treturn this.json.decodeOperation(serialized)\n\t}\n\n\tsetWireFormat(format: WireFormat): void {\n\t\tthis.wireFormat = format\n\t}\n\n\tgetWireFormat(): WireFormat {\n\t\treturn this.wireFormat\n\t}\n}\n\ninterface ProtoVectorEntry {\n\tkey: string\n\tvalue: number\n}\n\ninterface ProtoTimestamp {\n\twallTime: number\n\tlogical: number\n\tnodeId: string\n}\n\ninterface ProtoOperation {\n\tid: string\n\tnodeId: string\n\ttype: string\n\tcollection: string\n\trecordId: string\n\tdataJson: string\n\tpreviousDataJson: string\n\ttimestamp: ProtoTimestamp\n\tsequenceNumber: number\n\tcausalDeps: string[]\n\tschemaVersion: number\n\thasData: boolean\n\thasPreviousData: boolean\n\t/** Field 14: content-hash version (protocol v2). 0/absent = 1. */\n\thashVersion?: number\n\t/** Field 15: serialized fold state of a server scope-entry operation. */\n\tfoldState?: string\n\t/** Field 16: JSON of the encryption envelope v2. */\n\tencryptedJson?: string\n}\n\ninterface ProtoEnvelope {\n\ttype: SyncMessage['type']\n\tmessageId: string\n\tnodeId?: string\n\tversionVector?: ProtoVectorEntry[]\n\tschemaVersion?: number\n\tauthToken?: string\n\tsupportedWireFormats?: string[]\n\taccepted?: boolean\n\trejectReason?: string\n\tselectedWireFormat?: string\n\tserverTime?: number\n\toperations?: ProtoOperation[]\n\tisFinal?: boolean\n\tbatchIndex?: number\n\tacknowledgedMessageId?: string\n\tlastSequenceNumber?: number\n\terrorCode?: string\n\terrorMessage?: string\n\tretriable?: boolean\n\trequestId?: string\n\thash?: string\n\t/** Base64-encoded chunk bytes for a blob-chunk-response or blob-chunk-push. */\n\tchunkBytes?: string\n\t/** Distinguishes a held chunk (true) from \"not held\" (false) when bytes is empty/absent. */\n\thasBytes?: boolean\n\t/** Whether the server persists blob bytes centrally (handshake-response). */\n\tblobStorageEnabled?: boolean\n\t/** Fields 26-28: per-operation rejection (operation-rejected). code/message/retriable reuse the error fields. */\n\toperationId?: string\n\tcollection?: string\n\trecordId?: string\n\t/** Field 29: client's delivery watermark on a handshake. */\n\tlastDeliverySequence?: number\n\t/** Fields 30-31: delivery-stream chaining on an operation-batch. */\n\tbaseDeliverySequence?: number\n\tmaxDeliverySequence?: number\n\t/** Field 32: acked delivery sequence on an acknowledgment. */\n\tdeliverySequence?: number\n\t/** Field 33: server's max delivery sequence on a handshake-response. */\n\tserverMaxDeliverySequence?: number\n\t/** Fields 34-37: directional scope/retraction protocol JSON and policy. */\n\tacceptedScopeJson?: string\n\tacceptedDownlinkScopesJson?: string\n\tacceptedUplinkScopesJson?: string\n\tretractionsJson?: string\n\tscopeExitPolicy?: string\n\t/** Field 39: per-device node token (handshake, handshake-response, RT-12; acknowledgment, RT-21). */\n\tnodeToken?: string\n\t/** Field 40: peer-relay server asks for blob possession proofs (handshake-response, RT-23). */\n\tblobPossessionProof?: boolean\n\t/** Fields 41-42: a throttled blob-chunk-response and its retry delay (RT-24). */\n\tthrottled?: boolean\n\tretryAfterMs?: number\n\t/** Fields 43-44: the accepted view a handshake resumes (SYNC-11). */\n\tacceptedScopeKey?: string\n\tacceptedScopeWatermark?: number\n\t/** Field 45: the handshake's sequence-reservation capability (RT-37). */\n\tsequenceReservation?: boolean\n\t/** Field 46 (repeated): server-authoritative node ids (handshake-response, protocol v2). */\n\tauthoritativeNodeIds?: string[]\n\t/** Field 47: sync protocol version (handshake and handshake-response, protocol v2). */\n\tprotocolVersion?: number\n\t/** Field 48 (repeated): revoked explicit authoritative node ids (handshake-response, RT-81). */\n\trevokedAuthoritativeNodeIds?: string[]\n\t/**\n\t * Field 49: JSON `{ set?: {...}, unset?: [...] }` of the top-level message members the\n\t * native fields do not carry losslessly (SYNC-9). Absent when the native fields suffice.\n\t */\n\textJson?: string\n}\n\ninterface ResidualFields {\n\tset?: Record<string, unknown>\n\tunset?: string[]\n}\n\n/**\n * The difference between a message as the JSON wire carries it and what the native\n * protobuf fields decoded to: members to set, and members the decoder invented.\n */\nfunction residualFields(message: SyncMessage, decoded: SyncMessage): ResidualFields | undefined {\n\tconst want = JSON.parse(JSON.stringify(message)) as Record<string, unknown>\n\tconst got = JSON.parse(JSON.stringify(decoded)) as Record<string, unknown>\n\tconst set: Record<string, unknown> = {}\n\tconst unset: string[] = []\n\tfor (const [key, value] of Object.entries(want)) {\n\t\tif (!(key in got) || stableStringify(got[key]) !== stableStringify(value)) set[key] = value\n\t}\n\tfor (const key of Object.keys(got)) {\n\t\tif (!(key in want)) unset.push(key)\n\t}\n\tconst hasSet = Object.keys(set).length > 0\n\tif (!hasSet && unset.length === 0) return undefined\n\treturn { ...(hasSet ? { set } : {}), ...(unset.length > 0 ? { unset } : {}) }\n}\n\nfunction applyResidualFields(message: SyncMessage, extJson: string | undefined): SyncMessage {\n\tif (extJson === undefined || extJson.length === 0) return message\n\tlet ext: unknown\n\ttry {\n\t\text = JSON.parse(extJson)\n\t} catch {\n\t\tthrow new SyncError('Failed to decode sync message: invalid protobuf extension JSON', {\n\t\t\tlength: extJson.length,\n\t\t})\n\t}\n\tif (typeof ext !== 'object' || ext === null || Array.isArray(ext)) {\n\t\tthrow new SyncError('Failed to decode sync message: protobuf extension is not an object', {\n\t\t\ttype: message.type,\n\t\t})\n\t}\n\tconst out: Record<string, unknown> = { ...message }\n\tconst { set, unset } = ext as { set?: unknown; unset?: unknown }\n\tif (Array.isArray(unset)) {\n\t\tfor (const key of unset) if (typeof key === 'string') delete out[key]\n\t}\n\tif (typeof set === 'object' && set !== null && !Array.isArray(set)) {\n\t\tfor (const [key, value] of Object.entries(set)) {\n\t\t\t// The extension may never change what kind of message this is.\n\t\t\tif (key === 'type') continue\n\t\t\tout[key] = value\n\t\t}\n\t}\n\tif (!isSyncMessage(out)) {\n\t\tthrow new SyncError('Failed to decode sync message: invalid message structure', {\n\t\t\treceivedType: message.type,\n\t\t})\n\t}\n\treturn out\n}\n\n/** JSON with object keys sorted, so equal values compare equal whatever their key order. */\nfunction stableStringify(value: unknown): string {\n\tif (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`\n\tif (typeof value === 'object' && value !== null) {\n\t\tconst entries = Object.keys(value)\n\t\t\t.sort()\n\t\t\t.map(\n\t\t\t\t(key) =>\n\t\t\t\t\t`${JSON.stringify(key)}:${stableStringify((value as Record<string, unknown>)[key])}`,\n\t\t\t)\n\t\treturn `{${entries.join(',')}}`\n\t}\n\treturn JSON.stringify(value) ?? 'null'\n}\n\nfunction toProtoEnvelope(message: SyncMessage): ProtoEnvelope {\n\tswitch (message.type) {\n\t\tcase 'handshake':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\tnodeId: message.nodeId,\n\t\t\t\tversionVector: Object.entries(message.versionVector).map(([key, value]) => ({\n\t\t\t\t\tkey,\n\t\t\t\t\tvalue,\n\t\t\t\t})),\n\t\t\t\tschemaVersion: message.schemaVersion,\n\t\t\t\tauthToken: message.authToken,\n\t\t\t\tsupportedWireFormats: message.supportedWireFormats,\n\t\t\t\t...(message.lastDeliverySequence !== undefined\n\t\t\t\t\t? { lastDeliverySequence: message.lastDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.acceptedScopeKey !== undefined\n\t\t\t\t\t? { acceptedScopeKey: message.acceptedScopeKey }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.acceptedScopeWatermark !== undefined\n\t\t\t\t\t? { acceptedScopeWatermark: message.acceptedScopeWatermark }\n\t\t\t\t\t: {}),\n\t\t\t\tscopeExitPolicy: message.scopeExitPolicy,\n\t\t\t\t...(message.nodeToken !== undefined ? { nodeToken: message.nodeToken } : {}),\n\t\t\t\t...(message.sequenceReservation !== undefined\n\t\t\t\t\t? { sequenceReservation: message.sequenceReservation }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.protocolVersion !== undefined\n\t\t\t\t\t? { protocolVersion: message.protocolVersion }\n\t\t\t\t\t: {}),\n\t\t\t}\n\t\tcase 'handshake-response':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\tnodeId: message.nodeId,\n\t\t\t\tversionVector: Object.entries(message.versionVector).map(([key, value]) => ({\n\t\t\t\t\tkey,\n\t\t\t\t\tvalue,\n\t\t\t\t})),\n\t\t\t\tschemaVersion: message.schemaVersion,\n\t\t\t\taccepted: message.accepted,\n\t\t\t\trejectReason: message.rejectReason,\n\t\t\t\tselectedWireFormat: message.selectedWireFormat,\n\t\t\t\tserverTime: message.serverTime,\n\t\t\t\tblobStorageEnabled: message.blobStorageEnabled,\n\t\t\t\t...(message.serverMaxDeliverySequence !== undefined\n\t\t\t\t\t? { serverMaxDeliverySequence: message.serverMaxDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\tacceptedScopeJson: message.acceptedScope\n\t\t\t\t\t? JSON.stringify(message.acceptedScope)\n\t\t\t\t\t: undefined,\n\t\t\t\tacceptedDownlinkScopesJson: message.acceptedDownlinkScopes\n\t\t\t\t\t? JSON.stringify(message.acceptedDownlinkScopes)\n\t\t\t\t\t: undefined,\n\t\t\t\tacceptedUplinkScopesJson: message.acceptedUplinkScopes\n\t\t\t\t\t? JSON.stringify(message.acceptedUplinkScopes)\n\t\t\t\t\t: undefined,\n\t\t\t\t...(message.nodeToken !== undefined ? { nodeToken: message.nodeToken } : {}),\n\t\t\t\t...(message.blobPossessionProof !== undefined\n\t\t\t\t\t? { blobPossessionProof: message.blobPossessionProof }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.protocolVersion !== undefined\n\t\t\t\t\t? { protocolVersion: message.protocolVersion }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.authoritativeNodeIds !== undefined\n\t\t\t\t\t? { authoritativeNodeIds: [...message.authoritativeNodeIds] }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.revokedAuthoritativeNodeIds !== undefined\n\t\t\t\t\t? { revokedAuthoritativeNodeIds: [...message.revokedAuthoritativeNodeIds] }\n\t\t\t\t\t: {}),\n\t\t\t}\n\t\tcase 'operation-batch':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\toperations: message.operations.map(serializeProtoOperation),\n\t\t\t\tisFinal: message.isFinal,\n\t\t\t\tbatchIndex: message.batchIndex,\n\t\t\t\t...(message.baseDeliverySequence !== undefined\n\t\t\t\t\t? { baseDeliverySequence: message.baseDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.maxDeliverySequence !== undefined\n\t\t\t\t\t? { maxDeliverySequence: message.maxDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\tretractionsJson: message.retractions ? JSON.stringify(message.retractions) : undefined,\n\t\t\t}\n\t\tcase 'acknowledgment':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\tacknowledgedMessageId: message.acknowledgedMessageId,\n\t\t\t\tlastSequenceNumber: message.lastSequenceNumber,\n\t\t\t\t...(message.deliverySequence !== undefined\n\t\t\t\t\t? { deliverySequence: message.deliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(message.nodeToken !== undefined ? { nodeToken: message.nodeToken } : {}),\n\t\t\t}\n\t\tcase 'error':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\terrorCode: message.code,\n\t\t\t\terrorMessage: message.message,\n\t\t\t\tretriable: message.retriable,\n\t\t\t}\n\t\tcase 'operation-rejected':\n\t\t\t// Reuse errorCode/errorMessage/retriable (fields 16-18) for the shared\n\t\t\t// code/message/retriable; fields 26-28 carry the operation identity.\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\toperationId: message.operationId,\n\t\t\t\tcollection: message.collection,\n\t\t\t\trecordId: message.recordId,\n\t\t\t\terrorCode: message.code,\n\t\t\t\terrorMessage: message.message,\n\t\t\t\tretriable: message.retriable,\n\t\t\t}\n\t\tcase 'awareness-update':\n\t\tcase 'yjs-doc-update':\n\t\t\t// Ephemeral presence/doc messages use JSON serialization only. Return a minimal envelope.\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t}\n\t\tcase 'blob-chunk-request':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\trequestId: message.requestId,\n\t\t\t\thash: message.hash,\n\t\t\t}\n\t\tcase 'blob-chunk-response':\n\t\t\t// Blob chunks carry durable user data, so they are fully represented on the\n\t\t\t// protobuf wire (not JSON-only). hasBytes distinguishes a held chunk from\n\t\t\t// \"not held\" (bytes === null), which an empty string alone could not.\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\trequestId: message.requestId,\n\t\t\t\thasBytes: message.bytes !== null,\n\t\t\t\t...(message.bytes !== null ? { chunkBytes: message.bytes } : {}),\n\t\t\t\t...(message.throttled ? { throttled: true } : {}),\n\t\t\t\t...(message.retryAfterMs !== undefined ? { retryAfterMs: message.retryAfterMs } : {}),\n\t\t\t}\n\t\tcase 'blob-chunk-push':\n\t\t\treturn {\n\t\t\t\ttype: message.type,\n\t\t\t\tmessageId: message.messageId,\n\t\t\t\thash: message.hash,\n\t\t\t\tchunkBytes: message.bytes,\n\t\t\t}\n\t\tcase 'heartbeat':\n\t\t\treturn { type: message.type, messageId: message.messageId }\n\t\tcase 'encryption-key-request':\n\t\tcase 'encryption-key-put':\n\t\tcase 'encryption-key-response':\n\t\t\t// NegotiatedMessageSerializer sends key distribution as a JSON text frame on every\n\t\t\t// wire format (ENC-1). A transport that uses this serializer explicitly still gets\n\t\t\t// it losslessly: the body travels in the extension field (49), like awareness.\n\t\t\treturn { type: message.type, messageId: message.messageId }\n\t}\n}\n\nfunction fromProtoEnvelope(envelope: ProtoEnvelope): SyncMessage {\n\tswitch (envelope.type) {\n\t\tcase 'handshake':\n\t\t\treturn {\n\t\t\t\ttype: 'handshake',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\tnodeId: envelope.nodeId ?? '',\n\t\t\t\tversionVector: Object.fromEntries(\n\t\t\t\t\t(envelope.versionVector ?? []).map((entry) => [entry.key, entry.value]),\n\t\t\t\t),\n\t\t\t\tschemaVersion: envelope.schemaVersion ?? 0,\n\t\t\t\tauthToken: envelope.authToken,\n\t\t\t\tsupportedWireFormats: envelope.supportedWireFormats?.filter(\n\t\t\t\t\t(format): format is WireFormat => format === 'json' || format === 'protobuf',\n\t\t\t\t),\n\t\t\t\t...(envelope.lastDeliverySequence !== undefined\n\t\t\t\t\t? { lastDeliverySequence: envelope.lastDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.acceptedScopeKey !== undefined\n\t\t\t\t\t? { acceptedScopeKey: envelope.acceptedScopeKey }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.acceptedScopeWatermark !== undefined\n\t\t\t\t\t? { acceptedScopeWatermark: envelope.acceptedScopeWatermark }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.scopeExitPolicy === 'retain' || envelope.scopeExitPolicy === 'retract'\n\t\t\t\t\t? { scopeExitPolicy: envelope.scopeExitPolicy }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.nodeToken ? { nodeToken: envelope.nodeToken } : {}),\n\t\t\t\t...(envelope.sequenceReservation !== undefined\n\t\t\t\t\t? { sequenceReservation: envelope.sequenceReservation }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.protocolVersion !== undefined\n\t\t\t\t\t? { protocolVersion: envelope.protocolVersion }\n\t\t\t\t\t: {}),\n\t\t\t}\n\t\tcase 'handshake-response':\n\t\t\treturn {\n\t\t\t\ttype: 'handshake-response',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\tnodeId: envelope.nodeId ?? '',\n\t\t\t\tversionVector: Object.fromEntries(\n\t\t\t\t\t(envelope.versionVector ?? []).map((entry) => [entry.key, entry.value]),\n\t\t\t\t),\n\t\t\t\tschemaVersion: envelope.schemaVersion ?? 0,\n\t\t\t\taccepted: envelope.accepted ?? false,\n\t\t\t\trejectReason: envelope.rejectReason,\n\t\t\t\tselectedWireFormat:\n\t\t\t\t\tenvelope.selectedWireFormat === 'json' || envelope.selectedWireFormat === 'protobuf'\n\t\t\t\t\t\t? envelope.selectedWireFormat\n\t\t\t\t\t\t: undefined,\n\t\t\t\t// Preserve the server's wall-clock time so clock-skew detection and\n\t\t\t\t// automatic timestamp rebase work over the protobuf wire, not just JSON.\n\t\t\t\t...(envelope.serverTime !== undefined ? { serverTime: envelope.serverTime } : {}),\n\t\t\t\t...(envelope.blobStorageEnabled !== undefined\n\t\t\t\t\t? { blobStorageEnabled: envelope.blobStorageEnabled }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.serverMaxDeliverySequence !== undefined\n\t\t\t\t\t? { serverMaxDeliverySequence: envelope.serverMaxDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.acceptedScopeJson\n\t\t\t\t\t? { acceptedScope: JSON.parse(envelope.acceptedScopeJson) }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.acceptedDownlinkScopesJson\n\t\t\t\t\t? { acceptedDownlinkScopes: JSON.parse(envelope.acceptedDownlinkScopesJson) }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.acceptedUplinkScopesJson\n\t\t\t\t\t? { acceptedUplinkScopes: JSON.parse(envelope.acceptedUplinkScopesJson) }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.nodeToken ? { nodeToken: envelope.nodeToken } : {}),\n\t\t\t\t...(envelope.blobPossessionProof !== undefined\n\t\t\t\t\t? { blobPossessionProof: envelope.blobPossessionProof }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.protocolVersion !== undefined\n\t\t\t\t\t? { protocolVersion: envelope.protocolVersion }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.authoritativeNodeIds !== undefined\n\t\t\t\t\t? { authoritativeNodeIds: envelope.authoritativeNodeIds }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.revokedAuthoritativeNodeIds !== undefined\n\t\t\t\t\t? { revokedAuthoritativeNodeIds: envelope.revokedAuthoritativeNodeIds }\n\t\t\t\t\t: {}),\n\t\t\t}\n\t\tcase 'operation-batch':\n\t\t\treturn {\n\t\t\t\ttype: 'operation-batch',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\toperations: (envelope.operations ?? []).map(deserializeProtoOperation),\n\t\t\t\tisFinal: envelope.isFinal ?? false,\n\t\t\t\tbatchIndex: envelope.batchIndex ?? 0,\n\t\t\t\t...(envelope.baseDeliverySequence !== undefined\n\t\t\t\t\t? { baseDeliverySequence: envelope.baseDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.maxDeliverySequence !== undefined\n\t\t\t\t\t? { maxDeliverySequence: envelope.maxDeliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.retractionsJson ? { retractions: JSON.parse(envelope.retractionsJson) } : {}),\n\t\t\t}\n\t\tcase 'acknowledgment':\n\t\t\treturn {\n\t\t\t\ttype: 'acknowledgment',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\tacknowledgedMessageId: envelope.acknowledgedMessageId ?? '',\n\t\t\t\tlastSequenceNumber: envelope.lastSequenceNumber ?? 0,\n\t\t\t\t...(envelope.deliverySequence !== undefined\n\t\t\t\t\t? { deliverySequence: envelope.deliverySequence }\n\t\t\t\t\t: {}),\n\t\t\t\t...(envelope.nodeToken ? { nodeToken: envelope.nodeToken } : {}),\n\t\t\t}\n\t\tcase 'error':\n\t\t\treturn {\n\t\t\t\ttype: 'error',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\tcode: envelope.errorCode ?? 'UNKNOWN',\n\t\t\t\tmessage: envelope.errorMessage ?? 'Unknown error',\n\t\t\t\tretriable: envelope.retriable ?? false,\n\t\t\t}\n\t\tcase 'operation-rejected':\n\t\t\treturn {\n\t\t\t\ttype: 'operation-rejected',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\toperationId: envelope.operationId ?? '',\n\t\t\t\tcollection: envelope.collection ?? '',\n\t\t\t\trecordId: envelope.recordId ?? '',\n\t\t\t\tcode: envelope.errorCode ?? 'REJECTED',\n\t\t\t\tmessage: envelope.errorMessage ?? '',\n\t\t\t\tretriable: envelope.retriable ?? false,\n\t\t\t}\n\t\tcase 'blob-chunk-request':\n\t\t\treturn {\n\t\t\t\ttype: 'blob-chunk-request',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\trequestId: envelope.requestId ?? '',\n\t\t\t\thash: envelope.hash ?? '',\n\t\t\t}\n\t\tcase 'blob-chunk-response':\n\t\t\treturn {\n\t\t\t\ttype: 'blob-chunk-response',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\trequestId: envelope.requestId ?? '',\n\t\t\t\tbytes: envelope.hasBytes ? (envelope.chunkBytes ?? '') : null,\n\t\t\t\t...(envelope.throttled ? { throttled: true } : {}),\n\t\t\t\t...(envelope.retryAfterMs !== undefined ? { retryAfterMs: envelope.retryAfterMs } : {}),\n\t\t\t}\n\t\tcase 'blob-chunk-push':\n\t\t\treturn {\n\t\t\t\ttype: 'blob-chunk-push',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\thash: envelope.hash ?? '',\n\t\t\t\tbytes: envelope.chunkBytes ?? '',\n\t\t\t}\n\t\tcase 'heartbeat':\n\t\t\treturn { type: 'heartbeat', messageId: envelope.messageId }\n\t\tcase 'awareness-update':\n\t\t\t// The body travels in the extension field (49); these are its empty defaults.\n\t\t\treturn { type: 'awareness-update', messageId: envelope.messageId, clientId: 0, states: {} }\n\t\tcase 'yjs-doc-update':\n\t\t\treturn {\n\t\t\t\ttype: 'yjs-doc-update',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\tcollection: '',\n\t\t\t\trecordId: '',\n\t\t\t\tfield: '',\n\t\t\t\tupdate: '',\n\t\t\t}\n\t\t// Key-distribution bodies travel in the extension field (49); these are skeletons the\n\t\t// extension completes (applyResidualFields validates the result).\n\t\tcase 'encryption-key-request':\n\t\t\treturn {\n\t\t\t\ttype: 'encryption-key-request',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\trequestId: '',\n\t\t\t\tkeyring: '',\n\t\t\t}\n\t\tcase 'encryption-key-put':\n\t\t\t// `record` has no meaningful default; the extension always sets it, and a put\n\t\t\t// without one fails the structural check in applyResidualFields.\n\t\t\treturn {\n\t\t\t\ttype: 'encryption-key-put',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\trequestId: '',\n\t\t\t\tkeyring: '',\n\t\t\t\texpectedRevision: 0,\n\t\t\t} as SyncMessage\n\t\tcase 'encryption-key-response':\n\t\t\treturn {\n\t\t\t\ttype: 'encryption-key-response',\n\t\t\t\tmessageId: envelope.messageId,\n\t\t\t\tkeyring: '',\n\t\t\t\tstatus: 'invalid',\n\t\t\t\trecord: null,\n\t\t\t}\n\t\tdefault:\n\t\t\tthrow new SyncError('Failed to decode sync message: unknown protobuf type', {\n\t\t\t\ttype: envelope.type,\n\t\t\t})\n\t}\n}\n\nfunction serializeProtoOperation(operation: SerializedOperation): ProtoOperation {\n\t// Embed metadata in the data JSON when present (piggyback on existing field)\n\tconst hasMetadata =\n\t\toperation.transactionId !== undefined ||\n\t\toperation.mutationName !== undefined ||\n\t\t(operation.atomicOps !== undefined && Object.keys(operation.atomicOps).length > 0)\n\tlet dataJson = ''\n\tif (operation.data !== null) {\n\t\tconst dataPayload: Record<string, unknown> = { ...operation.data }\n\t\tif (operation.atomicOps !== undefined && Object.keys(operation.atomicOps).length > 0) {\n\t\t\tdataPayload.__kora_atomic_ops__ = operation.atomicOps\n\t\t}\n\t\tif (operation.transactionId !== undefined) {\n\t\t\tdataPayload.__kora_tx_id__ = operation.transactionId\n\t\t}\n\t\tif (operation.mutationName !== undefined) {\n\t\t\tdataPayload.__kora_mutation__ = operation.mutationName\n\t\t}\n\t\tif (operation.fieldVersions !== undefined) {\n\t\t\tdataPayload.__kora_field_versions__ = operation.fieldVersions\n\t\t}\n\t\tdataJson = JSON.stringify(dataPayload)\n\t} else if (hasMetadata) {\n\t\t// For operations without data (deletes), still carry metadata. hasData stays\n\t\t// false, so the decoder keeps data null.\n\t\tconst meta: Record<string, unknown> = {}\n\t\tif (operation.atomicOps !== undefined && Object.keys(operation.atomicOps).length > 0) {\n\t\t\tmeta.__kora_atomic_ops__ = operation.atomicOps\n\t\t}\n\t\tif (operation.transactionId !== undefined) meta.__kora_tx_id__ = operation.transactionId\n\t\tif (operation.mutationName !== undefined) meta.__kora_mutation__ = operation.mutationName\n\t\tdataJson = JSON.stringify(meta)\n\t}\n\n\treturn {\n\t\tid: operation.id,\n\t\tnodeId: operation.nodeId,\n\t\ttype: operation.type,\n\t\tcollection: operation.collection,\n\t\trecordId: operation.recordId,\n\t\tdataJson,\n\t\tpreviousDataJson: operation.previousData === null ? '' : JSON.stringify(operation.previousData),\n\t\ttimestamp: {\n\t\t\twallTime: operation.timestamp.wallTime,\n\t\t\tlogical: operation.timestamp.logical,\n\t\t\tnodeId: operation.timestamp.nodeId,\n\t\t},\n\t\tsequenceNumber: operation.sequenceNumber,\n\t\tcausalDeps: [...operation.causalDeps],\n\t\tschemaVersion: operation.schemaVersion,\n\t\thasData: operation.data !== null,\n\t\thasPreviousData: operation.previousData !== null,\n\t\t...(operation.hashVersion !== undefined ? { hashVersion: operation.hashVersion } : {}),\n\t\t...(operation.foldState !== undefined ? { foldState: operation.foldState } : {}),\n\t\t...(operation.encrypted !== undefined\n\t\t\t? { encryptedJson: JSON.stringify(operation.encrypted) }\n\t\t\t: {}),\n\t}\n}\n\nfunction deserializeProtoOperation(operation: ProtoOperation): SerializedOperation {\n\tlet data: Record<string, unknown> | null = null\n\tlet atomicOps: Record<string, unknown> | undefined\n\tlet transactionId: string | undefined\n\tlet mutationName: string | undefined\n\tlet fieldVersions: Record<string, HLCTimestamp> | undefined\n\n\tif (operation.hasData || operation.dataJson.length > 0) {\n\t\tconst parsed = JSON.parse(operation.dataJson) as Record<string, unknown>\n\t\tif ('__kora_atomic_ops__' in parsed) {\n\t\t\tatomicOps = parsed.__kora_atomic_ops__ as Record<string, unknown>\n\t\t}\n\t\tif ('__kora_tx_id__' in parsed) {\n\t\t\ttransactionId = parsed.__kora_tx_id__ as string\n\t\t}\n\t\tif ('__kora_mutation__' in parsed) {\n\t\t\tmutationName = parsed.__kora_mutation__ as string\n\t\t}\n\t\tif ('__kora_field_versions__' in parsed) {\n\t\t\tfieldVersions = normalizeFieldVersions(parsed.__kora_field_versions__)\n\t\t}\n\t\tconst {\n\t\t\t__kora_atomic_ops__: _a,\n\t\t\t__kora_tx_id__: _t,\n\t\t\t__kora_mutation__: _m,\n\t\t\t__kora_field_versions__: _f,\n\t\t\t...rest\n\t\t} = parsed\n\t\t// `hasData` says whether the op carried data: `{}` stays `{}` (its id covers it).\n\t\tdata = operation.hasData ? rest : null\n\t}\n\n\treturn {\n\t\tid: operation.id,\n\t\tnodeId: operation.nodeId,\n\t\ttype: operation.type as SerializedOperation['type'],\n\t\tcollection: operation.collection,\n\t\trecordId: operation.recordId,\n\t\tdata,\n\t\tpreviousData: operation.hasPreviousData\n\t\t\t? (JSON.parse(operation.previousDataJson) as Record<string, unknown>)\n\t\t\t: null,\n\t\ttimestamp: {\n\t\t\twallTime: operation.timestamp.wallTime,\n\t\t\tlogical: operation.timestamp.logical,\n\t\t\tnodeId: operation.timestamp.nodeId,\n\t\t},\n\t\tsequenceNumber: operation.sequenceNumber,\n\t\tcausalDeps: [...operation.causalDeps],\n\t\tschemaVersion: operation.schemaVersion,\n\t\t...(atomicOps !== undefined\n\t\t\t? { atomicOps: atomicOps as SerializedOperation['atomicOps'] }\n\t\t\t: {}),\n\t\t...(transactionId !== undefined ? { transactionId } : {}),\n\t\t...(mutationName !== undefined ? { mutationName } : {}),\n\t\t...(fieldVersions !== undefined ? { fieldVersions } : {}),\n\t\t...(operation.hashVersion !== undefined && operation.hashVersion !== 0\n\t\t\t? { hashVersion: operation.hashVersion as 1 | 2 }\n\t\t\t: {}),\n\t\t...(operation.foldState !== undefined ? { foldState: operation.foldState } : {}),\n\t\t...decodeEnvelopeJson(operation.encryptedJson),\n\t}\n}\n\nfunction decodeEnvelopeJson(json: string | undefined): {\n\tencrypted?: EncryptedOperationEnvelope\n} {\n\tif (json === undefined || json.length === 0) return {}\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(json)\n\t} catch {\n\t\tthrow new SyncError('Failed to decode sync message: invalid encryption envelope JSON', {\n\t\t\tlength: json.length,\n\t\t})\n\t}\n\tconst encrypted = normalizeEnvelope(parsed)\n\treturn encrypted !== undefined ? { encrypted } : {}\n}\n\nfunction decodeTextPayload(data: string | Uint8Array | ArrayBuffer): string {\n\tif (typeof data === 'string') return data\n\treturn new TextDecoder().decode(toBytes(data))\n}\n\nfunction toBytes(data: string | Uint8Array | ArrayBuffer): Uint8Array {\n\tif (typeof data === 'string') {\n\t\treturn new TextEncoder().encode(data)\n\t}\n\n\tif (data instanceof Uint8Array) {\n\t\treturn data\n\t}\n\n\tif (data instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(data)\n\t}\n\n\tthrow new SyncError('Unsupported sync payload type', { receivedType: typeof data })\n}\n\nfunction encodeEnvelope(envelope: ProtoEnvelope): Uint8Array {\n\tconst writer = Writer.create()\n\tif (envelope.type.length > 0) writer.uint32(10).string(envelope.type)\n\tif (envelope.messageId.length > 0) writer.uint32(18).string(envelope.messageId)\n\tif (envelope.nodeId && envelope.nodeId.length > 0) writer.uint32(26).string(envelope.nodeId)\n\tfor (const entry of envelope.versionVector ?? []) {\n\t\twriter.uint32(34).fork()\n\t\twriter.uint32(10).string(entry.key)\n\t\twriter.uint32(16).int64(entry.value)\n\t\twriter.ldelim()\n\t}\n\tif (envelope.schemaVersion !== undefined) writer.uint32(40).int32(envelope.schemaVersion)\n\tif (envelope.authToken && envelope.authToken.length > 0)\n\t\twriter.uint32(50).string(envelope.authToken)\n\tfor (const format of envelope.supportedWireFormats ?? []) {\n\t\twriter.uint32(58).string(format)\n\t}\n\tif (envelope.accepted !== undefined) writer.uint32(64).bool(envelope.accepted)\n\tif (envelope.rejectReason && envelope.rejectReason.length > 0)\n\t\twriter.uint32(74).string(envelope.rejectReason)\n\tif (envelope.selectedWireFormat && envelope.selectedWireFormat.length > 0) {\n\t\twriter.uint32(82).string(envelope.selectedWireFormat)\n\t}\n\tfor (const operation of envelope.operations ?? []) {\n\t\twriter.uint32(90).fork()\n\t\tencodeProtoOperation(writer, operation)\n\t\twriter.ldelim()\n\t}\n\tif (envelope.isFinal !== undefined) writer.uint32(96).bool(envelope.isFinal)\n\tif (envelope.batchIndex !== undefined) writer.uint32(104).uint32(envelope.batchIndex)\n\tif (envelope.acknowledgedMessageId && envelope.acknowledgedMessageId.length > 0) {\n\t\twriter.uint32(114).string(envelope.acknowledgedMessageId)\n\t}\n\tif (envelope.lastSequenceNumber !== undefined)\n\t\twriter.uint32(120).int64(envelope.lastSequenceNumber)\n\tif (envelope.errorCode && envelope.errorCode.length > 0)\n\t\twriter.uint32(130).string(envelope.errorCode)\n\tif (envelope.errorMessage && envelope.errorMessage.length > 0) {\n\t\twriter.uint32(138).string(envelope.errorMessage)\n\t}\n\tif (envelope.retriable !== undefined) writer.uint32(144).bool(envelope.retriable)\n\t// Field 19: server wall-clock time (ms epoch). Optional; only handshake-response sets it.\n\tif (envelope.serverTime !== undefined) writer.uint32(152).int64(envelope.serverTime)\n\t// Fields 20-23: blob chunk side channel (out-of-band blob transfer).\n\tif (envelope.requestId && envelope.requestId.length > 0)\n\t\twriter.uint32(162).string(envelope.requestId)\n\tif (envelope.hash && envelope.hash.length > 0) writer.uint32(170).string(envelope.hash)\n\tif (envelope.chunkBytes && envelope.chunkBytes.length > 0)\n\t\twriter.uint32(178).string(envelope.chunkBytes)\n\tif (envelope.hasBytes !== undefined) writer.uint32(184).bool(envelope.hasBytes)\n\t// Field 25: server advertises central blob storage (handshake-response).\n\tif (envelope.blobStorageEnabled !== undefined)\n\t\twriter.uint32(200).bool(envelope.blobStorageEnabled)\n\t// Fields 26-28: per-operation rejection identity (operation-rejected).\n\tif (envelope.operationId && envelope.operationId.length > 0)\n\t\twriter.uint32(210).string(envelope.operationId)\n\tif (envelope.collection && envelope.collection.length > 0)\n\t\twriter.uint32(218).string(envelope.collection)\n\tif (envelope.recordId && envelope.recordId.length > 0)\n\t\twriter.uint32(226).string(envelope.recordId)\n\t// Fields 29-32: delivery-watermark protocol. int64 (field << 3 | wiretype 0).\n\tif (envelope.lastDeliverySequence !== undefined)\n\t\twriter.uint32(232).int64(envelope.lastDeliverySequence)\n\tif (envelope.baseDeliverySequence !== undefined)\n\t\twriter.uint32(240).int64(envelope.baseDeliverySequence)\n\tif (envelope.maxDeliverySequence !== undefined)\n\t\twriter.uint32(248).int64(envelope.maxDeliverySequence)\n\tif (envelope.deliverySequence !== undefined) writer.uint32(256).int64(envelope.deliverySequence)\n\tif (envelope.serverMaxDeliverySequence !== undefined)\n\t\twriter.uint32(264).int64(envelope.serverMaxDeliverySequence)\n\tif (envelope.acceptedScopeJson) writer.uint32(274).string(envelope.acceptedScopeJson)\n\tif (envelope.acceptedDownlinkScopesJson)\n\t\twriter.uint32(282).string(envelope.acceptedDownlinkScopesJson)\n\tif (envelope.acceptedUplinkScopesJson)\n\t\twriter.uint32(290).string(envelope.acceptedUplinkScopesJson)\n\tif (envelope.retractionsJson) writer.uint32(298).string(envelope.retractionsJson)\n\tif (envelope.scopeExitPolicy) writer.uint32(306).string(envelope.scopeExitPolicy)\n\tif (envelope.nodeToken) writer.uint32(314).string(envelope.nodeToken)\n\t// Field 40 (bool, wiretype 0): 40 << 3 = 320. Fields 41-42: 328, 336.\n\tif (envelope.blobPossessionProof !== undefined)\n\t\twriter.uint32(320).bool(envelope.blobPossessionProof)\n\tif (envelope.throttled) writer.uint32(328).bool(envelope.throttled)\n\tif (envelope.retryAfterMs !== undefined) writer.uint32(336).int64(envelope.retryAfterMs)\n\tif (envelope.acceptedScopeKey !== undefined) writer.uint32(346).string(envelope.acceptedScopeKey)\n\tif (envelope.acceptedScopeWatermark !== undefined)\n\t\twriter.uint32(352).int64(envelope.acceptedScopeWatermark)\n\t// Field 45 (bool, wiretype 0): 45 << 3 = 360. Written whenever set (false included),\n\t// so a client that explicitly opts out is told apart from one that predates it.\n\tif (envelope.sequenceReservation !== undefined)\n\t\twriter.uint32(360).bool(envelope.sequenceReservation)\n\t// Field 46 (repeated string, wiretype 2): 46 << 3 | 2 = 370.\n\tfor (const nodeId of envelope.authoritativeNodeIds ?? []) writer.uint32(370).string(nodeId)\n\t// Field 47 (uint32, wiretype 0): 47 << 3 = 376.\n\tif (envelope.protocolVersion !== undefined) writer.uint32(376).uint32(envelope.protocolVersion)\n\t// Field 48 (repeated string, wiretype 2): 48 << 3 | 2 = 386.\n\tfor (const nodeId of envelope.revokedAuthoritativeNodeIds ?? []) writer.uint32(386).string(nodeId)\n\t// Field 49 (string, wiretype 2): 49 << 3 | 2 = 394.\n\tif (envelope.extJson) writer.uint32(394).string(envelope.extJson)\n\treturn writer.finish()\n}\n\nfunction decodeEnvelope(bytes: Uint8Array): ProtoEnvelope {\n\tconst reader = Reader.create(bytes)\n\tconst envelope: ProtoEnvelope = { type: 'error', messageId: '' }\n\n\twhile (reader.pos < reader.len) {\n\t\tconst tag = reader.uint32()\n\t\tswitch (tag >>> 3) {\n\t\t\tcase 1:\n\t\t\t\tenvelope.type = reader.string() as SyncMessage['type']\n\t\t\t\tbreak\n\t\t\tcase 2:\n\t\t\t\tenvelope.messageId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 3:\n\t\t\t\tenvelope.nodeId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 4:\n\t\t\t\tenvelope.versionVector = [\n\t\t\t\t\t...(envelope.versionVector ?? []),\n\t\t\t\t\tdecodeVectorEntry(reader, reader.uint32()),\n\t\t\t\t]\n\t\t\t\tbreak\n\t\t\tcase 5:\n\t\t\t\tenvelope.schemaVersion = reader.int32()\n\t\t\t\tbreak\n\t\t\tcase 6:\n\t\t\t\tenvelope.authToken = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 7:\n\t\t\t\tenvelope.supportedWireFormats = [...(envelope.supportedWireFormats ?? []), reader.string()]\n\t\t\t\tbreak\n\t\t\tcase 8:\n\t\t\t\tenvelope.accepted = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 9:\n\t\t\t\tenvelope.rejectReason = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 10:\n\t\t\t\tenvelope.selectedWireFormat = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 11:\n\t\t\t\tenvelope.operations = [\n\t\t\t\t\t...(envelope.operations ?? []),\n\t\t\t\t\tdecodeProtoOperation(reader, reader.uint32()),\n\t\t\t\t]\n\t\t\t\tbreak\n\t\t\tcase 12:\n\t\t\t\tenvelope.isFinal = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 13:\n\t\t\t\tenvelope.batchIndex = reader.uint32()\n\t\t\t\tbreak\n\t\t\tcase 14:\n\t\t\t\tenvelope.acknowledgedMessageId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 15:\n\t\t\t\tenvelope.lastSequenceNumber = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 16:\n\t\t\t\tenvelope.errorCode = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 17:\n\t\t\t\tenvelope.errorMessage = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 18:\n\t\t\t\tenvelope.retriable = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 19:\n\t\t\t\tenvelope.serverTime = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 20:\n\t\t\t\tenvelope.requestId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 21:\n\t\t\t\tenvelope.hash = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 22:\n\t\t\t\tenvelope.chunkBytes = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 23:\n\t\t\t\tenvelope.hasBytes = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 25:\n\t\t\t\tenvelope.blobStorageEnabled = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 26:\n\t\t\t\tenvelope.operationId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 27:\n\t\t\t\tenvelope.collection = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 28:\n\t\t\t\tenvelope.recordId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 29:\n\t\t\t\tenvelope.lastDeliverySequence = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 30:\n\t\t\t\tenvelope.baseDeliverySequence = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 31:\n\t\t\t\tenvelope.maxDeliverySequence = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 32:\n\t\t\t\tenvelope.deliverySequence = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 33:\n\t\t\t\tenvelope.serverMaxDeliverySequence = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 34:\n\t\t\t\tenvelope.acceptedScopeJson = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 35:\n\t\t\t\tenvelope.acceptedDownlinkScopesJson = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 36:\n\t\t\t\tenvelope.acceptedUplinkScopesJson = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 37:\n\t\t\t\tenvelope.retractionsJson = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 38:\n\t\t\t\tenvelope.scopeExitPolicy = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 39:\n\t\t\t\tenvelope.nodeToken = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 40:\n\t\t\t\tenvelope.blobPossessionProof = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 41:\n\t\t\t\tenvelope.throttled = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 42:\n\t\t\t\tenvelope.retryAfterMs = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 43:\n\t\t\t\tenvelope.acceptedScopeKey = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 44:\n\t\t\t\tenvelope.acceptedScopeWatermark = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 45:\n\t\t\t\tenvelope.sequenceReservation = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 46:\n\t\t\t\tenvelope.authoritativeNodeIds = [...(envelope.authoritativeNodeIds ?? []), reader.string()]\n\t\t\t\tbreak\n\t\t\tcase 47:\n\t\t\t\tenvelope.protocolVersion = reader.uint32()\n\t\t\t\tbreak\n\t\t\tcase 48:\n\t\t\t\tenvelope.revokedAuthoritativeNodeIds = [\n\t\t\t\t\t...(envelope.revokedAuthoritativeNodeIds ?? []),\n\t\t\t\t\treader.string(),\n\t\t\t\t]\n\t\t\t\tbreak\n\t\t\tcase 49:\n\t\t\t\tenvelope.extJson = reader.string()\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\treader.skipType(tag & 7)\n\t\t}\n\t}\n\n\treturn envelope\n}\n\nfunction encodeProtoOperation(writer: Writer, operation: ProtoOperation): void {\n\tif (operation.id.length > 0) writer.uint32(10).string(operation.id)\n\tif (operation.nodeId.length > 0) writer.uint32(18).string(operation.nodeId)\n\tif (operation.type.length > 0) writer.uint32(26).string(operation.type)\n\tif (operation.collection.length > 0) writer.uint32(34).string(operation.collection)\n\tif (operation.recordId.length > 0) writer.uint32(42).string(operation.recordId)\n\tif (operation.dataJson.length > 0) writer.uint32(50).string(operation.dataJson)\n\tif (operation.previousDataJson.length > 0) writer.uint32(58).string(operation.previousDataJson)\n\twriter.uint32(66).fork()\n\twriter.uint32(8).int64(operation.timestamp.wallTime)\n\twriter.uint32(16).uint32(operation.timestamp.logical)\n\twriter.uint32(26).string(operation.timestamp.nodeId)\n\twriter.ldelim()\n\twriter.uint32(72).int64(operation.sequenceNumber)\n\tfor (const dep of operation.causalDeps) {\n\t\twriter.uint32(82).string(dep)\n\t}\n\twriter.uint32(88).int32(operation.schemaVersion)\n\twriter.uint32(96).bool(operation.hasData)\n\twriter.uint32(104).bool(operation.hasPreviousData)\n\t// Fields 14-16 (protocol v2). Older decoders skip them as unknown fields.\n\tif (operation.hashVersion !== undefined) writer.uint32(112).uint32(operation.hashVersion)\n\tif (operation.foldState !== undefined) writer.uint32(122).string(operation.foldState)\n\tif (operation.encryptedJson !== undefined) writer.uint32(130).string(operation.encryptedJson)\n}\n\nfunction decodeProtoOperation(reader: Reader, length: number): ProtoOperation {\n\tconst end = reader.pos + length\n\tconst operation: ProtoOperation = {\n\t\tid: '',\n\t\tnodeId: '',\n\t\ttype: 'insert',\n\t\tcollection: '',\n\t\trecordId: '',\n\t\tdataJson: '',\n\t\tpreviousDataJson: '',\n\t\ttimestamp: { wallTime: 0, logical: 0, nodeId: '' },\n\t\tsequenceNumber: 0,\n\t\tcausalDeps: [],\n\t\tschemaVersion: 0,\n\t\thasData: false,\n\t\thasPreviousData: false,\n\t}\n\n\twhile (reader.pos < end) {\n\t\tconst tag = reader.uint32()\n\t\tswitch (tag >>> 3) {\n\t\t\tcase 1:\n\t\t\t\toperation.id = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 2:\n\t\t\t\toperation.nodeId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 3:\n\t\t\t\toperation.type = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 4:\n\t\t\t\toperation.collection = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 5:\n\t\t\t\toperation.recordId = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 6:\n\t\t\t\toperation.dataJson = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 7:\n\t\t\t\toperation.previousDataJson = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 8: {\n\t\t\t\tconst timestampEnd = reader.pos + reader.uint32()\n\t\t\t\twhile (reader.pos < timestampEnd) {\n\t\t\t\t\tconst timestampTag = reader.uint32()\n\t\t\t\t\tswitch (timestampTag >>> 3) {\n\t\t\t\t\t\tcase 1:\n\t\t\t\t\t\t\toperation.timestamp.wallTime = longToNumber(reader.int64())\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tcase 2:\n\t\t\t\t\t\t\toperation.timestamp.logical = reader.uint32()\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tcase 3:\n\t\t\t\t\t\t\toperation.timestamp.nodeId = reader.string()\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treader.skipType(timestampTag & 7)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 9:\n\t\t\t\toperation.sequenceNumber = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tcase 10:\n\t\t\t\toperation.causalDeps.push(reader.string())\n\t\t\t\tbreak\n\t\t\tcase 11:\n\t\t\t\toperation.schemaVersion = reader.int32()\n\t\t\t\tbreak\n\t\t\tcase 12:\n\t\t\t\toperation.hasData = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 13:\n\t\t\t\toperation.hasPreviousData = reader.bool()\n\t\t\t\tbreak\n\t\t\tcase 14:\n\t\t\t\toperation.hashVersion = reader.uint32()\n\t\t\t\tbreak\n\t\t\tcase 15:\n\t\t\t\toperation.foldState = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 16:\n\t\t\t\toperation.encryptedJson = reader.string()\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\treader.skipType(tag & 7)\n\t\t}\n\t}\n\n\treturn operation\n}\n\nfunction decodeVectorEntry(reader: Reader, length: number): ProtoVectorEntry {\n\tconst end = reader.pos + length\n\tconst entry: ProtoVectorEntry = { key: '', value: 0 }\n\twhile (reader.pos < end) {\n\t\tconst tag = reader.uint32()\n\t\tswitch (tag >>> 3) {\n\t\t\tcase 1:\n\t\t\t\tentry.key = reader.string()\n\t\t\t\tbreak\n\t\t\tcase 2:\n\t\t\t\tentry.value = longToNumber(reader.int64())\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\treader.skipType(tag & 7)\n\t\t}\n\t}\n\treturn entry\n}\n\nfunction longToNumber(value: unknown): number {\n\tif (typeof value === 'number') return value\n\tif (typeof value === 'string') return Number.parseInt(value, 10)\n\tif (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\t'toNumber' in value &&\n\t\ttypeof (value as { toNumber: unknown }).toNumber === 'function'\n\t) {\n\t\treturn (value as { toNumber(): number }).toNumber()\n\t}\n\n\tthrow new SyncError('Failed to decode int64 value', {\n\t\treceivedType: typeof value,\n\t})\n}\n","import { SyncError } from '@korajs/core'\nimport type { SyncMessage } from '../protocol/messages'\nimport { JsonMessageSerializer } from '../protocol/serializer'\nimport type { MessageSerializer } from '../protocol/serializer'\nimport type {\n\tSyncTransport,\n\tTransportCloseHandler,\n\tTransportErrorHandler,\n\tTransportMessageHandler,\n\tTransportOptions,\n} from './transport'\n\n/**\n * WebSocket event interface for dependency injection.\n * Matches the subset of the browser WebSocket API that we need.\n */\nexport interface WebSocketLike {\n\treadonly readyState: number\n\tsend(data: string | Uint8Array): void\n\tclose(code?: number, reason?: string): void\n\tonopen: ((event: unknown) => void) | null\n\tonmessage: ((event: { data: unknown }) => void) | null\n\tonclose: ((event: { reason: string; code: number }) => void) | null\n\tonerror: ((event: unknown) => void) | null\n}\n\n/**\n * Constructor for WebSocket-like objects. Allows injection of mock WebSocket for testing.\n */\nexport type WebSocketConstructor = new (url: string, protocols?: string | string[]) => WebSocketLike\n\n/**\n * Options for the WebSocket transport.\n */\nexport interface WebSocketTransportOptions {\n\t/** Custom serializer. Defaults to JSON. */\n\tserializer?: MessageSerializer\n\t/** Injectable WebSocket constructor for testing. Defaults to globalThis.WebSocket. */\n\tWebSocketImpl?: WebSocketConstructor\n\t/** Connection timeout in ms. Defaults to 10000 (10s). */\n\tconnectTimeout?: number\n\t/**\n\t * Also append the auth token to the connection URL as `?token=`. Off by\n\t * default: the token already travels in the handshake message, and URLs are\n\t * recorded by reverse proxies, load balancers and access logs. Enable only for an\n\t * intermediary that authenticates the WebSocket upgrade by query parameter.\n\t */\n\ttokenInUrl?: boolean\n\t/**\n\t * Detect dead connections with the server's application-level heartbeat (LMS #12).\n\t * The transport advertises support in the handshake; a server that sends\n\t * heartbeats states their interval, and a connection that then receives nothing\n\t * for `heartbeatTimeoutFactor` intervals is closed as dead, which starts a\n\t * reconnect. Browsers cannot observe WebSocket pings, so without this a half-open\n\t * connection (a phone that changed networks) looks alive until TCP gives up, often\n\t * minutes later. Defaults to true.\n\t */\n\theartbeat?: boolean\n\t/** Silent intervals tolerated before the connection is declared dead. Defaults to 2.5. */\n\theartbeatTimeoutFactor?: number\n}\n\n// WebSocket readyState constants\nconst WS_OPEN = 1\n\n/**\n * WebSocket-based sync transport implementation.\n *\n * Every socket is bound to a generation (SYNC-5): `connect()` closes and detaches the\n * previous socket first, and callbacks of a superseded socket are ignored, so a stale\n * socket closing late can never sever (or feed messages into) the live session.\n */\nexport class WebSocketTransport implements SyncTransport {\n\tprivate ws: WebSocketLike | null = null\n\t/** Incremented for every socket; callbacks check theirs is still current. */\n\tprivate generation = 0\n\tprivate messageHandler: TransportMessageHandler | null = null\n\tprivate closeHandler: TransportCloseHandler | null = null\n\tprivate errorHandler: TransportErrorHandler | null = null\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly WebSocketImpl: WebSocketConstructor\n\tprivate readonly connectTimeout: number\n\tprivate readonly tokenInUrl: boolean\n\tprivate readonly heartbeat: boolean\n\tprivate readonly heartbeatTimeoutFactor: number\n\t/** Silence (ms) after which the current connection is declared dead; 0 = unarmed. */\n\tprivate livenessTimeoutMs = 0\n\tprivate livenessTimer: ReturnType<typeof setTimeout> | null = null\n\n\tconstructor(options?: WebSocketTransportOptions) {\n\t\tthis.serializer = options?.serializer ?? new JsonMessageSerializer()\n\t\tthis.connectTimeout = options?.connectTimeout ?? 10000\n\t\tthis.tokenInUrl = options?.tokenInUrl ?? false\n\t\tthis.heartbeat = options?.heartbeat ?? true\n\t\tthis.heartbeatTimeoutFactor = Math.max(1.5, options?.heartbeatTimeoutFactor ?? 2.5)\n\n\t\tif (options?.WebSocketImpl) {\n\t\t\tthis.WebSocketImpl = options.WebSocketImpl\n\t\t} else if (typeof globalThis.WebSocket !== 'undefined') {\n\t\t\tthis.WebSocketImpl = globalThis.WebSocket as unknown as WebSocketConstructor\n\t\t} else {\n\t\t\t// Deferred — will throw on connect() if no implementation available\n\t\t\tthis.WebSocketImpl = null as unknown as WebSocketConstructor\n\t\t}\n\t}\n\n\tasync connect(url: string, options?: TransportOptions): Promise<void> {\n\t\tif (!this.WebSocketImpl) {\n\t\t\tthrow new SyncError('WebSocket is not available in this environment', {\n\t\t\t\thint: 'Provide a WebSocketImpl option or use a polyfill',\n\t\t\t})\n\t\t}\n\n\t\t// Never let two sockets feed one engine: close and detach any previous socket\n\t\t// before opening the next (SYNC-5).\n\t\tthis.releaseSocket('Client reconnecting')\n\t\tconst generation = ++this.generation\n\t\tconst isCurrent = (): boolean => generation === this.generation\n\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tlet settled = false\n\n\t\t\tconst settle = (fn: () => void): void => {\n\t\t\t\tif (settled) return\n\t\t\t\tsettled = true\n\t\t\t\tclearTimeout(timer)\n\t\t\t\tfn()\n\t\t\t}\n\n\t\t\t// Connection timeout — prevents hanging for 30+ seconds on mobile when offline\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tsettle(() => {\n\t\t\t\t\tconst err = new SyncError('WebSocket connection timed out', {\n\t\t\t\t\t\turl,\n\t\t\t\t\t\ttimeout: this.connectTimeout,\n\t\t\t\t\t})\n\t\t\t\t\tif (isCurrent()) {\n\t\t\t\t\t\tthis.releaseSocket('Connection timed out')\n\t\t\t\t\t\tthis.errorHandler?.(err)\n\t\t\t\t\t}\n\t\t\t\t\treject(err)\n\t\t\t\t})\n\t\t\t}, this.connectTimeout)\n\n\t\t\ttry {\n\t\t\t\t// The token is NOT put in the URL by default (it is sent in the handshake);\n\t\t\t\t// see WebSocketTransportOptions.tokenInUrl for the explicit opt-in.\n\t\t\t\tconst connectUrl =\n\t\t\t\t\tthis.tokenInUrl && options?.authToken\n\t\t\t\t\t\t? `${url}${url.includes('?') ? '&' : '?'}token=${encodeURIComponent(options.authToken)}`\n\t\t\t\t\t\t: url\n\n\t\t\t\tconst ws = new this.WebSocketImpl(connectUrl)\n\t\t\t\tthis.ws = ws\n\n\t\t\t\tws.onopen = () => {\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tsettle(() => resolve())\n\t\t\t\t}\n\n\t\t\t\tws.onmessage = (event: { data: unknown }) => {\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\t// Any inbound frame proves the connection alive.\n\t\t\t\t\tthis.touchLiveness()\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof event.data !== 'string' &&\n\t\t\t\t\t\t\t!(event.data instanceof Uint8Array) &&\n\t\t\t\t\t\t\t!(event.data instanceof ArrayBuffer)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst message = this.serializer.decode(event.data)\n\t\t\t\t\t\tif (message.type === 'heartbeat') return\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tmessage.type === 'handshake-response' &&\n\t\t\t\t\t\t\ttypeof message.heartbeatIntervalMs === 'number' &&\n\t\t\t\t\t\t\tmessage.heartbeatIntervalMs > 0\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tthis.armLiveness(message.heartbeatIntervalMs)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.messageHandler?.(message)\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthis.errorHandler?.(new SyncError('Failed to decode incoming message'))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tws.onclose = (event: { reason: string; code: number }) => {\n\t\t\t\t\t// A superseded socket closing late must not touch the live one (SYNC-5).\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tthis.ws = null\n\t\t\t\t\tthis.clearLiveness()\n\t\t\t\t\tthis.livenessTimeoutMs = 0\n\t\t\t\t\tthis.closeHandler?.(event.reason || `WebSocket closed with code ${event.code}`)\n\t\t\t\t}\n\n\t\t\t\tws.onerror = (_event: unknown) => {\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tconst err = new SyncError('WebSocket error', {\n\t\t\t\t\t\turl,\n\t\t\t\t\t})\n\t\t\t\t\tthis.errorHandler?.(err)\n\t\t\t\t\t// If we haven't connected yet, reject the connect promise\n\t\t\t\t\tif (!this.isConnected()) {\n\t\t\t\t\t\tthis.ws = null\n\t\t\t\t\t\tsettle(() => reject(err))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tsettle(() =>\n\t\t\t\t\treject(\n\t\t\t\t\t\terr instanceof SyncError\n\t\t\t\t\t\t\t? err\n\t\t\t\t\t\t\t: new SyncError('Failed to create WebSocket', {\n\t\t\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t\t\t\terror: String(err),\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\t// Invalidate the current socket's callbacks: an intentional disconnect fires no\n\t\t// close handler.\n\t\tthis.generation++\n\t\tthis.releaseSocket('Client disconnecting')\n\t}\n\n\tsend(message: SyncMessage): void {\n\t\tif (!this.ws || this.ws.readyState !== WS_OPEN) {\n\t\t\tthrow new SyncError('Cannot send message: WebSocket is not connected', {\n\t\t\t\tmessageType: message.type,\n\t\t\t})\n\t\t}\n\t\t// The heartbeat is a transport capability, so the transport advertises it.\n\t\tconst outgoing: SyncMessage =\n\t\t\tthis.heartbeat && message.type === 'handshake'\n\t\t\t\t? { ...message, supportsHeartbeat: true }\n\t\t\t\t: message\n\t\tconst encoded = this.serializer.encode(outgoing)\n\t\tthis.ws.send(encoded)\n\t}\n\n\tonMessage(handler: TransportMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: TransportCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t}\n\n\tonError(handler: TransportErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.ws !== null && this.ws.readyState === WS_OPEN\n\t}\n\n\t/** Close and detach the current socket, if any, without firing the close handler. */\n\tprivate releaseSocket(reason: string): void {\n\t\tthis.clearLiveness()\n\t\tthis.livenessTimeoutMs = 0\n\t\tconst ws = this.ws\n\t\tthis.ws = null\n\t\tif (!ws) return\n\t\tws.onopen = null\n\t\tws.onmessage = null\n\t\tws.onclose = null\n\t\tws.onerror = null\n\t\ttry {\n\t\t\tws.close(1000, reason)\n\t\t} catch {\n\t\t\t// Closing an already-failed socket can throw; it is detached either way.\n\t\t}\n\t}\n\n\t/** Start watching for silence once the server stated its heartbeat interval. */\n\tprivate armLiveness(heartbeatIntervalMs: number): void {\n\t\tif (!this.heartbeat) return\n\t\tthis.livenessTimeoutMs = Math.round(heartbeatIntervalMs * this.heartbeatTimeoutFactor)\n\t\tthis.touchLiveness()\n\t}\n\n\tprivate touchLiveness(): void {\n\t\tif (this.livenessTimeoutMs <= 0) return\n\t\tthis.clearLiveness()\n\t\tconst generation = this.generation\n\t\tthis.livenessTimer = setTimeout(() => {\n\t\t\tthis.livenessTimer = null\n\t\t\tif (generation !== this.generation || this.ws === null) return\n\t\t\t// Nothing arrived for several heartbeat intervals: the connection is dead even\n\t\t\t// though the browser still reports it open. Drop it and report the close so\n\t\t\t// the engine reconnects.\n\t\t\tconst silenceMs = this.livenessTimeoutMs\n\t\t\tthis.generation++\n\t\t\tthis.releaseSocket('Heartbeat timeout')\n\t\t\tthis.closeHandler?.(`Heartbeat timeout: no message from the server for ${silenceMs} ms`)\n\t\t}, this.livenessTimeoutMs)\n\t}\n\n\tprivate clearLiveness(): void {\n\t\tif (this.livenessTimer !== null) {\n\t\t\tclearTimeout(this.livenessTimer)\n\t\t\tthis.livenessTimer = null\n\t\t}\n\t}\n}\n","import { SyncError } from '@korajs/core'\nimport { NegotiatedMessageSerializer } from '../protocol/serializer'\nimport type { MessageSerializer } from '../protocol/serializer'\nimport type {\n\tSyncTransport,\n\tTransportCloseHandler,\n\tTransportErrorHandler,\n\tTransportMessageHandler,\n\tTransportOptions,\n} from './transport'\nimport { WebSocketTransport } from './websocket-transport'\n\nconst DEFAULT_RETRY_DELAY_MS = 250\n\n/**\n * Header carrying the server-issued HTTP sync session id. The server returns it on\n * the response to the first POST (the handshake); the client sends it on every\n * later request, together with its bearer credential.\n */\nexport const HTTP_SYNC_SESSION_HEADER = 'x-kora-session'\n\n/** Statuses after which the server-side HTTP session is gone: reconnect from scratch. */\nconst SESSION_ENDED_STATUSES = new Set([401, 403, 404, 410])\n\nexport interface HttpLongPollingTransportOptions {\n\tserializer?: MessageSerializer\n\tfetchImpl?: typeof fetch\n\tretryDelayMs?: number\n\tpreferWebSocket?: boolean\n\twebSocketFactory?: () => SyncTransport\n}\n\n/**\n * HTTP long-polling transport with optional WebSocket upgrade.\n *\n * The first POST (the handshake) opens a server-side session; the server answers\n * with a high-entropy session id in the `x-kora-session` header. Every later\n * request carries that id and the bearer credential, and the long-poll loop starts\n * only once the id is known. POSTs are sent one at a time, in order. A 401, 403,\n * 404 or 410 means the server-side session is gone: the transport closes so the\n * sync engine reconnects (and re-authenticates) from scratch.\n */\nexport class HttpLongPollingTransport implements SyncTransport {\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly fetchImpl: typeof fetch\n\tprivate readonly retryDelayMs: number\n\tprivate readonly preferWebSocket: boolean\n\tprivate readonly webSocketFactory: () => SyncTransport\n\n\tprivate messageHandler: TransportMessageHandler | null = null\n\tprivate closeHandler: TransportCloseHandler | null = null\n\tprivate errorHandler: TransportErrorHandler | null = null\n\n\tprivate connected = false\n\tprivate polling = false\n\tprivate url: string | null = null\n\tprivate authToken: string | undefined\n\tprivate pollAbort: AbortController | null = null\n\tprivate upgradedTransport: SyncTransport | null = null\n\t/** Server-issued session id; null until the handshake POST is answered. */\n\tprivate sessionId: string | null = null\n\t/** Serializes POSTs so the server receives messages in send order. */\n\tprivate postChain: Promise<void> = Promise.resolve()\n\n\tconstructor(options?: HttpLongPollingTransportOptions) {\n\t\tthis.serializer = options?.serializer ?? new NegotiatedMessageSerializer('json')\n\t\tthis.fetchImpl = options?.fetchImpl ?? fetch\n\t\tthis.retryDelayMs = options?.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS\n\t\tthis.preferWebSocket = options?.preferWebSocket ?? true\n\t\tthis.webSocketFactory =\n\t\t\toptions?.webSocketFactory ?? (() => new WebSocketTransport({ serializer: this.serializer }))\n\t}\n\n\tasync connect(url: string, options?: TransportOptions): Promise<void> {\n\t\tif (this.connected) {\n\t\t\tthrow new SyncError('HTTP long-poll transport already connected', { url })\n\t\t}\n\n\t\tthis.url = normalizeHttpUrl(url)\n\t\tthis.authToken = options?.authToken\n\n\t\tif (this.preferWebSocket) {\n\t\t\tconst upgraded = await this.tryUpgradeToWebSocket(url, options)\n\t\t\tif (upgraded) {\n\t\t\t\tthis.upgradedTransport = upgraded\n\t\t\t\tthis.connected = true\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.connected = true\n\t\tthis.sessionId = null\n\t\tthis.postChain = Promise.resolve()\n\t\tthis.pollAbort = new AbortController()\n\t\t// Polling starts once the server has issued a session id (first POST).\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\tif (this.upgradedTransport) {\n\t\t\tawait this.upgradedTransport.disconnect()\n\t\t\tthis.upgradedTransport = null\n\t\t}\n\n\t\tthis.connected = false\n\t\tthis.polling = false\n\t\tthis.pollAbort?.abort()\n\t\tthis.pollAbort = null\n\t\tthis.url = null\n\t\tthis.sessionId = null\n\t}\n\n\tsend(message: import('../protocol/messages').SyncMessage): void {\n\t\tif (!this.connected) {\n\t\t\tthrow new SyncError('Cannot send message: HTTP long-poll transport is not connected', {\n\t\t\t\tmessageType: message.type,\n\t\t\t})\n\t\t}\n\n\t\tif (this.upgradedTransport) {\n\t\t\tthis.upgradedTransport.send(message)\n\t\t\treturn\n\t\t}\n\n\t\tthis.postChain = this.postChain.then(() => this.postMessage(message))\n\t}\n\n\tonMessage(handler: TransportMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t\tif (this.upgradedTransport) {\n\t\t\tthis.upgradedTransport.onMessage(handler)\n\t\t}\n\t}\n\n\tonClose(handler: TransportCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t\tif (this.upgradedTransport) {\n\t\t\tthis.upgradedTransport.onClose(handler)\n\t\t}\n\t}\n\n\tonError(handler: TransportErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t\tif (this.upgradedTransport) {\n\t\t\tthis.upgradedTransport.onError(handler)\n\t\t}\n\t}\n\n\tisConnected(): boolean {\n\t\tif (this.upgradedTransport) {\n\t\t\treturn this.upgradedTransport.isConnected()\n\t\t}\n\t\treturn this.connected\n\t}\n\n\tprivate async tryUpgradeToWebSocket(\n\t\turl: string,\n\t\toptions?: TransportOptions,\n\t): Promise<SyncTransport | null> {\n\t\tconst wsTransport = this.webSocketFactory()\n\n\t\tif (this.messageHandler) wsTransport.onMessage(this.messageHandler)\n\t\tif (this.closeHandler) wsTransport.onClose(this.closeHandler)\n\t\tif (this.errorHandler) wsTransport.onError(this.errorHandler)\n\n\t\ttry {\n\t\t\tawait wsTransport.connect(normalizeWebSocketUrl(url), options)\n\t\t\treturn wsTransport\n\t\t} catch {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tprivate async postMessage(message: import('../protocol/messages').SyncMessage): Promise<void> {\n\t\tif (!this.url || !this.connected) return\n\n\t\tconst encoded = this.serializer.encode(message)\n\t\tconst headers = this.makeHeaders()\n\n\t\tconst isBinary = encoded instanceof Uint8Array\n\t\theaders.set('content-type', isBinary ? 'application/x-protobuf' : 'application/json')\n\n\t\ttry {\n\t\t\tconst requestBody = isBinary ? toArrayBuffer(encoded) : encoded\n\n\t\t\tconst response = await this.fetchImpl(this.url, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders,\n\t\t\t\tbody: requestBody,\n\t\t\t})\n\n\t\t\tif (SESSION_ENDED_STATUSES.has(response.status)) {\n\t\t\t\tthis.endSession(response.status)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new SyncError('HTTP transport send failed', {\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\tmessageType: message.type,\n\t\t\t\t})\n\t\t\t}\n\t\t\tconst issued = response.headers.get(HTTP_SYNC_SESSION_HEADER)\n\t\t\tif (this.sessionId === null && issued) {\n\t\t\t\tthis.sessionId = issued\n\t\t\t\tthis.startPolling()\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.errorHandler?.(error instanceof Error ? error : new Error(String(error)))\n\t\t}\n\t}\n\n\tprivate startPolling(): void {\n\t\tif (this.polling || !this.connected) return\n\t\tthis.polling = true\n\t\tvoid this.runPollLoop()\n\t}\n\n\t/** The server-side session is gone: stop and report a close so the engine reconnects. */\n\tprivate endSession(status: number): void {\n\t\tif (!this.connected) return\n\t\tthis.connected = false\n\t\tthis.polling = false\n\t\tthis.pollAbort?.abort()\n\t\tthis.pollAbort = null\n\t\tthis.sessionId = null\n\t\tthis.closeHandler?.(`http sync session ended (${String(status)})`)\n\t}\n\n\tprivate async runPollLoop(): Promise<void> {\n\t\twhile (this.polling && this.connected && this.url) {\n\t\t\ttry {\n\t\t\t\tconst response = await this.fetchImpl(this.url, {\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\theaders: this.makeHeaders(),\n\t\t\t\t\tsignal: this.pollAbort?.signal,\n\t\t\t\t})\n\n\t\t\t\tif (SESSION_ENDED_STATUSES.has(response.status)) {\n\t\t\t\t\tthis.endSession(response.status)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (response.status === 204) {\n\t\t\t\t\tawait sleep(this.retryDelayMs)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new SyncError('HTTP long-poll request failed', {\n\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tconst payload = await readResponsePayload(response)\n\t\t\t\tif (payload === null) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tconst message = this.serializer.decode(payload)\n\t\t\t\tthis.messageHandler?.(message)\n\t\t\t} catch (error) {\n\t\t\t\tif (!this.connected || !this.polling) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tif (isAbortError(error)) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tthis.errorHandler?.(error instanceof Error ? error : new Error(String(error)))\n\t\t\t\tawait sleep(this.retryDelayMs)\n\t\t\t}\n\t\t}\n\n\t\tif (!this.connected) {\n\t\t\tthis.closeHandler?.('http long-polling disconnected')\n\t\t}\n\t}\n\n\t/** Accept, bearer credential and (once issued) the session id: sent on every request. */\n\tprivate makeHeaders(): Headers {\n\t\tconst headers = new Headers()\n\t\theaders.set('accept', 'application/json, application/x-protobuf')\n\t\tif (this.authToken) {\n\t\t\theaders.set('authorization', `Bearer ${this.authToken}`)\n\t\t}\n\t\tif (this.sessionId) {\n\t\t\theaders.set(HTTP_SYNC_SESSION_HEADER, this.sessionId)\n\t\t}\n\t\treturn headers\n\t}\n}\n\nfunction normalizeHttpUrl(url: string): string {\n\tif (url.startsWith('http://') || url.startsWith('https://')) {\n\t\treturn url\n\t}\n\n\tif (url.startsWith('ws://')) {\n\t\treturn `http://${url.slice('ws://'.length)}`\n\t}\n\n\tif (url.startsWith('wss://')) {\n\t\treturn `https://${url.slice('wss://'.length)}`\n\t}\n\n\treturn url\n}\n\nfunction normalizeWebSocketUrl(url: string): string {\n\tif (url.startsWith('ws://') || url.startsWith('wss://')) {\n\t\treturn url\n\t}\n\n\tif (url.startsWith('http://')) {\n\t\treturn `ws://${url.slice('http://'.length)}`\n\t}\n\n\tif (url.startsWith('https://')) {\n\t\treturn `wss://${url.slice('https://'.length)}`\n\t}\n\n\treturn url\n}\n\nasync function readResponsePayload(response: Response): Promise<string | Uint8Array | null> {\n\tconst contentType = response.headers.get('content-type') ?? ''\n\tif (contentType.includes('application/x-protobuf')) {\n\t\tconst buffer = await response.arrayBuffer()\n\t\tif (buffer.byteLength === 0) return null\n\t\treturn new Uint8Array(buffer)\n\t}\n\n\tconst text = await response.text()\n\tif (text.length === 0) return null\n\treturn text\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === 'object' &&\n\t\terror !== null &&\n\t\t'name' in error &&\n\t\t(error as { name?: string }).name === 'AbortError'\n\t)\n}\n\nfunction toArrayBuffer(data: Uint8Array): ArrayBuffer {\n\tconst copied = new Uint8Array(data.byteLength)\n\tcopied.set(data)\n\treturn copied.buffer\n}\n","import type { SyncMessage } from '../protocol/messages'\nimport type {\n\tSyncTransport,\n\tTransportCloseHandler,\n\tTransportErrorHandler,\n\tTransportMessageHandler,\n\tTransportOptions,\n} from './transport'\n\n/**\n * Configuration for the chaos transport.\n */\nexport interface ChaosConfig {\n\t/** Probability of dropping a message (0-1). Defaults to 0. */\n\tdropRate?: number\n\t/** Probability of duplicating a message (0-1). Defaults to 0. */\n\tduplicateRate?: number\n\t/** Probability of reordering messages (0-1). Defaults to 0. */\n\treorderRate?: number\n\t/** Maximum latency in ms for delayed messages. Defaults to 0. */\n\tmaxLatency?: number\n\t/** Injectable random source for deterministic testing. Returns value in [0, 1). */\n\trandomSource?: () => number\n\t/**\n\t * Deterministic targeted drop. When it returns true for a message and direction,\n\t * that message is dropped unconditionally (independent of `dropRate`). Enables\n\t * reproducing an exact gap (e.g. drop one specific operation) instead of relying\n\t * on probabilistic drops. `direction` is 'outgoing' for `send` and 'incoming' for\n\t * messages arriving from the inner transport.\n\t */\n\tdropPredicate?: (message: SyncMessage, direction: 'outgoing' | 'incoming') => boolean\n}\n\n/**\n * Chaos transport that wraps another transport and injects faults.\n * Used for testing sync convergence under unreliable network conditions.\n *\n * Supports message dropping, duplication, reordering, and latency injection.\n * All random behavior is injectable for deterministic, reproducible tests.\n */\nexport class ChaosTransport implements SyncTransport {\n\tprivate readonly inner: SyncTransport\n\tprivate readonly dropRate: number\n\tprivate readonly duplicateRate: number\n\tprivate readonly reorderRate: number\n\tprivate readonly maxLatency: number\n\tprivate readonly random: () => number\n\tprivate readonly dropPredicate:\n\t\t| ((message: SyncMessage, direction: 'outgoing' | 'incoming') => boolean)\n\t\t| null\n\n\tprivate messageHandler: TransportMessageHandler | null = null\n\tprivate reorderBuffer: SyncMessage[] = []\n\tprivate pending: Array<{ timer: ReturnType<typeof setTimeout>; message: SyncMessage }> = []\n\n\tconstructor(inner: SyncTransport, config?: ChaosConfig) {\n\t\tthis.inner = inner\n\t\tthis.dropRate = config?.dropRate ?? 0\n\t\tthis.duplicateRate = config?.duplicateRate ?? 0\n\t\tthis.reorderRate = config?.reorderRate ?? 0\n\t\tthis.maxLatency = config?.maxLatency ?? 0\n\t\tthis.random = config?.randomSource ?? Math.random\n\t\tthis.dropPredicate = config?.dropPredicate ?? null\n\t}\n\n\tasync connect(url: string, options?: TransportOptions): Promise<void> {\n\t\t// Intercept incoming messages from the inner transport\n\t\tthis.inner.onMessage((msg) => this.handleIncoming(msg))\n\t\treturn this.inner.connect(url, options)\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\t// Flush reorder buffer\n\t\tthis.flushReorderBuffer()\n\t\t// Deliver any incoming messages still sitting in their simulated-latency\n\t\t// window rather than discarding them. `maxLatency` models delay already\n\t\t// incurred on the wire before we hung up: the peer already sent these,\n\t\t// so a local disconnect() shouldn't retroactively erase them. Cancelling\n\t\t// the timer without delivering silently inflates the effective drop\n\t\t// rate past what `dropRate` configures, and makes convergence depend on\n\t\t// the real wall-clock race between each message's random delay and\n\t\t// whenever a test happens to call disconnect() next, exactly the kind\n\t\t// of timing dependency that makes \"seeded\" chaos tests flaky anyway.\n\t\tconst flushed = this.pending\n\t\tthis.pending = []\n\t\tfor (const { timer } of flushed) {\n\t\t\tclearTimeout(timer)\n\t\t}\n\t\tfor (const { message } of flushed) {\n\t\t\tthis.deliverIncoming(message)\n\t\t}\n\t\treturn this.inner.disconnect()\n\t}\n\n\tsend(message: SyncMessage): void {\n\t\t// Deterministic targeted drop takes precedence over probabilistic drop.\n\t\tif (this.dropPredicate?.(message, 'outgoing')) {\n\t\t\treturn // Dropped\n\t\t}\n\t\t// Apply chaos to outgoing messages\n\t\tif (this.random() < this.dropRate) {\n\t\t\treturn // Dropped\n\t\t}\n\n\t\tif (this.random() < this.reorderRate) {\n\t\t\tthis.reorderBuffer.push(message)\n\t\t\t// Flush buffer on next non-reordered send\n\t\t\treturn\n\t\t}\n\n\t\t// Flush any buffered messages first\n\t\tthis.flushReorderBuffer()\n\n\t\tthis.inner.send(message)\n\n\t\t// Duplicate?\n\t\tif (this.random() < this.duplicateRate) {\n\t\t\tthis.inner.send(message)\n\t\t}\n\t}\n\n\tonMessage(handler: TransportMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: TransportCloseHandler): void {\n\t\tthis.inner.onClose(handler)\n\t}\n\n\tonError(handler: TransportErrorHandler): void {\n\t\tthis.inner.onError(handler)\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.inner.isConnected()\n\t}\n\n\tprivate handleIncoming(message: SyncMessage): void {\n\t\tif (!this.messageHandler) return\n\n\t\t// Deterministic targeted drop takes precedence over probabilistic drop.\n\t\tif (this.dropPredicate?.(message, 'incoming')) {\n\t\t\treturn // Dropped\n\t\t}\n\t\t// Apply chaos to incoming messages\n\t\tif (this.random() < this.dropRate) {\n\t\t\treturn // Dropped\n\t\t}\n\n\t\tif (this.maxLatency > 0) {\n\t\t\tconst delay = Math.floor(this.random() * this.maxLatency)\n\t\t\t// `timer` is assigned before the callback can possibly run (setTimeout\n\t\t\t// callbacks never fire synchronously), so the closure below always\n\t\t\t// sees the real handle, letting the entry remove itself from\n\t\t\t// `pending` once its delivery actually happens.\n\t\t\tconst timer: ReturnType<typeof setTimeout> = setTimeout(() => {\n\t\t\t\tthis.pending = this.pending.filter((p) => p.timer !== timer)\n\t\t\t\tthis.deliverIncoming(message)\n\t\t\t}, delay)\n\t\t\tthis.pending.push({ timer, message })\n\t\t\treturn\n\t\t}\n\n\t\tthis.deliverIncoming(message)\n\t}\n\n\tprivate deliverIncoming(message: SyncMessage): void {\n\t\tif (!this.messageHandler) return\n\n\t\tthis.messageHandler(message)\n\n\t\t// Duplicate incoming?\n\t\tif (this.random() < this.duplicateRate) {\n\t\t\tthis.messageHandler(message)\n\t\t}\n\t}\n\n\tprivate flushReorderBuffer(): void {\n\t\t// Send buffered messages in random order\n\t\tconst buffer = [...this.reorderBuffer]\n\t\tthis.reorderBuffer = []\n\n\t\t// Fisher-Yates shuffle with injectable random\n\t\tfor (let i = buffer.length - 1; i > 0; i--) {\n\t\t\tconst j = Math.floor(this.random() * (i + 1))\n\t\t\tconst temp = buffer[i] as SyncMessage\n\t\t\tbuffer[i] = buffer[j] as SyncMessage\n\t\t\tbuffer[j] = temp\n\t\t}\n\n\t\tfor (const msg of buffer) {\n\t\t\tthis.inner.send(msg)\n\t\t}\n\t}\n}\n","import type {\n\tApplyFailureReason,\n\tApplyResult,\n\tKoraEventEmitter,\n\tOperation,\n\tOperationTransform,\n\tSyncDiagnosticsSnapshot,\n\tVersionVector,\n} from '@korajs/core'\nimport {\n\tAPPLY_FAILURE_CODES,\n\tClockDriftError,\n\tHybridLogicalClock,\n\tInvalidTimestampError,\n\tKoraError,\n\tRemoteClockDriftError,\n\tSyncError,\n\tcanonicalizeProvenLegacyClear,\n\tdefaultApplyFailureReason,\n\tisAuthoritativeNodeId,\n\tisServerNodeId,\n\toperationSchemaView,\n\toperationTransformsFingerprint,\n} from '@korajs/core'\nimport { AwarenessManager } from '../awareness/awareness-manager'\nimport type { AwarenessMessage, AwarenessState } from '../awareness/types'\nimport { BlobChunkChannel } from '../blob/blob-chunk-channel'\nimport {\n\tcreateDeltaCursorFromBatch,\n\tdecodeDeltaCursor,\n\tencodeDeltaCursor,\n\tsliceOperationsAfterCursor,\n} from '../delta/delta-cursor'\nimport { SyncMetricsCollector } from '../diagnostics/metrics-collector'\nimport type { MetricsCollectorConfig } from '../diagnostics/metrics-collector'\nimport type {\n\tEncryptionKeyResponseMessage,\n\tEncryptionKeyResponseStatus,\n} from '../encryption/key-messages'\nimport type { WrappedKeyRecord } from '../encryption/key-record'\nimport type { EncryptionKeyring, KeyServiceChannel, KeyServiceReply } from '../encryption/keyring'\nimport { EncryptionKeyError } from '../encryption/keyring'\nimport type { SyncEncryptor } from '../encryption/sync-encryptor'\nimport type {\n\tAcknowledgmentMessage,\n\tAwarenessStateWire,\n\tAwarenessUpdateMessage,\n\tBlobChunkPushMessage,\n\tBlobChunkRequestMessage,\n\tBlobChunkResponseMessage,\n\tHandshakeResponseMessage,\n\tOperationBatchMessage,\n\tOperationRejectedMessage,\n\tSyncMessage,\n\tWireFormat,\n\tYjsDocUpdateMessage,\n} from '../protocol/messages'\nimport { SYNC_PROTOCOL_VERSION, declaredProtocolVersion } from '../protocol/protocol-version'\nimport { isSchemaMismatchReject } from '../protocol/schema-version'\nimport {\n\tNegotiatedMessageSerializer,\n\tversionVectorToWire,\n\twireToVersionVector,\n} from '../protocol/serializer'\nimport type { MessageSerializer } from '../protocol/serializer'\nimport { RichtextDocChannel } from '../richtext/richtext-doc-channel'\nimport {\n\ttype SyncQuerySubset,\n\tdedupeQuerySubsets,\n\tquerySubsetContains,\n} from '../scopes/query-subset'\nimport { operationMatchesScope } from '../scopes/scope-filter'\nimport { scopeViewKey } from '../scopes/scope-view-key'\nimport type { SyncTransport } from '../transport/transport'\nimport type { DeltaCursor } from '../types'\nimport {\n\ttype AdoptionScheduleInfo,\n\ttype HeldNodeInfo,\n\ttype HeldReason,\n\ttype LocalNodeInfo,\n\tMemoryRejectedOperationStorage,\n\ttype QuarantinedOperation,\n\ttype QueueStorage,\n\ttype RejectedOperation,\n\ttype RejectedOperationStorage,\n\ttype SyncConfig,\n\ttype SyncScopeMap,\n\ttype SyncState,\n\ttype SyncStatePersistence,\n\ttype SyncStatusInfo,\n} from '../types'\nimport { MemoryQueueStorage } from './memory-queue-storage'\nimport type { OutboundBatch } from './outbound-queue'\nimport { OutboundQueue } from './outbound-queue'\nimport type { SyncStore } from './sync-store'\nimport { verifyInboundOperation } from './verify-inbound'\n\nconst DEFAULT_BATCH_SIZE = 100\nconst DEFAULT_SCHEMA_VERSION = 1\nconst DEFAULT_OUTBOUND_ACK_TIMEOUT_MS = 30000\nconst DEFAULT_OUTBOUND_RETRY_BASE_DELAY_MS = 250\nconst DEFAULT_OUTBOUND_RETRY_MAX_DELAY_MS = 30000\n/**\n * How many own sequence numbers the engine reads from the op log at a time when it\n * (re)builds the upload set. Bounds memory during the one-time upgrade re-upload of a\n * device's whole history: the next chunk is read only once the previous one is resolved.\n * Large on purpose: an engine without sync-state persistence re-reads its history on\n * every start, and against a legacy (version-vector) server a relay of a later chunk\n * that overtakes a dropped earlier one leaves a vector gap on peers.\n */\nconst OWN_LOG_SCAN_CHUNK = 2000\n/**\n * A delivered operation stamped further than this ahead of the trusted reference time\n * is quarantined instead of applied (SYNC-7). Matches the HLC's own refusal threshold.\n */\nconst MAX_REMOTE_FUTURE_MS = 5 * 60_000\n/**\n * Consecutive failed durability barriers before uploads stop waiting for local durability\n * (RT-49). A transient failure (a busy IndexedDB transaction) postpones the upload and\n * retries; a persistent one (quota exceeded, a broken store) would otherwise withhold every\n * write from the reachable server while the app keeps writing into memory. Once the device\n * may lose its local tail anyway, the server is the better place for it: RT-35 recovery\n * (handshake raise, full resync, renumber on SEQUENCE_CONFLICT) restores it after a reload.\n */\nconst DURABILITY_FAILURES_BEFORE_DEGRADED = 3\n/**\n * Backoff of a parked adoption (RT-46): an adopted node whose uploads the server keeps\n * deferring is retried once anything else from this database uploaded, or after this\n * delay (doubling per consecutive park, capped).\n */\nconst ADOPTION_PARK_BASE_MS = 30_000\nconst ADOPTION_PARK_MAX_MS = 60 * 60_000\n/** Terminal marker of held writes the app discarded from sync (RT-50). */\nconst HELD_DISCARDED_CODE = 'HELD_DISCARDED'\n/** `FoldStateError.code` (`@korajs/core`): a record state the fold cannot merge into. */\nconst FOLD_STATE_INVALID_CODE = 'FOLD_STATE_INVALID'\n/** Times one start() begins again because the signed-in user changed while connecting (RT-52). */\nconst MAX_PRINCIPAL_RESTARTS = 2\n/**\n * A live session of this tab's own node ends to retry a parked adoption (that other\n * uploads may have unblocked) at most this soon after it was parked, so a node the\n * server defers for good costs at most one reconnect per interval (RT-46).\n */\nconst ADOPTION_YIELD_MIN_MS = 30_000\n/** Minimum time between two full resyncs triggered by SEQUENCE_CONFLICT recoveries (RT-44). */\nconst FULL_RESYNC_MIN_INTERVAL_MS = 60_000\n/** Quarantine reason codes for inbound operations the client did not apply (W4). */\nconst QUARANTINE_CODES = {\n\tDECRYPT_FAILED: 'DECRYPT_FAILED',\n\tREMOTE_CLOCK_DRIFT: 'REMOTE_CLOCK_DRIFT',\n\tTRANSFORM_UNAVAILABLE: 'SCHEMA_TRANSFORM_UNAVAILABLE',\n\tTRANSFORM_INVALID: 'SCHEMA_TRANSFORM_INVALID',\n} as const\n\n/** A taken outbound batch, from the moment it leaves the queue until it is resolved. */\ninterface InFlightUpload {\n\tbatch: OutboundBatch\n\t/** Wire message id once sent; acks are matched to batches by it (SYNC-4). */\n\tmessageId: string | null\n\t/** Session the batch was taken in; a batch from an older session is never sent. */\n\tepoch: number\n\t/** A retriable rejection hit this batch: back off before flushing again. */\n\tretryBackoff: boolean\n}\n\n/** A local node's unsynced operations (see SyncEngine.countUnsyncedOfNode). */\ninterface UnsyncedOfNode {\n\tcount: number\n\tnotQueued: number\n\tids: ReadonlySet<string>\n\tdeps: ReadonlySet<string>\n}\n\n/**\n * Valid state transitions for the sync engine state machine.\n */\n/** Server error codes that end a session because its credential expired or was revoked. */\nfunction isCredentialEndingCode(code: string): boolean {\n\treturn code === 'AUTH_EXPIRED' || code === 'AUTH_REVOKED'\n}\n\nconst VALID_TRANSITIONS: Record<SyncState, SyncState[]> = {\n\tdisconnected: ['connecting'],\n\tconnecting: ['handshaking', 'error', 'disconnected'],\n\thandshaking: ['syncing', 'error', 'disconnected'],\n\tsyncing: ['streaming', 'error', 'disconnected'],\n\tstreaming: ['disconnected', 'error'],\n\terror: ['disconnected'],\n}\n\n/**\n * Options for creating a SyncEngine.\n */\nexport interface SyncEngineOptions {\n\t/** Transport implementation (WebSocket, memory, etc.) */\n\ttransport: SyncTransport\n\t/** Local store implementing SyncStore */\n\tstore: SyncStore\n\t/** Sync configuration */\n\tconfig: SyncConfig\n\t/** Message serializer. Defaults to JSON. */\n\tserializer?: MessageSerializer\n\t/** Event emitter for DevTools integration */\n\temitter?: KoraEventEmitter\n\t/** Queue storage for persistent outbound queue. Defaults to in-memory. */\n\tqueueStorage?: QueueStorage\n\t/**\n\t * Durable storage for operations the server rejected. Defaults to in-memory.\n\t * Provide a store-backed implementation so rejections survive a page refresh.\n\t */\n\trejectedStorage?: RejectedOperationStorage\n\t/**\n\t * Optional encryptor for end-to-end encryption.\n\t * When provided, `data` and `previousData` fields of operations are encrypted\n\t * before sending and decrypted after receiving. The server never sees plaintext data.\n\t */\n\tencryptor?: SyncEncryptor\n\t/**\n\t * End-to-end encryption keyring (ENC-1): the server-stored, passphrase-wrapped data\n\t * keys, opened during each handshake before any operation is exchanged. Takes the\n\t * place of `encryptor`. While it is locked, sync does not connect.\n\t */\n\tkeyring?: EncryptionKeyring\n\t/** How long a key-service request may take before the session is retried (ms). */\n\tkeyServiceTimeoutMs?: number\n\t/** Optional configuration for the metrics collector. */\n\tmetricsConfig?: MetricsCollectorConfig\n\t/** Op-log backed sync state (last acked server vector, unsynced counts). */\n\tsyncState?: SyncStatePersistence\n}\n\n/**\n * Diagnostics snapshot for debugging and support.\n */\nexport interface SyncDiagnostics {\n\tstate: SyncState\n\tstatus: SyncStatusInfo\n\tnodeId: string\n\turl: string\n\tschemaVersion: number\n\tlastSyncedAt: number | null\n\tlastSuccessfulPush: number | null\n\tlastSuccessfulPull: number | null\n\tconflicts: number\n\tpendingOperations: number\n\thasInFlightBatch: boolean\n\treconnecting: boolean\n\tdeliveryWatermark: number\n\tdeliveryGapRepeatCount: number\n\ttimestamp: number\n}\n\nlet nextMessageId = 0\nlet nextQuerySubsetId = 0\n\n/** Rejection code for a local operation the uplink scope refuses (recorded client-side). */\nconst OUT_OF_UPLINK_SCOPE = 'OUT_OF_UPLINK_SCOPE'\n/** The server holds another operation of this node under the sequence number (RT-35). */\nconst SEQUENCE_CONFLICT = 'SEQUENCE_CONFLICT'\n/**\n * Non-retriable rejection codes that never become a durable terminal marker (RT-36): the\n * operation was not refused for its content, so uploading it later may succeed.\n */\nconst NON_TERMINAL_REJECTION_CODES: ReadonlySet<string> = new Set([\n\tSEQUENCE_CONFLICT,\n\t'NODE_ID_MISMATCH',\n\tOUT_OF_UPLINK_SCOPE,\n])\n/** Default deadline of one key-service request. */\nconst DEFAULT_KEY_SERVICE_TIMEOUT_MS = 15_000\n\nfunction generateMessageId(): string {\n\treturn `msg-${Date.now()}-${nextMessageId++}`\n}\n\n/**\n * Upper bound on the number of per-view delivery watermarks retained (in memory and in\n * persistence). A client that churns through many distinct views (for example a search that\n * registers a fresh query subscription per keystroke) would otherwise accumulate an\n * unbounded number of `_kora_meta` rows. Cold views are evicted least-recently-used; the\n * default view and the live view are never evicted. Eviction is a storage/performance\n * tradeoff only, never a correctness one: an evicted view back-fills from 0 (deduplicated)\n * when next visited.\n */\nconst MAX_DELIVERY_VIEW_WATERMARKS = 64\n\n/**\n * Deterministic JSON stringify with sorted object keys, so two equal values always\n * produce the identical string. Used to build a stable sync-view signature for keying the\n * per-view delivery watermark.\n */\nfunction stableStringify(value: unknown): string {\n\tif (value === null || typeof value !== 'object') {\n\t\treturn JSON.stringify(value) ?? 'null'\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn `[${value.map(stableStringify).join(',')}]`\n\t}\n\tconst entries = Object.entries(value as Record<string, unknown>)\n\t\t.filter(([, v]) => v !== undefined)\n\t\t.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n\t\t.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`)\n\treturn `{${entries.join(',')}}`\n}\n\n/**\n * Core sync orchestrator. Manages the sync lifecycle:\n * disconnected → connecting → handshaking → syncing → streaming\n *\n * Coordinates handshake, delta exchange, and real-time streaming\n * between a local store and a remote sync server.\n */\nexport class SyncEngine {\n\tprivate state: SyncState = 'disconnected'\n\t/** Listeners registered through {@link onStateChange}. */\n\tprivate readonly stateListeners = new Set<(state: SyncState) => void>()\n\tprivate readonly transport: SyncTransport\n\tprivate readonly store: SyncStore\n\tprivate readonly config: SyncConfig\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly emitter: KoraEventEmitter | null\n\tprivate readonly outboundQueue: OutboundQueue\n\tprivate readonly rejectedStorage: RejectedOperationStorage\n\tprivate readonly batchSize: number\n\tprivate readonly outboundAckTimeoutMs: number\n\tprivate readonly outboundRetryBaseDelayMs: number\n\tprivate readonly outboundRetryMaxDelayMs: number\n\tprivate readonly encryptor: SyncEncryptor | null\n\tprivate readonly keyring: EncryptionKeyring | null\n\tprivate readonly keyServiceTimeoutMs: number\n\t/** Key-service requests awaiting their response, by request id. */\n\tprivate readonly keyRequests = new Map<\n\t\tstring,\n\t\t{\n\t\t\tresolve: (message: EncryptionKeyResponseMessage) => void\n\t\t\treject: (error: Error) => void\n\t\t\ttimer: ReturnType<typeof setTimeout>\n\t\t}\n\t>()\n\t/** Sync is paused because the encryption keyring is locked (ENC-1). */\n\tprivate encryptionLocked = false\n\t/** A key-record refresh for an unknown key version is running. */\n\tprivate keyRefreshInFlight = false\n\tprivate readonly awarenessManager: AwarenessManager\n\tprivate readonly richtextDocChannel: RichtextDocChannel\n\tprivate readonly blobChunkChannel: BlobChunkChannel\n\tprivate readonly metricsCollector: SyncMetricsCollector\n\tprivate readonly syncState: SyncStatePersistence | null\n\n\tprivate remoteVector: VersionVector = new Map()\n\t/** Runs before each outbound operation batch is sent (see setOutboundPreparer). */\n\tprivate outboundPreparer: ((operations: Operation[]) => Promise<void>) | null = null\n\t/**\n\t * Per-device node token the server issued at this node id's first anonymous claim\n\t * (RT-12). Presented in every handshake; persisted next to the node id when the\n\t * sync-state persistence supports it, otherwise kept for this engine's lifetime.\n\t */\n\tprivate nodeToken: string | null = null\n\t/**\n\t * Node ids the server named authoritative in its last accepted handshake response\n\t * (protocol v2). Null until a protocol-2 server answered in this engine's lifetime;\n\t * loaded from persistence on start. See {@link SyncEngine.getAuthoritativeNodeIds}.\n\t */\n\tprivate authoritativeNodeIds: string[] | null = null\n\t/** Protocol version of the server in the current session (1 for a beta.12-era server). */\n\tprivate serverProtocolVersion = 1\n\t/** Whether any server answered a handshake (serverProtocolVersion is then real). */\n\tprivate serverProtocolKnown = false\n\tprivate lastAckedServerVector: VersionVector = new Map()\n\tprivate cachedUnsyncedCount = 0\n\tprivate lastSyncedAt: number | null = null\n\tprivate lastSuccessfulPush: number | null = null\n\tprivate lastSuccessfulPull: number | null = null\n\tprivate conflictCount = 0\n\t/**\n\t * Every outbound batch taken from the queue and not yet resolved, keyed by queue batch\n\t * id: handshake-delta and streaming batches alike. An ack resolves only the batch it\n\t * names (SYNC-4); a close returns all of them to the queue.\n\t */\n\tprivate readonly inFlightUploads = new Map<string, InFlightUpload>()\n\t/** Wire message id -> queue batch id, for ack and rejection correlation. */\n\tprivate readonly uploadByMessageId = new Map<string, string>()\n\t/** Incremented on every connect and every disconnect; stale async sends check it. */\n\tprivate sessionEpoch = 0\n\t/** Set by destroy(): the engine never connects or arms a timer again. */\n\tprivate destroyed = false\n\tprivate readonly statusListeners = new Set<() => void>()\n\t/**\n\t * Contiguous acknowledged prefix of this device's own operations (W3 step 2): every\n\t * own op with sequence <= this is stored on the server, terminally rejected and\n\t * recorded, or not upload-eligible. Never advanced by a server-advertised vector.\n\t */\n\tprivate ownAckedThrough = 0\n\t/** Node id the prefix belongs to (a rotation starts a new prefix at 0). */\n\tprivate ownTrackingNodeId: string | null = null\n\t/** Highest own sequence read from the op log this session (above the prefix). */\n\tprivate ownScannedThrough = 0\n\t/**\n\t * Own sequences above the prefix that this session knows about, mapped to the ids of\n\t * their operations that are still unresolved. An empty set marks a sequence resolved.\n\t */\n\tprivate readonly ownUnresolved = new Map<number, Set<string>>()\n\t/** Ids resolved at sequences the log scan has not reached yet (skip on scan). */\n\tprivate readonly ownResolvedAhead = new Map<string, number>()\n\tprivate ownTrackingChain: Promise<void> = Promise.resolve()\n\t/** Inbound quarantine kept in memory when the engine has no sync-state persistence. */\n\tprivate readonly memoryQuarantine = new Map<string, QuarantinedOperation>()\n\tprivate outboundRetryAttempt = 0\n\tprivate outboundRetryTimer: ReturnType<typeof setTimeout> | null = null\n\t/** {@link pushOperation} calls not yet enqueued; the streaming flush waits for 0. */\n\tprivate pendingPushes = 0\n\tprivate reconnecting = false\n\tprivate schemaBlocked = false\n\tprivate clockBlocked = false\n\tprivate clockSkewMs: number | null = null\n\tprivate blobStorageEnabled = false\n\tprivate blobPossessionProof = false\n\tprivate suspensionReason: string | null = null\n\tprivate authRejected = false\n\t/**\n\t * Set when the server ended the session with AUTH_EXPIRED or AUTH_REVOKED. The\n\t * next connect asks the auth callback for a refreshed token (never the cached\n\t * one the server just refused) and is cleared by an accepted handshake.\n\t */\n\tprivate credentialRefreshRequired = false\n\tprivate serverFrontier: number | null = null\n\tprivate hasInFlightDeliveryBatch = false\n\tprivate blockedFailure: import('../types').ActiveApplyFailure | null = null\n\tprivate outboundAckTimer: ReturnType<typeof setTimeout> | null = null\n\tprivate startPromise: Promise<void> | null = null\n\tprivate stopPromise: Promise<void> | null = null\n\tprivate reconnectPromise: Promise<void> | null = null\n\t/** A node-id rotation started by NODE_ID_CLAIMED; the next connect waits for it (RT-21). */\n\tprivate nodeRotation: Promise<void> | null = null\n\t/**\n\t * Node id of the current (or last) session. Usually the store's node id; while the\n\t * engine adopts the unsynced writes of a node no live tab uses (RT-40) it is that\n\t * node. A session uploads only operations authored under its node id.\n\t */\n\tprivate sessionNodeId: string | null = null\n\t/** Node id the in-memory node token belongs to. */\n\tprivate nodeTokenNodeId: string | null = null\n\t/** Releases the lock held on an adopted node (RT-40); null when not adopting. */\n\tprivate releaseAdoption: (() => void) | null = null\n\t/**\n\t * Ids of operations the server refused for good (RT-36). Cache of the durable markers,\n\t * and the only record when the persistence layer has none (engine lifetime).\n\t */\n\tprivate readonly terminalRejected = new Set<string>()\n\t/** Nodes accepted at a handshake, when the persistence layer keeps no registry. */\n\tprivate readonly memoryAcceptedNodes = new Set<string>()\n\t/** Every node id this database authored operations under (loaded at start). */\n\tprivate localNodeIds = new Set<string>()\n\t/** Local nodes whose unsynced writes are held for another principal (RT-38). */\n\tprivate heldNodeIds = new Set<string>()\n\t/** Unsynced, unqueued operations of other uploadable local nodes (RT-40), counted pending. */\n\tprivate otherNodesPending = 0\n\t/** Unsynced, unqueued operations of held nodes (RT-38), reported separately. */\n\tprivate heldPending = 0\n\t/** Held local nodes as of the last session start (RT-50), for status.heldNodes. */\n\tprivate heldNodeInfos: HeldNodeInfo[] = []\n\t/**\n\t * The server holds operations of this node that the device lost (RT-35): the delivery\n\t * watermark was reset so the next session resyncs from 0, which fetches them back.\n\t * The session ends once its in-flight uploads are resolved.\n\t */\n\tprivate ownRecoveryPending = false\n\t/**\n\t * The signed-in user as resolved for the current session (RT-42): a user id, null\n\t * (nobody signed in) or undefined (the app does not tell the engine).\n\t */\n\tprivate principal: string | null | undefined = undefined\n\t/**\n\t * The user whose credential the current session's handshake carried (RT-50, RT-52):\n\t * re-read after the credential fetch, so an accepted handshake binds a node to the\n\t * user the server accepted it for, never to a user who signed out meanwhile.\n\t */\n\tprivate sessionPrincipal: string | null | undefined = undefined\n\t/** Serializes store bindings to the signed-in user (RT-52). */\n\tprivate principalChain: Promise<void> = Promise.resolve()\n\t/** Adoption schedule kept in memory when the persistence layer has none (RT-46). */\n\tprivate memorySchedule: AdoptionScheduleInfo = { progress: 0, parked: {} }\n\t/** The schedule had parked adoptions when last read or written (RT-46). */\n\tprivate scheduleHasParked = false\n\t/**\n\t * Local nodes with unsynced writes this session did not adopt (RT-46): parked (no\n\t * progress last time) or waiting for another local node's unsynced parent.\n\t */\n\tprivate deferredAdoptions: Array<{\n\t\tnodeId: string\n\t\treason: 'parked' | 'causal'\n\t\tparkedAtMs: number\n\t\tuntilMs: number\n\t\tprogressMark: number\n\t}> = []\n\t/** Operations acknowledged in this session (RT-46: a yield needs progress). */\n\tprivate sessionAcked = 0\n\t/** An upload of the adopted node came back unprocessed in this session (RT-46). */\n\tprivate adoptionStalled = false\n\t/**\n\t * The session node's entry in this session's handshake vector (RT-44). The server held\n\t * nothing of the node above it then; a SEQUENCE_CONFLICT above it was caused by an\n\t * operation stored during this session by another live copy of this database.\n\t */\n\tprivate handshakeOwnEntry = 0\n\t/** When this engine last reset delivery for a SEQUENCE_CONFLICT recovery (RT-44). */\n\tprivate lastFullResyncAt = Number.NEGATIVE_INFINITY\n\t/** A recovery full resync was rate-limited; the next session start after the interval runs it. */\n\tprivate fullResyncDeferred = false\n\t/** Consecutive failed durability barriers before an upload (RT-49). */\n\tprivate durabilityFailures = 0\n\t/** Uploads proceed without local durability: the barrier failed persistently (RT-49). */\n\tprivate durabilityDegraded = false\n\n\t// Track delta exchange state\n\tprivate deltaBatchesReceived = 0\n\tprivate deltaReceiveComplete = false\n\tprivate deltaSendComplete = false\n\t/** Outbound delta batch message IDs awaiting ACK when strictHandshake is enabled */\n\tprivate pendingDeltaBatchAcks = new Set<string>()\n\n\t/**\n\t * The effective scope for this sync session.\n\t * Starts as the configured scopeMap. After handshake, may be replaced\n\t * with the server-accepted scope (server is authoritative).\n\t */\n\tprivate activeScope: SyncScopeMap | undefined\n\t/** Server-authoritative upload authorization, separate from the downloaded view. */\n\tprivate activeUplinkScope: SyncScopeMap | undefined\n\t/**\n\t * The downlink scope the server accepted at the last handshake that named one\n\t * (SYNC-11), persisted. The next handshake reports its key and that view's watermark\n\t * so the server can resume the stream of an unchanged grant. Never sent as the\n\t * requested scope: a later-widened grant must not be narrowed to it.\n\t */\n\tprivate lastAcceptedScope: SyncScopeMap | null = null\n\n\t/** Live query subsets registered from reactive subscriptions */\n\tprivate querySubsets = new Map<string, SyncQuerySubset>()\n\tprivate staticQuerySubsets: SyncQuerySubset[] = []\n\tprivate querySubsetReconnectTimer: ReturnType<typeof setTimeout> | null = null\n\t/**\n\t * Fires when the earliest parked adoption's backoff runs out (RT-53): an idle session of\n\t * this tab's own node then hands over to it, so a closed tab's deferred writes are\n\t * retried even if this tab never writes or reconnects.\n\t */\n\tprivate adoptionRetryTimer: ReturnType<typeof setTimeout> | null = null\n\n\t/** Resume cursor for paginated initial sync (persisted across reconnects) */\n\tprivate resumeDeltaCursor: DeltaCursor | null = null\n\tprivate initialSyncTotalBatches = 0\n\n\t/**\n\t * The delivery watermark for the CURRENT sync view: the highest server delivery\n\t * sequence up to which every operation in the current view (scope + active query\n\t * subscriptions) has been contiguously applied. Advanced only through the server's\n\t * gap-free delivery stream (never through live relay), so it is a durable lower bound\n\t * on what this client holds for this view. Reported at handshake to resume the stream.\n\t *\n\t * The watermark is keyed by view: switching views (a scope or subscription change)\n\t * saves the current view's watermark and loads the target view's, so returning to a\n\t * previously-synced view resumes exactly where it left off instead of re-syncing. This\n\t * is correct because a view's watermark is a lower bound specific to that view's\n\t * filter; a widened view is simply a different (initially unsynced) view.\n\t */\n\tprivate deliveryWatermark = 0\n\t/**\n\t * In-memory cache of watermark per view signature, preloaded from persistence at start\n\t * so a view switch resolves synchronously (no race with the reconnect it triggers).\n\t */\n\tprivate readonly deliverySignatureWatermarks = new Map<string, number>()\n\t/** Schema transforms: the engine checks views, the store folds them (RT-84). */\n\tprivate readonly operationTransforms: readonly OperationTransform[]\n\tprivate deliveryGapRepeatCount = 0\n\tprivate lastDeliveryGapKey: string | null = null\n\n\tconstructor(options: SyncEngineOptions) {\n\t\tthis.transport = options.transport\n\t\tthis.store = options.store\n\t\tthis.config = options.config\n\t\tthis.operationTransforms = resolveOperationTransforms(options.config, options.store)\n\t\tthis.serializer = options.serializer ?? new NegotiatedMessageSerializer('json')\n\t\tthis.emitter = options.emitter ?? null\n\t\tthis.batchSize = options.config.batchSize ?? DEFAULT_BATCH_SIZE\n\t\tthis.outboundAckTimeoutMs =\n\t\t\toptions.config.outboundAckTimeoutMs ?? DEFAULT_OUTBOUND_ACK_TIMEOUT_MS\n\t\tthis.outboundRetryBaseDelayMs = Math.max(\n\t\t\t0,\n\t\t\toptions.config.outboundRetryBaseDelayMs ?? DEFAULT_OUTBOUND_RETRY_BASE_DELAY_MS,\n\t\t)\n\t\tthis.outboundRetryMaxDelayMs = Math.max(\n\t\t\tthis.outboundRetryBaseDelayMs,\n\t\t\toptions.config.outboundRetryMaxDelayMs ?? DEFAULT_OUTBOUND_RETRY_MAX_DELAY_MS,\n\t\t)\n\t\tthis.encryptor = options.encryptor ?? null\n\t\tthis.keyring = options.keyring ?? null\n\t\tthis.keyServiceTimeoutMs = options.keyServiceTimeoutMs ?? DEFAULT_KEY_SERVICE_TIMEOUT_MS\n\t\tthis.syncState = options.syncState ?? null\n\t\tthis.activeScope = options.config.scopeMap\n\t\tthis.activeUplinkScope = options.config.scopeMap\n\n\t\tconst queueStorage = options.queueStorage ?? new MemoryQueueStorage()\n\t\tthis.outboundQueue = new OutboundQueue(queueStorage)\n\t\tthis.rejectedStorage = options.rejectedStorage ?? new MemoryRejectedOperationStorage()\n\n\t\tthis.metricsCollector = new SyncMetricsCollector(options.metricsConfig)\n\t\tif (this.emitter) {\n\t\t\tthis.metricsCollector.attachEmitter(this.emitter)\n\t\t}\n\n\t\tthis.awarenessManager = new AwarenessManager({\n\t\t\temitter: this.emitter ?? undefined,\n\t\t})\n\n\t\tthis.richtextDocChannel = new RichtextDocChannel({\n\t\t\tlargeDocThreshold: options.config.richtextDocChannelThreshold,\n\t\t\tonSend: (message: YjsDocUpdateMessage) => {\n\t\t\t\tif (this.state !== 'streaming') {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.transport.send(message)\n\t\t\t},\n\t\t})\n\n\t\tthis.blobChunkChannel = new BlobChunkChannel({\n\t\t\tonSend: (\n\t\t\t\tmessage: BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage,\n\t\t\t) => {\n\t\t\t\t// Blob transfer needs an accepted handshake. Pushes may also go out while\n\t\t\t\t// syncing, so the bytes behind a handshake-delta operation reach the server\n\t\t\t\t// before the operation does (RT-11). A request dropped here is safe: the\n\t\t\t\t// puller times out and retries, and blob transfer is resumable.\n\t\t\t\tconst live =\n\t\t\t\t\tthis.state === 'streaming' ||\n\t\t\t\t\t(this.state === 'syncing' && message.type === 'blob-chunk-push')\n\t\t\t\tif (!live) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.transport.send(message)\n\t\t\t},\n\t\t})\n\n\t\t// Wire awareness manager to send messages through the transport\n\t\tthis.awarenessManager.onSend((message: AwarenessMessage) => {\n\t\t\tif (this.state !== 'streaming') return\n\n\t\t\tconst wireMessage: SyncMessage = {\n\t\t\t\ttype: 'awareness-update',\n\t\t\t\tmessageId: generateMessageId(),\n\t\t\t\tclientId: message.clientId,\n\t\t\t\tstates: awarenessStatesToWire(message.states),\n\t\t\t}\n\t\t\tthis.transport.send(wireMessage)\n\t\t})\n\t}\n\n\t/**\n\t * Start the sync engine: connect → handshake → delta exchange → streaming.\n\t */\n\tasync start(): Promise<void> {\n\t\tif (this.state === 'streaming') return\n\t\tif (this.startPromise) return this.startPromise\n\n\t\tthis.startPromise = this.startInternal().finally(() => {\n\t\t\tthis.startPromise = null\n\t\t})\n\t\treturn this.startPromise\n\t}\n\n\tprivate async startInternal(principalRestarts = 0): Promise<void> {\n\t\tif (this.stopPromise) {\n\t\t\tawait this.stopPromise\n\t\t}\n\t\tif (this.nodeRotation) {\n\t\t\tawait this.nodeRotation\n\t\t}\n\t\tif (this.state === 'streaming') return\n\t\tif (this.destroyed) return\n\t\tif (this.state !== 'disconnected') {\n\t\t\tawait this.stop()\n\t\t}\n\t\tif (this.authRejected) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:suspended',\n\t\t\t\treason: this.suspensionReason ?? 'auth-rejected',\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tif (this.config.authState) {\n\t\t\tconst authState = await this.config.authState()\n\t\t\tif (authState.state === 'loading' || authState.state === 'signed-out') {\n\t\t\t\tthis.suspensionReason = authState.state === 'loading' ? 'auth-loading' : 'auth-required'\n\t\t\t\tthis.emitter?.emit({ type: 'sync:suspended', reason: this.suspensionReason })\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tthis.suspensionReason = null\n\t\tif (this.keyring && !this.keyring.canConnect()) {\n\t\t\t// Nothing to open the keyring with: sync stays paused (local reads and writes go\n\t\t\t// on) until the app unlocks it. No connection, so no plaintext can leave.\n\t\t\tthis.encryptionLocked = true\n\t\t\tthis.emitter?.emit({ type: 'sync:suspended', reason: 'encryption-locked' })\n\t\t\treturn\n\t\t}\n\t\tthis.encryptionLocked = false\n\t\t// Each handshake reports the watermark of the REQUESTED view (SYNC-11). The server\n\t\t// resumes from it only when it serves exactly the requested scope; when it serves\n\t\t// another (it restarts that stream from 0), the response switches to that view.\n\t\t// Reporting the previously accepted view's watermark instead could make a server\n\t\t// that now serves the requested scope skip operations of it.\n\t\t{\n\t\t\tconst previousSignature = this.deliverySignature()\n\t\t\tthis.activeScope = this.config.scopeMap\n\t\t\tthis.switchDeliveryView(previousSignature)\n\t\t}\n\t\tawait this.outboundQueue.initialize()\n\t\tthis.ownRecoveryPending = false\n\t\tthis.sessionAcked = 0\n\t\tthis.adoptionStalled = false\n\t\tif (\n\t\t\tthis.fullResyncDeferred &&\n\t\t\tDate.now() - this.lastFullResyncAt >= FULL_RESYNC_MIN_INTERVAL_MS\n\t\t) {\n\t\t\tthis.fullResyncDeferred = false\n\t\t\tthis.lastFullResyncAt = Date.now()\n\t\t\tawait this.resetDeliveryForFullResync()\n\t\t}\n\t\tif (!(await this.withPrincipalLock(() => this.applyPrincipal()))) return\n\t\t// The user this session's node was chosen for (RT-52): re-checked after the\n\t\t// credential fetch, before the handshake.\n\t\tconst boundPrincipal = this.principal\n\t\tawait this.chooseSessionNode()\n\t\t{\n\t\t\tconst sessionNode = this.currentNodeId()\n\t\t\tif (this.nodeTokenNodeId !== sessionNode) {\n\t\t\t\t// A token belongs to one node id: never present another node's (RT-38, RT-40).\n\t\t\t\tthis.nodeToken = null\n\t\t\t\tthis.nodeTokenNodeId = sessionNode\n\t\t\t}\n\t\t}\n\t\tif (this.syncState) {\n\t\t\tthis.lastAckedServerVector = await this.syncState.loadLastAckedServerVector()\n\t\t\tif (this.nodeToken === null && this.syncState.loadNodeToken) {\n\t\t\t\tthis.nodeToken = await this.syncState.loadNodeToken(this.currentNodeId())\n\t\t\t}\n\t\t\tif (this.authoritativeNodeIds === null && this.syncState.loadAuthoritativeNodeIds) {\n\t\t\t\tthis.authoritativeNodeIds = await this.syncState.loadAuthoritativeNodeIds()\n\t\t\t}\n\t\t\tif (this.syncState.loadDeltaCursor) {\n\t\t\t\tthis.resumeDeltaCursor = await this.syncState.loadDeltaCursor()\n\t\t\t}\n\t\t\t// Preload every view's watermark so a view switch resolves synchronously.\n\t\t\tif (this.syncState.loadAllDeliveryWatermarks) {\n\t\t\t\tconst all = await this.syncState.loadAllDeliveryWatermarks()\n\t\t\t\tfor (const [signature, watermark] of Object.entries(all)) {\n\t\t\t\t\tthis.deliverySignatureWatermarks.set(signature, watermark)\n\t\t\t\t}\n\t\t\t} else if (this.syncState.loadDeliveryWatermark) {\n\t\t\t\t// Fallback: load just the current (default) view's watermark.\n\t\t\t\tthis.deliverySignatureWatermarks.set('', await this.syncState.loadDeliveryWatermark(''))\n\t\t\t}\n\t\t\tthis.deliveryWatermark = this.deliverySignatureWatermarks.get(this.deliverySignature()) ?? 0\n\t\t\tif (this.syncState.loadAcceptedDownlinkScope) {\n\t\t\t\tthis.lastAcceptedScope = await this.syncState.loadAcceptedDownlinkScope()\n\t\t\t}\n\t\t\t// Bound a set that predates the retention cap (older clients persisted views\n\t\t\t// without a limit); this one-time trim removes cold rows down to the cap.\n\t\t\tthis.evictColdViewWatermarks()\n\t\t}\n\t\tawait this.loadOwnTracking()\n\t\tawait this.reconcileOutboundFromOpLog()\n\t\tawait this.replayQuarantine()\n\t\tawait this.refreshPendingCount()\n\t\tif (this.destroyed) return\n\n\t\t// Set up transport handlers\n\t\tthis.transport.onMessage((msg) => this.enqueueMessage(msg))\n\t\tthis.transport.onClose((reason) => this.handleTransportClose(reason))\n\t\tthis.transport.onError((err) => this.handleTransportError(err))\n\n\t\tif (this.schemaBlocked) {\n\t\t\tthrow new SyncError(\n\t\t\t\t'Sync is blocked due to schema version mismatch. Upgrade the app schema or align sync.schemaVersion with the server.',\n\t\t\t\t{ code: 'SCHEMA_MISMATCH_BLOCKED' },\n\t\t\t)\n\t\t}\n\t\tthis.transitionTo('connecting')\n\n\t\ttry {\n\t\t\tconst forceRefresh = this.credentialRefreshRequired\n\t\t\tconst authToken = this.config.auth\n\t\t\t\t? (await (forceRefresh ? this.config.auth({ forceRefresh: true }) : this.config.auth()))\n\t\t\t\t\t\t.token\n\t\t\t\t: undefined\n\t\t\tif (this.state !== 'connecting') return\n\t\t\tif (forceRefresh && !authToken) {\n\t\t\t\t// The refresh did not produce a token yet (offline, auth server down).\n\t\t\t\t// Handshaking with an empty or stale token would be refused as AUTH_FAILED\n\t\t\t\t// and suspend sync for good; fail this attempt so reconnection retries.\n\t\t\t\tthrow new SyncError(\n\t\t\t\t\t'Waiting for refreshed credentials before reconnecting: the server ended the previous session because its credential expired or was revoked.',\n\t\t\t\t\t{\n\t\t\t\t\t\tcode: 'AUTH_REFRESH_PENDING',\n\t\t\t\t\t\tfix: 'Sync reconnects automatically once the auth client can refresh its token. If the user was signed out, sign in again.',\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tawait this.transport.connect(this.config.url, { authToken })\n\t\t\tif (this.state !== 'connecting' || this.destroyed) {\n\t\t\t\tawait this.transport.disconnect()\n\t\t\t\tthis.ensureDisconnected()\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// RT-52: the signed-in user may have changed while the credential was fetched\n\t\t\t// or the transport connected (up to the connect timeout). The node was chosen\n\t\t\t// for the previous user and the credential may be the next one's: never\n\t\t\t// handshake that pair. Start over (the store is rebound to the current user).\n\t\t\tconst principalNow = await this.resolvePrincipal()\n\t\t\tif (this.config.principal && principalNow !== boundPrincipal) {\n\t\t\t\tawait this.transport.disconnect()\n\t\t\t\tthis.ensureDisconnected()\n\t\t\t\tif (principalRestarts >= MAX_PRINCIPAL_RESTARTS) {\n\t\t\t\t\tthrow new SyncError(\n\t\t\t\t\t\t'The signed-in user kept changing while sync was connecting; this attempt was abandoned so no write is uploaded as the wrong user.',\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcode: 'PRINCIPAL_CHANGED',\n\t\t\t\t\t\t\tfix: 'Sync retries automatically once the signed-in user is stable.',\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn this.startInternal(principalRestarts + 1)\n\t\t\t}\n\t\t\tthis.sessionPrincipal = principalNow\n\t\t\tthis.sessionEpoch++\n\t\t\tthis.transitionTo('handshaking')\n\n\t\t\t// Send handshake\n\t\t\tconst localVector = this.store.getVersionVector()\n\t\t\tconst activeQuerySubsets = this.getActiveQuerySubsets()\n\t\t\tconst handshake: SyncMessage = {\n\t\t\t\ttype: 'handshake',\n\t\t\t\tmessageId: generateMessageId(),\n\t\t\t\tnodeId: this.currentNodeId(),\n\t\t\t\tversionVector: versionVectorToWire(localVector),\n\t\t\t\tschemaVersion: this.config.schemaVersion ?? DEFAULT_SCHEMA_VERSION,\n\t\t\t\tauthToken,\n\t\t\t\t// JSON only (SYNC-9): the transports frame with their own serializer, so a\n\t\t\t\t// negotiated protobuf switch would never reach the wire. Protobuf stays an\n\t\t\t\t// explicit, both-ends transport choice.\n\t\t\t\tsupportedWireFormats: ['json'],\n\t\t\t\t...(this.config.scopeMap ? { syncScope: this.config.scopeMap } : {}),\n\t\t\t\t...(this.config.scopeExit ? { scopeExitPolicy: this.config.scopeExit } : {}),\n\t\t\t\t...(activeQuerySubsets.length > 0 ? { syncQueries: activeQuerySubsets } : {}),\n\t\t\t\t...(this.resumeDeltaCursor\n\t\t\t\t\t? { deltaCursor: encodeDeltaCursor(this.resumeDeltaCursor) }\n\t\t\t\t\t: {}),\n\t\t\t\t// Resume the server's gap-free delivery stream from our watermark. A server\n\t\t\t\t// that understands it drives server->client sync from delivery sequences; an\n\t\t\t\t// older server ignores it and falls back to the version-vector delta.\n\t\t\t\tlastDeliverySequence: this.deliveryWatermark,\n\t\t\t\t// The accepted view this client last streamed under, with its own watermark\n\t\t\t\t// (SYNC-11): a server that resolves the same scope again resumes from it.\n\t\t\t\t...this.acceptedViewHandshakeFields(),\n\t\t\t\t...(this.nodeToken ? { nodeToken: this.nodeToken } : {}),\n\t\t\t\t// Sequence numbers are reserved inside the writing transaction (W6), and a\n\t\t\t\t// SEQUENCE_CONFLICT is recovered from (RT-35), so the server may enforce\n\t\t\t\t// (node, sequence) uniqueness against this client (RT-37).\n\t\t\t\tsequenceReservation: true,\n\t\t\t\t// Protocol v2: hash-version-2 ids, the encryption envelope v2 (D2).\n\t\t\t\tprotocolVersion: SYNC_PROTOCOL_VERSION,\n\t\t\t}\n\t\t\tthis.transport.send(handshake)\n\t\t} catch (err) {\n\t\t\t// Transport error/close handlers may have already transitioned to disconnected.\n\t\t\t// Guard against invalid state transitions.\n\t\t\tthis.ensureDisconnected()\n\t\t\tthrow err\n\t\t}\n\t}\n\n\t/**\n\t * Stop the sync engine. Disconnects the transport.\n\t */\n\tasync stop(): Promise<void> {\n\t\tif (this.stopPromise) return this.stopPromise\n\n\t\tthis.stopPromise = this.stopInternal().finally(() => {\n\t\t\tthis.stopPromise = null\n\t\t})\n\t\treturn this.stopPromise\n\t}\n\n\tprivate async stopInternal(): Promise<void> {\n\t\t// Every timer goes first, before any early return (SYNC-10): an engine that is\n\t\t// already disconnected (the server closed the session) still owns the awareness\n\t\t// cleanup interval and a pending query-subset reconnect.\n\t\tthis.clearAllTimers()\n\t\tthis.sessionEpoch++\n\t\tthis.returnAllInFlightUploads()\n\n\t\tif (this.state === 'disconnected') {\n\t\t\t// The session may have ended without the transport being closed (an error the\n\t\t\t// engine gave up on): close it now so no socket is left open (SYNC-5).\n\t\t\tawait this.closeTransportQuietly()\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.transport.disconnect()\n\t\t} finally {\n\t\t\t// The transport.disconnect() callback may have already transitioned\n\t\t\t// to 'disconnected' via handleTransportClose. Re-read the mutable field.\n\t\t\tthis.ensureDisconnected()\n\t\t}\n\t}\n\n\t/**\n\t * Stop for good (`app.close()`): disconnect, clear every timer and listener, and refuse\n\t * any later start. Unlike stop(), nothing the engine scheduled can run afterwards.\n\t */\n\tasync destroy(): Promise<void> {\n\t\tthis.destroyed = true\n\t\tawait this.stop()\n\t\tthis.clearAllTimers()\n\t\tthis.endAdoption()\n\t\tthis.statusListeners.clear()\n\t}\n\n\t/** Clear every timer the engine owns. */\n\tprivate clearAllTimers(): void {\n\t\tthis.clearOutboundAckTimer()\n\t\tthis.clearOutboundRetryTimer()\n\t\tthis.awarenessManager.stopCleanupTimer()\n\t\tif (this.querySubsetReconnectTimer) {\n\t\t\tclearTimeout(this.querySubsetReconnectTimer)\n\t\t\tthis.querySubsetReconnectTimer = null\n\t\t}\n\t\tthis.clearAdoptionRetryTimer()\n\t}\n\n\tprivate clearAdoptionRetryTimer(): void {\n\t\tif (!this.adoptionRetryTimer) return\n\t\tclearTimeout(this.adoptionRetryTimer)\n\t\tthis.adoptionRetryTimer = null\n\t}\n\n\t/** Close the transport, logging nothing: it may already be closed. */\n\tprivate async closeTransportQuietly(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.transport.disconnect()\n\t\t} catch {\n\t\t\t// Already closed or never opened; the engine is disconnected either way.\n\t\t}\n\t}\n\n\t/**\n\t * The engine gave up on this session (a message it could not process, a transport\n\t * error, a server error): move to disconnected and close the transport (SYNC-5). The\n\t * close starts synchronously in this same frame, so the socket is released before\n\t * auto-reconnect (which is asynchronous) can open another one.\n\t */\n\tprivate abandonSession(reason: string): void {\n\t\tthis.handleTransportClose(reason)\n\t\tvoid this.closeTransportQuietly()\n\t}\n\n\t/**\n\t * Subscribe to status changes that no event announces (an upload ack lowering the\n\t * pending count, reaching streaming). Status bridges refresh on it, so a waiter such\n\t * as `waitForSettled` sees the change immediately (RT-28).\n\t */\n\tonStatusChange(listener: () => void): () => void {\n\t\tthis.statusListeners.add(listener)\n\t\treturn () => {\n\t\t\tthis.statusListeners.delete(listener)\n\t\t}\n\t}\n\n\tprivate notifyStatusChange(): void {\n\t\tfor (const listener of this.statusListeners) {\n\t\t\ttry {\n\t\t\t\tlistener()\n\t\t\t} catch {\n\t\t\t\t// A listener's failure must not affect sync.\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate ensureDisconnected(): void {\n\t\tif (this.state !== 'disconnected') {\n\t\t\tthis.transitionTo('disconnected')\n\t\t}\n\t}\n\n\t/**\n\t * Push a local operation to the outbound queue.\n\t * If streaming, flushes immediately.\n\t *\n\t * Uploads are judged against the UPLINK scope only. Query subsets narrow what\n\t * this client downloads, never what it uploads: an edit that moves a record out\n\t * of a reactive query's view must still reach the server (SYNC-1).\n\t *\n\t * Operations on collections that do not sync at all stay local-only by design.\n\t * An operation on a synced collection that falls outside the uplink scope would\n\t * be refused by the server, so it is recorded in the rejected store and\n\t * surfaced as `sync:operation-rejected` (code `OUT_OF_UPLINK_SCOPE`) rather than\n\t * kept as a silent local fork.\n\t */\n\tasync pushOperation(op: Operation): Promise<void> {\n\t\t// A committed write publishes all of its operations synchronously (a delete and\n\t\t// its cascades, a transaction). The streaming flush waits until every push that\n\t\t// started is enqueued, so the commit block uploads in one batch: the server then\n\t\t// sees a delete's authored cascades with the delete and does not derive a second\n\t\t// copy of each (RT-69).\n\t\tthis.pendingPushes += 1\n\t\ttry {\n\t\t\tif (!(await this.operationAllowedForUpload(op))) {\n\t\t\t\tawait this.recordOutOfUplinkScope(op)\n\t\t\t\t// Not upload-eligible: resolved for the contiguous prefix (W3 step 2).\n\t\t\t\tawait this.withOwnTracking(async () => {\n\t\t\t\t\tthis.trackOwnOperation(op, false)\n\t\t\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tawait this.withOwnTracking(async () => {\n\t\t\t\tawait this.outboundQueue.enqueue(op)\n\t\t\t\tthis.trackOwnOperation(op, true)\n\t\t\t})\n\t\t\tawait this.refreshPendingCount()\n\t\t} finally {\n\t\t\tthis.pendingPushes -= 1\n\t\t\tif (this.pendingPushes === 0 && this.state === 'streaming') {\n\t\t\t\tthis.flushQueue()\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Mark the engine as being in a reconnection loop. When reconnecting,\n\t * `getStatus()` returns 'offline' instead of 'syncing' for intermediate\n\t * states (connecting, handshaking, syncing), since the user is effectively\n\t * disconnected until reconnection succeeds.\n\t */\n\tsetReconnecting(value: boolean): void {\n\t\tthis.reconnecting = value\n\t}\n\n\t/**\n\t * Get the current developer-facing sync status.\n\t */\n\t/**\n\t * Node ids the sync server named authoritative (protocol v2): operations from these\n\t * nodes are server-authored and are the only ones that may carry `fieldVersions` or\n\t * `foldState`. Null until a protocol-2 server answered (or one answered in an earlier\n\t * session and the list was persisted).\n\t */\n\tgetAuthoritativeNodeIds(): readonly string[] | null {\n\t\treturn this.authoritativeNodeIds\n\t}\n\n\t/** Protocol version of the server in the current (or last) session; 1 before any. */\n\tgetServerProtocolVersion(): number {\n\t\treturn this.serverProtocolVersion\n\t}\n\n\tgetStatus(): SyncStatusInfo {\n\t\tconst pendingOperations = this.computePendingCount()\n\t\tconst locked = this.isLockedSuspension()\n\t\tconst base = {\n\t\t\tphase: this.resolvePhase(),\n\t\t\t...(this.suspensionReason\n\t\t\t\t? { reason: this.suspensionReason }\n\t\t\t\t: locked\n\t\t\t\t\t? { reason: 'encryption-locked' }\n\t\t\t\t\t: {}),\n\t\t\treconnecting: this.reconnecting,\n\t\t\tpendingOperations,\n\t\t\tlastSyncedAt: this.lastSyncedAt,\n\t\t\tlastSuccessfulPush: this.lastSuccessfulPush,\n\t\t\tlastSuccessfulPull: this.lastSuccessfulPull,\n\t\t\tconflicts: this.conflictCount,\n\t\t\theldOperations: this.computeHeldCount(),\n\t\t\theldNodes: this.heldNodeInfos.map((node) => ({ ...node })),\n\t\t\tlocalDurability: this.durabilityDegraded ? ('degraded' as const) : ('durable' as const),\n\t\t\tserverProtocolVersion: this.serverProtocolKnown ? this.serverProtocolVersion : null,\n\t\t\tprotocolDeprecated:\n\t\t\t\tthis.serverProtocolKnown && this.serverProtocolVersion < SYNC_PROTOCOL_VERSION,\n\t\t\tclockSkewMs: this.clockSkewMs,\n\t\t\tinFlightUploadOperations: this.inFlightUploadCount(),\n\t\t\thasInFlightDeliveryBatch: this.hasInFlightDeliveryBatch,\n\t\t\tactiveViewId: this.deliverySignature(),\n\t\t\tactiveViewComplete:\n\t\t\t\tthis.state === 'streaming' &&\n\t\t\t\t!this.hasInFlightDeliveryBatch &&\n\t\t\t\tthis.blockedFailure === null &&\n\t\t\t\t(this.serverFrontier === null || this.deliveryWatermark >= this.serverFrontier),\n\t\t\tinitialSync: {\n\t\t\t\tcomplete: this.deltaReceiveComplete && this.deltaSendComplete,\n\t\t\t\treceivedBatches: this.deltaBatchesReceived,\n\t\t\t\ttotalBatches: this.initialSyncTotalBatches || null,\n\t\t\t\tprogress:\n\t\t\t\t\tthis.initialSyncTotalBatches > 0\n\t\t\t\t\t\t? Math.min(1, this.deltaBatchesReceived / this.initialSyncTotalBatches)\n\t\t\t\t\t\t: null,\n\t\t\t},\n\t\t\tdeliveryWatermark: this.deliveryWatermark,\n\t\t\tserverFrontier: this.serverFrontier,\n\t\t\tblockedFailure: this.blockedFailure,\n\t\t}\n\t\tif (this.suspensionReason) return { ...base, status: 'auth-required' }\n\t\tif (locked) return { ...base, status: 'encryption-locked' }\n\t\tswitch (this.state) {\n\t\t\tcase 'disconnected':\n\t\t\t\t// A durable block outranks plain offline: the user must act\n\t\t\t\t// (fix the clock / upgrade the schema) before sync can resume.\n\t\t\t\tif (this.clockBlocked) {\n\t\t\t\t\treturn { ...base, status: 'clock-error' }\n\t\t\t\t}\n\t\t\t\tif (this.schemaBlocked) {\n\t\t\t\t\treturn { ...base, status: 'schema-mismatch' }\n\t\t\t\t}\n\t\t\t\treturn { ...base, status: 'offline' }\n\t\t\tcase 'connecting':\n\t\t\tcase 'handshaking':\n\t\t\tcase 'syncing':\n\t\t\t\treturn { ...base, status: this.reconnecting ? 'reconnecting' : 'syncing' }\n\t\t\tcase 'streaming':\n\t\t\t\treturn { ...base, status: pendingOperations > 0 ? 'syncing' : 'synced' }\n\t\t\tcase 'error':\n\t\t\t\tif (this.clockBlocked) {\n\t\t\t\t\treturn { ...base, status: 'clock-error' }\n\t\t\t\t}\n\t\t\t\treturn { ...base, status: this.schemaBlocked ? 'schema-mismatch' : 'error' }\n\t\t}\n\t}\n\n\t/**\n\t * Sync is paused on a locked keyring (ENC-1): no session runs until the app unlocks\n\t * it. Reported as a suspension (`phase: 'suspended'`, `reason: 'encryption-locked'`,\n\t * `status: 'encryption-locked'`) rather than as plain offline, so the UI can ask for\n\t * the passphrase and `waitForSettled` reports why it cannot settle.\n\t */\n\tprivate isLockedSuspension(): boolean {\n\t\treturn this.encryptionLocked && (this.state === 'disconnected' || this.state === 'error')\n\t}\n\n\tprivate resolvePhase(): import('../types').SyncPhase {\n\t\tif (this.suspensionReason) return 'suspended'\n\t\tif (this.isLockedSuspension()) return 'suspended'\n\t\tif (this.blockedFailure || this.schemaBlocked || this.clockBlocked) return 'blocked'\n\t\tif (this.hasInFlightDeliveryBatch) return 'applying'\n\t\tif (this.inFlightUploads.size > 0) return 'uploading'\n\t\tswitch (this.state) {\n\t\t\tcase 'disconnected':\n\t\t\t\treturn 'offline'\n\t\t\tcase 'connecting':\n\t\t\t\treturn 'connecting'\n\t\t\tcase 'handshaking':\n\t\t\t\treturn 'handshaking'\n\t\t\tcase 'syncing':\n\t\t\t\treturn 'receiving'\n\t\t\tcase 'streaming':\n\t\t\t\treturn 'streaming'\n\t\t\tcase 'error':\n\t\t\t\treturn 'blocked'\n\t\t}\n\t}\n\n\t/**\n\t * True when the server rejected the client's schema version at handshake.\n\t * Sync stays blocked until the app schema is upgraded or sync config changes.\n\t */\n\tisSchemaBlocked(): boolean {\n\t\treturn this.schemaBlocked\n\t}\n\n\t/**\n\t * True when sync is blocked because this device's clock is too far ahead.\n\t * Local writes continue to work and queue; only sync is paused.\n\t */\n\tisClockBlocked(): boolean {\n\t\treturn this.clockBlocked\n\t}\n\n\t/**\n\t * True when sync is paused because the end-to-end encryption keyring is locked (no\n\t * passphrase, a wrong one, or a key record that must not be used). Local writes go\n\t * on and queue; `retryNow()` after an unlock resumes sync.\n\t */\n\tisEncryptionLocked(): boolean {\n\t\treturn this.encryptionLocked\n\t}\n\n\t/**\n\t * The key service over the live sync session, for keyring management (rotation,\n\t * passphrase change, recovery). Null while not connected: those calls need the\n\t * server.\n\t */\n\tgetKeyServiceChannel(): KeyServiceChannel | null {\n\t\tif (this.state !== 'syncing' && this.state !== 'streaming') return null\n\t\treturn this.liveKeyServiceChannel()\n\t}\n\n\t/** serverTime - localTime measured at the last handshake, or null before first connect. */\n\tgetClockSkewMs(): number | null {\n\t\treturn this.clockSkewMs\n\t}\n\n\t/**\n\t * Clears the clock block after the user corrects the device clock.\n\t * Moves the engine back to `disconnected` so `start()` can run again.\n\t */\n\tclearClockBlock(): void {\n\t\tthis.clockBlocked = false\n\t\tif (this.state === 'error') {\n\t\t\tthis.transitionTo('disconnected')\n\t\t}\n\t}\n\n\t/**\n\t * Clears schema-mismatch block after upgrading the local schema / sync config.\n\t * Moves the engine back to `disconnected` so `start()` can run again.\n\t */\n\tclearSchemaBlock(): void {\n\t\tthis.schemaBlocked = false\n\t\tif (this.state === 'error') {\n\t\t\tthis.transitionTo('disconnected')\n\t\t}\n\t}\n\n\t/**\n\t * Record a merge conflict. Called by the merge-aware sync store\n\t * to increment the conflict counter for status reporting.\n\t */\n\trecordConflict(): void {\n\t\tthis.conflictCount++\n\t}\n\n\t/**\n\t * Count of this device's own operations not yet stored on the server (or terminally\n\t * rejected and recorded): queued, in flight, or above the acknowledged prefix.\n\t */\n\tasync getUnsyncedOperationCount(): Promise<number> {\n\t\tawait this.refreshPendingCount()\n\t\treturn this.getStatus().pendingOperations\n\t}\n\n\t/**\n\t * Surface an inbound apply failure. `blocking` marks the stream as stalled on this op\n\t * (the watermark does not pass it); `quarantined` means the op is durably recorded and\n\t * the stream moves on, so it never marks the stream blocked, whatever `retriable` says\n\t * (retriable there means the quarantine replays it later).\n\t */\n\tprivate emitApplyFailure(\n\t\top: Operation,\n\t\tresult: Exclude<ApplyResult, 'applied' | 'duplicate'>,\n\t\toverrides?: Partial<ApplyFailureReason>,\n\t\tmode: 'default' | 'blocking' | 'quarantined' = 'default',\n\t): void {\n\t\tconst reason = defaultApplyFailureReason(result, overrides)\n\t\tif (mode === 'blocking' || (mode === 'default' && reason.retriable)) {\n\t\t\tconst prior = this.blockedFailure\n\t\t\tthis.blockedFailure = {\n\t\t\t\toperationId: op.id,\n\t\t\t\tcollection: op.collection,\n\t\t\t\trecordId: op.recordId,\n\t\t\t\tcode: reason.code,\n\t\t\t\tmessage: reason.message,\n\t\t\t\tretriable: reason.retriable,\n\t\t\t\tfirstSeenAt: prior?.operationId === op.id ? prior.firstSeenAt : Date.now(),\n\t\t\t\tretryCount: prior?.operationId === op.id ? prior.retryCount + 1 : 0,\n\t\t\t}\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: prior?.operationId === op.id ? 'sync:apply-retrying' : 'sync:apply-blocked',\n\t\t\t\tfailure: this.blockedFailure,\n\t\t\t})\n\t\t}\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:apply-failed',\n\t\t\toperationId: op.id,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tcode: reason.code,\n\t\t\tmessage: reason.message,\n\t\t\tretriable: reason.retriable,\n\t\t})\n\t}\n\n\t/**\n\t * Force an immediate reconnection attempt. If the engine is disconnected\n\t * or in error state, restarts the sync. If already connected, no-op.\n\t */\n\tasync retryNow(): Promise<void> {\n\t\tif (this.schemaBlocked) return\n\t\tthis.encryptionLocked = false\n\t\tthis.authRejected = false\n\t\tthis.suspensionReason = null\n\t\tif (this.state === 'disconnected' || this.state === 'error') {\n\t\t\tthis.reconnecting = false\n\t\t\tawait this.start()\n\t\t}\n\t}\n\n\t/** Wake a permanent auth suspension after the binding reports a new auth state. */\n\tnotifyAuthChanged(): void {\n\t\tthis.authRejected = false\n\t\tthis.suspensionReason = null\n\t}\n\n\t/**\n\t * The signed-in user may have changed (RT-42): end the live session (it belongs to the\n\t * previous user), then bind the store to the user `config.principal` reports, so the\n\t * next local write is authored under that user's own node. Call it before the next\n\t * write after an auth change; `createApp` does so for `sync.authClient`. The next\n\t * `start()` connects as that user's node.\n\t */\n\tasync refreshPrincipal(): Promise<void> {\n\t\tif (!this.config.principal || !this.store.bindPrincipal) return\n\t\tlet next: string | null | undefined\n\t\ttry {\n\t\t\tnext = await this.config.principal()\n\t\t} catch {\n\t\t\treturn\n\t\t}\n\t\t// A token or scope refresh of the same user keeps the session.\n\t\tif (next === this.principal) return\n\t\tif (this.nodeRotation) await this.nodeRotation\n\t\tif (this.state !== 'disconnected') await this.stop()\n\t\tawait this.withPrincipalLock(() => this.applyPrincipal())\n\t}\n\n\t/**\n\t * Bind the store to the user the app reports NOW (RT-52), without ending or waiting\n\t * for any session: call it on every auth event, before queueing the reconnect that\n\t * follows. Writes made from then on are authored under that user's node, even while\n\t * an earlier reconnect is still fetching a credential or connecting; that attempt\n\t * notices the change before its handshake and starts over. A live session keeps\n\t * running as the previous user's node and uploads only that node's writes, until the\n\t * queued reconnect replaces it.\n\t */\n\tasync bindSignedInUser(): Promise<void> {\n\t\tif (!this.config.principal || !this.store.bindPrincipal) return\n\t\tawait this.withPrincipalLock(async () => {\n\t\t\tconst next = await this.resolvePrincipal()\n\t\t\tif (next === this.principal) return\n\t\t\tawait this.applyPrincipal({ keepSession: this.state !== 'disconnected' })\n\t\t})\n\t}\n\n\t/** The signed-in user as `config.principal` reports it (undefined when it throws). */\n\tprivate async resolvePrincipal(): Promise<string | null | undefined> {\n\t\tif (!this.config.principal) return undefined\n\t\ttry {\n\t\t\treturn await this.config.principal()\n\t\t} catch {\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\t/** Run `fn` after every earlier principal binding (one store binding at a time). */\n\tprivate withPrincipalLock<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst run = this.principalChain.then(fn, fn)\n\t\tthis.principalChain = run.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t)\n\t\treturn run\n\t}\n\n\t/**\n\t * Resolve the signed-in user and bind the store's node to them (RT-42).\n\t *\n\t * @returns false when sync must stay suspended: the store's node id is pinned and\n\t * belongs to another user, so nothing on this database may upload as this user.\n\t */\n\tprivate async applyPrincipal(options: { keepSession?: boolean } = {}): Promise<boolean> {\n\t\tif (!this.config.principal) return true\n\t\t// Unknown (undefined, or the resolver threw): no binding is changed, and no bound\n\t\t// node is judged against it.\n\t\tconst principal = await this.resolvePrincipal()\n\t\tthis.principal = principal\n\t\tif (!principal || !this.store.bindPrincipal) return true\n\t\tconst binding = await this.store.bindPrincipal(principal)\n\t\tif (binding.conflict) {\n\t\t\tthis.suspensionReason = 'node-owned-by-another-user'\n\t\t\tthis.emitter?.emit({ type: 'sync:suspended', reason: this.suspensionReason })\n\t\t\treturn false\n\t\t}\n\t\tif (binding.switched) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:local-node',\n\t\t\t\tnodeId: binding.nodeId,\n\t\t\t\taction: 'principal-switched',\n\t\t\t})\n\t\t\t// A live session stays the previous user's node until it is replaced: only\n\t\t\t// that node's writes upload on it (RT-52). The next session starts from the\n\t\t\t// store's new node.\n\t\t\tif (!options.keepSession) await this.afterIdentityChange()\n\t\t}\n\t\treturn true\n\t}\n\n\t/**\n\t * Atomically refresh the transport session. Overlapping callers share one\n\t * reconnect, in-flight outbound batches are returned to the queue by stop(), and the\n\t * next start handshakes with the latest auth token, sync scope, and query subsets.\n\t */\n\tasync reconnect(): Promise<void> {\n\t\tif (this.schemaBlocked) return\n\t\tif (this.reconnectPromise) return this.reconnectPromise\n\n\t\tthis.reconnecting = false\n\t\tthis.reconnectPromise = this.reconnectInternal().finally(() => {\n\t\t\tthis.reconnectPromise = null\n\t\t})\n\t\treturn this.reconnectPromise\n\t}\n\n\tprivate async reconnectInternal(): Promise<void> {\n\t\tawait this.stop()\n\t\tawait this.start()\n\t}\n\n\t/**\n\t * Export a diagnostics snapshot for debugging and support tickets.\n\t * Contains connection state, timing info, and queue metrics.\n\t */\n\texportDiagnostics(): SyncDiagnostics {\n\t\treturn {\n\t\t\tstate: this.state,\n\t\t\tstatus: this.getStatus(),\n\t\t\tnodeId: this.currentNodeId(),\n\t\t\turl: this.config.url,\n\t\t\tschemaVersion: this.config.schemaVersion ?? DEFAULT_SCHEMA_VERSION,\n\t\t\tlastSyncedAt: this.lastSyncedAt,\n\t\t\tlastSuccessfulPush: this.lastSuccessfulPush,\n\t\t\tlastSuccessfulPull: this.lastSuccessfulPull,\n\t\t\tconflicts: this.conflictCount,\n\t\t\tpendingOperations: this.outboundQueue.totalPending,\n\t\t\thasInFlightBatch: this.inFlightUploads.size > 0,\n\t\t\treconnecting: this.reconnecting,\n\t\t\tdeliveryWatermark: this.deliveryWatermark,\n\t\t\tdeliveryGapRepeatCount: this.deliveryGapRepeatCount,\n\t\t\ttimestamp: Date.now(),\n\t\t}\n\t}\n\n\t/**\n\t * Get the current internal state (for testing).\n\t */\n\tgetState(): SyncState {\n\t\treturn this.state\n\t}\n\n\t/**\n\t * Subscribe to internal state changes (SYNC-8). The reconnection loop uses it to\n\t * count an attempt as successful only once the session reaches `streaming`, not\n\t * when the handshake is merely sent.\n\t *\n\t * @param listener - Called with the new state after every transition\n\t * @returns Unsubscribe function\n\t */\n\tonStateChange(listener: (state: SyncState) => void): () => void {\n\t\tthis.stateListeners.add(listener)\n\t\treturn () => {\n\t\t\tthis.stateListeners.delete(listener)\n\t\t}\n\t}\n\n\t/**\n\t * Get the outbound queue (for testing).\n\t */\n\tgetOutboundQueue(): OutboundQueue {\n\t\treturn this.outboundQueue\n\t}\n\n\t/**\n\t * Update the sync scope map. Takes effect on the next connection attempt.\n\t *\n\t * When the scope changes (e.g., user switches organization), call this method\n\t * then reconnect. The new scope will be sent in the handshake, and the server\n\t * will send back data matching the new scope.\n\t *\n\t * Data that no longer matches the new scope is NOT deleted locally.\n\t * It simply stops being synced.\n\t *\n\t * @param scopeMap - New per-collection scope filters, or undefined to remove scope\n\t */\n\tupdateScope(scopeMap: SyncScopeMap | undefined): void {\n\t\tconst previousSignature = this.deliverySignature()\n\t\tthis.activeScope = scopeMap\n\t\t// Until the server accepts distinct directional maps, preserve the legacy\n\t\t// shorthand semantics locally as well.\n\t\tthis.activeUplinkScope = scopeMap\n\t\t// Also update the config so that the next handshake sends the new scope\n\t\tthis.config.scopeMap = scopeMap\n\t\tthis.switchDeliveryView(previousSignature)\n\t}\n\n\t/**\n\t * A stable identifier for the current sync view: the auth/tenant scope plus the set of\n\t * active query subscriptions, canonicalized so the same view always produces the same\n\t * string. The delivery watermark is keyed by this, so each distinct view resumes from\n\t * its own last position.\n\t */\n\tprivate deliverySignature(): string {\n\t\treturn this.deliverySignatureFor(this.activeScope)\n\t}\n\n\t/** The view signature {@link deliverySignature} gives under `scope` and today's subsets. */\n\tprivate deliverySignatureFor(scope: SyncScopeMap | undefined): string {\n\t\tconst subsets = this.getActiveQuerySubsets()\n\t\tif (!scope && subsets.length === 0) {\n\t\t\treturn '' // the default, unfiltered view (maps to the legacy watermark key)\n\t\t}\n\t\tconst normalizedSubsets = subsets\n\t\t\t.map((s) => `${s.collection}:${stableStringify(s.where)}`)\n\t\t\t.sort()\n\t\treturn stableStringify({ scope: scope ?? null, subsets: normalizedSubsets })\n\t}\n\n\t/**\n\t * Handshake fields naming the accepted view this client last streamed under (SYNC-11):\n\t * its canonical scope key and its delivery watermark. Omitted when no scope was ever\n\t * accepted, or when it equals the requested scope (the requested watermark covers it).\n\t */\n\tprivate acceptedViewHandshakeFields(): {\n\t\tacceptedScopeKey?: string\n\t\tacceptedScopeWatermark?: number\n\t} {\n\t\tconst accepted = this.lastAcceptedScope\n\t\tif (accepted === null) return {}\n\t\tconst key = scopeViewKey(accepted)\n\t\tif (key === scopeViewKey(this.activeScope)) return {}\n\t\tconst watermark = this.deliverySignatureWatermarks.get(this.deliverySignatureFor(accepted))\n\t\tif (watermark === undefined) return {}\n\t\treturn { acceptedScopeKey: key, acceptedScopeWatermark: watermark }\n\t}\n\n\t/** Persist the downlink scope the server accepted at this handshake (null: none). */\n\tprivate rememberAcceptedScope(accepted: SyncScopeMap | null): void {\n\t\tif (scopeViewKey(accepted) === scopeViewKey(this.lastAcceptedScope)) return\n\t\tthis.lastAcceptedScope = accepted\n\t\tif (this.syncState?.saveAcceptedDownlinkScope) {\n\t\t\tthis.syncState.saveAcceptedDownlinkScope(accepted).catch((error: unknown) => {\n\t\t\t\t// Losing it only costs a rescan from 0 at the next handshake.\n\t\t\t\tthis.emitter?.emit({\n\t\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\t\tmessage: error instanceof Error ? error.message : 'Saving the accepted scope failed',\n\t\t\t\t\tcode: 'ACCEPTED_SCOPE_SAVE_FAILED',\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Switch the active delivery watermark to the current view after a scope or subscription\n\t * change. The prior view's watermark is saved so returning to it resumes; the new view's\n\t * watermark is restored from the in-memory cache (0 for a never-synced view, which then\n\t * does a one-time resync under that view). No data is lost: a view's watermark is a lower\n\t * bound for that specific view's filter.\n\t */\n\tprivate switchDeliveryView(previousSignature: string): void {\n\t\tconst nextSignature = this.deliverySignature()\n\t\tif (nextSignature === previousSignature) {\n\t\t\treturn\n\t\t}\n\t\tthis.setViewWatermark(previousSignature, this.deliveryWatermark)\n\t\tvoid this.persistDeliveryWatermark(this.deliveryWatermark, previousSignature)\n\t\tthis.deliveryWatermark = this.deliverySignatureWatermarks.get(nextSignature) ?? 0\n\t}\n\n\t/**\n\t * Record a view's watermark in the in-memory cache as the most-recently-used entry (the\n\t * Map preserves insertion order, so re-inserting moves it to the tail), then evict cold\n\t * views past the retention cap. This is the single place the cache is written, so LRU\n\t * order and the size bound are always maintained together.\n\t */\n\tprivate setViewWatermark(signature: string, watermark: number): void {\n\t\tthis.deliverySignatureWatermarks.delete(signature)\n\t\tthis.deliverySignatureWatermarks.set(signature, watermark)\n\t\tthis.evictColdViewWatermarks()\n\t}\n\n\t/**\n\t * Trim the per-view watermark cache to `MAX_DELIVERY_VIEW_WATERMARKS`, evicting the\n\t * least-recently-used views. The default view ('') and the live view are never evicted\n\t * (evicting the live view would force an immediate re-scan of what we are actively\n\t * syncing). Each eviction also removes the persisted row, bounding storage. Safe by\n\t * construction: an evicted view simply back-fills from 0 (deduplicated) when next seen.\n\t */\n\tprivate evictColdViewWatermarks(): void {\n\t\tif (this.deliverySignatureWatermarks.size <= MAX_DELIVERY_VIEW_WATERMARKS) {\n\t\t\treturn\n\t\t}\n\t\tconst liveSignature = this.deliverySignature()\n\t\tfor (const signature of this.deliverySignatureWatermarks.keys()) {\n\t\t\tif (this.deliverySignatureWatermarks.size <= MAX_DELIVERY_VIEW_WATERMARKS) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif (signature === '' || signature === liveSignature) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tthis.deliverySignatureWatermarks.delete(signature)\n\t\t\tvoid this.deleteDeliveryWatermark(signature)\n\t\t}\n\t}\n\n\t/**\n\t * Remove a view's persisted watermark row (best-effort; a persistence layer that omits\n\t * the delete simply keeps the row, which is harmless).\n\t */\n\tprivate async deleteDeliveryWatermark(signature: string): Promise<void> {\n\t\tif (!this.syncState?.deleteDeliveryWatermark) {\n\t\t\treturn\n\t\t}\n\t\tawait this.syncState.deleteDeliveryWatermark(signature)\n\t}\n\n\t/**\n\t * Get the currently active scope map. Returns undefined if no scope is configured.\n\t */\n\tgetActiveScope(): SyncScopeMap | undefined {\n\t\treturn this.activeScope\n\t}\n\n\t/** Get the server-accepted upload authorization scope. */\n\tgetActiveUplinkScope(): SyncScopeMap | undefined {\n\t\treturn this.activeUplinkScope\n\t}\n\n\t/**\n\t * Register a live query subset that narrows synced data for a collection.\n\t * Takes effect on the next connection; reconnects when already connected.\n\t */\n\tregisterQuerySubset(subset: SyncQuerySubset): () => void {\n\t\tif ((this.config.querySubsets?.mode ?? 'reactive') !== 'reactive') return () => {}\n\t\tconst id = `query-${nextQuerySubsetId++}`\n\t\tconst previousSignature = this.deliverySignature()\n\t\tconst previousEffective = this.getActiveQuerySubsets()\n\t\tthis.querySubsets.set(id, subset)\n\t\t// Registering or unregistering a subset changes the sync view. Switch the watermark\n\t\t// to the new view (resuming it if seen before, or resyncing it once if new); returning\n\t\t// to a previously-synced view resumes instead of re-syncing. The debounce coalesces a\n\t\t// burst of subscription changes into a single reconnect.\n\t\tthis.switchDeliveryViewWithInheritance(previousSignature, previousEffective)\n\t\tif (this.deliverySignature() !== previousSignature) this.scheduleQuerySubsetReconnect()\n\t\treturn () => {\n\t\t\tconst sigBeforeRemove = this.deliverySignature()\n\t\t\tconst effectiveBeforeRemove = this.getActiveQuerySubsets()\n\t\t\tthis.querySubsets.delete(id)\n\t\t\tthis.switchDeliveryViewWithInheritance(sigBeforeRemove, effectiveBeforeRemove)\n\t\t\tif (this.deliverySignature() !== sigBeforeRemove) this.scheduleQuerySubsetReconnect()\n\t\t}\n\t}\n\n\tprivate switchDeliveryViewWithInheritance(\n\t\tpreviousSignature: string,\n\t\tpreviousSubsets: SyncQuerySubset[],\n\t): void {\n\t\tconst nextSubsets = this.getActiveQuerySubsets()\n\t\tconst previousWatermark = this.deliveryWatermark\n\t\tthis.switchDeliveryView(previousSignature)\n\t\tif (this.deliveryWatermark === 0 && querySubsetContains(previousSubsets, nextSubsets)) {\n\t\t\tthis.deliveryWatermark = previousWatermark\n\t\t\tthis.setViewWatermark(this.deliverySignature(), previousWatermark)\n\t\t\tvoid this.persistDeliveryWatermark(previousWatermark)\n\t\t}\n\t}\n\n\t/** Atomically replace the manifest used by static query-subset mode. */\n\tsetQuerySubsets(subsets: SyncQuerySubset[]): void {\n\t\tif ((this.config.querySubsets?.mode ?? 'reactive') !== 'static')\n\t\t\tthrow new Error('setQuerySubsets() requires sync.querySubsets.mode = \"static\"')\n\t\tconst previousSignature = this.deliverySignature()\n\t\tconst previousEffective = this.getActiveQuerySubsets()\n\t\tthis.staticQuerySubsets = dedupeQuerySubsets(subsets)\n\t\tthis.switchDeliveryViewWithInheritance(previousSignature, previousEffective)\n\t\tif (this.deliverySignature() !== previousSignature) this.scheduleQuerySubsetReconnect()\n\t}\n\n\t/**\n\t * Returns deduplicated active query subsets from registered subscriptions.\n\t */\n\tgetActiveQuerySubsets(): SyncQuerySubset[] {\n\t\tconst mode = this.config.querySubsets?.mode ?? 'reactive'\n\t\tif (mode === 'disabled') return []\n\t\treturn mode === 'static'\n\t\t\t? this.staticQuerySubsets\n\t\t\t: dedupeQuerySubsets([...this.querySubsets.values()])\n\t}\n\n\t/**\n\t * Get the awareness manager for collaborative presence.\n\t * Use this to set local presence, observe remote collaborators,\n\t * and track cursor positions in richtext fields.\n\t */\n\tgetAwarenessManager(): AwarenessManager {\n\t\treturn this.awarenessManager\n\t}\n\n\t/**\n\t * Optional side channel for incremental Yjs updates on large richtext fields.\n\t */\n\tgetRichtextDocChannel(): RichtextDocChannel {\n\t\treturn this.richtextDocChannel\n\t}\n\n\t/**\n\t * Side channel for out-of-band blob chunk transfer over the sync connection.\n\t * The app layer bridges this to a `ChunkMessagePort` to pull/serve blob bytes.\n\t */\n\tgetBlobChunkChannel(): BlobChunkChannel {\n\t\treturn this.blobChunkChannel\n\t}\n\n\t/**\n\t * Whether the connected server persists blob bytes centrally (learned from the\n\t * handshake response). When true, the app uploads the bytes behind `blob`\n\t * fields so they stay available after the authoring device goes offline.\n\t */\n\tisBlobStorageEnabled(): boolean {\n\t\treturn this.blobStorageEnabled\n\t}\n\n\t/**\n\t * Whether the connected server (peer-relay mode, no central storage) asks for the\n\t * bytes behind blob references as proof of possession (RT-23). The server verifies\n\t * and drops them; the app uploads exactly as for central storage.\n\t */\n\tisBlobPossessionProofRequested(): boolean {\n\t\treturn this.blobPossessionProof\n\t}\n\n\t/**\n\t * Upload a blob chunk (or manifest) to the server, for central persistence or as\n\t * proof of possession. A no-op unless the handshake was accepted (syncing or\n\t * streaming) and the server advertised blob storage or asked for proofs.\n\t */\n\tuploadBlobChunk(hash: string, bytes: Uint8Array): void {\n\t\tif (\n\t\t\t(this.state !== 'streaming' && this.state !== 'syncing') ||\n\t\t\t!(this.blobStorageEnabled || this.blobPossessionProof)\n\t\t) {\n\t\t\treturn\n\t\t}\n\t\tthis.blobChunkChannel.send({ type: 'blob-chunk-push', hash, bytes })\n\t}\n\n\t/**\n\t * Register work that must reach the server before each outbound operation batch,\n\t * on the same connection (messages are processed in order). Used to upload the\n\t * bytes behind blob references first: the server only accepts a reference to\n\t * content the writer can read or has uploaded (RT-11). Errors are ignored; the\n\t * batch is sent regardless.\n\t *\n\t * @param preparer - Called with the operations about to be sent; null to remove\n\t * @returns A function that removes this preparer\n\t */\n\tsetOutboundPreparer(preparer: ((operations: Operation[]) => Promise<void>) | null): () => void {\n\t\tthis.outboundPreparer = preparer\n\t\treturn () => {\n\t\t\tif (this.outboundPreparer === preparer) this.outboundPreparer = null\n\t\t}\n\t}\n\n\t// --- Private methods ---\n\n\tprivate messageChain: Promise<void> = Promise.resolve()\n\n\tprivate enqueueMessage(message: SyncMessage): void {\n\t\t// The server sends a credential-ending error and closes the socket right away.\n\t\t// The close is handled synchronously (and may start a reconnect) before this\n\t\t// queued message is processed, so record the need to refresh now (AUTH-11).\n\t\tif (message.type === 'error' && isCredentialEndingCode(message.code)) {\n\t\t\tthis.credentialRefreshRequired = true\n\t\t}\n\t\t// A key-service answer resolves its request directly, outside the message chain:\n\t\t// the handshake response awaits it inside the chain (ENC-1).\n\t\tif (message.type === 'encryption-key-response' && message.requestId !== undefined) {\n\t\t\tconst pending = this.keyRequests.get(message.requestId)\n\t\t\tif (pending) {\n\t\t\t\tthis.keyRequests.delete(message.requestId)\n\t\t\t\tclearTimeout(pending.timer)\n\t\t\t\tpending.resolve(message)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tthis.messageChain = this.messageChain\n\t\t\t.then(() => this.handleMessageAsync(message))\n\t\t\t.catch((error) => this.handleMessageFailure(error))\n\t}\n\n\tprivate async handleMessageAsync(message: SyncMessage): Promise<void> {\n\t\tswitch (message.type) {\n\t\t\tcase 'handshake-response':\n\t\t\t\tawait this.handleHandshakeResponse(message)\n\t\t\t\tbreak\n\t\t\tcase 'operation-batch':\n\t\t\t\tawait this.handleOperationBatch(message)\n\t\t\t\tbreak\n\t\t\tcase 'acknowledgment':\n\t\t\t\tawait this.handleAcknowledgment(message)\n\t\t\t\tbreak\n\t\t\tcase 'error':\n\t\t\t\tthis.handleError(message)\n\t\t\t\tbreak\n\t\t\tcase 'operation-rejected':\n\t\t\t\tawait this.handleOperationRejected(message)\n\t\t\t\tbreak\n\t\t\tcase 'awareness-update':\n\t\t\t\tthis.handleAwarenessUpdate(message)\n\t\t\t\tbreak\n\t\t\tcase 'yjs-doc-update':\n\t\t\t\tthis.richtextDocChannel.deliver(message)\n\t\t\t\tbreak\n\t\t\tcase 'blob-chunk-request':\n\t\t\tcase 'blob-chunk-response':\n\t\t\tcase 'blob-chunk-push':\n\t\t\t\tthis.blobChunkChannel.deliver(message)\n\t\t\t\tbreak\n\t\t\tcase 'encryption-key-response':\n\t\t\t\t// Unsolicited: another device of this user rotated keys or changed the\n\t\t\t\t// passphrase. In the chain, so operations after it decrypt with the new key.\n\t\t\t\tawait this.handlePushedKeyRecord(message)\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t/** Adopt a key record the server pushed; stop the session if it locks the keyring. */\n\tprivate async handlePushedKeyRecord(message: EncryptionKeyResponseMessage): Promise<void> {\n\t\tif (!this.keyring || message.status !== 'ok' || message.record === null) return\n\t\tif (message.keyring !== this.keyring.name) return\n\t\tconst before = this.keyring.getStatus().availableVersions.length\n\t\tconst outcome = await this.keyring.adoptPushed(message.record)\n\t\tif (outcome === 'ready') {\n\t\t\t// Operations that arrived under a key this device did not hold yet decrypt now.\n\t\t\tif (this.keyring.getStatus().availableVersions.length > before) {\n\t\t\t\tawait this.replayQuarantine()\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tthis.pauseForLockedKeyring()\n\t}\n\n\t/**\n\t * An operation sealed under a key version newer than any this device holds: another\n\t * device rotated keys and the server's push did not reach this session (another\n\t * server instance). Fetch the record once, then replay the quarantine in the chain.\n\t */\n\tprivate maybeRefreshKeyring(op: Operation): void {\n\t\tconst keyring = this.keyring\n\t\tconst keyId = op.encrypted?.keyId\n\t\tif (!keyring || keyId === undefined || this.keyRefreshInFlight) return\n\t\t// By key id, not version: after a forked ring is merged (RT-104), an operation can\n\t\t// name a key under a version number this device already uses for another key.\n\t\tif (keyring.getEncryptor() === null || keyring.holdsKeyId(keyId)) return\n\t\tconst channel = this.getKeyServiceChannel()\n\t\tif (!channel) return\n\t\tthis.keyRefreshInFlight = true\n\t\tvoid keyring\n\t\t\t.synchronize(channel, this.sessionPrincipal ?? null)\n\t\t\t.then((outcome) => {\n\t\t\t\tif (outcome !== 'ready') {\n\t\t\t\t\tthis.pauseForLockedKeyring()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.messageChain = this.messageChain\n\t\t\t\t\t.then(async () => {\n\t\t\t\t\t\tawait this.replayQuarantine()\n\t\t\t\t\t})\n\t\t\t\t\t.catch((error) => this.handleMessageFailure(error))\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\t// The next handshake opens the record again.\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tthis.keyRefreshInFlight = false\n\t\t\t})\n\t}\n\n\t/**\n\t * End the session because the keyring is locked; no reconnect until unlocked. A lock\n\t * caused by the server's record (rolled back, lost, malformed, forked: RT-96, RT-104)\n\t * needs no user action, so the session ends without pausing and the reconnection\n\t * loop retries with backoff; the next handshake re-uploads, merges or opens it.\n\t */\n\tprivate pauseForLockedKeyring(): void {\n\t\tif (this.keyring?.isRetryableLock()) {\n\t\t\tconst status = this.keyring.getStatus()\n\t\t\tthis.abandonSession(\n\t\t\t\t`End-to-end encryption: ${status.code ?? 'key record'} (${status.message ?? 'retrying'})`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tthis.encryptionLocked = true\n\t\tthis.emitter?.emit({ type: 'sync:suspended', reason: 'encryption-locked' })\n\t\tthis.abandonSession('End-to-end encryption is locked')\n\t}\n\n\t/** One key-service round trip over the live session. */\n\tprivate keyServiceRequest(\n\t\trequest:\n\t\t\t| { kind: 'fetch'; keyring: string }\n\t\t\t| { kind: 'put'; keyring: string; record: WrappedKeyRecord; expectedRevision: number },\n\t): Promise<KeyServiceReply> {\n\t\tif (this.serverProtocolVersion < SYNC_PROTOCOL_VERSION) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew EncryptionKeyError(\n\t\t\t\t\t'The sync server does not provide end-to-end encryption keys. Upgrade @korajs/server to 1.0.0-beta.13 or later.',\n\t\t\t\t\t{ code: 'KEY_SERVICE_UNSUPPORTED' },\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tconst requestId = generateMessageId()\n\t\tconst response = new Promise<EncryptionKeyResponseMessage>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.keyRequests.delete(requestId)\n\t\t\t\treject(\n\t\t\t\t\tnew EncryptionKeyError(\n\t\t\t\t\t\t`The sync server did not answer a key request within ${this.keyServiceTimeoutMs}ms.`,\n\t\t\t\t\t\t{ code: 'KEY_SERVICE_TIMEOUT' },\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}, this.keyServiceTimeoutMs)\n\t\t\tthis.keyRequests.set(requestId, { resolve, reject, timer })\n\t\t})\n\t\ttry {\n\t\t\tthis.transport.send(\n\t\t\t\trequest.kind === 'fetch'\n\t\t\t\t\t? {\n\t\t\t\t\t\t\ttype: 'encryption-key-request',\n\t\t\t\t\t\t\tmessageId: generateMessageId(),\n\t\t\t\t\t\t\trequestId,\n\t\t\t\t\t\t\tkeyring: request.keyring,\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\ttype: 'encryption-key-put',\n\t\t\t\t\t\t\tmessageId: generateMessageId(),\n\t\t\t\t\t\t\trequestId,\n\t\t\t\t\t\t\tkeyring: request.keyring,\n\t\t\t\t\t\t\trecord: request.record,\n\t\t\t\t\t\t\texpectedRevision: request.expectedRevision,\n\t\t\t\t\t\t},\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tconst pending = this.keyRequests.get(requestId)\n\t\t\tif (pending) {\n\t\t\t\tclearTimeout(pending.timer)\n\t\t\t\tthis.keyRequests.delete(requestId)\n\t\t\t}\n\t\t\treturn Promise.reject(error)\n\t\t}\n\t\treturn response.then((message) => keyServiceReply(message))\n\t}\n\n\t/** Fail every pending key-service request (the session ended). */\n\tprivate rejectKeyRequests(reason: string): void {\n\t\tfor (const [requestId, pending] of this.keyRequests) {\n\t\t\tclearTimeout(pending.timer)\n\t\t\tpending.reject(\n\t\t\t\tnew EncryptionKeyError(\n\t\t\t\t\t`The sync session ended before the key service answered (${reason}).`,\n\t\t\t\t\t{\n\t\t\t\t\t\tcode: 'KEY_SERVICE_DISCONNECTED',\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t)\n\t\t\tthis.keyRequests.delete(requestId)\n\t\t}\n\t}\n\n\tprivate handleMessageFailure(error: unknown): void {\n\t\tthis.hasInFlightDeliveryBatch = false\n\t\tconst reason = error instanceof Error ? error.message : 'Message handling failed'\n\t\tthis.abandonSession(reason)\n\t}\n\n\t/**\n\t * Compares server wall-clock time from the handshake with local time.\n\t * Negative skew = this device's clock is fast (dangerous for LWW and rejected\n\t * by server ingest beyond 60s). Positive skew = slow (accepted but surfaced).\n\t * Zero developer work required: the result flows into sync status and events.\n\t */\n\tprivate evaluateClockSkew(serverTime: number): void {\n\t\tconst skewMs = serverTime - Date.now()\n\t\tthis.clockSkewMs = skewMs\n\t\tconst FAST_BLOCK_MS = 60_000\n\t\tconst SLOW_WARN_MS = 10 * 60_000\n\t\tlet severity: 'info' | 'slow-warning' | 'fast-blocked' = 'info'\n\t\tif (skewMs < -FAST_BLOCK_MS) {\n\t\t\tseverity = 'fast-blocked'\n\t\t} else if (skewMs > SLOW_WARN_MS) {\n\t\t\tseverity = 'slow-warning'\n\t\t}\n\t\tthis.emitter?.emit({ type: 'sync:clock-skew', skewMs, severity, source: 'handshake' })\n\t\tif (severity === 'fast-blocked') {\n\t\t\tthis.clockBlocked = true\n\t\t} else {\n\t\t\t// Auto-heal: an acceptable measured skew is authoritative proof the\n\t\t\t// device clock has been corrected, so a block engaged earlier (at a\n\t\t\t// previous handshake or via a server INVALID_TIMESTAMP reject) no\n\t\t\t// longer applies and must not require a manual clearClockBlock().\n\t\t\tthis.clockBlocked = false\n\t\t}\n\t}\n\n\tprivate async handleHandshakeResponse(msg: HandshakeResponseMessage): Promise<void> {\n\t\tif (this.state !== 'handshaking') return\n\n\t\tthis.blobStorageEnabled = msg.blobStorageEnabled === true\n\t\tthis.blobPossessionProof = msg.blobPossessionProof === true\n\n\t\tif (typeof msg.serverTime === 'number') {\n\t\t\tthis.evaluateClockSkew(msg.serverTime)\n\t\t\tif (this.clockBlocked) {\n\t\t\t\tthis.metricsCollector.updateStatus('error')\n\t\t\t\tthis.transitionTo('error')\n\t\t\t\tvoid this.transport.disconnect()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif (msg.accepted) {\n\t\t\tthis.credentialRefreshRequired = false\n\t\t\t// W7: server node ids authoritative for merge('server-authoritative') fields.\n\t\t\t// Read structurally: the protocol field lands with protocol v2.\n\t\t\tconst authoritative = (msg as { authoritativeNodeIds?: unknown }).authoritativeNodeIds\n\t\t\tif (Array.isArray(authoritative) && this.store.setAuthoritativeNodeIds) {\n\t\t\t\tawait this.store.setAuthoritativeNodeIds(\n\t\t\t\t\tauthoritative.filter((id): id is string => typeof id === 'string'),\n\t\t\t\t\trevokedAuthoritativeIds(msg),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tif (!msg.accepted) {\n\t\t\tconst reason = msg.rejectReason ?? 'Handshake rejected'\n\t\t\tif (isSchemaMismatchReject(msg.rejectReason)) {\n\t\t\t\tthis.schemaBlocked = true\n\t\t\t\tconst supportedMin = msg.supportedSchemaMin ?? msg.schemaVersion\n\t\t\t\tconst supportedMax = msg.supportedSchemaMax ?? msg.schemaVersion\n\t\t\t\tthis.emitter?.emit({\n\t\t\t\t\ttype: 'sync:schema-mismatch',\n\t\t\t\t\tclientSchemaVersion: this.config.schemaVersion ?? DEFAULT_SCHEMA_VERSION,\n\t\t\t\t\tserverSchemaVersion: msg.schemaVersion,\n\t\t\t\t\tsupportedMin,\n\t\t\t\t\tsupportedMax,\n\t\t\t\t\treason,\n\t\t\t\t})\n\t\t\t\tthis.metricsCollector.updateStatus('error')\n\t\t\t\tthis.transitionTo('error')\n\t\t\t\tvoid this.transport.disconnect()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.transitionTo('error')\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:disconnected',\n\t\t\t\treason,\n\t\t\t})\n\t\t\tthis.transitionTo('disconnected')\n\t\t\tvoid this.closeTransportQuietly()\n\t\t\treturn\n\t\t}\n\n\t\tconst sessionNode = this.currentNodeId()\n\t\t// Accepted as this node: from now on a refusal of it means another principal owns\n\t\t// it (RT-38). Recorded before anything is uploaded in this session.\n\t\tawait this.recordNodeAccepted(sessionNode)\n\n\t\tif (typeof msg.nodeToken === 'string' && msg.nodeToken.length > 0) {\n\t\t\tthis.nodeToken = msg.nodeToken\n\t\t\tthis.nodeTokenNodeId = sessionNode\n\t\t\tawait this.syncState?.saveNodeToken?.(msg.nodeToken, sessionNode)\n\t\t\t// The claim stays provisional until the server knows the token is saved:\n\t\t\t// confirm it now, so a lost response never locks this device out (RT-21).\n\t\t\t// Without persistence the token lives for this engine only; confirming would\n\t\t\t// tie the node to a secret the next process does not have, so don't.\n\t\t\tif (this.syncState?.saveNodeToken) {\n\t\t\t\tthis.transport.send({\n\t\t\t\t\ttype: 'acknowledgment',\n\t\t\t\t\tmessageId: generateMessageId(),\n\t\t\t\t\tacknowledgedMessageId: msg.messageId,\n\t\t\t\t\tlastSequenceNumber: 0,\n\t\t\t\t\tnodeToken: msg.nodeToken,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tthis.serverProtocolVersion = declaredProtocolVersion(msg.protocolVersion)\n\t\tthis.serverProtocolKnown = true\n\n\t\t// Open the encryption keyring against the server's record BEFORE any operation is\n\t\t// exchanged (ENC-1): inbound batches wait behind this in the message chain, and\n\t\t// nothing is uploaded until it is unlocked with the user's shared keys.\n\t\tif (this.keyring) {\n\t\t\tconst keySession = this.sessionEpoch\n\t\t\tconst outcome = await this.keyring.synchronize(\n\t\t\t\tthis.liveKeyServiceChannel(),\n\t\t\t\tthis.sessionPrincipal ?? null,\n\t\t\t)\n\t\t\tif (this.state !== 'handshaking' || this.sessionEpoch !== keySession) return\n\t\t\tif (outcome !== 'ready') {\n\t\t\t\tthis.pauseForLockedKeyring()\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Operations quarantined while the keyring could not open them decrypt now.\n\t\t\tawait this.replayQuarantine()\n\t\t}\n\t\tif (Array.isArray(msg.authoritativeNodeIds)) {\n\t\t\t// The union of every explicit id learned, never a replacement (RT-75): each\n\t\t\t// server instance lists what it knows, and `kora:server:` ids are\n\t\t\t// authoritative by prefix, so they are not kept.\n\t\t\t// Ids the server revoked (RT-81) leave the union and are never learned again (the\n\t\t\t// store persists them, and its union, which this list mirrors, filters them).\n\t\t\tconst revoked = new Set(revokedAuthoritativeIds(msg))\n\t\t\tconst previous = (this.authoritativeNodeIds ?? []).filter((id) => !isServerNodeId(id))\n\t\t\tconst known = new Set(previous.filter((id) => !revoked.has(id)))\n\t\t\tconst learned = msg.authoritativeNodeIds.filter(\n\t\t\t\t(id): id is string =>\n\t\t\t\t\ttypeof id === 'string' && !isServerNodeId(id) && !known.has(id) && !revoked.has(id),\n\t\t\t)\n\t\t\tif (\n\t\t\t\tlearned.length > 0 ||\n\t\t\t\tknown.size < previous.length ||\n\t\t\t\tthis.authoritativeNodeIds === null\n\t\t\t) {\n\t\t\t\tthis.authoritativeNodeIds = [...known, ...learned].sort()\n\t\t\t\tawait this.syncState?.saveAuthoritativeNodeIds?.(this.authoritativeNodeIds)\n\t\t\t}\n\t\t}\n\n\t\tthis.remoteVector = wireToVersionVector(msg.versionVector)\n\t\tvoid this.persistLastAckedServerVector(this.remoteVector)\n\n\t\t// The server's accepted downlink scope is authoritative for what it delivers, so\n\t\t// the delivery watermark belongs to that view. When it differs from the requested\n\t\t// view (the one the handshake watermark was read under), switch views (SYNC-11):\n\t\t// the server restarts such a stream from 0, and only that view's own watermark\n\t\t// can tell the duplicates in it from operations this client never saw.\n\t\tconst previousSignature = this.deliverySignature()\n\t\tconst acceptedDownlink = msg.acceptedDownlinkScopes ?? msg.acceptedScope\n\t\t// A grant equal to the request (by canonical key) keeps the requested view: the\n\t\t// server resumed from the requested view's watermark, and a re-ordered copy of the\n\t\t// same scope must not switch to a differently keyed watermark.\n\t\tif (acceptedDownlink && scopeViewKey(acceptedDownlink) !== scopeViewKey(this.activeScope)) {\n\t\t\tthis.activeScope = acceptedDownlink\n\t\t}\n\t\tthis.activeUplinkScope = msg.acceptedUplinkScopes ?? msg.acceptedScope ?? this.activeScope\n\t\tthis.switchDeliveryView(previousSignature)\n\t\tthis.rememberAcceptedScope(acceptedDownlink ?? null)\n\n\t\t// If our watermark is ahead of the server's frontier, the server's log was rolled\n\t\t// back (for example a backup restore reset the delivery sequence). Reset to a full\n\t\t// resync so we do not sit above operations the server will re-send from the start.\n\t\t// The server independently resyncs such a client from 0, so the stream that follows\n\t\t// this response chains from 0 and this reset lets us apply it.\n\t\tif (\n\t\t\ttypeof msg.serverMaxDeliverySequence === 'number' &&\n\t\t\tthis.deliveryWatermark > msg.serverMaxDeliverySequence\n\t\t) {\n\t\t\tthis.deliveryWatermark = 0\n\t\t\tthis.setViewWatermark(this.deliverySignature(), 0)\n\t\t\tawait this.persistDeliveryWatermark(0)\n\t\t}\n\t\tthis.serverFrontier = msg.serverMaxDeliverySequence ?? null\n\n\t\tif (msg.selectedWireFormat) {\n\t\t\tthis.setSerializerWireFormat(msg.selectedWireFormat)\n\t\t}\n\n\t\tif (this.config.scopeExit === 'retract' && this.activeScope && this.store.applyScopeNarrowing) {\n\t\t\tconst narrowed = await this.store.applyScopeNarrowing(this.activeScope)\n\t\t\tfor (const retraction of narrowed) {\n\t\t\t\tawait this.applyScopeRetraction(retraction, false)\n\t\t\t}\n\t\t}\n\n\t\t// A server that holds MORE of this node's operations than the device's log (the\n\t\t// device lost the tail of its log, RT-35) is never trusted as an acknowledgment,\n\t\t// but the numbers it holds are taken: the counter moves past them before the next\n\t\t// write, and a full resync fetches the operations back.\n\t\tconst advertisedOwn = this.remoteVector.get(sessionNode)\n\t\tthis.handshakeOwnEntry = advertisedOwn ?? 0\n\t\tconst localOwn = this.store.getVersionVector().get(sessionNode) ?? 0\n\t\t// The persisted counter decides (raiseSequenceFloor reads it in a transaction): the\n\t\t// in-memory vector of a tab sharing the node may simply lag another tab's writes.\n\t\tif (\n\t\t\tadvertisedOwn !== undefined &&\n\t\t\tadvertisedOwn > localOwn &&\n\t\t\tthis.store.raiseSequenceFloor &&\n\t\t\t(await this.store.raiseSequenceFloor(sessionNode, advertisedOwn))\n\t\t) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:local-node',\n\t\t\t\tnodeId: sessionNode,\n\t\t\t\taction: 'history-behind',\n\t\t\t\tlocalSequence: localOwn,\n\t\t\t\tserverSequence: advertisedOwn,\n\t\t\t})\n\t\t\tawait this.resetDeliveryForFullResync()\n\t\t\tthis.abandonSession(\n\t\t\t\t'The server holds operations of this device that its local database lost; resyncing them',\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\t// A server that holds FEWER of this device's operations than the device believes\n\t\t// (a restored backup) lowers the acknowledged prefix, so the device re-uploads\n\t\t// them; the server dedups by id. A higher value is never trusted (RT-12, W3).\n\t\t// An ABSENT own entry means the server holds none of them (RT-45): every server\n\t\t// names the session's own node whenever it holds an operation of it (the entry is\n\t\t// never scope-filtered), so absence is the strongest \"behind\", never \"unknown\".\n\t\tconst serverHoldsOwn = advertisedOwn ?? 0\n\t\tif (serverHoldsOwn < this.ownAckedThrough && this.ownTrackingNodeId === sessionNode) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:local-node',\n\t\t\t\tnodeId: sessionNode,\n\t\t\t\taction: 'server-behind',\n\t\t\t\tlocalSequence: this.ownAckedThrough,\n\t\t\t\tserverSequence: serverHoldsOwn,\n\t\t\t})\n\t\t\tawait this.withOwnTracking(async () => {\n\t\t\t\tawait this.lowerOwnPrefixLocked(serverHoldsOwn)\n\t\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t\t})\n\t\t}\n\t\tawait this.refreshPendingCount()\n\n\t\tthis.emitter?.emit({ type: 'sync:connected', nodeId: sessionNode })\n\t\tthis.metricsCollector.recordConnected()\n\t\tthis.metricsCollector.updateStatus('syncing')\n\t\tthis.metricsCollector.recordSyncStarted()\n\n\t\tthis.transitionTo('syncing')\n\t\tthis.deltaBatchesReceived = 0\n\t\tthis.deltaReceiveComplete = false\n\t\tthis.deltaSendComplete = false\n\t\tthis.pendingDeltaBatchAcks.clear()\n\t\tthis.initialSyncTotalBatches = 0\n\n\t\t// Rebase must finish BEFORE the delta exchange starts so only the\n\t\t// re-stamped operation ids ever reach the wire.\n\t\tif (typeof msg.serverTime === 'number') {\n\t\t\tawait this.maybeRebaseQueuedOperations(msg.serverTime)\n\t\t}\n\n\t\t// Send our delta to the server. Not awaited: the outbound preparer (blob bytes)\n\t\t// may need server messages that this message chain has yet to process.\n\t\tvoid this.sendDelta().catch((error) => {\n\t\t\tthis.abandonSession(error instanceof Error ? error.message : 'Delta upload failed')\n\t\t})\n\t\tthis.notifyStatusChange()\n\t}\n\n\t/**\n\t * The server refused this session's node id (`NODE_ID_CLAIMED`). What happens to the\n\t * node's unsynced writes depends on whether this database was ever accepted as it:\n\t *\n\t * - Never accepted (RT-21: the claim's token was lost before the device saw it, or\n\t * another device took the id first): no principal ever received these writes under\n\t * this node, so the never-sent ones are re-authored under a fresh node id and\n\t * uploaded. An operation flagged sent is never re-authored (RT-38): it may already\n\t * be stored, and a copy under a new id would store the change twice.\n\t * - Accepted before (RT-38: another user signed in on a database shared between\n\t * users): the node belongs to the principal that used it, so its unsynced writes\n\t * are held for that principal, never re-authored or uploaded under another one. The\n\t * device moves to a node the signed-in principal may own (a held node not yet tried\n\t * in this refusal cycle, typically that user's own earlier node) or to a fresh one.\n\t * - An adopted node (RT-40) is held; the store's own node is left as it is.\n\t */\n\tprivate async rotateNodeIdentity(): Promise<void> {\n\t\tconst refused = this.currentNodeId()\n\t\tconst storeNode = this.store.getNodeId()\n\t\tthis.returnAllInFlightUploads()\n\t\ttry {\n\t\t\t// The server says the node is not the signed-in user's (RT-50): a guessed\n\t\t\t// binding was wrong, and an unbound node is never tried for them again.\n\t\t\tconst principal = this.sessionPrincipal\n\t\t\t// The binding as it was when the session started: the refusal clears an\n\t\t\t// app-assigned one below.\n\t\t\tconst before =\n\t\t\t\trefused !== storeNode\n\t\t\t\t\t? (await this.syncState?.listLocalNodes?.())?.find((entry) => entry.nodeId === refused)\n\t\t\t\t\t: undefined\n\t\t\tif (typeof principal === 'string') {\n\t\t\t\tawait this.syncState?.recordLocalNodeRefusedFor?.(refused, principal)\n\t\t\t}\n\t\t\tif (refused !== storeNode) {\n\t\t\t\tif (before && (await this.reauthorRefusedAdoption(refused, before))) return\n\t\t\t\tconst node = (await this.syncState?.listLocalNodes?.())?.find(\n\t\t\t\t\t(entry) => entry.nodeId === refused,\n\t\t\t\t)\n\t\t\t\t// An unbound node (learning its owner) is not held for good: another user's\n\t\t\t\t// session may still be accepted for it.\n\t\t\t\tconst unbound = node !== undefined && (node.principal ?? null) === null\n\t\t\t\tawait this.syncState?.markLocalNodeRefused?.(refused, !unbound)\n\t\t\t\tthis.heldNodeIds.add(refused)\n\t\t\t\tthis.endAdoption()\n\t\t\t\tthis.emitter?.emit({ type: 'sync:local-node', nodeId: refused, action: 'adoption-refused' })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst rotate = this.store.rotateNodeId?.bind(this.store)\n\t\t\tif (!rotate) {\n\t\t\t\tthis.emitter?.emit({ type: 'sync:suspended', reason: 'node-id-claimed' })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (await this.wasNodeAccepted(refused)) {\n\t\t\t\tawait this.holdNodeAndSwitch(refused, rotate)\n\t\t\t} else {\n\t\t\t\tawait this.reauthorUnsentAndRotate(refused, rotate)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Node id rotation failed',\n\t\t\t\tcode: 'NODE_ROTATION_FAILED',\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * The server refused an adopted node that belongs to the signed-in user (the app\n\t * assigned its held writes, or it was created for them) but that no claims-aware\n\t * server ever accepted: typically a beta.12 node whose history the server recorded no\n\t * owner for, and whose id is not this device's verified id, so no handover applies.\n\t * Holding it would hold the user's own writes forever. Like the store's own node\n\t * (RT-21), its never-sent writes are re-authored under a fresh node of the user's,\n\t * which a later session adopts and uploads; what a beta.12 server acknowledged under\n\t * the node is not re-authored (it is stored there already).\n\t *\n\t * @returns Whether the node's writes were re-authored (false: the caller holds it)\n\t */\n\tprivate async reauthorRefusedAdoption(refused: string, node: LocalNodeInfo): Promise<boolean> {\n\t\tconst reauthor = this.store.reauthorLocalNode?.bind(this.store)\n\t\tconst principal = this.sessionPrincipal\n\t\tif (!reauthor || typeof principal !== 'string') return false\n\t\tif ((node.principal ?? null) !== principal || node.held) return false\n\t\t// Only the user's own writes by an explicit decision: the app assigned them, or the\n\t\t// node was created for this user. A binding confirmed by a server is an accepted\n\t\t// node, which is held instead (RT-38).\n\t\tif (node.binding !== 'app' && node.binding !== 'fresh') return false\n\t\tif (node.accepted || (await this.wasNodeAccepted(refused))) return false\n\t\tawait this.syncState?.markLocalNodeRefused?.(refused, false)\n\t\tthis.endAdoption()\n\t\tawait this.rotateUnsent(refused, (ids) => reauthor(refused, ids, principal))\n\t\tthis.emitter?.emit({ type: 'sync:local-node', nodeId: refused, action: 'adoption-refused' })\n\t\treturn true\n\t}\n\n\t/** RT-38: hold a refused, previously accepted node's writes and move to another node. */\n\tprivate async holdNodeAndSwitch(\n\t\trefused: string,\n\t\trotate: (ids: string[]) => Promise<{ nodeId: string; operations: Operation[] }>,\n\t): Promise<void> {\n\t\tawait this.syncState?.markLocalNodeRefused?.(refused, true)\n\t\tconst held = await this.countUnsyncedOfNode(refused)\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:local-node',\n\t\t\tnodeId: refused,\n\t\t\taction: 'held',\n\t\t\toperationCount: held.count,\n\t\t})\n\t\tlet target: string | null = null\n\t\tconst switchTo = this.store.switchNodeId?.bind(this.store)\n\t\tif (switchTo && this.syncState?.listLocalNodes) {\n\t\t\t// A held node refused before the last accepted handshake was not tried in this\n\t\t\t// refusal cycle: it may be the signed-in principal's own node.\n\t\t\tconst cycle = (await this.syncState.loadAcceptedCycle?.()) ?? 0\n\t\t\tconst nodes = await this.syncState.listLocalNodes()\n\t\t\tconst candidate = nodes.find(\n\t\t\t\t(node) =>\n\t\t\t\t\tnode.nodeId !== refused &&\n\t\t\t\t\tnode.held &&\n\t\t\t\t\t!this.belongsToAnotherPrincipal(node) &&\n\t\t\t\t\t// An unbound node is never handed to the signed-in user (RT-50).\n\t\t\t\t\t!(typeof this.principal === 'string' && (node.principal ?? null) === null) &&\n\t\t\t\t\t(node.refusedCycle === null || node.refusedCycle < cycle),\n\t\t\t)\n\t\t\tif (candidate) {\n\t\t\t\tawait switchTo(candidate.nodeId)\n\t\t\t\ttarget = candidate.nodeId\n\t\t\t}\n\t\t}\n\t\tif (target === null) target = (await rotate([])).nodeId\n\t\tawait this.afterIdentityChange()\n\t\tawait this.bindFreshNode()\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:node-id-rotated',\n\t\t\tpreviousNodeId: refused,\n\t\t\tnodeId: target,\n\t\t\treenqueuedCount: 0,\n\t\t\theldCount: held.count,\n\t\t})\n\t}\n\n\t/** RT-21: re-author a never-accepted node's never-sent writes under a fresh node. */\n\tprivate async reauthorUnsentAndRotate(\n\t\trefused: string,\n\t\trotate: (ids: string[]) => Promise<{ nodeId: string; operations: Operation[] }>,\n\t): Promise<void> {\n\t\tawait this.syncState?.markLocalNodeRefused?.(refused, false)\n\t\tawait this.rotateUnsent(refused, rotate)\n\t}\n\n\t/**\n\t * Move `nodeId`'s never-sent unsynced operations to a fresh node id (re-authored, so\n\t * they upload under it) and start using it. An operation flagged sent stays: it may\n\t * already be stored under `nodeId` (RT-38).\n\t *\n\t * @returns How many unsynced operations stayed under `nodeId` (sent, unacknowledged)\n\t */\n\tprivate async rotateUnsent(\n\t\tnodeId: string,\n\t\trotate: (ids: string[]) => Promise<{ nodeId: string; operations: Operation[] }>,\n\t): Promise<number> {\n\t\t// A database a beta.12 (or older) client synced has no acknowledged prefix yet (its\n\t\t// one-time re-upload restarts at 0 and queues its whole history), but that server\n\t\t// acknowledged the node through the persisted vector entry: operations at or below\n\t\t// it are on the server under this node, and a copy under the new node would apply\n\t\t// them twice (an increment counted twice; RT-92). They are not re-authored, and the\n\t\t// refused node's prefix moves up to that entry so they no longer count as unsynced.\n\t\tconst ackedFloor = Math.max(this.ownAckedThrough, this.lastAckedServerVector.get(nodeId) ?? 0)\n\t\tconst unsent = this.outboundQueue\n\t\t\t.getAll()\n\t\t\t.filter((op) => op.nodeId === nodeId && !this.outboundQueue.wasSent(op.id))\n\t\tconst queuedIds = unsent.filter((op) => op.sequenceNumber > ackedFloor).map((op) => op.id)\n\t\tconst acknowledgedIds = unsent\n\t\t\t.filter((op) => op.sequenceNumber <= ackedFloor)\n\t\t\t.map((op) => op.id)\n\t\tconst ids = new Set(queuedIds)\n\t\tlet staying = this.outboundQueue.countMatching(\n\t\t\t(op) => op.nodeId === nodeId && this.outboundQueue.wasSent(op.id),\n\t\t)\n\t\t// Own operations above the acknowledged prefix that were never read into the\n\t\t// queue (the unscanned tail) are unsynced too.\n\t\tconst localSeq = this.store.getVersionVector().get(nodeId) ?? 0\n\t\tif (localSeq > ackedFloor) {\n\t\t\tconst above = await this.store.getOperationRange(nodeId, ackedFloor + 1, localSeq)\n\t\t\tconst candidates = above.filter(\n\t\t\t\t(op) =>\n\t\t\t\t\top.nodeId === nodeId &&\n\t\t\t\t\top.sequenceNumber > ackedFloor &&\n\t\t\t\t\t!this.isOwnOperationResolved(op) &&\n\t\t\t\t\t!this.outboundQueue.has(op.id),\n\t\t\t)\n\t\t\tconst terminal = await this.findTerminalRejections(candidates.map((op) => op.id))\n\t\t\tfor (const op of candidates) {\n\t\t\t\tif (terminal.has(op.id)) continue\n\t\t\t\tif (this.outboundQueue.wasSent(op.id)) staying++\n\t\t\t\telse ids.add(op.id)\n\t\t\t}\n\t\t}\n\t\tconst result = await rotate([...ids])\n\t\tawait this.outboundQueue.replace([...queuedIds, ...acknowledgedIds], result.operations)\n\t\tif (ackedFloor > this.ownAckedThrough) {\n\t\t\tawait this.syncState?.saveOwnAckedThrough?.(nodeId, ackedFloor)\n\t\t}\n\t\tawait this.afterIdentityChange()\n\t\tawait this.bindFreshNode()\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:node-id-rotated',\n\t\t\tpreviousNodeId: nodeId,\n\t\t\tnodeId: result.nodeId,\n\t\t\treenqueuedCount: result.operations.length,\n\t\t})\n\t\treturn staying\n\t}\n\n\t/** A fresh node id belongs to the signed-in user, when the app says who that is (RT-42). */\n\tprivate async bindFreshNode(): Promise<void> {\n\t\tif (typeof this.principal !== 'string' || !this.store.bindPrincipal) return\n\t\ttry {\n\t\t\tawait this.store.bindPrincipal(this.principal)\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Binding the node to the user failed',\n\t\t\t\tcode: 'NODE_REGISTRY_FAILED',\n\t\t\t})\n\t\t}\n\t}\n\n\t/** The store moved to another node id: the next session starts from it. */\n\tprivate async afterIdentityChange(): Promise<void> {\n\t\tthis.nodeToken = null\n\t\tthis.nodeTokenNodeId = null\n\t\tthis.sessionNodeId = this.store.getNodeId()\n\t\t// The new node id has its own acknowledged prefix.\n\t\tawait this.withOwnTracking(async () => {\n\t\t\tthis.ownTrackingNodeId = null\n\t\t\tawait this.loadOwnTrackingLocked()\n\t\t})\n\t\tawait this.refreshPendingCount()\n\t}\n\n\t/** Whether a handshake as `nodeId` was ever accepted on this database. */\n\tprivate async wasNodeAccepted(nodeId: string): Promise<boolean> {\n\t\tif (this.memoryAcceptedNodes.has(nodeId)) return true\n\t\tif (!this.syncState?.listLocalNodes) return false\n\t\tconst nodes = await this.syncState.listLocalNodes()\n\t\treturn nodes.some((node) => node.nodeId === nodeId && node.accepted)\n\t}\n\n\t/**\n\t * Re-stamps queued (never-acknowledged) operations whose timestamps are far\n\t * enough in the future that the server would reject them, using the server's\n\t * own handshake time as the trusted \"now\". This is the automatic recovery\n\t * path after a user corrects a fast device clock: the queue drains\n\t * immediately instead of waiting for real time to catch up.\n\t */\n\tprivate async maybeRebaseQueuedOperations(serverTime: number): Promise<void> {\n\t\t// Optional store capability — hand-rolled SyncStore implementations\n\t\t// without it silently keep the old (blocked-until-time-catches-up) behavior.\n\t\tconst rebase = this.store.rebaseUnsyncedOperations?.bind(this.store)\n\t\tif (!rebase) return\n\n\t\t// Only operations that never reached the wire may be re-stamped (W3 step 4): a\n\t\t// sent one may already be stored on the server with its ack lost, and re-stamping\n\t\t// it would upload the same change twice under two ids.\n\t\tconst queued = this.outboundQueue\n\t\t\t.getAll()\n\t\t\t.filter((op) => op.nodeId === this.currentNodeId() && !this.outboundQueue.wasSent(op.id))\n\t\tif (queued.length === 0) return\n\n\t\tlet maxQueuedWallTime = Number.NEGATIVE_INFINITY\n\t\tfor (const op of queued) {\n\t\t\tif (op.timestamp.wallTime > maxQueuedWallTime) {\n\t\t\t\tmaxQueuedWallTime = op.timestamp.wallTime\n\t\t\t}\n\t\t}\n\n\t\t// Mirror the server's ingest tolerance: ops within +60s of server time\n\t\t// would be accepted as-is, so rewriting them would churn ids for nothing.\n\t\tconst SERVER_FUTURE_TOLERANCE_MS = 60_000\n\t\tif (maxQueuedWallTime <= serverTime + SERVER_FUTURE_TOLERANCE_MS) return\n\n\t\ttry {\n\t\t\tconst result = await rebase(\n\t\t\t\tqueued.map((op) => op.id),\n\t\t\t\tserverTime,\n\t\t\t)\n\t\t\tawait this.withOwnTracking(async () => {\n\t\t\t\tawait this.outboundQueue.replace(\n\t\t\t\t\tqueued.map((op) => op.id),\n\t\t\t\t\tresult.operations,\n\t\t\t\t)\n\t\t\t\t// The rewritten ops keep their sequence numbers but get new ids.\n\t\t\t\tfor (const [oldId, newId] of Object.entries(result.idMapping)) {\n\t\t\t\t\tfor (const ids of this.ownUnresolved.values()) {\n\t\t\t\t\t\tif (ids.delete(oldId)) ids.add(newId)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:clock-rebase',\n\t\t\t\trebasedCount: result.rebasedCount,\n\t\t\t\tmaxSkewMs: maxQueuedWallTime - serverTime,\n\t\t\t})\n\t\t} catch (error) {\n\t\t\t// Never let a failed rebase crash the handshake: the old ops stay\n\t\t\t// queued, the server will reject them with INVALID_TIMESTAMP, and the\n\t\t\t// existing clock-block path takes over. Surface the failure through an\n\t\t\t// existing event type rather than swallowing it.\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Timestamp rebase failed',\n\t\t\t\tcode: 'CLOCK_REBASE_FAILED',\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Upload this device's unsynced operations as the handshake delta. The delta is the\n\t * outbound queue itself (rebuilt from the op log above the acknowledged prefix), sent\n\t * as tracked batches: nothing leaves the queue until the server acknowledges the batch\n\t * that carried it (SYNC-4).\n\t */\n\tprivate async sendDelta(): Promise<void> {\n\t\tconst epoch = this.sessionEpoch\n\t\tconst strict = this.config.strictHandshake === true\n\t\tconst entries: InFlightUpload[] = []\n\t\tfor (let entry = this.takeUpload(); entry; entry = this.takeUpload()) {\n\t\t\tentries.push(entry)\n\t\t}\n\n\t\tif (entries.length === 0) {\n\t\t\tconst messageId = generateMessageId()\n\t\t\tif (strict) {\n\t\t\t\tthis.pendingDeltaBatchAcks.add(messageId)\n\t\t\t}\n\t\t\tconst emptyBatch: SyncMessage = {\n\t\t\t\ttype: 'operation-batch',\n\t\t\t\tmessageId,\n\t\t\t\toperations: [],\n\t\t\t\tisFinal: true,\n\t\t\t\tbatchIndex: 0,\n\t\t\t\ttotalBatches: 1,\n\t\t\t}\n\t\t\tthis.transport.send(emptyBatch)\n\t\t\tif (!strict) {\n\t\t\t\tthis.deltaSendComplete = true\n\t\t\t\tvoid this.checkDeltaComplete()\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tconst totalBatches = entries.length\n\t\tthis.initialSyncTotalBatches = Math.max(this.initialSyncTotalBatches, totalBatches)\n\t\t// One durability barrier for the whole delta (RT-35), not one per batch: flag every\n\t\t// batch sent first (flagging writes, which would dirty the store again between\n\t\t// batches), then make it all durable. sendUpload re-checks both, as no-ops; a\n\t\t// failure here surfaces there, per batch, and postpones that batch.\n\t\ttry {\n\t\t\tfor (const entry of entries) await this.outboundQueue.markSent(entry.batch.batchId)\n\t\t\tawait this.awaitUploadDurability()\n\t\t} catch {\n\t\t\t// Reported by sendUpload, which retries the barrier before each batch.\n\t\t}\n\t\tfor (const [i, entry] of entries.entries()) {\n\t\t\tif (this.sessionEpoch !== epoch) {\n\t\t\t\t// The session ended mid-delta: the rest goes back to the queue.\n\t\t\t\tif (this.inFlightUploads.get(entry.batch.batchId) === entry) this.returnUpload(entry)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst cursor = createDeltaCursorFromBatch(entry.batch.operations, i)\n\t\t\tawait this.sendUpload(entry, {\n\t\t\t\tisFinal: i === totalBatches - 1,\n\t\t\t\tbatchIndex: i,\n\t\t\t\ttotalBatches,\n\t\t\t\t...(cursor ? { cursor: encodeDeltaCursor(cursor) } : {}),\n\t\t\t\tbeforeSend: (messageId) => {\n\t\t\t\t\tif (strict) this.pendingDeltaBatchAcks.add(messageId)\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\n\t\tif (!strict && this.sessionEpoch === epoch) {\n\t\t\tthis.deltaSendComplete = true\n\t\t\tvoid this.checkDeltaComplete()\n\t\t}\n\t}\n\n\tprivate markDeltaSendCompleteIfReady(): void {\n\t\tif (this.config.strictHandshake && this.pendingDeltaBatchAcks.size > 0) {\n\t\t\treturn\n\t\t}\n\t\tthis.deltaSendComplete = true\n\t\tvoid this.checkDeltaComplete()\n\t}\n\n\t/** Take the next batch from the queue and track it until it is resolved. */\n\tprivate takeUpload(): InFlightUpload | null {\n\t\tconst batch = this.outboundQueue.takeBatch(this.batchSize, this.isSessionOperation)\n\t\tif (!batch) return null\n\t\tconst entry: InFlightUpload = {\n\t\t\tbatch,\n\t\t\tmessageId: null,\n\t\t\tepoch: this.sessionEpoch,\n\t\t\tretryBackoff: false,\n\t\t}\n\t\tthis.inFlightUploads.set(batch.batchId, entry)\n\t\treturn entry\n\t}\n\n\t/** Whether a taken batch still belongs to the live session (SYNC-10). */\n\tprivate isUploadCurrent(entry: InFlightUpload): boolean {\n\t\treturn (\n\t\t\tthis.inFlightUploads.get(entry.batch.batchId) === entry &&\n\t\t\tentry.epoch === this.sessionEpoch &&\n\t\t\t(this.state === 'syncing' || this.state === 'streaming')\n\t\t)\n\t}\n\n\t/** Return one taken batch to the queue and forget its tracking. */\n\tprivate returnUpload(entry: InFlightUpload): void {\n\t\tthis.outboundQueue.returnBatch(entry.batch.batchId)\n\t\tthis.inFlightUploads.delete(entry.batch.batchId)\n\t\tif (entry.messageId) this.uploadByMessageId.delete(entry.messageId)\n\t\tif (this.inFlightUploads.size === 0) this.clearOutboundAckTimer()\n\t}\n\n\t/** Return every taken batch to the queue (the session ended; nothing was resolved). */\n\tprivate returnAllInFlightUploads(): void {\n\t\tfor (const entry of [...this.inFlightUploads.values()]) {\n\t\t\tthis.outboundQueue.returnBatch(entry.batch.batchId)\n\t\t}\n\t\tthis.inFlightUploads.clear()\n\t\tthis.uploadByMessageId.clear()\n\t\tthis.clearOutboundAckTimer()\n\t}\n\n\t/**\n\t * The oldest batch taken from the queue and not yet resolved (diagnostics and\n\t * tests); null when nothing is in flight.\n\t */\n\tprivate get currentBatch(): OutboundBatch | null {\n\t\tconst first = this.inFlightUploads.values().next()\n\t\treturn first.done ? null : first.value.batch\n\t}\n\n\tprivate inFlightUploadCount(): number {\n\t\tlet count = 0\n\t\tfor (const entry of this.inFlightUploads.values()) count += entry.batch.operations.length\n\t\treturn count\n\t}\n\n\t/**\n\t * Prepare, encrypt and send one taken batch, then register its message id so the ack\n\t * that names it resolves exactly this batch. Every await is followed by a check that\n\t * the batch still belongs to the live session; a send that throws returns the batch\n\t * to the queue instead of rejecting unhandled (SYNC-10).\n\t *\n\t * @returns The wire message id, or null when the batch was not sent.\n\t */\n\tprivate async sendUpload(\n\t\tentry: InFlightUpload,\n\t\tframe: {\n\t\t\tisFinal: boolean\n\t\t\tbatchIndex: number\n\t\t\ttotalBatches?: number\n\t\t\tcursor?: string\n\t\t\tbeforeSend?: (messageId: string) => void\n\t\t},\n\t): Promise<string | null> {\n\t\t// The server acknowledges a batch as \"processed through sequence n\", in the order\n\t\t// it processes them; sending own operations in sequence order keeps that a prefix.\n\t\tconst operations = [...entry.batch.operations].sort(compareForUpload)\n\t\tconst preparer = this.outboundPreparer\n\t\tif (preparer) {\n\t\t\t// Let the app send what the server needs BEFORE these operations (blob bytes,\n\t\t\t// so the server sees proof of possession before the reference, RT-11).\n\t\t\ttry {\n\t\t\t\tawait preparer(operations)\n\t\t\t} catch {\n\t\t\t\t// Best effort: the server decides on the operations themselves.\n\t\t\t}\n\t\t\tif (!this.isUploadCurrent(entry)) return null\n\t\t}\n\n\t\tlet wireOperations = operations\n\t\tif (this.encryptionConfigured()) {\n\t\t\ttry {\n\t\t\t\twireOperations = await this.requireEncryptor().encryptBatch(operations)\n\t\t\t} catch (err) {\n\t\t\t\t// Nothing was sent: return the batch so no data is lost.\n\t\t\t\tif (this.inFlightUploads.get(entry.batch.batchId) === entry) this.returnUpload(entry)\n\t\t\t\tthis.emitter?.emit({\n\t\t\t\t\ttype: 'sync:disconnected',\n\t\t\t\t\treason: err instanceof Error ? err.message : 'Encryption failed',\n\t\t\t\t})\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (!this.isUploadCurrent(entry)) return null\n\t\t}\n\n\t\tif (!this.isUploadCurrent(entry)) return null\n\t\t// Flag the operations as sent BEFORE they reach the wire: from then on they may be\n\t\t// stored on the server even if no ack ever arrives, so a rebase or a node rotation\n\t\t// must leave them (RT-38). Then make every local write durable (RT-35): the server\n\t\t// must never hold an operation, or the device lack a sent flag, that a reload could\n\t\t// lose. The in-memory flag is set synchronously, before the first await.\n\t\ttry {\n\t\t\tawait this.outboundQueue.markSent(entry.batch.batchId)\n\t\t\tawait this.awaitUploadDurability()\n\t\t} catch (error) {\n\t\t\tif (this.inFlightUploads.get(entry.batch.batchId) === entry) this.returnUpload(entry)\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage:\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? `Upload postponed: local writes are not durable yet (${error.message})`\n\t\t\t\t\t\t: 'Upload postponed: local writes are not durable yet',\n\t\t\t\tcode: 'UPLOAD_NOT_DURABLE',\n\t\t\t})\n\t\t\tthis.scheduleOutboundRetry()\n\t\t\treturn null\n\t\t}\n\t\tif (!this.isUploadCurrent(entry)) return null\n\n\t\tconst messageId = generateMessageId()\n\t\tconst message: SyncMessage = {\n\t\t\ttype: 'operation-batch',\n\t\t\tmessageId,\n\t\t\toperations: wireOperations.map((op) => this.serializer.encodeOperation(op)),\n\t\t\tisFinal: frame.isFinal,\n\t\t\tbatchIndex: frame.batchIndex,\n\t\t\t...(frame.totalBatches !== undefined ? { totalBatches: frame.totalBatches } : {}),\n\t\t\t...(frame.cursor !== undefined ? { cursor: frame.cursor } : {}),\n\t\t}\n\t\tentry.messageId = messageId\n\t\tthis.uploadByMessageId.set(messageId, entry.batch.batchId)\n\t\tframe.beforeSend?.(messageId)\n\t\ttry {\n\t\t\tthis.transport.send(message)\n\t\t} catch (error) {\n\t\t\tthis.returnUpload(entry)\n\t\t\tthis.pendingDeltaBatchAcks.delete(messageId)\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:disconnected',\n\t\t\t\treason: error instanceof Error ? error.message : 'Send failed',\n\t\t\t})\n\t\t\treturn null\n\t\t}\n\t\tthis.startOutboundAckTimer()\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:sent',\n\t\t\toperations,\n\t\t\tbatchSize: operations.length,\n\t\t})\n\t\tthis.notifyStatusChange()\n\t\treturn messageId\n\t}\n\n\t/**\n\t * The durability barrier before an upload (RT-35), with a bounded number of failures\n\t * (RT-49). Throws while the failures may be transient; after\n\t * {@link DURABILITY_FAILURES_BEFORE_DEGRADED} consecutive failures it returns anyway\n\t * (degraded mode, `localDurability: 'degraded'`, `sync:durability-degraded`) so the\n\t * server keeps a durable copy. The barrier is still attempted every time: the first\n\t * success leaves degraded mode (`sync:durability-restored`).\n\t */\n\tprivate async awaitUploadDurability(): Promise<void> {\n\t\tif (!this.store.ensureDurable) return\n\t\ttry {\n\t\t\tawait this.store.ensureDurable()\n\t\t} catch (error) {\n\t\t\tthis.durabilityFailures++\n\t\t\tif (this.durabilityDegraded) return\n\t\t\tif (this.durabilityFailures < DURABILITY_FAILURES_BEFORE_DEGRADED) throw error\n\t\t\tthis.durabilityDegraded = true\n\t\t\tconst message = error instanceof Error ? error.message : 'The local database is not durable'\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:durability-degraded',\n\t\t\t\tmessage,\n\t\t\t\tfailedAttempts: this.durabilityFailures,\n\t\t\t})\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: `Uploading without local durability after ${this.durabilityFailures} failed attempts; the server holds the only durable copy of new writes (${message})`,\n\t\t\t\tcode: 'DURABILITY_DEGRADED',\n\t\t\t})\n\t\t\tthis.notifyStatusChange()\n\t\t\treturn\n\t\t}\n\t\tthis.durabilityFailures = 0\n\t\tif (this.durabilityDegraded) {\n\t\t\tthis.durabilityDegraded = false\n\t\t\tthis.emitter?.emit({ type: 'sync:durability-restored' })\n\t\t\tthis.notifyStatusChange()\n\t\t}\n\t}\n\n\tprivate async handleOperationBatch(msg: OperationBatchMessage): Promise<void> {\n\t\tconst isDeliveryBatch = msg.maxDeliverySequence !== undefined\n\n\t\t// Delivery-stream chain control. The server sends in-scope operations in delivery\n\t\t// order, each batch chaining base -> max. The watermark stays a sound, gap-free\n\t\t// lower bound, and a dropped batch recovers without a lost operation:\n\t\tif (isDeliveryBatch) {\n\t\t\tconst base = msg.baseDeliverySequence ?? 0\n\t\t\tconst max = msg.maxDeliverySequence ?? base\n\t\t\tif (base > this.deliveryWatermark) {\n\t\t\t\t// A gap: an earlier batch has not arrived. Do not apply out of order and do\n\t\t\t\t// not acknowledge, so the server's reliable retransmit (or the next handshake\n\t\t\t\t// resend from the watermark) redelivers the missing batch first.\n\t\t\t\tconst gapKey = `${this.deliveryWatermark}:${base}`\n\t\t\t\tthis.deliveryGapRepeatCount =\n\t\t\t\t\tthis.lastDeliveryGapKey === gapKey ? this.deliveryGapRepeatCount + 1 : 1\n\t\t\t\tthis.lastDeliveryGapKey = gapKey\n\t\t\t\tthis.emitter?.emit({\n\t\t\t\t\ttype: 'sync:delivery-gap',\n\t\t\t\t\texpectedBase: this.deliveryWatermark,\n\t\t\t\t\treceivedBase: base,\n\t\t\t\t\tcurrentWatermark: this.deliveryWatermark,\n\t\t\t\t\tmessageId: msg.messageId,\n\t\t\t\t\trepeatCount: this.deliveryGapRepeatCount,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (base < this.deliveryWatermark && max <= this.deliveryWatermark) {\n\t\t\t\t// Entirely below the watermark: a retransmit that crossed an ack, or a server\n\t\t\t\t// restarting the stream from 0 (it does so whenever the resolved scope differs\n\t\t\t\t// from the handshake scope). Re-acknowledge without re-applying, but still run\n\t\t\t\t// the initial-sync bookkeeping: a final batch must complete the handshake, or\n\t\t\t\t// the engine would sit in 'syncing' forever (NEW-SYNC-1).\n\t\t\t\tthis.sendDeliveryAck(msg.messageId, this.deliveryWatermark)\n\t\t\t\tawait this.recordReceivedBatch(msg, true, true, [], [])\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// base === watermark continues the chain. base < watermark < max straddles it:\n\t\t\t// the batch is applied whole (re-applying the part below the watermark is an\n\t\t\t// idempotent no-op) and the watermark advances to its max, so the next chained\n\t\t\t// batch (base == max) is not mistaken for a gap.\n\t\t\tthis.hasInFlightDeliveryBatch = true\n\t\t}\n\n\t\tconst operations = msg.operations.map((s) => this.serializer.decodeOperation(s))\n\t\tconst deliverySequence = isDeliveryBatch ? (msg.maxDeliverySequence ?? null) : null\n\n\t\t// Whether every operation in this batch was durably applied, was a harmless\n\t\t// duplicate, or was durably quarantined. A retriable failure clears this so the\n\t\t// delivery watermark does not advance past the failed operation.\n\t\tlet fullyApplied = true\n\n\t\tfor (const retraction of msg.retractions ?? []) {\n\t\t\ttry {\n\t\t\t\tawait this.applyScopeRetraction(retraction, true)\n\t\t\t} catch {\n\t\t\t\tfullyApplied = false\n\t\t\t}\n\t\t}\n\t\tif ((msg.retractions?.length ?? 0) > 0) {\n\t\t\tawait this.refreshPendingCount()\n\t\t}\n\n\t\t// Apply what the server delivered, in order (SYNC-2, NEW-SYNC-3): the server already\n\t\t// enforced the downlink scope and query view, and judging delivered operations\n\t\t// against the client's uplink scope dropped read-only data. Anything the client\n\t\t// deliberately does not apply is quarantined, never silently skipped.\n\t\tconst received: Operation[] = []\n\t\tconst quarantined: QuarantinedOperation[] = []\n\t\tfor (const delivered of operations) {\n\t\t\tconst outcome = await this.applyInbound(delivered, deliverySequence)\n\t\t\tif (outcome.operation) received.push(outcome.operation)\n\t\t\tif (outcome.kind === 'quarantine') quarantined.push(outcome.entry)\n\t\t\telse if (outcome.kind === 'stall') fullyApplied = false\n\t\t}\n\n\t\tif (received.length > 0) {\n\t\t\tthis.lastSuccessfulPull = Date.now()\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:received',\n\t\t\t\toperations: received,\n\t\t\t\tbatchSize: received.length,\n\t\t\t})\n\t\t}\n\n\t\t// Make the outcome durable BEFORE acknowledging. For a chained delivery batch the\n\t\t// quarantine rows and the watermark advance commit in one transaction, so the\n\t\t// watermark never passes an operation that is neither applied nor quarantined.\n\t\tconst advance = isDeliveryBatch && fullyApplied && msg.maxDeliverySequence !== undefined\n\t\tif (advance && msg.maxDeliverySequence !== undefined) {\n\t\t\tthis.deliveryGapRepeatCount = 0\n\t\t\tthis.lastDeliveryGapKey = null\n\t\t\tconst watermark = Math.max(this.deliveryWatermark, msg.maxDeliverySequence)\n\t\t\tawait this.commitDeliveryProgress(quarantined, watermark)\n\t\t\tthis.deliveryWatermark = watermark\n\t\t\tthis.setViewWatermark(this.deliverySignature(), watermark)\n\t\t\tif (\n\t\t\t\tthis.blockedFailure &&\n\t\t\t\treceived.some((op) => op.id === this.blockedFailure?.operationId)\n\t\t\t) {\n\t\t\t\tconst recovered = this.blockedFailure\n\t\t\t\tthis.blockedFailure = null\n\t\t\t\tthis.emitter?.emit({ type: 'sync:apply-recovered', failure: recovered })\n\t\t\t}\n\t\t} else if (!isDeliveryBatch && quarantined.length > 0) {\n\t\t\t// A legacy (version-vector) or relay batch has no watermark to move, but its\n\t\t\t// quarantined operations must still be recorded before the ack releases them.\n\t\t\tawait this.commitDeliveryProgress(quarantined, null)\n\t\t}\n\n\t\t// Acknowledge the batch. A legacy (version-vector) batch is always acknowledged.\n\t\t// A delivery-stream batch is acknowledged only when it fully applied: a failed\n\t\t// delivery batch must NOT be acknowledged, so the server keeps re-sending it from\n\t\t// the client's last acknowledged position rather than releasing it.\n\t\tif (!isDeliveryBatch || advance) {\n\t\t\tconst lastOp = operations[operations.length - 1]\n\t\t\tconst ack: SyncMessage = {\n\t\t\t\ttype: 'acknowledgment',\n\t\t\t\tmessageId: generateMessageId(),\n\t\t\t\tacknowledgedMessageId: msg.messageId,\n\t\t\t\tlastSequenceNumber: lastOp ? lastOp.sequenceNumber : 0,\n\t\t\t\t...(advance && msg.maxDeliverySequence !== undefined\n\t\t\t\t\t? { deliverySequence: this.deliveryWatermark }\n\t\t\t\t\t: {}),\n\t\t\t}\n\t\t\tthis.transport.send(ack)\n\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:acknowledged',\n\t\t\t\tsequenceNumber: lastOp ? lastOp.sequenceNumber : 0,\n\t\t\t})\n\t\t}\n\t\tthis.hasInFlightDeliveryBatch = false\n\n\t\tconst streaming = this.state === 'streaming'\n\t\tawait this.recordReceivedBatch(msg, isDeliveryBatch, fullyApplied, received, operations)\n\t\t// A streaming device is caught up again at the end of every fully applied final\n\t\t// batch, so it settles the provisional effects of remote deletes (RT-69) there too,\n\t\t// exactly as at a catch-up. A server copy that confirms an effect is stored with\n\t\t// its delete and arrives with it; an effect no copy confirms (a protocol-1 server,\n\t\t// beta.12 and older, derives no cascades, RT-93; a delete this server derived\n\t\t// nothing for, RT-94) was otherwise kept until the next reconnect, while every\n\t\t// device that received the same delete during a catch-up dropped it.\n\t\tif (streaming && msg.isFinal && (!isDeliveryBatch || fullyApplied)) {\n\t\t\tawait this.settleAfterCatchUp({ provisionalOnly: true })\n\t\t}\n\t\tthis.notifyStatusChange()\n\t}\n\n\t/**\n\t * Initial-sync bookkeeping for a received batch: progress, the legacy resume cursor,\n\t * and completion on the final batch. Runs for duplicate batches too (NEW-SYNC-1).\n\t */\n\tprivate async recordReceivedBatch(\n\t\tmsg: OperationBatchMessage,\n\t\tisDeliveryBatch: boolean,\n\t\tfullyApplied: boolean,\n\t\treceived: Operation[],\n\t\toperations: Operation[],\n\t): Promise<void> {\n\t\tif (this.state !== 'syncing') {\n\t\t\treturn\n\t\t}\n\t\tthis.deltaBatchesReceived++\n\t\tconst totalBatches = msg.totalBatches ?? this.initialSyncTotalBatches\n\t\tif (msg.totalBatches !== undefined) {\n\t\t\tthis.initialSyncTotalBatches = msg.totalBatches\n\t\t}\n\t\tthis.metricsCollector.updateInitialSyncProgress(this.deltaBatchesReceived, totalBatches)\n\n\t\t// The version-vector resume cursor is only used for the legacy (non-delivery)\n\t\t// delta path. A delivery stream resumes from the watermark instead.\n\t\tif (!isDeliveryBatch) {\n\t\t\tconst cursorFromBatch =\n\t\t\t\tmsg.cursor !== undefined\n\t\t\t\t\t? decodeDeltaCursor(msg.cursor)\n\t\t\t\t\t: createDeltaCursorFromBatch(received.length > 0 ? received : operations, msg.batchIndex)\n\t\t\tif (cursorFromBatch) {\n\t\t\t\tthis.resumeDeltaCursor = cursorFromBatch\n\t\t\t\tawait this.persistDeltaCursor(cursorFromBatch)\n\t\t\t}\n\t\t}\n\n\t\t// A delivery batch with a retriable failure must not complete initial sync (its\n\t\t// operations still need to arrive), so completion is gated on fullyApplied.\n\t\tif (msg.isFinal && (!isDeliveryBatch || fullyApplied)) {\n\t\t\tthis.deltaReceiveComplete = true\n\t\t\tthis.resumeDeltaCursor = null\n\t\t\tawait this.persistDeltaCursor(null)\n\t\t\tawait this.settleAfterCatchUp()\n\t\t\tthis.metricsCollector.recordSyncCompleted()\n\t\t\tawait this.checkDeltaComplete()\n\t\t}\n\t}\n\n\t/**\n\t * Apply one delivered operation. Outcomes:\n\t * - `applied`: applied, a duplicate, or a merge-decided skip (the record is settled);\n\t * - `quarantine`: deliberately not applied and to be recorded durably (unknown\n\t * collection, transform unavailable, rejected or deferred apply, far-future\n\t * timestamp, undecryptable payload, referential failure), with `sync:apply-failed`;\n\t * - `stall`: a retriable failure; the watermark must not pass it (or the engine\n\t * cannot record a quarantine durably), so the server re-sends it.\n\t */\n\tprivate async applyInbound(\n\t\tdelivered: Operation,\n\t\tdeliverySequence: number | null,\n\t): Promise<\n\t\t| { kind: 'applied'; operation: Operation }\n\t\t| { kind: 'quarantine'; entry: QuarantinedOperation; operation: Operation | null }\n\t\t| { kind: 'stall'; operation: Operation | null }\n\t> {\n\t\tconst quarantine = (\n\t\t\top: Operation,\n\t\t\tcode: string,\n\t\t\tmessage: string,\n\t\t\tresult: Exclude<ApplyResult, 'applied' | 'duplicate'>,\n\t\t\tretriable: boolean,\n\t\t\tdecrypted: Operation | null,\n\t\t):\n\t\t\t| { kind: 'quarantine'; entry: QuarantinedOperation; operation: Operation | null }\n\t\t\t| { kind: 'stall'; operation: Operation | null } => {\n\t\t\tif (!this.canQuarantine()) {\n\t\t\t\t// No durable quarantine next to a durable watermark: stall rather than lose.\n\t\t\t\tthis.emitApplyFailure(op, result, { code, message, retriable }, 'blocking')\n\t\t\t\treturn { kind: 'stall', operation: decrypted }\n\t\t\t}\n\t\t\tthis.emitApplyFailure(op, result, { code, message, retriable }, 'quarantined')\n\t\t\treturn {\n\t\t\t\tkind: 'quarantine',\n\t\t\t\toperation: decrypted,\n\t\t\t\tentry: { operation: op, deliverySequence, code, message, quarantinedAt: Date.now() },\n\t\t\t}\n\t\t}\n\n\t\t// Decrypt per operation (ENC-2): one undecryptable operation (another key, a\n\t\t// corrupted payload) is quarantined; the session and every other operation go on.\n\t\tlet op = delivered\n\t\tif (this.encryptionConfigured() && !this.isServerAuthoredCleartext(delivered)) {\n\t\t\ttry {\n\t\t\t\top = await this.requireEncryptor().decryptOperation(delivered)\n\t\t\t} catch (error) {\n\t\t\t\tthis.maybeRefreshKeyring(delivered)\n\t\t\t\treturn quarantine(\n\t\t\t\t\tdelivered,\n\t\t\t\t\tQUARANTINE_CODES.DECRYPT_FAILED,\n\t\t\t\t\terror instanceof Error ? error.message : 'Failed to decrypt operation',\n\t\t\t\t\t'rejected',\n\t\t\t\t\ttrue,\n\t\t\t\t\tnull,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// Content-addressed id check (CORE-1, protocol v2), on the plaintext and before\n\t\t// any transform. A forged or altered operation is kept in quarantine, never\n\t\t// applied; it never verifies later, so the replay leaves it there.\n\t\tconst integrity = await verifyInboundOperation(op, {\n\t\t\tencrypted: delivered.encrypted !== undefined,\n\t\t})\n\t\tif (!integrity.ok) {\n\t\t\treturn quarantine(delivered, integrity.code, integrity.message, 'rejected', false, null)\n\t\t}\n\n\t\t// A far-future timestamp is never applied: adopting it would make every later\n\t\t// local edit lose to it until real time caught up (SYNC-7). The HLC also refuses\n\t\t// it once warm; this check covers a cold clock and does not depend on the store.\n\t\tconst reference = Date.now() + (this.clockSkewMs ?? 0)\n\t\tif (op.timestamp.wallTime > reference + MAX_REMOTE_FUTURE_MS) {\n\t\t\treturn quarantine(\n\t\t\t\top,\n\t\t\t\tQUARANTINE_CODES.REMOTE_CLOCK_DRIFT,\n\t\t\t\t`Operation timestamp ${op.timestamp.wallTime} is more than ${MAX_REMOTE_FUTURE_MS}ms ahead of the reference time ${reference}; it is kept and retried once time catches up.`,\n\t\t\t\t'rejected',\n\t\t\t\ttrue,\n\t\t\t\top,\n\t\t\t)\n\t\t}\n\n\t\t// A beta.12 clear the id proves (delivered by a beta.12 server, which stored the\n\t\t// body without it) is made explicit before the body is stored (RT-85): the fold\n\t\t// folds bodies as written.\n\t\top = await canonicalizeProvenLegacyClear(op)\n\n\t\t// Transforms at fold time (RT-84): the operation is stored exactly as delivered and\n\t\t// the store folds its view. Here the view is only checked: an operation with no\n\t\t// view (authored under a newer schema than this device's) is kept aside and\n\t\t// replayed after an upgrade, as before.\n\t\tconst targetSchemaVersion = this.config.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\tlet transformed: Operation | null\n\t\ttry {\n\t\t\ttransformed = operationSchemaView(op, targetSchemaVersion, this.operationTransforms)\n\t\t} catch (error) {\n\t\t\treturn quarantine(\n\t\t\t\top,\n\t\t\t\tQUARANTINE_CODES.TRANSFORM_INVALID,\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t'rejected',\n\t\t\t\tfalse,\n\t\t\t\top,\n\t\t\t)\n\t\t}\n\t\tif (transformed === null) {\n\t\t\treturn quarantine(\n\t\t\t\top,\n\t\t\t\tQUARANTINE_CODES.TRANSFORM_UNAVAILABLE,\n\t\t\t\t`No transform from schema v${op.schemaVersion} to v${targetSchemaVersion} for this operation; it is kept and replayed after an upgrade.`,\n\t\t\t\t'skipped',\n\t\t\t\tfalse,\n\t\t\t\top,\n\t\t\t)\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.store.applyRemoteOperation(op)\n\t\t\tif (result === 'applied' || result === 'duplicate') {\n\t\t\t\treturn { kind: 'applied', operation: op }\n\t\t\t}\n\t\t\tif (result === 'skipped' && this.store.hasCollection?.(transformed.collection) === true) {\n\t\t\t\t// A merge decision (for example a local update outranking a remote delete):\n\t\t\t\t// the record is settled; nothing to keep.\n\t\t\t\tthis.emitApplyFailure(transformed, result, undefined, 'quarantined')\n\t\t\t\treturn { kind: 'applied', operation: op }\n\t\t\t}\n\t\t\tconst reason = defaultApplyFailureReason(result)\n\t\t\treturn quarantine(transformed, reason.code, reason.message, result, reason.retriable, op)\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : 'Apply failed'\n\t\t\tconst code =\n\t\t\t\terror instanceof SyncError\n\t\t\t\t\t? error.code\n\t\t\t\t\t: error instanceof KoraError\n\t\t\t\t\t\t? error.code\n\t\t\t\t\t\t: error instanceof Error && 'code' in error && typeof error.code === 'string'\n\t\t\t\t\t\t\t? error.code\n\t\t\t\t\t\t\t: APPLY_FAILURE_CODES.APPLY_FAILED\n\t\t\tif (error instanceof ClockDriftError) {\n\t\t\t\t// This device's own clock is broken: a local condition, not the operation's\n\t\t\t\t// fault. Stall (blocking) so it is retried once the clock is fixed.\n\t\t\t\tthis.emitApplyFailure(\n\t\t\t\t\ttransformed,\n\t\t\t\t\t'rejected',\n\t\t\t\t\t{ code: APPLY_FAILURE_CODES.CLOCK_DRIFT, message: error.message, retriable: false },\n\t\t\t\t\t'blocking',\n\t\t\t\t)\n\t\t\t\treturn { kind: 'stall', operation: op }\n\t\t\t}\n\t\t\tif (error instanceof RemoteClockDriftError || error instanceof InvalidTimestampError) {\n\t\t\t\treturn quarantine(transformed, code, message, 'rejected', true, op)\n\t\t\t}\n\t\t\tif (code === FOLD_STATE_INVALID_CODE) {\n\t\t\t\t// The record's fold state cannot take this operation even after the store\n\t\t\t\t// re-folded it once (RT-63; for example a carried state of an unknown plan).\n\t\t\t\t// Not transient: quarantined (and replayed on start, after an upgrade)\n\t\t\t\t// rather than stalling every later operation of the delivery stream.\n\t\t\t\treturn quarantine(transformed, code, message, 'rejected', false, op)\n\t\t\t}\n\t\t\tif (code === APPLY_FAILURE_CODES.REFERENTIAL_INTEGRITY) {\n\t\t\t\t// Not appliable now (for example a child whose parent is outside this view);\n\t\t\t\t// kept and replayed rather than stalling every later operation.\n\t\t\t\treturn quarantine(transformed, code, message, 'rejected', false, op)\n\t\t\t}\n\t\t\t// Anything else may be transient (a busy database): the store did not take\n\t\t\t// custody, so stall; the server re-sends it.\n\t\t\tthis.emitApplyFailure(transformed, 'rejected', { code, message, retriable: true }, 'blocking')\n\t\t\treturn { kind: 'stall', operation: op }\n\t\t}\n\t}\n\n\t/**\n\t * The delivery stream caught up: let the store retire provisional cascades of\n\t * remote deletes (RT-69) and settle row-snapshot records after a full resync\n\t * (RT-68). A failure is reported, never fatal: both are retried on the next\n\t * catch-up.\n\t */\n\tprivate async settleAfterCatchUp(options?: { provisionalOnly?: boolean }): Promise<void> {\n\t\tif (!this.store.settleAfterCatchUp) return\n\t\ttry {\n\t\t\tawait this.store.settleAfterCatchUp(options)\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: '',\n\t\t\t\tmessage: `Settling provisional effects and row snapshots after catch-up failed: ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`,\n\t\t\t\tcode: 'SETTLE_AFTER_CATCH_UP_FAILED',\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Whether a not-applied operation can be recorded so the watermark may pass it: with\n\t * a durable quarantine, or when nothing is durable (no persisted watermark either, so\n\t * a restart re-delivers everything anyway).\n\t */\n\tprivate canQuarantine(): boolean {\n\t\tif (!this.syncState) return true\n\t\tif (this.syncState.saveQuarantine) return true\n\t\treturn !this.syncState.saveDeliveryWatermark\n\t}\n\n\t/**\n\t * Record quarantined operations and (for a delivery batch) the watermark advance in\n\t * one durable step.\n\t */\n\tprivate async commitDeliveryProgress(\n\t\tquarantined: QuarantinedOperation[],\n\t\twatermark: number | null,\n\t): Promise<void> {\n\t\tconst signature = this.deliverySignature()\n\t\tif (quarantined.length > 0 && this.syncState?.saveQuarantine) {\n\t\t\tawait this.syncState.saveQuarantine(\n\t\t\t\tquarantined,\n\t\t\t\twatermark === null ? undefined : { signature, watermark },\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tfor (const entry of quarantined) {\n\t\t\tthis.memoryQuarantine.set(entry.operation.id, entry)\n\t\t}\n\t\tif (watermark !== null) {\n\t\t\tawait this.persistDeliveryWatermark(watermark, signature)\n\t\t}\n\t}\n\n\t/** Every inbound operation held in quarantine (durable when persistence supports it). */\n\tasync getQuarantinedOperations(): Promise<QuarantinedOperation[]> {\n\t\tif (this.syncState?.loadQuarantine) {\n\t\t\treturn this.syncState.loadQuarantine()\n\t\t}\n\t\treturn [...this.memoryQuarantine.values()]\n\t}\n\n\t/**\n\t * Try to apply every quarantined operation again, oldest delivery first and in HLC\n\t * order within a delivery. Runs on start (after a schema upgrade the unknown\n\t * collection now exists; after time catches up a far-future op is acceptable) and on\n\t * demand. Applied operations leave the quarantine; the rest stay.\n\t *\n\t * @returns How many operations left the quarantine.\n\t */\n\tasync retryQuarantinedOperations(): Promise<number> {\n\t\treturn this.replayQuarantine()\n\t}\n\n\tprivate async replayQuarantine(): Promise<number> {\n\t\tconst entries = await this.getQuarantinedOperations()\n\t\tif (entries.length === 0) return 0\n\t\tentries.sort(\n\t\t\t(a, b) =>\n\t\t\t\t(a.deliverySequence ?? 0) - (b.deliverySequence ?? 0) ||\n\t\t\t\tHybridLogicalClock.compare(a.operation.timestamp, b.operation.timestamp),\n\t\t)\n\t\tconst released: string[] = []\n\t\tfor (const entry of entries) {\n\t\t\tlet outcome: Awaited<ReturnType<SyncEngine['applyInboundQuietly']>>\n\t\t\ttry {\n\t\t\t\toutcome = await this.applyInboundQuietly(entry.operation)\n\t\t\t} catch {\n\t\t\t\toutcome = false\n\t\t\t}\n\t\t\tif (outcome) {\n\t\t\t\treleased.push(entry.operation.id)\n\t\t\t\tthis.emitter?.emit({\n\t\t\t\t\ttype: 'sync:apply-recovered',\n\t\t\t\t\tfailure: {\n\t\t\t\t\t\toperationId: entry.operation.id,\n\t\t\t\t\t\tcollection: entry.operation.collection,\n\t\t\t\t\t\trecordId: entry.operation.recordId,\n\t\t\t\t\t\tcode: entry.code,\n\t\t\t\t\t\tmessage: entry.message,\n\t\t\t\t\t\tretriable: true,\n\t\t\t\t\t\tfirstSeenAt: entry.quarantinedAt,\n\t\t\t\t\t\tretryCount: 0,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tif (released.length === 0) return 0\n\t\tif (this.syncState?.removeQuarantine) {\n\t\t\tawait this.syncState.removeQuarantine(released)\n\t\t}\n\t\tfor (const id of released) this.memoryQuarantine.delete(id)\n\t\treturn released.length\n\t}\n\n\t/**\n\t * Under end-to-end encryption, a plaintext operation authored by the server (a node\n\t * the handshake named authoritative: cascades, set-nulls, constraint corrections,\n\t * route writes) that touches only the collection's cleartext fields is accepted\n\t * without an envelope: the server holds no key, so it cannot seal its own writes, and\n\t * such an operation carries nothing the server cannot already read. Any sealed field\n\t * (or an operation from any other node) still has to arrive sealed.\n\t */\n\tprivate isServerAuthoredCleartext(op: Operation): boolean {\n\t\tconst encryptor = this.activeEncryptor()\n\t\tif (!encryptor || op.encrypted !== undefined) return false\n\t\t// A server node: `kora:server:<id>` by prefix, or a legacy id the handshake listed.\n\t\tif (!isAuthoritativeNodeId(op.nodeId, new Set(this.authoritativeNodeIds ?? []))) return false\n\t\treturn encryptor.isCleartextOnly(op)\n\t}\n\n\t/** Whether end-to-end encryption is configured (a keyring or a fixed encryptor). */\n\tprivate encryptionConfigured(): boolean {\n\t\treturn this.keyring !== null || this.encryptor !== null\n\t}\n\n\t/** The encryptor in force: the unlocked keyring's, or the fixed one; null when locked. */\n\tprivate activeEncryptor(): SyncEncryptor | null {\n\t\treturn this.keyring ? this.keyring.getEncryptor() : this.encryptor\n\t}\n\n\t/**\n\t * The encryptor in force. With encryption configured and the keyring locked this\n\t * throws: an operation is never sent or applied unencrypted because a key is missing.\n\t */\n\tprivate requireEncryptor(): SyncEncryptor {\n\t\tconst encryptor = this.activeEncryptor()\n\t\tif (!encryptor) {\n\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t'End-to-end encryption is locked: unlock it with the passphrase before syncing.',\n\t\t\t\t{ code: 'ENCRYPTION_LOCKED' },\n\t\t\t)\n\t\t}\n\t\treturn encryptor\n\t}\n\n\t/** The key service over this session (the handshake has been accepted). */\n\tprivate liveKeyServiceChannel(): KeyServiceChannel {\n\t\treturn {\n\t\t\tfetch: (keyring) => this.keyServiceRequest({ kind: 'fetch', keyring }),\n\t\t\tput: (keyring, record, expectedRevision) =>\n\t\t\t\tthis.keyServiceRequest({ kind: 'put', keyring, record, expectedRevision }),\n\t\t}\n\t}\n\n\t/** Re-apply a quarantined operation; true when it no longer needs to be kept. */\n\tprivate async applyInboundQuietly(stored: Operation): Promise<boolean> {\n\t\tlet op = stored\n\t\tif (this.encryptionConfigured() && !this.isServerAuthoredCleartext(stored)) {\n\t\t\ttry {\n\t\t\t\top = await this.requireEncryptor().decryptOperation(stored)\n\t\t\t} catch {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\tif (!(await verifyInboundOperation(op, { encrypted: stored.encrypted !== undefined })).ok) {\n\t\t\treturn false\n\t\t}\n\t\tconst reference = Date.now() + (this.clockSkewMs ?? 0)\n\t\tif (op.timestamp.wallTime > reference + MAX_REMOTE_FUTURE_MS) return false\n\t\top = await canonicalizeProvenLegacyClear(op)\n\t\tconst targetSchemaVersion = this.config.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\ttry {\n\t\t\tif (operationSchemaView(op, targetSchemaVersion, this.operationTransforms) === null) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t\tconst result = await this.store.applyRemoteOperation(op)\n\t\tif (result === 'applied' || result === 'duplicate') return true\n\t\treturn result === 'skipped' && this.store.hasCollection?.(op.collection) === true\n\t}\n\n\t/** Acknowledge a delivery batch (used for duplicates) without re-applying it. */\n\tprivate sendDeliveryAck(acknowledgedMessageId: string, deliverySequence: number): void {\n\t\tconst ack: SyncMessage = {\n\t\t\ttype: 'acknowledgment',\n\t\t\tmessageId: generateMessageId(),\n\t\t\tacknowledgedMessageId,\n\t\t\tlastSequenceNumber: 0,\n\t\t\tdeliverySequence,\n\t\t}\n\t\tthis.transport.send(ack)\n\t}\n\n\t/**\n\t * An acknowledgment resolves exactly the upload batch it names (SYNC-4): operations\n\t * the server processed (sequence <= `lastSequenceNumber`) leave the queue and count\n\t * toward the acknowledged prefix; the rest return to the queue for retry. An ack that\n\t * names no tracked batch (a node-token confirmation echo, a batch from an older\n\t * session) resolves nothing.\n\t */\n\tprivate async handleAcknowledgment(msg: AcknowledgmentMessage): Promise<void> {\n\t\tif (this.state === 'syncing' && this.config.strictHandshake) {\n\t\t\tthis.pendingDeltaBatchAcks.delete(msg.acknowledgedMessageId)\n\t\t\tthis.markDeltaSendCompleteIfReady()\n\t\t}\n\n\t\tconst batchId = this.uploadByMessageId.get(msg.acknowledgedMessageId)\n\t\tconst entry = batchId === undefined ? undefined : this.inFlightUploads.get(batchId)\n\t\tif (!entry || batchId === undefined) {\n\t\t\treturn\n\t\t}\n\t\tthis.uploadByMessageId.delete(msg.acknowledgedMessageId)\n\t\tthis.inFlightUploads.delete(batchId)\n\t\tif (this.inFlightUploads.size === 0) {\n\t\t\tthis.clearOutboundAckTimer()\n\t\t} else {\n\t\t\tthis.startOutboundAckTimer()\n\t\t}\n\n\t\tconst { acknowledged, returned } = await this.outboundQueue.acknowledgeThrough(\n\t\t\tbatchId,\n\t\t\tmsg.lastSequenceNumber,\n\t\t)\n\t\tif (acknowledged.length > 0) {\n\t\t\tconst now = Date.now()\n\t\t\tthis.lastSyncedAt = now\n\t\t\tthis.lastSuccessfulPush = now\n\t\t}\n\t\tawait this.withOwnTracking(async () => {\n\t\t\tfor (const op of acknowledged) this.markOwnResolved(op)\n\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t})\n\t\tif (acknowledged.length > 0) {\n\t\t\tthis.sessionAcked += acknowledged.length\n\t\t\tawait this.recordUploadProgress(this.currentNodeId())\n\t\t}\n\t\tif (returned.length > 0 && this.currentNodeId() !== this.store.getNodeId()) {\n\t\t\t// The server did not process part of an adopted node's batch (a retriable\n\t\t\t// rejection): retrying it in this session would hold every other node's\n\t\t\t// uploads behind it (RT-46). The session ends and the node is parked.\n\t\t\tthis.adoptionStalled = true\n\t\t}\n\t\tawait this.refreshPendingCount()\n\t\tif (await this.maybeEndSessionForNodeWork()) return\n\n\t\t// Continue flushing if more ops in queue\n\t\tconst backoff = entry.retryBackoff || returned.length > 0\n\t\tif (this.state === 'streaming' && this.hasUploadable()) {\n\t\t\tif (backoff) {\n\t\t\t\tthis.scheduleOutboundRetry()\n\t\t\t} else {\n\t\t\t\tthis.outboundRetryAttempt = 0\n\t\t\t\tthis.flushQueue()\n\t\t\t}\n\t\t} else if (!backoff) {\n\t\t\tthis.outboundRetryAttempt = 0\n\t\t}\n\t\tthis.notifyStatusChange()\n\t}\n\n\tprivate handleError(msg: {\n\t\tcode: string\n\t\tmessage: string\n\t\tretriable: boolean\n\t\tnodeOwnership?: 'other-principal' | 'unowned'\n\t}): void {\n\t\tif (msg.code === 'INVALID_TIMESTAMP') {\n\t\t\t// The server refused the batch at its first future-stamped operation and stored\n\t\t\t// none of the future-stamped ones, so a clock rebase may re-stamp them.\n\t\t\tconst limit = Date.now() + (this.clockSkewMs ?? 0) + 60_000\n\t\t\tconst refused = this.outboundQueue\n\t\t\t\t.getInFlight()\n\t\t\t\t.filter((op) => op.timestamp.wallTime > limit)\n\t\t\t\t.map((op) => op.id)\n\t\t\tthis.outboundQueue.clearSent(refused)\n\t\t}\n\t\t// In-flight outbound batches will never be acknowledged now. Return them to the\n\t\t// queue so they are retried, instead of wedging every future flush behind a batch\n\t\t// that can never clear.\n\t\tthis.sessionEpoch++\n\t\tthis.returnAllInFlightUploads()\n\t\t// The server usually closes the socket right after an error, so the close may\n\t\t// already have moved the engine to disconnected. The error still carries\n\t\t// meaning (auth rejection, clock block, credential refresh): record it either way.\n\t\tconst live = this.state !== 'disconnected'\n\t\tif (live) {\n\t\t\tthis.transitionTo('error')\n\t\t}\n\t\tif (isCredentialEndingCode(msg.code)) {\n\t\t\t// The server ended this session because its credential expired or was\n\t\t\t// revoked (AUTH-11). Not fatal and not a sign-out: refresh, then reconnect.\n\t\t\t// A truly revoked device is refused at the next handshake (AUTH_FAILED), or\n\t\t\t// its refresh is rejected and the auth client signs it out.\n\t\t\tthis.credentialRefreshRequired = true\n\t\t}\n\t\tif (\n\t\t\tmsg.code === 'NODE_ID_CLAIMED' &&\n\t\t\tmsg.nodeOwnership === 'other-principal' &&\n\t\t\tthis.currentNodeId() === this.store.getNodeId()\n\t\t) {\n\t\t\t// Another user owns this device's node: a pinned store cannot move away from it,\n\t\t\t// so its local writes are refused rather than authored as theirs (F9).\n\t\t\tthis.store.setPinnedNodeOwnedElsewhere?.(true)\n\t\t}\n\t\tif (msg.code === 'NODE_ID_CLAIMED' && !this.nodeRotation) {\n\t\t\t// Another device holds this node id (for example the node token issued at the\n\t\t\t// first claim was lost). Move to a fresh node id with every unsynced write;\n\t\t\t// the next connect uses it (RT-21).\n\t\t\tthis.nodeRotation = this.rotateNodeIdentity().finally(() => {\n\t\t\t\tthis.nodeRotation = null\n\t\t\t})\n\t\t}\n\t\tif (msg.code === 'AUTH_FAILED' || msg.code === 'DEVICE_REVOKED') {\n\t\t\tthis.authRejected = true\n\t\t\tthis.suspensionReason = msg.code === 'DEVICE_REVOKED' ? 'device-revoked' : 'auth-rejected'\n\t\t\tthis.emitter?.emit({ type: 'sync:auth-failed', reason: msg.message })\n\t\t\tthis.emitter?.emit({ type: 'sync:suspended', reason: this.suspensionReason })\n\t\t}\n\t\tif (msg.code === 'INVALID_TIMESTAMP') {\n\t\t\t// The server refused an operation stamped too far in the future: this\n\t\t\t// device's clock is (or was) fast. Block sync so the queue is preserved\n\t\t\t// and the app can tell the user to fix the clock.\n\t\t\tthis.clockBlocked = true\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:clock-skew',\n\t\t\t\tskewMs: this.clockSkewMs ?? Number.NaN,\n\t\t\t\tseverity: 'fast-blocked',\n\t\t\t\tsource: 'server-reject',\n\t\t\t})\n\t\t\tif (live) {\n\t\t\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason: msg.message })\n\t\t\t}\n\t\t\t// Stay in 'error' (clock-error status) until the server closes the session or\n\t\t\t// the app clears the block; the block, not the socket, is what stops sync.\n\t\t\treturn\n\t\t}\n\t\tif (live) {\n\t\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason: msg.message })\n\t\t\tthis.transitionTo('disconnected')\n\t\t\t// The engine ended this session: release the socket too (SYNC-5).\n\t\t\tvoid this.closeTransportQuietly()\n\t\t}\n\t}\n\n\t/**\n\t * Handle a per-operation rejection: divert the op out of the pending outbound\n\t * queue (so it is never retried or resurrected on reconnect), record it in the\n\t * durable rejected store (so it is kept and explainable), and emit an event so\n\t * the app can reconcile. Unlike {@link handleError}, this is a normal per-op\n\t * signal, so the connection stays up.\n\t */\n\tprivate async applyScopeRetraction(\n\t\tretraction: { collection: string; recordId: string },\n\t\tapplyToStore: boolean,\n\t): Promise<void> {\n\t\tif (applyToStore) {\n\t\t\tif (!this.store.applyScopeRetraction) {\n\t\t\t\tthrow new Error('The configured sync store does not support scope retractions')\n\t\t\t}\n\t\t\tawait this.store.applyScopeRetraction(retraction.collection, retraction.recordId)\n\t\t}\n\t\tconst quarantined = await this.outboundQueue.rejectRecord(\n\t\t\tretraction.collection,\n\t\t\tretraction.recordId,\n\t\t)\n\t\tawait this.recordTerminalRejections(quarantined, 'SCOPE_RETRACTED')\n\t\tfor (const operation of quarantined) {\n\t\t\tawait this.rejectedStorage.record({\n\t\t\t\toperationId: operation.id,\n\t\t\t\tcollection: operation.collection,\n\t\t\t\trecordId: operation.recordId,\n\t\t\t\tcode: 'SCOPE_RETRACTED',\n\t\t\t\tmessage: 'Authorization changed before this operation could be uploaded.',\n\t\t\t\tretriable: false,\n\t\t\t\trejectedAt: Date.now(),\n\t\t\t})\n\t\t}\n\t\tif (quarantined.length > 0) {\n\t\t\tawait this.withOwnTracking(async () => {\n\t\t\t\tfor (const operation of quarantined) this.markOwnResolved(operation)\n\t\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t\t})\n\t\t}\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:scope-retracted',\n\t\t\tcollection: retraction.collection,\n\t\t\trecordId: retraction.recordId,\n\t\t\tquarantinedOperationIds: quarantined.map((operation) => operation.id),\n\t\t})\n\t}\n\n\t/**\n\t * The local nodes whose unsynced writes are held now (RT-38, RT-50), for the user the\n\t * app reports as signed in. See {@link SyncStatusInfo.heldNodes}.\n\t */\n\tasync getHeldNodes(): Promise<HeldNodeInfo[]> {\n\t\tconst nodes = (await this.syncState?.listLocalNodes?.()) ?? []\n\t\tconst principal = this.config.principal ? await this.resolvePrincipal() : this.principal\n\t\tconst storeNode = this.store.getNodeId()\n\t\tconst held: HeldNodeInfo[] = []\n\t\tfor (const node of nodes) {\n\t\t\tif (node.nodeId === storeNode) continue\n\t\t\tconst reason = this.holdReason(node, principal)\n\t\t\tif (!reason) continue\n\t\t\tconst unsynced = await this.countUnsyncedOfNode(node.nodeId)\n\t\t\tif (unsynced.count === 0) continue\n\t\t\theld.push({\n\t\t\t\tnodeId: node.nodeId,\n\t\t\t\toperationCount: unsynced.count,\n\t\t\t\treason,\n\t\t\t\tprincipal: node.principal ?? null,\n\t\t\t})\n\t\t}\n\t\treturn held\n\t}\n\n\t/**\n\t * Assign the `unassigned` held writes of `nodeId` to the signed-in user (RT-50): writes\n\t * made on this device before the app knew who was signed in, on a node that never\n\t * synced. Only the app can know whose they are (for example, a single-user device).\n\t * They upload on the user's next session (the server then binds the node to them).\n\t *\n\t * @throws {SyncError} `HELD_ASSIGN_NO_USER` when nobody is signed in;\n\t * `HELD_NODE_NOT_ASSIGNABLE` when `nodeId` is not an unassigned held node (it belongs\n\t * to a user, or the server refused it for this one)\n\t */\n\tasync assignHeld(nodeId: string): Promise<void> {\n\t\tconst principal = await this.resolvePrincipal()\n\t\tif (typeof principal !== 'string') {\n\t\t\tthrow new SyncError('Held writes can only be assigned to a signed-in user.', {\n\t\t\t\tcode: 'HELD_ASSIGN_NO_USER',\n\t\t\t\tnodeId,\n\t\t\t\tfix: 'Call assignHeld after the user signed in.',\n\t\t\t})\n\t\t}\n\t\tconst node = (await this.syncState?.listLocalNodes?.())?.find(\n\t\t\t(entry) => entry.nodeId === nodeId,\n\t\t)\n\t\tconst assigned =\n\t\t\tnode !== undefined &&\n\t\t\tnodeId !== this.store.getNodeId() &&\n\t\t\tthis.holdReason(node, principal) === 'unassigned' &&\n\t\t\t((await this.syncState?.assignLocalNodePrincipal?.(nodeId, principal)) ?? false)\n\t\tif (!assigned) {\n\t\t\tthrow new SyncError(\n\t\t\t\t`Node \"${nodeId}\" holds no unassigned writes: only writes nobody can attribute can be assigned.`,\n\t\t\t\t{\n\t\t\t\t\tcode: 'HELD_NODE_NOT_ASSIGNABLE',\n\t\t\t\t\tnodeId,\n\t\t\t\t\tfix: 'Pick a node from status.heldNodes whose reason is \"unassigned\".',\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t\tthis.emitter?.emit({ type: 'sync:local-node', nodeId, action: 'held-assigned' })\n\t\tthis.heldNodeInfos = this.heldNodeInfos.filter((entry) => entry.nodeId !== nodeId)\n\t\tthis.notifyStatusChange()\n\t}\n\n\t/**\n\t * Discard the `unassigned` held writes of `nodeId` from sync (RT-50): they are never\n\t * uploaded. They are NOT rolled back: they stay in this device's local database, and\n\t * count as synced from now on (`deleteDatabase` no longer protects them). Writes held\n\t * for another user (`other-user`) cannot be discarded by this user.\n\t *\n\t * @returns How many writes were discarded\n\t * @throws {SyncError} `HELD_NODE_NOT_DISCARDABLE` when `nodeId` is not an unassigned\n\t * held node\n\t */\n\tasync discardHeld(nodeId: string): Promise<number> {\n\t\tconst principal = this.config.principal ? await this.resolvePrincipal() : this.principal\n\t\tconst node = (await this.syncState?.listLocalNodes?.())?.find(\n\t\t\t(entry) => entry.nodeId === nodeId,\n\t\t)\n\t\tif (\n\t\t\t!node ||\n\t\t\tnodeId === this.store.getNodeId() ||\n\t\t\tthis.holdReason(node, principal) !== 'unassigned'\n\t\t) {\n\t\t\tthrow new SyncError(\n\t\t\t\t`Node \"${nodeId}\" holds no unassigned writes: only writes nobody can attribute can be discarded.`,\n\t\t\t\t{\n\t\t\t\t\tcode: 'HELD_NODE_NOT_DISCARDABLE',\n\t\t\t\t\tnodeId,\n\t\t\t\t\tfix: 'Pick a node from status.heldNodes whose reason is \"unassigned\".',\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t\tconst unsynced = await this.countUnsyncedOfNode(nodeId)\n\t\tconst localSeq = this.store.getVersionVector().get(nodeId) ?? 0\n\t\tconst ops = (await this.store.getOperationRange(nodeId, 1, localSeq)).filter((op) =>\n\t\t\tunsynced.ids.has(op.id),\n\t\t)\n\t\tawait this.recordTerminalRejections(ops, HELD_DISCARDED_CODE)\n\t\tawait this.outboundQueue.removeByIds(ops.map((op) => op.id))\n\t\tawait this.syncState?.dropLocalNode?.(nodeId)\n\t\tthis.heldNodeInfos = this.heldNodeInfos.filter((entry) => entry.nodeId !== nodeId)\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:local-node',\n\t\t\tnodeId,\n\t\t\taction: 'held-discarded',\n\t\t\toperationCount: ops.length,\n\t\t})\n\t\tawait this.refreshPendingCount()\n\t\tthis.notifyStatusChange()\n\t\treturn ops.length\n\t}\n\n\tprivate async handleOperationRejected(msg: OperationRejectedMessage): Promise<void> {\n\t\tif (msg.retriable) {\n\t\t\t// The op stays in its batch; the batch's ack stops short of it and returns it\n\t\t\t// to the queue. Back off before retrying that batch's leftovers.\n\t\t\tfor (const entry of this.inFlightUploads.values()) {\n\t\t\t\tif (entry.batch.operations.some((op) => op.id === msg.operationId)) {\n\t\t\t\t\tentry.retryBackoff = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait this.refreshPendingCount()\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:operation-rejected',\n\t\t\t\toperationId: msg.operationId,\n\t\t\t\tcollection: msg.collection,\n\t\t\t\trecordId: msg.recordId,\n\t\t\t\tcode: msg.code,\n\t\t\t\tmessage: msg.message,\n\t\t\t\tretriable: true,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tif (msg.code === SEQUENCE_CONFLICT && (await this.recoverSequenceConflict(msg.operationId))) {\n\t\t\treturn\n\t\t}\n\n\t\t// The durable marker comes first (RT-36): once the prefix passes this op, only the\n\t\t// marker stops a later rescan of the device's history from submitting it again.\n\t\tconst refusedOp = this.findQueuedOperation(msg.operationId)\n\t\tif (refusedOp && !NON_TERMINAL_REJECTION_CODES.has(msg.code)) {\n\t\t\tawait this.recordTerminalRejections([refusedOp], msg.code)\n\t\t}\n\t\tconst removed = await this.outboundQueue.reject(msg.operationId)\n\n\t\tawait this.rejectedStorage.record({\n\t\t\toperationId: msg.operationId,\n\t\t\tcollection: msg.collection,\n\t\t\trecordId: msg.recordId,\n\t\t\tcode: msg.code,\n\t\t\tmessage: msg.message,\n\t\t\tretriable: msg.retriable,\n\t\t\trejectedAt: Date.now(),\n\t\t})\n\t\t// Terminally rejected AND recorded: resolved for the acknowledged prefix.\n\t\tif (removed) {\n\t\t\tawait this.withOwnTracking(async () => {\n\t\t\t\tthis.markOwnResolved(removed)\n\t\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t\t})\n\t\t}\n\n\t\t// The rejected op left the pending set, so the app-visible pending count\n\t\t// must be refreshed or it would over-count forever.\n\t\tawait this.refreshPendingCount()\n\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:operation-rejected',\n\t\t\toperationId: msg.operationId,\n\t\t\tcollection: msg.collection,\n\t\t\trecordId: msg.recordId,\n\t\t\tcode: msg.code,\n\t\t\tmessage: msg.message,\n\t\t\tretriable: msg.retriable,\n\t\t})\n\t}\n\n\t/**\n\t * Every operation the server rejected that has not yet been reconciled. The app\n\t * uses this (alongside the `sync:operation-rejected` event) to surface failed\n\t * submissions and decide whether to roll back or resubmit.\n\t */\n\tgetRejectedOperations(): Promise<RejectedOperation[]> {\n\t\treturn this.rejectedStorage.list()\n\t}\n\n\t/**\n\t * Forget rejected operations by id once the app has reconciled them (rolled the\n\t * optimistic write back or resubmitted a corrected op).\n\t */\n\tclearRejectedOperations(operationIds: string[]): Promise<void> {\n\t\treturn this.rejectedStorage.remove(operationIds)\n\t}\n\n\tprivate async checkDeltaComplete(): Promise<void> {\n\t\tif (!this.deltaSendComplete || !this.deltaReceiveComplete) {\n\t\t\treturn\n\t\t}\n\n\t\t// Idempotent: multiple final delta batches can race during handshake.\n\t\tif (this.state !== 'syncing') {\n\t\t\treturn\n\t\t}\n\n\t\tthis.lastSyncedAt = Date.now()\n\t\tthis.transitionTo('streaming')\n\n\t\t// Start awareness cleanup timer now that we're streaming\n\t\tthis.awarenessManager.startCleanupTimer()\n\n\t\t// Re-broadcast local awareness state to the new connection\n\t\tconst localState = this.awarenessManager.getLocalState()\n\t\tif (localState) {\n\t\t\tthis.awarenessManager.setLocalState(localState)\n\t\t}\n\n\t\t// Flush any queued operations accumulated during delta exchange. Delta batches\n\t\t// still awaiting their acks stay in flight: only their acks resolve them. A\n\t\t// stalled adoption is not retried in this session (RT-46).\n\t\tif (this.hasUploadable() && !this.adoptionStalled) {\n\t\t\tthis.flushQueue()\n\t\t}\n\n\t\tawait this.refreshPendingCount()\n\t\tthis.notifyStatusChange()\n\t\tif (await this.maybeEndSessionForNodeWork()) return\n\t\tthis.armAdoptionRetryTimer()\n\t}\n\n\t/**\n\t * In a session of this tab's own node, arm a timer for the earliest parked adoption's\n\t * backoff expiry (RT-53). Without it a parked node is retried only at a session start\n\t * or after this tab's own uploads made progress, so an idle tab (a dashboard, a kiosk)\n\t * would never retry a closed tab's deferred writes while its connection holds.\n\t */\n\tprivate armAdoptionRetryTimer(): void {\n\t\tthis.clearAdoptionRetryTimer()\n\t\tif (this.destroyed || this.state !== 'streaming') return\n\t\tif (this.currentNodeId() !== this.store.getNodeId()) return\n\t\tlet earliest = Number.POSITIVE_INFINITY\n\t\tfor (const deferred of this.deferredAdoptions) {\n\t\t\tif (deferred.reason === 'parked') earliest = Math.min(earliest, deferred.untilMs)\n\t\t}\n\t\tif (!Number.isFinite(earliest)) return\n\t\tconst epoch = this.sessionEpoch\n\t\tthis.adoptionRetryTimer = setTimeout(\n\t\t\t() => {\n\t\t\t\tthis.adoptionRetryTimer = null\n\t\t\t\tvoid this.onAdoptionRetryTimer(epoch)\n\t\t\t},\n\t\t\tMath.max(0, earliest - Date.now()),\n\t\t)\n\t}\n\n\t/**\n\t * A parked adoption's backoff ran out during this session (RT-53). An idle own-node\n\t * session hands over now: it reconnects, and the next session adopts the node (one\n\t * retry per backoff; a node deferred again is parked again for longer). A busy session\n\t * hands over when its uploads drain (maybeYieldToDeferredNode sees the expiry).\n\t */\n\tprivate async onAdoptionRetryTimer(epoch: number): Promise<void> {\n\t\tif (epoch !== this.sessionEpoch || this.destroyed) return\n\t\tif (this.state !== 'streaming' || this.currentNodeId() !== this.store.getNodeId()) return\n\t\tif (this.inFlightUploads.size > 0 || this.hasUploadable()) return\n\t\tthis.deferredAdoptions = []\n\t\ttry {\n\t\t\tawait this.reconnect()\n\t\t} catch {\n\t\t\t// The new session failed to start: the engine is disconnected, and the app's\n\t\t\t// reconnection loop (or the next sync) takes over, adopting the node then.\n\t\t}\n\t}\n\n\t// --- Own-operation upload tracking (W3: contiguous acknowledged prefix) ---\n\n\t/** Serialize every read-modify-write of the own-operation tracking. */\n\tprivate withOwnTracking<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst run = this.ownTrackingChain.then(fn)\n\t\tthis.ownTrackingChain = run.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t)\n\t\treturn run\n\t}\n\n\tprivate async loadOwnTracking(): Promise<void> {\n\t\tawait this.withOwnTracking(() => this.loadOwnTrackingLocked())\n\t}\n\n\t/**\n\t * Load the acknowledged prefix for the current node id, once per node id (an engine\n\t * restart keeps its in-memory progress). A persistence layer that has never stored a\n\t * prefix under this contract means an upgrade from a release that persisted a MAX:\n\t * that value can hide a lost op below it, so the prefix restarts at 0 and the device\n\t * re-uploads its own history once, chunk by chunk (the server dedups by id). The\n\t * prefix is persisted as it advances, so the re-upload resumes where it stopped.\n\t */\n\tprivate async loadOwnTrackingLocked(): Promise<void> {\n\t\tconst nodeId = this.currentNodeId()\n\t\tif (this.ownTrackingNodeId === nodeId) return\n\t\tlet prefix = 0\n\t\tif (this.syncState?.loadOwnAckedThrough) {\n\t\t\tconst stored = await this.syncState.loadOwnAckedThrough(nodeId)\n\t\t\tif (stored === null) {\n\t\t\t\tawait this.syncState.saveOwnAckedThrough?.(nodeId, 0)\n\t\t\t} else {\n\t\t\t\tprefix = stored\n\t\t\t}\n\t\t} else if (this.syncState) {\n\t\t\tprefix = this.lastAckedServerVector.get(nodeId) ?? 0\n\t\t}\n\t\tthis.ownTrackingNodeId = nodeId\n\t\tthis.ownAckedThrough = prefix\n\t\tthis.ownScannedThrough = prefix\n\t\tthis.ownUnresolved.clear()\n\t\tthis.ownResolvedAhead.clear()\n\t}\n\n\t/**\n\t * Register an own operation. `unresolved` ops are queued for upload; the others are\n\t * already resolved (not upload-eligible). An op at or below the prefix (a sequence\n\t * that was a gap when the prefix passed it) lowers the prefix: re-uploading is safe,\n\t * skipping is not.\n\t */\n\tprivate trackOwnOperation(op: Operation, unresolved: boolean): void {\n\t\tif (op.nodeId !== this.ownTrackingNodeId) return\n\t\tconst seq = op.sequenceNumber\n\t\tif (seq <= this.ownAckedThrough) {\n\t\t\tif (!unresolved) return\n\t\t\tthis.ownAckedThrough = seq - 1\n\t\t\tthis.ownScannedThrough = Math.min(this.ownScannedThrough, seq - 1)\n\t\t\tvoid this.persistOwnPrefix()\n\t\t}\n\t\tconst ids = this.ownUnresolved.get(seq) ?? new Set<string>()\n\t\tif (unresolved) ids.add(op.id)\n\t\tthis.ownUnresolved.set(seq, ids)\n\t}\n\n\t/** Mark an own operation resolved (stored, terminally rejected, or not eligible). */\n\tprivate markOwnResolved(op: Operation): void {\n\t\tif (op.nodeId !== this.ownTrackingNodeId || op.sequenceNumber <= this.ownAckedThrough) return\n\t\tconst ids = this.ownUnresolved.get(op.sequenceNumber)\n\t\tif (ids) {\n\t\t\tids.delete(op.id)\n\t\t} else if (op.sequenceNumber <= this.ownScannedThrough) {\n\t\t\tthis.ownUnresolved.set(op.sequenceNumber, new Set())\n\t\t}\n\t\tif (op.sequenceNumber > this.ownScannedThrough) {\n\t\t\t// The scan has not reached this sequence: remember the id so the scan does not\n\t\t\t// take the op for an unsent one and upload it again.\n\t\t\tthis.ownResolvedAhead.set(op.id, op.sequenceNumber)\n\t\t}\n\t}\n\n\tprivate isOwnOperationResolved(op: Operation): boolean {\n\t\tif (op.sequenceNumber <= this.ownAckedThrough) return true\n\t\tconst ids = this.ownUnresolved.get(op.sequenceNumber)\n\t\treturn ids !== undefined && !ids.has(op.id) && !this.outboundQueue.has(op.id)\n\t}\n\n\t/**\n\t * Lower the prefix (the server holds fewer of this device's operations than it\n\t * believed) and forget what this session knew above it: the next advance rescans the\n\t * log from there and re-enqueues what is missing.\n\t */\n\tprivate async lowerOwnPrefixLocked(sequence: number): Promise<void> {\n\t\tconst target = Math.max(0, sequence)\n\t\tif (target >= this.ownAckedThrough) return\n\t\tthis.ownAckedThrough = target\n\t\tthis.ownScannedThrough = target\n\t\tthis.ownUnresolved.clear()\n\t\tthis.ownResolvedAhead.clear()\n\t\tawait this.persistOwnPrefix()\n\t}\n\n\t/**\n\t * Advance the prefix over every resolved own sequence, reading the next chunk of the\n\t * op log whenever it reaches a sequence this session has not examined yet. Reading a\n\t * chunk enqueues its upload-eligible operations (and flushes when streaming), so the\n\t * one-time upgrade re-upload proceeds chunk by chunk as acks arrive.\n\t */\n\tprivate async advanceOwnPrefixLocked(): Promise<void> {\n\t\tif (this.ownTrackingNodeId !== this.currentNodeId()) return\n\t\tconst localSeq = this.store.getVersionVector().get(this.ownTrackingNodeId) ?? 0\n\t\tconst start = this.ownAckedThrough\n\t\tlet scanned = false\n\t\tfor (;;) {\n\t\t\tconst next = this.ownAckedThrough + 1\n\t\t\tif (next > localSeq && !this.ownUnresolved.has(next)) break\n\t\t\tconst ids = this.ownUnresolved.get(next)\n\t\t\tif (ids === undefined) {\n\t\t\t\tif (next > this.ownScannedThrough) {\n\t\t\t\t\tawait this.scanOwnLog(next, Math.min(localSeq, next + OWN_LOG_SCAN_CHUNK - 1))\n\t\t\t\t\tscanned = true\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// Examined, yet nothing registered: a hole in the log (a compacted or never\n\t\t\t\t// written sequence). Nothing there can be uploaded.\n\t\t\t\tthis.ownUnresolved.set(next, new Set())\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (ids.size > 0) break\n\t\t\tthis.ownUnresolved.delete(next)\n\t\t\tthis.ownAckedThrough = next\n\t\t\t// Everything at or below the prefix is settled, so it counts as examined.\n\t\t\tif (next > this.ownScannedThrough) this.ownScannedThrough = next\n\t\t}\n\t\tif (this.ownAckedThrough !== start) {\n\t\t\tfor (const [id, seq] of this.ownResolvedAhead) {\n\t\t\t\tif (seq <= this.ownScannedThrough) this.ownResolvedAhead.delete(id)\n\t\t\t}\n\t\t\tawait this.persistOwnPrefix()\n\t\t}\n\t\tif (scanned && this.state === 'streaming' && this.hasUploadable()) {\n\t\t\tthis.flushQueue()\n\t\t}\n\t}\n\n\t/**\n\t * Read own operations [from, to] from the op log and register each sequence: an\n\t * upload-eligible op not yet queued is enqueued; an ineligible one is recorded and\n\t * resolved; a sequence with no op is resolved. Every sequence in the range ends up\n\t * registered, which guarantees the advance loop makes progress.\n\t */\n\tprivate async scanOwnLog(from: number, to: number): Promise<void> {\n\t\tconst nodeId = this.ownTrackingNodeId\n\t\tif (nodeId === null || to < from) {\n\t\t\tthis.ownScannedThrough = Math.max(this.ownScannedThrough, to)\n\t\t\treturn\n\t\t}\n\t\tconst ops = await this.store.getOperationRange(nodeId, from, to)\n\t\tconst bySeq = new Map<number, Operation[]>()\n\t\tfor (const op of ops) {\n\t\t\tif (op.nodeId !== nodeId || op.sequenceNumber < from || op.sequenceNumber > to) continue\n\t\t\tconst list = bySeq.get(op.sequenceNumber) ?? []\n\t\t\tlist.push(op)\n\t\t\tbySeq.set(op.sequenceNumber, list)\n\t\t}\n\t\t// Operations the server refused for good stay refused (RT-36): they are resolved,\n\t\t// never enqueued again, whatever today's state would make of them.\n\t\tconst terminal = await this.findTerminalRejections(\n\t\t\tops.filter((op) => !this.outboundQueue.has(op.id)).map((op) => op.id),\n\t\t)\n\t\tconst eligible: Operation[] = []\n\t\tfor (let seq = from; seq <= to; seq++) {\n\t\t\tconst ids = this.ownUnresolved.get(seq) ?? new Set<string>()\n\t\t\tthis.ownUnresolved.set(seq, ids)\n\t\t\tfor (const op of bySeq.get(seq) ?? []) {\n\t\t\t\tif (this.ownResolvedAhead.delete(op.id)) continue\n\t\t\t\tif (ids.has(op.id)) continue\n\t\t\t\tif (terminal.has(op.id)) continue\n\t\t\t\tif (this.outboundQueue.has(op.id)) {\n\t\t\t\t\tids.add(op.id)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (await this.operationAllowedForUpload(op)) {\n\t\t\t\t\teligible.push(op)\n\t\t\t\t\tids.add(op.id)\n\t\t\t\t} else {\n\t\t\t\t\tawait this.recordOutOfUplinkScope(op)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// One causal re-sort for the whole chunk, not one per operation.\n\t\tawait this.outboundQueue.enqueueMany(eligible)\n\t\tthis.ownScannedThrough = Math.max(this.ownScannedThrough, to)\n\t}\n\n\tprivate async persistOwnPrefix(): Promise<void> {\n\t\tconst nodeId = this.ownTrackingNodeId\n\t\tif (!this.syncState || nodeId === null) return\n\t\tconst prefix = this.ownAckedThrough\n\t\tawait this.syncState.saveOwnAckedThrough?.(nodeId, prefix)\n\t\t// Mirror the prefix (exactly, never a max) into the acked server vector, which\n\t\t// compaction reads to decide which own operations the server already holds.\n\t\tconst vector = new Map(this.lastAckedServerVector)\n\t\tvector.set(nodeId, prefix)\n\t\tthis.lastAckedServerVector = vector\n\t\tawait this.syncState.saveLastAckedServerVector(vector)\n\t}\n\n\tprivate async persistLastAckedServerVector(vector: VersionVector): Promise<void> {\n\t\tif (!this.syncState) {\n\t\t\treturn\n\t\t}\n\t\t// Other nodes' entries record what the server advertised; a local node's entry is\n\t\t// only ever its acknowledged prefix (persistOwnPrefix), never an advertised value:\n\t\t// compaction reads it, and an advertised max can hide an unsynced op below it.\n\t\tconst nodeId = this.currentNodeId()\n\t\tconst others = new Map(vector)\n\t\tothers.delete(nodeId)\n\t\tfor (const local of this.localNodeIds) others.delete(local)\n\t\tconst merged = this.syncState.mergeServerVectors(this.lastAckedServerVector, others)\n\t\tif (this.ownTrackingNodeId === nodeId) merged.set(nodeId, this.ownAckedThrough)\n\t\tthis.lastAckedServerVector = merged\n\t\tawait this.syncState.saveLastAckedServerVector(merged)\n\t}\n\n\t/**\n\t * Refresh the pending count: own operations not yet stored on the server (queued or\n\t * in flight), plus the own operations above the prefix not read from the log yet.\n\t * Computed from the client's own acknowledgments only, so it drops the moment the\n\t * server acknowledges, without waiting for a later handshake (RT-28).\n\t */\n\tasync refreshPendingCount(): Promise<void> {\n\t\tconst previous = this.cachedUnsyncedCount\n\t\tthis.cachedUnsyncedCount = this.computePendingCount()\n\t\tif (this.cachedUnsyncedCount !== previous) this.notifyStatusChange()\n\t}\n\n\t/** Synchronous pending count (see {@link refreshPendingCount}). */\n\tprivate computePendingCount(): number {\n\t\tlet unscanned = 0\n\t\tconst nodeId = this.ownTrackingNodeId\n\t\tif (nodeId !== null) {\n\t\t\tconst localSeq = this.store.getVersionVector().get(nodeId) ?? 0\n\t\t\tunscanned = Math.max(0, localSeq - this.ownScannedThrough)\n\t\t\tfor (const seq of this.ownUnresolved.keys()) {\n\t\t\t\tif (seq > this.ownScannedThrough && seq <= localSeq) unscanned--\n\t\t\t}\n\t\t\tunscanned = Math.max(0, unscanned)\n\t\t}\n\t\t// Queued operations of held nodes are reported separately (heldOperations, RT-38).\n\t\tconst heldQueued =\n\t\t\tthis.heldNodeIds.size === 0\n\t\t\t\t? 0\n\t\t\t\t: this.outboundQueue.countMatching((op) => this.heldNodeIds.has(op.nodeId))\n\t\treturn this.outboundQueue.totalPending - heldQueued + unscanned + this.otherNodesPending\n\t}\n\n\t/** Unsynced operations held for a principal that is not signed in (RT-38). */\n\tprivate computeHeldCount(): number {\n\t\tif (this.heldNodeIds.size === 0) return 0\n\t\treturn (\n\t\t\tthis.heldPending + this.outboundQueue.countMatching((op) => this.heldNodeIds.has(op.nodeId))\n\t\t)\n\t}\n\n\t/**\n\t * This device's operations not yet stored on the server (queued or in flight).\n\t */\n\tasync getPendingSyncOperations(): Promise<Operation[]> {\n\t\treturn [\n\t\t\t...this.outboundQueue.getInFlight(),\n\t\t\t...this.outboundQueue.peek(Number.MAX_SAFE_INTEGER),\n\t\t]\n\t}\n\n\t/**\n\t * Rebuild the upload set from the op log: every own operation above the acknowledged\n\t * prefix that is upload-eligible is queued (first chunk now, the rest as acks move\n\t * the prefix). Persisted queue entries that are already resolved are dropped.\n\t */\n\tprivate async reconcileOutboundFromOpLog(): Promise<void> {\n\t\tawait this.withOwnTracking(async () => {\n\t\t\tconst nodeId = this.ownTrackingNodeId\n\t\t\tconst stale = this.outboundQueue\n\t\t\t\t.getAll()\n\t\t\t\t.filter((op) => op.nodeId === nodeId && op.sequenceNumber <= this.ownAckedThrough)\n\t\t\t\t.map((op) => op.id)\n\t\t\tif (stale.length > 0) {\n\t\t\t\tawait this.outboundQueue.removeByIds(stale)\n\t\t\t}\n\t\t\tfor (const op of this.outboundQueue.getAll()) {\n\t\t\t\tthis.trackOwnOperation(op, true)\n\t\t\t}\n\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t})\n\t}\n\n\t// --- Local nodes, terminal rejections and own-history recovery (Phase 2) ---\n\n\t/** Node id of the current session: the store's own, or an adopted node's (RT-40). */\n\tprivate currentNodeId(): string {\n\t\treturn this.sessionNodeId ?? this.store.getNodeId()\n\t}\n\n\t/** A session uploads only operations authored under its own node id (RT-38, RT-40). */\n\tprivate readonly isSessionOperation = (op: Operation): boolean =>\n\t\top.nodeId === this.currentNodeId()\n\n\t/** Whether the queue holds an operation this session may upload. */\n\tprivate hasUploadable(): boolean {\n\t\treturn this.outboundQueue.hasOperationsMatching(this.isSessionOperation)\n\t}\n\n\t/** A queued or in-flight operation by id. */\n\tprivate findQueuedOperation(operationId: string): Operation | undefined {\n\t\treturn (\n\t\t\tthis.outboundQueue.getInFlight().find((op) => op.id === operationId) ??\n\t\t\tthis.outboundQueue.getAll().find((op) => op.id === operationId)\n\t\t)\n\t}\n\n\t/**\n\t * Pick the node this session runs as (RT-40, RT-46). A session uploads under one node\n\t * id (the server confines uploads to the session's node), so the database's local\n\t * nodes take turns, each for one session:\n\t *\n\t * - A local node other than the store's own that still has unsynced writes, is not\n\t * held for another principal (RT-38) or bound to another user (RT-42), was not\n\t * refused in this refusal cycle, and is used by no live tab (its lock is free) may\n\t * be adopted: the session runs as that node and uploads its writes with their\n\t * original identity, then ends; the next session picks again.\n\t * - Never one whose writes depend causally on another local node's unsynced writes:\n\t * the parent's node goes first (a child is never uploaded before its parent).\n\t * - Never a parked one (its last adoption made no progress, for example the server\n\t * kept deferring a write) until something else uploaded or its backoff ran out, so\n\t * one stuck node never blocks the others or this tab's own writes.\n\t * - Otherwise the session runs as the store's own node; it yields to a deferred node\n\t * once its own uploads made that node worth retrying (see maybeEndSessionForNodeWork).\n\t *\n\t * Also counts the other local nodes' unsynced writes, so they are reported (pending,\n\t * or held).\n\t */\n\tprivate async chooseSessionNode(): Promise<void> {\n\t\tthis.endAdoption()\n\t\tconst storeNode = this.store.getNodeId()\n\t\tthis.sessionNodeId = storeNode\n\t\tthis.otherNodesPending = 0\n\t\tthis.heldPending = 0\n\t\tthis.heldNodeIds = new Set()\n\t\tthis.heldNodeInfos = []\n\t\tthis.localNodeIds = new Set([storeNode])\n\t\tthis.deferredAdoptions = []\n\t\tconst syncState = this.syncState\n\t\tif (!syncState?.listLocalNodes) return\n\t\tlet nodes: LocalNodeInfo[]\n\t\tlet cycle = 0\n\t\ttry {\n\t\t\tnodes = await syncState.listLocalNodes()\n\t\t\tcycle = (await syncState.loadAcceptedCycle?.()) ?? 0\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Reading the local node registry failed',\n\t\t\t\tcode: 'NODE_REGISTRY_FAILED',\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tconst schedule = await this.loadSchedule()\n\t\tconst now = Date.now()\n\t\tfor (const node of nodes) this.localNodeIds.add(node.nodeId)\n\n\t\tinterface Candidate {\n\t\t\tnodeId: string\n\t\t\tunsynced: UnsyncedOfNode\n\t\t}\n\t\tconst candidates: Candidate[] = []\n\t\tfor (const node of nodes) {\n\t\t\tif (node.nodeId === storeNode) continue\n\t\t\t// Held for its principal (RT-38), bound to another user (RT-42), refused for\n\t\t\t// this one or never attributed (RT-50): it is never uploaded on this session.\n\t\t\tconst heldReason = this.holdReason(node)\n\t\t\tif (heldReason) this.heldNodeIds.add(node.nodeId)\n\t\t\tconst unsynced = await this.countUnsyncedOfNode(node.nodeId)\n\t\t\tif (unsynced.count === 0) {\n\t\t\t\tif (!node.held) await this.forgetDrainedNode(node.nodeId)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (heldReason) {\n\t\t\t\tthis.heldPending += unsynced.notQueued\n\t\t\t\tthis.heldNodeInfos.push({\n\t\t\t\t\tnodeId: node.nodeId,\n\t\t\t\t\toperationCount: unsynced.count,\n\t\t\t\t\treason: heldReason,\n\t\t\t\t\tprincipal: node.principal ?? null,\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst refusedThisCycle = node.refusedCycle !== null && node.refusedCycle >= cycle\n\t\t\tif (refusedThisCycle || !this.store.claimLocalNode) {\n\t\t\t\tthis.otherNodesPending += unsynced.notQueued\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcandidates.push({ nodeId: node.nodeId, unsynced })\n\t\t}\n\t\tif (candidates.length === 0) return\n\n\t\t// Unsynced operation ids per uploadable node, to order adoptions by causality.\n\t\tconst own = await this.countUnsyncedOfNode(storeNode)\n\t\tconst pendingIds = new Map<string, ReadonlySet<string>>([[storeNode, own.ids]])\n\t\tfor (const candidate of candidates) pendingIds.set(candidate.nodeId, candidate.unsynced.ids)\n\t\tconst dependsOnAnotherNode = (candidate: Candidate): boolean => {\n\t\t\tfor (const dep of candidate.unsynced.deps) {\n\t\t\t\tfor (const [nodeId, ids] of pendingIds) {\n\t\t\t\t\tif (nodeId !== candidate.nodeId && ids.has(dep)) return true\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\n\t\tconst eligible: Candidate[] = []\n\t\tconst causallyDeferred: Candidate[] = []\n\t\tfor (const candidate of candidates) {\n\t\t\tconst parked = schedule.parked[candidate.nodeId]\n\t\t\tif (parked && schedule.progress <= parked.progressMark && now < parked.untilMs) {\n\t\t\t\tthis.deferredAdoptions.push({ nodeId: candidate.nodeId, reason: 'parked', ...parked })\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (dependsOnAnotherNode(candidate)) {\n\t\t\t\tcausallyDeferred.push(candidate)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\teligible.push(candidate)\n\t\t}\n\t\tif (eligible.length === 0 && causallyDeferred.length > 0) {\n\t\t\t// Every candidate waits for another one: a cycle among them (or the parent is\n\t\t\t// parked). Break it in registry order unless one waits for this tab's own\n\t\t\t// writes, which this session uploads first.\n\t\t\tconst breaker = causallyDeferred.find(\n\t\t\t\t(candidate) => ![...candidate.unsynced.deps].some((dep) => own.ids.has(dep)),\n\t\t\t)\n\t\t\tif (breaker) {\n\t\t\t\tcausallyDeferred.splice(causallyDeferred.indexOf(breaker), 1)\n\t\t\t\teligible.push(breaker)\n\t\t\t}\n\t\t}\n\t\tfor (const candidate of causallyDeferred) {\n\t\t\tthis.deferredAdoptions.push({\n\t\t\t\tnodeId: candidate.nodeId,\n\t\t\t\treason: 'causal',\n\t\t\t\tparkedAtMs: now,\n\t\t\t\tuntilMs: now,\n\t\t\t\tprogressMark: schedule.progress,\n\t\t\t})\n\t\t}\n\n\t\tlet adopted: Candidate | null = null\n\t\tfor (const candidate of eligible) {\n\t\t\tconst release = await this.store.claimLocalNode?.(candidate.nodeId)\n\t\t\tif (!release) continue\n\t\t\tadopted = candidate\n\t\t\tthis.releaseAdoption = release\n\t\t\tthis.sessionNodeId = candidate.nodeId\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:local-node',\n\t\t\t\tnodeId: candidate.nodeId,\n\t\t\t\taction: 'adoption-started',\n\t\t\t\toperationCount: candidate.unsynced.count,\n\t\t\t})\n\t\t\tbreak\n\t\t}\n\t\tfor (const candidate of candidates) {\n\t\t\tif (candidate !== adopted) this.otherNodesPending += candidate.unsynced.notQueued\n\t\t}\n\t\tif (adopted) {\n\t\t\t// While adopting, the store's own unsynced tail is not tracked by this session.\n\t\t\tthis.otherNodesPending += own.notQueued\n\t\t}\n\t}\n\n\t/**\n\t * Why a local node other than the store's own must not upload on this session, or\n\t * null when it may (RT-38, RT-42, RT-50):\n\t *\n\t * - Bound to another user than the signed-in one (or refused after acceptance,\n\t * RT-38): `other-user`.\n\t * - Unbound (its owner was never recorded, RT-50) while a user is signed in: its\n\t * owner is learned from the server, never guessed. If it was accepted before, the\n\t * server holds a claim on it: it is tried (adopted) on this user's session unless\n\t * the server already refused it for them, and an accepted handshake binds it to\n\t * them. If it never synced nobody can tell whose its writes are: `unassigned`,\n\t * held until the app assigns or discards them.\n\t */\n\tprivate holdReason(\n\t\tnode: LocalNodeInfo,\n\t\tprincipal: string | null | undefined = this.principal,\n\t): HeldReason | null {\n\t\tconst owner = node.principal ?? null\n\t\tif (this.config.principal && typeof principal === 'string') {\n\t\t\tif (owner !== null) return owner !== principal || node.held ? 'other-user' : null\n\t\t\tif (!node.accepted) return 'unassigned'\n\t\t\tif ((node.refusedPrincipals ?? []).includes(principal)) return 'other-user'\n\t\t\treturn null\n\t\t}\n\t\tif (node.held) return 'other-user'\n\t\tif (this.config.principal && principal === null && owner !== null) return 'other-user'\n\t\treturn null\n\t}\n\n\t/** A node bound to another user than the signed-in one (RT-42). */\n\tprivate belongsToAnotherPrincipal(node: LocalNodeInfo): boolean {\n\t\tif (!this.config.principal || this.principal === undefined) return false\n\t\tconst owner = node.principal ?? null\n\t\treturn owner !== null && owner !== this.principal\n\t}\n\n\t/**\n\t * A local node's own operations above its acknowledged prefix that are not\n\t * terminally rejected: `count` all of them, `notQueued` those not in the queue, their\n\t * `ids`, and the causal parents they name outside this set (`deps`).\n\t */\n\tprivate async countUnsyncedOfNode(nodeId: string): Promise<UnsyncedOfNode> {\n\t\tconst localSeq = this.store.getVersionVector().get(nodeId) ?? 0\n\t\tlet prefix = 0\n\t\tif (this.syncState?.loadOwnAckedThrough) {\n\t\t\tprefix = (await this.syncState.loadOwnAckedThrough(nodeId)) ?? 0\n\t\t} else {\n\t\t\tprefix = this.lastAckedServerVector.get(nodeId) ?? 0\n\t\t}\n\t\tconst empty: UnsyncedOfNode = { count: 0, notQueued: 0, ids: new Set(), deps: new Set() }\n\t\tif (localSeq <= prefix) return empty\n\t\tconst ops = (await this.store.getOperationRange(nodeId, prefix + 1, localSeq)).filter(\n\t\t\t(op) => op.nodeId === nodeId && op.sequenceNumber > prefix,\n\t\t)\n\t\tconst terminal = await this.findTerminalRejections(ops.map((op) => op.id))\n\t\tconst unsynced = ops.filter((op) => !terminal.has(op.id))\n\t\tconst ids = new Set(unsynced.map((op) => op.id))\n\t\tconst deps = new Set<string>()\n\t\tfor (const op of unsynced) {\n\t\t\tfor (const dep of op.causalDeps ?? []) if (!ids.has(dep)) deps.add(dep)\n\t\t}\n\t\treturn {\n\t\t\tcount: unsynced.length,\n\t\t\tnotQueued: unsynced.filter((op) => !this.outboundQueue.has(op.id)).length,\n\t\t\tids,\n\t\t\tdeps,\n\t\t}\n\t}\n\n\t/** The adoption schedule (RT-46), from persistence when it keeps one. */\n\tprivate async loadSchedule(): Promise<AdoptionScheduleInfo> {\n\t\tlet schedule = this.memorySchedule\n\t\tif (this.syncState?.loadAdoptionSchedule) {\n\t\t\ttry {\n\t\t\t\tschedule = await this.syncState.loadAdoptionSchedule()\n\t\t\t} catch {\n\t\t\t\t// Unreadable: the in-memory copy decides (at worst an early retry).\n\t\t\t}\n\t\t}\n\t\tthis.scheduleHasParked = Object.keys(schedule.parked).length > 0\n\t\treturn { progress: schedule.progress, parked: { ...schedule.parked } }\n\t}\n\n\tprivate async saveSchedule(schedule: AdoptionScheduleInfo): Promise<void> {\n\t\tthis.memorySchedule = schedule\n\t\tthis.scheduleHasParked = Object.keys(schedule.parked).length > 0\n\t\ttry {\n\t\t\tawait this.syncState?.saveAdoptionSchedule?.(schedule)\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Saving the adoption schedule failed',\n\t\t\t\tcode: 'ADOPTION_SCHEDULE_FAILED',\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Operations of `nodeId` were stored (RT-46): a parked adoption may have waited for\n\t * them, so the progress counter moves; the node itself is no longer parked.\n\t */\n\tprivate async recordUploadProgress(nodeId: string): Promise<void> {\n\t\tif (!this.scheduleHasParked) return\n\t\tconst schedule = await this.loadSchedule()\n\t\tschedule.progress += 1\n\t\tdelete schedule.parked[nodeId]\n\t\tawait this.saveSchedule(schedule)\n\t}\n\n\t/** Park the adopted node: it made no progress in this session (RT-46). */\n\tprivate async parkAdoption(nodeId: string): Promise<void> {\n\t\tconst schedule = await this.loadSchedule()\n\t\tconst now = Date.now()\n\t\tconst count = (schedule.parked[nodeId]?.count ?? 0) + 1\n\t\tconst backoff = Math.min(ADOPTION_PARK_MAX_MS, ADOPTION_PARK_BASE_MS * 2 ** (count - 1))\n\t\tschedule.parked[nodeId] = {\n\t\t\tprogressMark: schedule.progress,\n\t\t\tuntilMs: now + backoff,\n\t\t\tcount,\n\t\t\tparkedAtMs: now,\n\t\t}\n\t\tawait this.saveSchedule(schedule)\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:local-node',\n\t\t\tnodeId,\n\t\t\taction: 'adoption-parked',\n\t\t\toperationCount: (await this.countUnsyncedOfNode(nodeId)).count,\n\t\t})\n\t}\n\n\t/** Forget a drained, non-held local node no live tab uses (bounds the registry). */\n\tprivate async forgetDrainedNode(nodeId: string): Promise<void> {\n\t\tif (!this.syncState?.forgetLocalNode || !this.store.claimLocalNode) return\n\t\tconst release = await this.store.claimLocalNode(nodeId)\n\t\tif (!release) return\n\t\ttry {\n\t\t\tawait this.syncState.forgetLocalNode(nodeId)\n\t\t\tthis.localNodeIds.delete(nodeId)\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\t/** Release the lock on an adopted node (RT-40). */\n\tprivate endAdoption(): void {\n\t\tconst release = this.releaseAdoption\n\t\tthis.releaseAdoption = null\n\t\trelease?.()\n\t}\n\n\t/**\n\t * Record an accepted handshake as `nodeId` (RT-38), durably when supported. The server\n\t * accepted it for the session's user, so an unbound node is now theirs (RT-50).\n\t */\n\tprivate async recordNodeAccepted(nodeId: string): Promise<void> {\n\t\tthis.memoryAcceptedNodes.add(nodeId)\n\t\tthis.heldNodeIds.delete(nodeId)\n\t\t// The node is this user's after all (released and claimed, handed over): writes on.\n\t\tif (nodeId === this.store.getNodeId()) this.store.setPinnedNodeOwnedElsewhere?.(false)\n\t\ttry {\n\t\t\tawait this.syncState?.markLocalNodeAccepted?.(nodeId)\n\t\t\tconst principal = this.sessionPrincipal\n\t\t\tif (this.config.principal && typeof principal === 'string') {\n\t\t\t\tawait this.syncState?.confirmLocalNodePrincipal?.(nodeId, principal)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Recording the accepted node failed',\n\t\t\t\tcode: 'NODE_REGISTRY_FAILED',\n\t\t\t})\n\t\t}\n\t}\n\n\t/** Which of these operation ids the server refused for good (RT-36). */\n\tprivate async findTerminalRejections(operationIds: string[]): Promise<Set<string>> {\n\t\tconst found = new Set<string>()\n\t\tconst unknown: string[] = []\n\t\tfor (const id of operationIds) {\n\t\t\tif (this.terminalRejected.has(id)) found.add(id)\n\t\t\telse unknown.push(id)\n\t\t}\n\t\tif (unknown.length > 0 && this.syncState?.findTerminalRejections) {\n\t\t\tfor (const id of await this.syncState.findTerminalRejections(unknown)) {\n\t\t\t\tfound.add(id)\n\t\t\t\tthis.terminalRejected.add(id)\n\t\t\t}\n\t\t}\n\t\treturn found\n\t}\n\n\t/** Record durable terminal-rejection markers (RT-36). */\n\tprivate async recordTerminalRejections(operations: Operation[], code: string): Promise<void> {\n\t\tif (operations.length === 0 || NON_TERMINAL_REJECTION_CODES.has(code)) return\n\t\tfor (const op of operations) this.terminalRejected.add(op.id)\n\t\tconst rejectedAt = Date.now()\n\t\tawait this.syncState?.recordTerminalRejections?.(\n\t\t\toperations.map((op) => ({\n\t\t\t\toperationId: op.id,\n\t\t\t\tnodeId: op.nodeId,\n\t\t\t\tsequenceNumber: op.sequenceNumber,\n\t\t\t\tcode,\n\t\t\t\trejectedAt,\n\t\t\t})),\n\t\t)\n\t}\n\n\t/**\n\t * Restart server->client delivery from 0 at the next session, for the requested and the\n\t * accepted views. A full resync includes this client's own operations (a resumed one\n\t * does not), which is how operations the device lost come back (RT-35).\n\t */\n\tprivate async resetDeliveryForFullResync(): Promise<void> {\n\t\tconst signatures = new Set([\n\t\t\tthis.deliverySignature(),\n\t\t\tthis.deliverySignatureFor(this.config.scopeMap),\n\t\t])\n\t\tif (this.lastAcceptedScope) signatures.add(this.deliverySignatureFor(this.lastAcceptedScope))\n\t\tthis.deliveryWatermark = 0\n\t\tfor (const signature of signatures) {\n\t\t\tthis.setViewWatermark(signature, 0)\n\t\t\tawait this.persistDeliveryWatermark(0, signature)\n\t\t}\n\t}\n\n\t/**\n\t * The server refused an upload with `SEQUENCE_CONFLICT`: it holds another operation of\n\t * this node under that number, one this device lost (RT-35: a reload inside the\n\t * IndexedDB snapshot window, a restored older copy). Not a refusal of the write: it\n\t * gets a fresh number above everything the server holds (same id and content) and is\n\t * uploaded again, and the session ends once its uploads are resolved so the next one\n\t * resyncs from 0 and fetches the lost operation back.\n\t *\n\t * @returns Whether the conflict was recovered (false: treat as a plain rejection)\n\t */\n\tprivate async recoverSequenceConflict(operationId: string): Promise<boolean> {\n\t\tconst node = this.currentNodeId()\n\t\tconst op = this.findQueuedOperation(operationId)\n\t\tif (\n\t\t\top &&\n\t\t\top.nodeId === node &&\n\t\t\tnode === this.store.getNodeId() &&\n\t\t\top.sequenceNumber > this.handshakeOwnEntry &&\n\t\t\tthis.store.rotateNodeId\n\t\t) {\n\t\t\treturn this.recoverFromClone(op)\n\t\t}\n\t\tconst resequence = this.store.resequenceOperation?.bind(this.store)\n\t\tif (!op || op.nodeId !== node || !resequence) return false\n\t\tconst floor = Math.max(this.remoteVector.get(node) ?? 0, op.sequenceNumber)\n\t\t// A version-2 op is re-hashed under a new id; queued operations of this node that\n\t\t// were never put on the wire (the server cannot hold them) and name it in their\n\t\t// causalDeps are rewritten with it (seam 4).\n\t\tconst unsentDependents = this.outboundQueue\n\t\t\t.getAll()\n\t\t\t.filter(\n\t\t\t\t(queued) =>\n\t\t\t\t\tqueued.nodeId === node && queued.id !== op.id && !this.outboundQueue.wasSent(queued.id),\n\t\t\t)\n\t\t\t.map((queued) => queued.id)\n\t\tconst resequenced = await resequence(op.id, node, floor, unsentDependents)\n\t\tconst renumbered = resequenced?.operation ?? null\n\t\tawait this.withOwnTracking(async () => {\n\t\t\t// Out of its batch, so the batch's ack cannot resolve it under the old number.\n\t\t\tawait this.outboundQueue.reject(op.id)\n\t\t\t// The old number belongs to the server's operation.\n\t\t\tthis.markOwnResolved(op)\n\t\t\tif (renumbered) {\n\t\t\t\tawait this.outboundQueue.enqueue(renumbered)\n\t\t\t\tthis.trackOwnOperation(renumbered, true)\n\t\t\t}\n\t\t\tif (resequenced && resequenced.dependents.length > 0) {\n\t\t\t\tconst previousId = new Map<string, string>()\n\t\t\t\tfor (const [oldId, newId] of Object.entries(resequenced.idMapping)) {\n\t\t\t\t\tpreviousId.set(newId, oldId)\n\t\t\t\t}\n\t\t\t\tconst oldIds = resequenced.dependents.map((dep) => previousId.get(dep.id) ?? dep.id)\n\t\t\t\tawait this.outboundQueue.replace(oldIds, resequenced.dependents)\n\t\t\t\t// Same sequence, new id: the tracked id moves with it, or the prefix would\n\t\t\t\t// wait forever for the old one.\n\t\t\t\tfor (const dependent of resequenced.dependents) {\n\t\t\t\t\tconst oldId = previousId.get(dependent.id)\n\t\t\t\t\tif (oldId) this.ownUnresolved.get(dependent.sequenceNumber)?.delete(oldId)\n\t\t\t\t\tthis.trackOwnOperation(dependent, true)\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait this.advanceOwnPrefixLocked()\n\t\t})\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:local-node',\n\t\t\tnodeId: node,\n\t\t\taction: 'history-behind',\n\t\t\tlocalSequence: op.sequenceNumber,\n\t\t\tserverSequence: floor,\n\t\t})\n\t\t// A full resync downloads the whole dataset again: at most one per interval\n\t\t// (RT-44, a clone this store cannot move away from keeps colliding). A later\n\t\t// one is deferred to the first session start after the interval.\n\t\tif (Date.now() - this.lastFullResyncAt >= FULL_RESYNC_MIN_INTERVAL_MS) {\n\t\t\tthis.lastFullResyncAt = Date.now()\n\t\t\tawait this.resetDeliveryForFullResync()\n\t\t\tthis.ownRecoveryPending = true\n\t\t} else {\n\t\t\tthis.fullResyncDeferred = true\n\t\t}\n\t\tawait this.refreshPendingCount()\n\t\tawait this.maybeEndSessionForNodeWork()\n\t\treturn true\n\t}\n\n\t/**\n\t * A SEQUENCE_CONFLICT above this session's handshake entry (RT-44): the server held\n\t * nothing of this node above that entry when the session began, so the operation now\n\t * holding the number was stored during this session, and not by this session. Another\n\t * live copy of this database uses the same node id (app data copied to a new machine,\n\t * a restored image used alongside the original). Renumbering would only collide again,\n\t * and each copy's writes count as the other's own operations (never delivered on a\n\t * resumed stream), so this copy moves to a fresh node id: the refused write (refused,\n\t * so certainly not stored) and every never-sent one are re-authored under it, the\n\t * session ends, and the next one resyncs from 0 to fetch what the other copy wrote.\n\t */\n\tprivate async recoverFromClone(op: Operation): Promise<boolean> {\n\t\tconst rotate = this.store.rotateNodeId?.bind(this.store)\n\t\tif (!rotate) return false\n\t\tconst cloned = op.nodeId\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:local-node',\n\t\t\tnodeId: cloned,\n\t\t\taction: 'clone-detected',\n\t\t\tlocalSequence: op.sequenceNumber,\n\t\t\tserverSequence: this.handshakeOwnEntry,\n\t\t})\n\t\t// The session belongs to the cloned node: end it before the identity moves, so\n\t\t// nothing of the new node is ever uploaded on it.\n\t\tthis.abandonSession(\n\t\t\t'Another live copy of this database uses the same node id; moving this copy to a new one',\n\t\t)\n\t\tthis.outboundQueue.clearSent([op.id])\n\t\ttry {\n\t\t\tconst staying = await this.rotateUnsent(cloned, rotate)\n\t\t\tawait this.resetDeliveryForFullResync()\n\t\t\tif (staying === 0) {\n\t\t\t\t// Nothing of this copy waits under the cloned node: stop tracking it, so\n\t\t\t\t// the other copy's writes under it are never taken for unsynced ones.\n\t\t\t\tawait this.syncState?.forgetLocalNode?.(cloned)\n\t\t\t\tthis.localNodeIds.delete(cloned)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:persistence-error',\n\t\t\t\tdbName: 'kora-oplog',\n\t\t\t\tmessage: error instanceof Error ? error.message : 'Moving to a new node id failed',\n\t\t\t\tcode: 'NODE_ROTATION_FAILED',\n\t\t\t})\n\t\t}\n\t\tawait this.refreshPendingCount()\n\t\treturn true\n\t}\n\n\t/**\n\t * End the session when it has done its node work and nothing is in flight:\n\t * - an own-history recovery is pending (RT-35: reconnect to resync from 0);\n\t * - an adopted node's writes are all stored (RT-40: reconnect as the next node, or\n\t * this tab's own);\n\t * - an adopted node stalled (RT-46: the server deferred part of it): it is parked and\n\t * the next session serves another node;\n\t * - this tab's own uploads are drained, made progress, and a deferred node is now\n\t * worth retrying (RT-46: its parent was among them, or its park has expired).\n\t *\n\t * @returns Whether the session was ended\n\t */\n\tprivate async maybeEndSessionForNodeWork(): Promise<boolean> {\n\t\tif (this.state !== 'streaming' && this.state !== 'syncing') return false\n\t\tif (this.inFlightUploads.size > 0) return false\n\t\tif (this.ownRecoveryPending) {\n\t\t\tthis.ownRecoveryPending = false\n\t\t\tthis.abandonSession('Resyncing operations of this device that its local database lost')\n\t\t\treturn true\n\t\t}\n\t\tconst node = this.currentNodeId()\n\t\tif (node === this.store.getNodeId()) return this.maybeYieldToDeferredNode()\n\t\tif (this.state !== 'streaming') return false\n\t\tif (this.adoptionStalled) {\n\t\t\tthis.adoptionStalled = false\n\t\t\tconst epoch = this.sessionEpoch\n\t\t\tawait this.parkAdoption(node)\n\t\t\tif (epoch !== this.sessionEpoch || this.currentNodeId() !== node) return true\n\t\t\tthis.endAdoption()\n\t\t\tthis.abandonSession('An adopted node made no upload progress; serving the other nodes first')\n\t\t\treturn true\n\t\t}\n\t\tif (this.hasUploadable()) return false\n\t\tconst localSeq = this.store.getVersionVector().get(node) ?? 0\n\t\tif (this.ownTrackingNodeId !== node || this.ownAckedThrough < localSeq) return false\n\t\tthis.emitter?.emit({ type: 'sync:local-node', nodeId: node, action: 'adoption-completed' })\n\t\tthis.endAdoption()\n\t\tthis.abandonSession('Uploaded the unsynced writes of a closed tab; reconnecting')\n\t\treturn true\n\t}\n\n\t/**\n\t * This tab's own session: once its uploads are drained and it stored something, end\n\t * it when a node deferred at session start is worth retrying now (RT-46), so other\n\t * nodes' writes do not wait for an unrelated reconnect. Bounded: each yield needs new\n\t * own progress, and a parked node at most once per {@link ADOPTION_YIELD_MIN_MS}.\n\t */\n\tprivate async maybeYieldToDeferredNode(): Promise<boolean> {\n\t\tif (this.state !== 'streaming' || this.deferredAdoptions.length === 0) return false\n\t\tif (this.hasUploadable()) return false\n\t\tconst now = Date.now()\n\t\tconst schedule = await this.loadSchedule()\n\t\t// A park whose backoff ran out is due whatever this session did (RT-53: one retry\n\t\t// per backoff). Anything else waits for this tab's own progress (RT-46).\n\t\tconst progressed = this.sessionAcked > 0\n\t\tconst ready = this.deferredAdoptions.some((deferred) =>\n\t\t\tdeferred.reason === 'causal'\n\t\t\t\t? progressed\n\t\t\t\t: now >= deferred.untilMs ||\n\t\t\t\t\t(progressed &&\n\t\t\t\t\t\tschedule.progress > deferred.progressMark &&\n\t\t\t\t\t\tnow - deferred.parkedAtMs >= ADOPTION_YIELD_MIN_MS),\n\t\t)\n\t\tif (!ready || this.state !== 'streaming' || this.inFlightUploads.size > 0) return false\n\t\tthis.deferredAdoptions = []\n\t\tthis.abandonSession(\n\t\t\t\"Uploaded this tab's writes; serving another local node that waited for them\",\n\t\t)\n\t\treturn true\n\t}\n\n\tprivate flushQueue(): void {\n\t\tif (this.state !== 'streaming') return\n\t\tif (this.inFlightUploads.size > 0) return // one streaming batch in flight at a time\n\t\tif (this.outboundRetryTimer) return // Transient rejection backoff is active\n\t\tif (!this.hasUploadable()) return\n\n\t\tconst entry = this.takeUpload()\n\t\tif (!entry) return\n\t\tvoid this.sendUpload(entry, { isFinal: true, batchIndex: 0 }).catch(() => {\n\t\t\tif (this.inFlightUploads.get(entry.batch.batchId) === entry) this.returnUpload(entry)\n\t\t})\n\t}\n\n\tprivate startOutboundAckTimer(): void {\n\t\tthis.clearOutboundAckTimer()\n\t\tif (this.outboundAckTimeoutMs <= 0 || this.destroyed) return\n\t\tconst epoch = this.sessionEpoch\n\t\tthis.outboundAckTimer = setTimeout(() => {\n\t\t\tvoid this.handleOutboundAckTimeout(epoch)\n\t\t}, this.outboundAckTimeoutMs)\n\t}\n\n\tprivate clearOutboundAckTimer(): void {\n\t\tif (!this.outboundAckTimer) return\n\t\tclearTimeout(this.outboundAckTimer)\n\t\tthis.outboundAckTimer = null\n\t}\n\n\tprivate scheduleOutboundRetry(): void {\n\t\tif (this.outboundRetryTimer || !this.hasUploadable() || this.destroyed) return\n\t\tconst delay = Math.min(\n\t\t\tthis.outboundRetryMaxDelayMs,\n\t\t\tthis.outboundRetryBaseDelayMs * 2 ** this.outboundRetryAttempt,\n\t\t)\n\t\tthis.outboundRetryAttempt += 1\n\t\tthis.outboundRetryTimer = setTimeout(() => {\n\t\t\tthis.outboundRetryTimer = null\n\t\t\tif (this.state === 'streaming') this.flushQueue()\n\t\t}, delay)\n\t}\n\n\tprivate clearOutboundRetryTimer(): void {\n\t\tif (!this.outboundRetryTimer) return\n\t\tclearTimeout(this.outboundRetryTimer)\n\t\tthis.outboundRetryTimer = null\n\t}\n\n\t/** No ack arrived in time: return every batch in flight and reconnect. */\n\tprivate async handleOutboundAckTimeout(epoch: number): Promise<void> {\n\t\tthis.outboundAckTimer = null\n\t\tif (epoch !== this.sessionEpoch || this.inFlightUploads.size === 0) return\n\n\t\tconst batchIds = [...this.inFlightUploads.keys()].join(', ')\n\t\tthis.sessionEpoch++\n\t\tthis.returnAllInFlightUploads()\n\t\tawait this.refreshPendingCount()\n\n\t\tif (this.state === 'disconnected') return\n\n\t\tconst reason = `Timed out waiting for acknowledgment of outbound batch ${batchIds}`\n\t\ttry {\n\t\t\tawait this.transport.disconnect()\n\t\t} finally {\n\t\t\tthis.ensureDisconnected()\n\t\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason })\n\t\t}\n\t}\n\n\tprivate handleAwarenessUpdate(msg: AwarenessUpdateMessage): void {\n\t\tconst awarenessMessage: AwarenessMessage = {\n\t\t\ttype: 'awareness',\n\t\t\tclientId: msg.clientId,\n\t\t\tstates: wireToAwarenessStates(msg.states),\n\t\t}\n\t\tthis.awarenessManager.handleRemoteMessage(awarenessMessage)\n\t}\n\n\tprivate handleTransportClose(reason: string): void {\n\t\tthis.rejectKeyRequests(reason)\n\t\tthis.clearOutboundRetryTimer()\n\t\tthis.awarenessManager.stopCleanupTimer()\n\t\t// Return in-flight batches to the queue: nothing unacknowledged is resolved.\n\t\tthis.sessionEpoch++\n\t\tthis.returnAllInFlightUploads()\n\n\t\tif (this.schemaBlocked) {\n\t\t\treturn\n\t\t}\n\n\t\tif (this.state !== 'disconnected') {\n\t\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason })\n\t\t\tthis.transitionTo('disconnected')\n\t\t\tthis.notifyStatusChange()\n\t\t}\n\t}\n\n\tprivate handleTransportError(err: Error): void {\n\t\t// A transport error ends the session; close the transport so the socket is\n\t\t// released before any reconnect opens another (SYNC-5).\n\t\tif (this.state !== 'disconnected') {\n\t\t\tthis.transitionTo('error')\n\t\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason: err.message })\n\t\t\tthis.transitionTo('disconnected')\n\t\t\tthis.sessionEpoch++\n\t\t\tthis.returnAllInFlightUploads()\n\t\t\tvoid this.closeTransportQuietly()\n\t\t\tthis.notifyStatusChange()\n\t\t}\n\t}\n\n\tprivate transitionTo(newState: SyncState): void {\n\t\tconst validTargets = VALID_TRANSITIONS[this.state]\n\t\tif (!validTargets.includes(newState)) {\n\t\t\tthrow new SyncError(`Invalid sync state transition: ${this.state} → ${newState}`, {\n\t\t\t\tfrom: this.state,\n\t\t\t\tto: newState,\n\t\t\t})\n\t\t}\n\t\tthis.state = newState\n\t\tfor (const listener of [...this.stateListeners]) {\n\t\t\ttry {\n\t\t\t\tlistener(newState)\n\t\t\t} catch {\n\t\t\t\t// A listener must never break a state transition.\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate setSerializerWireFormat(format: WireFormat): void {\n\t\tif (typeof this.serializer.setWireFormat === 'function') {\n\t\t\tthis.serializer.setWireFormat(format)\n\t\t}\n\t}\n\n\t/**\n\t * Upload predicate: the UPLINK scope only, never query subsets (SYNC-1). Query\n\t * subsets describe what this client wants to download; the server authorizes\n\t * uploads independently of the client's downloaded view.\n\t */\n\tprivate async operationAllowedForUpload(op: Operation): Promise<boolean> {\n\t\t// Fast path: the bare operation already carries the scope fields.\n\t\tif (this.matchesUplinkScope(op)) {\n\t\t\treturn true\n\t\t}\n\t\t// A partial update or delete may not restate the scope fields. Backfill the\n\t\t// record's current fields before deciding, so an in-scope edit to an\n\t\t// unrelated field is never dropped.\n\t\tconst fullRecord = await this.readRecordForBackfill(op)\n\t\tif (!fullRecord) {\n\t\t\treturn false\n\t\t}\n\t\treturn this.matchesUplinkScope(op, fullRecord)\n\t}\n\n\t/**\n\t * Record a local operation that the uplink scope refuses, so it is visible to\n\t * the app instead of silently diverging from the server. Operations on\n\t * collections that are not synced in either direction are local-only by design\n\t * and are not recorded.\n\t *\n\t * Such an operation counts as resolved for the contiguous acknowledged prefix: it is\n\t * recorded here and never uploaded (W3 step 2).\n\t */\n\tprivate async recordOutOfUplinkScope(op: Operation): Promise<void> {\n\t\tconst uplink = this.activeUplinkScope\n\t\tif (!uplink) return\n\t\t// Local-only means the CLIENT leaves the collection out: its own scope map, or, with\n\t\t// none, the schema's synced collections. A synced collection the server's grant\n\t\t// omits is a refusal to surface, never a silent local fork (RT-89: a token-auth\n\t\t// grant that named no collection dropped every write with status \"synced\").\n\t\tconst requested = this.config.scopeMap\n\t\tconst syncsCollection =\n\t\t\tuplink[op.collection] !== undefined ||\n\t\t\tthis.activeScope?.[op.collection] !== undefined ||\n\t\t\t(requested !== undefined\n\t\t\t\t? requested[op.collection] !== undefined\n\t\t\t\t: (this.config.syncedCollections?.includes(op.collection) ?? false))\n\t\tif (!syncsCollection) return\n\n\t\tconst message = `Operation on \"${op.collection}\" record \"${op.recordId}\" is outside this client's upload scope and was not sent to the server. The local change is not synced; roll it back or move the record back into scope.`\n\t\tawait this.rejectedStorage.record({\n\t\t\toperationId: op.id,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tcode: OUT_OF_UPLINK_SCOPE,\n\t\t\tmessage,\n\t\t\tretriable: false,\n\t\t\trejectedAt: Date.now(),\n\t\t})\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:operation-rejected',\n\t\t\toperationId: op.id,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tcode: OUT_OF_UPLINK_SCOPE,\n\t\t\tmessage,\n\t\t\tretriable: false,\n\t\t})\n\t}\n\n\t/**\n\t * Whether an operation falls inside the uplink scope. Query subsets never apply to\n\t * uploads (SYNC-1), and nothing applies to inbound operations: the client applies\n\t * what the server delivered (SYNC-2).\n\t */\n\tprivate matchesUplinkScope(op: Operation, fullRecord?: Record<string, unknown> | null): boolean {\n\t\t// A client judging its own local view may trust previousData: this is not a\n\t\t// cross-tenant visibility decision (the server judges those on its own rows).\n\t\treturn operationMatchesScope(op, this.activeUplinkScope, fullRecord, {\n\t\t\tincludePreviousData: true,\n\t\t})\n\t}\n\n\tprivate async readRecordForBackfill(op: Operation): Promise<Record<string, unknown> | null> {\n\t\tif (!this.store.readRecordFields) {\n\t\t\treturn null\n\t\t}\n\t\ttry {\n\t\t\treturn await this.store.readRecordFields(op.collection, op.recordId)\n\t\t} catch {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tprivate async persistDeltaCursor(cursor: DeltaCursor | null): Promise<void> {\n\t\tif (!this.syncState?.saveDeltaCursor) {\n\t\t\treturn\n\t\t}\n\t\tawait this.syncState.saveDeltaCursor(cursor)\n\t}\n\n\tprivate async persistDeliveryWatermark(\n\t\twatermark: number,\n\t\tsignature = this.deliverySignature(),\n\t): Promise<void> {\n\t\tif (!this.syncState?.saveDeliveryWatermark) {\n\t\t\treturn\n\t\t}\n\t\tawait this.syncState.saveDeliveryWatermark(signature, watermark)\n\t}\n\n\tprivate scheduleQuerySubsetReconnect(): void {\n\t\tif (this.querySubsetReconnectTimer) {\n\t\t\tclearTimeout(this.querySubsetReconnectTimer)\n\t\t}\n\n\t\tthis.querySubsetReconnectTimer = setTimeout(() => {\n\t\t\tthis.querySubsetReconnectTimer = null\n\t\t\tif (this.state === 'streaming' || this.state === 'syncing' || this.state === 'handshaking') {\n\t\t\t\tvoid this.reconnectForQuerySubsets()\n\t\t\t}\n\t\t}, 500)\n\t}\n\n\tprivate async reconnectForQuerySubsets(): Promise<void> {\n\t\tawait this.reconnect()\n\t}\n}\n\n/**\n * Upload order inside a batch: by node, then sequence. For this device's own operations\n * sequence order is a causal order (each local op follows the ones before it), and it\n * makes the server's \"processed through sequence n\" ack an exact prefix of the batch.\n */\nfunction compareForUpload(a: Operation, b: Operation): number {\n\tif (a.nodeId !== b.nodeId) return a.nodeId < b.nodeId ? -1 : 1\n\treturn a.sequenceNumber - b.sequenceNumber\n}\n\n// --- Awareness wire format conversion helpers ---\n\n/**\n * Convert internal awareness states to wire format for transport.\n */\nfunction awarenessStatesToWire(\n\tstates: Record<number, AwarenessState | null>,\n): Record<string, AwarenessStateWire | null> {\n\tconst wire: Record<string, AwarenessStateWire | null> = {}\n\tfor (const [clientId, state] of Object.entries(states)) {\n\t\tif (state === null) {\n\t\t\twire[clientId] = null\n\t\t} else {\n\t\t\tconst wireState: AwarenessStateWire = {\n\t\t\t\tuser: { ...state.user },\n\t\t\t}\n\t\t\tif (state.cursor) {\n\t\t\t\twireState.cursor = { ...state.cursor }\n\t\t\t}\n\t\t\twire[clientId] = wireState\n\t\t}\n\t}\n\treturn wire\n}\n\n/**\n * Convert wire format awareness states to internal representation.\n */\nfunction wireToAwarenessStates(\n\twire: Record<string, AwarenessStateWire | null>,\n): Record<number, AwarenessState | null> {\n\tconst states: Record<number, AwarenessState | null> = {}\n\tfor (const [clientId, wireState] of Object.entries(wire)) {\n\t\tif (wireState === null) {\n\t\t\tstates[Number(clientId)] = null\n\t\t} else {\n\t\t\tconst state: AwarenessState = {\n\t\t\t\tuser: { ...wireState.user },\n\t\t\t}\n\t\t\tif (wireState.cursor) {\n\t\t\t\tstate.cursor = { ...wireState.cursor }\n\t\t\t}\n\t\t\tstates[Number(clientId)] = state\n\t\t}\n\t}\n\treturn states\n}\n\n/**\n * The explicit authoritative ids a handshake response revokes (RT-81), read structurally\n * (the field is optional, protocol v2).\n */\nfunction revokedAuthoritativeIds(msg: unknown): string[] {\n\tconst revoked = (msg as { revokedAuthoritativeNodeIds?: unknown }).revokedAuthoritativeNodeIds\n\treturn Array.isArray(revoked)\n\t\t? revoked.filter((id): id is string => typeof id === 'string' && !isServerNodeId(id))\n\t\t: []\n}\n\n/**\n * The schema transforms of an engine: its own `operationTransforms`, or the store's.\n * Both sides must use the same list, because the store folds what the engine judges\n * (transforms at fold time, RT-84): a mismatch is refused at construction.\n */\nfunction resolveOperationTransforms(\n\tconfig: SyncConfig,\n\tstore: SyncStore,\n): readonly OperationTransform[] {\n\tconst own = config.operationTransforms\n\tconst stores = store.getOperationTransforms?.()\n\tif (own === undefined) return stores ?? []\n\tif (stores !== undefined) {\n\t\tconst version = config.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\tif (\n\t\t\toperationTransformsFingerprint(version, own) !==\n\t\t\toperationTransformsFingerprint(version, stores)\n\t\t) {\n\t\t\tthrow new SyncError(\n\t\t\t\t'The sync engine and the local store were given different operationTransforms. The store folds every operation through its transforms (transforms at fold time); pass the same list to both (createApp does).',\n\t\t\t\t{ engineTransforms: own.length, storeTransforms: stores.length },\n\t\t\t)\n\t\t}\n\t}\n\treturn own\n}\n\n/** Map a key-service response to the keyring's reply, or the error it stands for. */\nfunction keyServiceReply(message: EncryptionKeyResponseMessage): KeyServiceReply {\n\tif (message.status === 'ok' || message.status === 'conflict') {\n\t\treturn {\n\t\t\tstatus: message.status,\n\t\t\trecord: message.record,\n\t\t\t...(Array.isArray(message.knownKeyIds)\n\t\t\t\t? {\n\t\t\t\t\t\tknownKeyIds: message.knownKeyIds.filter(\n\t\t\t\t\t\t\t(keyId): keyId is string => typeof keyId === 'string',\n\t\t\t\t\t\t),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t}\n\t}\n\tconst codes: Record<Exclude<EncryptionKeyResponseStatus, 'ok' | 'conflict'>, string> = {\n\t\tforbidden: 'KEY_SERVICE_FORBIDDEN',\n\t\tinvalid: 'KEY_RECORD_REFUSED',\n\t\tunsupported: 'KEY_SERVICE_UNSUPPORTED',\n\t\tthrottled: 'KEY_SERVICE_THROTTLED',\n\t}\n\tthrow new EncryptionKeyError(\n\t\tmessage.message ?? `The sync server refused the key request (${message.status}).`,\n\t\t{ code: codes[message.status] },\n\t)\n}\n","import type { KoraEventEmitter } from '@korajs/core'\nimport type { AwarenessChange, AwarenessMessage, AwarenessState } from './types'\n\n/**\n * Callback for awareness change events.\n */\ntype AwarenessChangeListener = (change: AwarenessChange) => void\n\n// Timeout after which a remote client's awareness is considered stale\n// and will be cleaned up. This is a safety net -- normally the server\n// sends explicit removal on disconnect.\nconst DEFAULT_TIMEOUT_MS = 30_000\n\nlet nextLocalClientId = 1\n\n/**\n * Manages collaborative awareness state (cursor positions, user presence).\n *\n * Awareness is ephemeral -- states are never persisted, only shared with\n * currently connected peers via the sync transport. This implements a\n * lightweight protocol compatible with Yjs awareness semantics.\n *\n * Each AwarenessManager has a unique client ID and tracks both its own\n * local state and all remote clients' states.\n */\nexport class AwarenessManager {\n\t/** Unique client ID for this instance */\n\treadonly clientId: number\n\n\tprivate localState: AwarenessState | null = null\n\tprivate readonly remoteStates = new Map<number, AwarenessState>()\n\tprivate readonly listeners = new Set<AwarenessChangeListener>()\n\tprivate readonly emitter: KoraEventEmitter | null\n\tprivate readonly timeoutMs: number\n\n\t// Track when we last received an update from each remote client.\n\t// Used for timeout-based cleanup as a safety net in case the server\n\t// does not send an explicit removal on disconnect.\n\tprivate readonly lastUpdated = new Map<number, number>()\n\tprivate cleanupTimer: ReturnType<typeof setInterval> | null = null\n\n\tprivate sendHandler: ((message: AwarenessMessage) => void) | null = null\n\tprivate destroyed = false\n\n\tconstructor(options?: {\n\t\tclientId?: number\n\t\temitter?: KoraEventEmitter\n\t\ttimeoutMs?: number\n\t}) {\n\t\tthis.clientId = options?.clientId ?? nextLocalClientId++\n\t\tthis.emitter = options?.emitter ?? null\n\t\tthis.timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\t}\n\n\t/**\n\t * Set the local user's awareness state and broadcast to peers.\n\t *\n\t * @param state - The awareness state to share. Pass null to clear presence.\n\t */\n\tsetLocalState(state: AwarenessState | null): void {\n\t\tif (this.destroyed) return\n\n\t\tthis.localState = state\n\n\t\tconst message: AwarenessMessage = {\n\t\t\ttype: 'awareness',\n\t\t\tclientId: this.clientId,\n\t\t\tstates: { [this.clientId]: state },\n\t\t}\n\n\t\tthis.sendHandler?.(message)\n\n\t\tthis.emitAwarenessEvent()\n\t}\n\n\t/**\n\t * Get the local awareness state.\n\t */\n\tgetLocalState(): AwarenessState | null {\n\t\treturn this.localState\n\t}\n\n\t/**\n\t * Get all known awareness states (local + remote).\n\t * Returns a new Map on each call.\n\t */\n\tgetStates(): Map<number, AwarenessState> {\n\t\tconst result = new Map<number, AwarenessState>()\n\t\tif (this.localState) {\n\t\t\tresult.set(this.clientId, this.localState)\n\t\t}\n\t\tfor (const [id, state] of this.remoteStates) {\n\t\t\tresult.set(id, state)\n\t\t}\n\t\treturn result\n\t}\n\n\t/**\n\t * Handle an incoming awareness message from the transport.\n\t * This processes remote state updates and notifies listeners.\n\t */\n\thandleRemoteMessage(message: AwarenessMessage): void {\n\t\tif (this.destroyed) return\n\n\t\tconst added: number[] = []\n\t\tconst updated: number[] = []\n\t\tconst removed: number[] = []\n\t\tconst now = Date.now()\n\n\t\tfor (const [clientIdStr, state] of Object.entries(message.states)) {\n\t\t\tconst clientId = Number(clientIdStr)\n\n\t\t\t// Ignore our own state echoed back\n\t\t\tif (clientId === this.clientId) continue\n\n\t\t\tif (state === null) {\n\t\t\t\t// Removal\n\t\t\t\tif (this.remoteStates.has(clientId)) {\n\t\t\t\t\tthis.remoteStates.delete(clientId)\n\t\t\t\t\tthis.lastUpdated.delete(clientId)\n\t\t\t\t\tremoved.push(clientId)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (this.remoteStates.has(clientId)) {\n\t\t\t\t\tthis.remoteStates.set(clientId, state)\n\t\t\t\t\tthis.lastUpdated.set(clientId, now)\n\t\t\t\t\tupdated.push(clientId)\n\t\t\t\t} else {\n\t\t\t\t\tthis.remoteStates.set(clientId, state)\n\t\t\t\t\tthis.lastUpdated.set(clientId, now)\n\t\t\t\t\tadded.push(clientId)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (added.length > 0 || updated.length > 0 || removed.length > 0) {\n\t\t\tconst change: AwarenessChange = { added, updated, removed }\n\t\t\tthis.notifyListeners(change)\n\t\t\tthis.emitAwarenessEvent()\n\t\t}\n\t}\n\n\t/**\n\t * Remove a specific remote client's awareness state.\n\t * Called when the server notifies that a client has disconnected.\n\t */\n\tremoveClient(clientId: number): void {\n\t\tif (this.destroyed) return\n\t\tif (!this.remoteStates.has(clientId)) return\n\n\t\tthis.remoteStates.delete(clientId)\n\t\tthis.lastUpdated.delete(clientId)\n\n\t\tconst change: AwarenessChange = {\n\t\t\tadded: [],\n\t\t\tupdated: [],\n\t\t\tremoved: [clientId],\n\t\t}\n\t\tthis.notifyListeners(change)\n\t\tthis.emitAwarenessEvent()\n\t}\n\n\t/**\n\t * Register a handler for sending awareness messages through the transport.\n\t * The sync engine calls this to wire outgoing awareness messages to the transport.\n\t */\n\tonSend(handler: (message: AwarenessMessage) => void): void {\n\t\tthis.sendHandler = handler\n\t}\n\n\t/**\n\t * Register a listener for awareness state changes.\n\t * Returns an unsubscribe function.\n\t */\n\ton(_event: 'change', listener: AwarenessChangeListener): () => void {\n\t\tthis.listeners.add(listener)\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener)\n\t\t}\n\t}\n\n\t/**\n\t * Remove a specific change listener.\n\t */\n\toff(_event: 'change', listener: AwarenessChangeListener): void {\n\t\tthis.listeners.delete(listener)\n\t}\n\n\t/**\n\t * Start the cleanup timer that removes stale remote states.\n\t * Called when the sync engine transitions to streaming state.\n\t */\n\tstartCleanupTimer(): void {\n\t\tif (this.cleanupTimer) return\n\n\t\tthis.cleanupTimer = setInterval(() => {\n\t\t\tthis.cleanupStaleStates()\n\t\t}, this.timeoutMs)\n\t}\n\n\t/**\n\t * Stop the cleanup timer.\n\t */\n\tstopCleanupTimer(): void {\n\t\tif (this.cleanupTimer) {\n\t\t\tclearInterval(this.cleanupTimer)\n\t\t\tthis.cleanupTimer = null\n\t\t}\n\t}\n\n\t/**\n\t * Clean up all resources. After calling destroy(), the manager\n\t * will no longer send or receive awareness updates.\n\t * Broadcasts removal of local state before shutting down.\n\t */\n\tdestroy(): void {\n\t\tif (this.destroyed) return\n\t\tthis.destroyed = true\n\n\t\t// Broadcast removal of our local state before shutting down\n\t\tif (this.localState) {\n\t\t\tconst message: AwarenessMessage = {\n\t\t\t\ttype: 'awareness',\n\t\t\t\tclientId: this.clientId,\n\t\t\t\tstates: { [this.clientId]: null },\n\t\t\t}\n\t\t\tthis.sendHandler?.(message)\n\t\t}\n\n\t\tthis.localState = null\n\t\tthis.remoteStates.clear()\n\t\tthis.lastUpdated.clear()\n\t\tthis.listeners.clear()\n\t\tthis.sendHandler = null\n\t\tthis.stopCleanupTimer()\n\t}\n\n\t// --- Private ---\n\n\tprivate cleanupStaleStates(): void {\n\t\tconst now = Date.now()\n\t\tconst removed: number[] = []\n\n\t\tfor (const [clientId, lastTime] of this.lastUpdated) {\n\t\t\tif (now - lastTime > this.timeoutMs) {\n\t\t\t\tthis.remoteStates.delete(clientId)\n\t\t\t\tthis.lastUpdated.delete(clientId)\n\t\t\t\tremoved.push(clientId)\n\t\t\t}\n\t\t}\n\n\t\tif (removed.length > 0) {\n\t\t\tconst change: AwarenessChange = { added: [], updated: [], removed }\n\t\t\tthis.notifyListeners(change)\n\t\t\tthis.emitAwarenessEvent()\n\t\t}\n\t}\n\n\tprivate notifyListeners(change: AwarenessChange): void {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener(change)\n\t\t}\n\t}\n\n\tprivate emitAwarenessEvent(): void {\n\t\t// Cast states to Map<number, unknown> to satisfy KoraEvent's generic type\n\t\tconst states: Map<number, unknown> = this.getStates()\n\t\tthis.emitter?.emit({ type: 'awareness:updated', states })\n\t}\n}\n","import { generateUUIDv7 } from '@korajs/core'\nimport type {\n\tBlobChunkPushMessage,\n\tBlobChunkRequestMessage,\n\tBlobChunkResponseMessage,\n} from '../protocol/messages'\n\n/**\n * A blob-chunk message in decoded form (chunk bytes as a `Uint8Array` rather\n * than the base64 wire string). This is the shape callers of the channel work\n * with; the channel handles base64 encode/decode at the wire boundary.\n *\n * The shape mirrors `@korajs/store`'s `ChunkMessage` so an app-layer adapter can\n * bridge the two with no field translation.\n */\nexport type BlobChunkChannelMessage =\n\t| { type: 'blob-chunk-request'; requestId: string; hash: string }\n\t| {\n\t\t\ttype: 'blob-chunk-response'\n\t\t\trequestId: string\n\t\t\tbytes: Uint8Array | null\n\t\t\t/** The server refused the request for rate; retry after `retryAfterMs` (RT-24). */\n\t\t\tthrottled?: boolean\n\t\t\tretryAfterMs?: number\n\t }\n\t| { type: 'blob-chunk-push'; hash: string; bytes: Uint8Array }\n\n/** Encode chunk bytes to a base64 string for the JSON wire. */\nexport function encodeBlobChunkBytes(bytes: Uint8Array): string {\n\tlet binary = ''\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\tbinary += String.fromCharCode(bytes[i] as number)\n\t}\n\treturn btoa(binary)\n}\n\n/** Decode base64 chunk bytes from the wire back to a `Uint8Array`. */\nexport function decodeBlobChunkBytes(encoded: string): Uint8Array {\n\tconst binary = atob(encoded)\n\tconst out = new Uint8Array(binary.length)\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tout[i] = binary.charCodeAt(i)\n\t}\n\treturn out\n}\n\nexport interface BlobChunkChannelOptions {\n\t/**\n\t * Called when a local blob-chunk message should go out on the wire. The\n\t * caller (the sync engine) decides whether the connection is ready to send.\n\t */\n\tonSend?: (\n\t\tmessage: BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage,\n\t) => void\n}\n\n/**\n * Ephemeral side channel for out-of-band blob chunk transfer over the sync\n * connection. Symmetric: a peer can both request chunks it needs and answer\n * requests for chunks it holds. Never persisted in the operation log — durable\n * state is the `BlobRef` inside a record's fields; this channel only moves the\n * referenced bytes.\n *\n * The channel owns the base64 wire encoding so callers work purely in\n * `Uint8Array`, and its {@link BlobChunkChannelMessage} shape mirrors\n * `@korajs/store`'s `ChunkMessage` so the app layer can expose it as a\n * `ChunkMessagePort` without translating fields.\n */\nexport class BlobChunkChannel {\n\tprivate readonly onSend:\n\t\t| ((message: BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage) => void)\n\t\t| null\n\tprivate readonly handlers = new Set<(message: BlobChunkChannelMessage) => void>()\n\n\tconstructor(options?: BlobChunkChannelOptions) {\n\t\tthis.onSend = options?.onSend ?? null\n\t}\n\n\t/**\n\t * Send a blob-chunk message to the peer. Response bytes are base64-encoded\n\t * onto the wire here; callers pass a `Uint8Array`.\n\t */\n\tsend(message: BlobChunkChannelMessage): void {\n\t\tif (!this.onSend) {\n\t\t\treturn\n\t\t}\n\t\tif (message.type === 'blob-chunk-request') {\n\t\t\tthis.onSend({\n\t\t\t\ttype: 'blob-chunk-request',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\trequestId: message.requestId,\n\t\t\t\thash: message.hash,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tif (message.type === 'blob-chunk-push') {\n\t\t\tthis.onSend({\n\t\t\t\ttype: 'blob-chunk-push',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\thash: message.hash,\n\t\t\t\tbytes: encodeBlobChunkBytes(message.bytes),\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tthis.onSend({\n\t\t\ttype: 'blob-chunk-response',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\trequestId: message.requestId,\n\t\t\tbytes: message.bytes === null ? null : encodeBlobChunkBytes(message.bytes),\n\t\t\t...(message.throttled ? { throttled: true } : {}),\n\t\t\t...(message.retryAfterMs !== undefined ? { retryAfterMs: message.retryAfterMs } : {}),\n\t\t})\n\t}\n\n\t/**\n\t * Register a handler for inbound blob-chunk messages. Multiple handlers are\n\t * allowed; each receives every delivered message.\n\t */\n\tonMessage(handler: (message: BlobChunkChannelMessage) => void): () => void {\n\t\tthis.handlers.add(handler)\n\t\treturn () => {\n\t\t\tthis.handlers.delete(handler)\n\t\t}\n\t}\n\n\t/**\n\t * Deliver an inbound wire message to registered handlers, decoding response\n\t * bytes from base64 back to a `Uint8Array` first.\n\t */\n\tdeliver(\n\t\tmessage: BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage,\n\t): void {\n\t\tif (this.handlers.size === 0) {\n\t\t\treturn\n\t\t}\n\t\tconst decoded: BlobChunkChannelMessage =\n\t\t\tmessage.type === 'blob-chunk-request'\n\t\t\t\t? { type: 'blob-chunk-request', requestId: message.requestId, hash: message.hash }\n\t\t\t\t: message.type === 'blob-chunk-push'\n\t\t\t\t\t? {\n\t\t\t\t\t\t\ttype: 'blob-chunk-push',\n\t\t\t\t\t\t\thash: message.hash,\n\t\t\t\t\t\t\tbytes: decodeBlobChunkBytes(message.bytes),\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\ttype: 'blob-chunk-response',\n\t\t\t\t\t\t\trequestId: message.requestId,\n\t\t\t\t\t\t\tbytes: message.bytes === null ? null : decodeBlobChunkBytes(message.bytes),\n\t\t\t\t\t\t\t...(message.throttled ? { throttled: true } : {}),\n\t\t\t\t\t\t\t...(message.retryAfterMs !== undefined ? { retryAfterMs: message.retryAfterMs } : {}),\n\t\t\t\t\t\t}\n\t\tfor (const handler of this.handlers) {\n\t\t\thandler(decoded)\n\t\t}\n\t}\n}\n","import type { TimeSource } from '@korajs/core'\n\n/**\n * A single bandwidth measurement sample: bytes transferred over a known duration.\n */\ninterface BandwidthSample {\n\tbytes: number\n\tdurationMs: number\n\ttimestamp: number\n}\n\n/**\n * Estimates effective bandwidth by tracking byte transfers over time.\n *\n * Maintains a sliding window of transfer samples and computes a\n * weighted average bandwidth, giving more weight to recent samples.\n * This provides a smooth estimate that adapts to changing network conditions.\n */\nexport class BandwidthEstimator {\n\tprivate readonly maxSamples: number\n\tprivate readonly timeSource: TimeSource\n\tprivate readonly samples: BandwidthSample[] = []\n\n\t/**\n\t * @param maxSamples - Maximum number of samples to retain. Defaults to 20.\n\t * @param timeSource - Injectable time source for deterministic testing.\n\t */\n\tconstructor(maxSamples = 20, timeSource?: TimeSource) {\n\t\tthis.maxSamples = maxSamples\n\t\tthis.timeSource = timeSource ?? { now: () => Date.now() }\n\t}\n\n\t/**\n\t * Record a transfer of `bytes` that took `durationMs` to complete.\n\t * Samples with zero or negative duration are ignored to avoid division errors.\n\t */\n\trecordTransfer(bytes: number, durationMs: number): void {\n\t\tif (durationMs <= 0 || bytes <= 0) return\n\n\t\tthis.samples.push({\n\t\t\tbytes,\n\t\t\tdurationMs,\n\t\t\ttimestamp: this.timeSource.now(),\n\t\t})\n\n\t\t// Evict oldest samples beyond the window\n\t\twhile (this.samples.length > this.maxSamples) {\n\t\t\tthis.samples.shift()\n\t\t}\n\t}\n\n\t/**\n\t * Estimate current effective bandwidth in bytes per second.\n\t *\n\t * Uses exponential weighting so recent samples have more influence.\n\t * Returns null if fewer than 2 samples exist (not enough data).\n\t */\n\testimate(): number | null {\n\t\tif (this.samples.length < 2) return null\n\n\t\tlet weightedSum = 0\n\t\tlet totalWeight = 0\n\n\t\t// Exponential decay: most recent sample gets weight 1.0,\n\t\t// each older sample is multiplied by 0.9\n\t\tconst decayFactor = 0.9\n\t\tfor (let i = this.samples.length - 1; i >= 0; i--) {\n\t\t\tconst sample = this.samples[i]\n\t\t\tif (!sample) continue\n\t\t\tconst bytesPerSec = (sample.bytes / sample.durationMs) * 1000\n\t\t\tconst age = this.samples.length - 1 - i\n\t\t\tconst weight = decayFactor ** age\n\n\t\t\tweightedSum += bytesPerSec * weight\n\t\t\ttotalWeight += weight\n\t\t}\n\n\t\tif (totalWeight === 0) return null\n\t\treturn Math.round(weightedSum / totalWeight)\n\t}\n\n\t/**\n\t * Reset all recorded samples.\n\t */\n\treset(): void {\n\t\tthis.samples.length = 0\n\t}\n\n\t/**\n\t * Get the number of samples currently stored.\n\t */\n\tsampleCount(): number {\n\t\treturn this.samples.length\n\t}\n}\n","/**\n * Sliding window percentile calculator.\n *\n * Maintains a fixed-size circular buffer of numeric samples and\n * computes percentiles (p50, p95, p99, etc.) on demand by sorting\n * the current window. This approach is simple, correct, and fast\n * for the expected window sizes (100 samples).\n */\nexport class SlidingWindowPercentile {\n\tprivate readonly samples: number[]\n\tprivate readonly maxSize: number\n\tprivate writeIndex = 0\n\tprivate count = 0\n\n\t/**\n\t * @param maxSize - Maximum number of samples in the sliding window.\n\t * Older samples are overwritten when the buffer is full.\n\t */\n\tconstructor(maxSize: number) {\n\t\tif (maxSize < 1) {\n\t\t\tthrow new Error(`SlidingWindowPercentile maxSize must be >= 1, got ${maxSize}`)\n\t\t}\n\t\tthis.maxSize = maxSize\n\t\tthis.samples = new Array<number>(maxSize)\n\t}\n\n\t/**\n\t * Add a sample to the sliding window.\n\t * If the window is full, the oldest sample is overwritten.\n\t */\n\taddSample(value: number): void {\n\t\tthis.samples[this.writeIndex] = value\n\t\tthis.writeIndex = (this.writeIndex + 1) % this.maxSize\n\t\tif (this.count < this.maxSize) {\n\t\t\tthis.count++\n\t\t}\n\t}\n\n\t/**\n\t * Compute a percentile value from the current window.\n\t *\n\t * Uses the nearest-rank method: the percentile value is the smallest\n\t * value in the dataset such that at least p% of the data is <= that value.\n\t *\n\t * @param p - Percentile to compute (0-100). E.g., 50 for median, 95 for p95.\n\t * @returns The percentile value, or 0 if no samples have been recorded.\n\t */\n\tpercentile(p: number): number {\n\t\tif (this.count === 0) return 0\n\n\t\t// Extract only the valid portion of the buffer and sort\n\t\tconst sorted = this.samples.slice(0, this.count).sort((a, b) => a - b)\n\n\t\t// Nearest-rank method: index = ceil(p/100 * n) - 1, clamped to [0, n-1]\n\t\tconst rank = Math.ceil((p / 100) * sorted.length) - 1\n\t\tconst index = Math.max(0, Math.min(rank, sorted.length - 1))\n\t\treturn sorted[index] ?? 0\n\t}\n\n\t/**\n\t * Get the most recently added sample, or 0 if no samples exist.\n\t */\n\tlatest(): number {\n\t\tif (this.count === 0) return 0\n\t\t// writeIndex points to the next write position, so the last written is one behind\n\t\tconst lastIndex = (this.writeIndex - 1 + this.maxSize) % this.maxSize\n\t\treturn this.samples[lastIndex] ?? 0\n\t}\n\n\t/**\n\t * Get the number of samples currently in the window.\n\t */\n\tsize(): number {\n\t\treturn this.count\n\t}\n\n\t/**\n\t * Reset the sliding window, clearing all samples.\n\t */\n\treset(): void {\n\t\tthis.writeIndex = 0\n\t\tthis.count = 0\n\t}\n}\n","import type {\n\tConnectionQuality,\n\tKoraEventEmitter,\n\tSyncDiagnosticsSnapshot,\n\tTimeSource,\n} from '@korajs/core'\nimport type { SyncStatus } from '../types'\nimport { BandwidthEstimator } from './bandwidth-estimator'\nimport { SlidingWindowPercentile } from './percentile'\n\n/**\n * Configuration for the SyncMetricsCollector.\n */\nexport interface MetricsCollectorConfig {\n\t/** Number of RTT samples to keep for percentile calculations. Defaults to 100. */\n\trttWindowSize?: number\n\t/** Number of bandwidth samples to keep. Defaults to 20. */\n\tbandwidthWindowSize?: number\n\t/** Interval in ms between periodic diagnostics emissions. Defaults to 5000. */\n\tdiagnosticsInterval?: number\n\t/** Injectable time source for deterministic testing. */\n\ttimeSource?: TimeSource\n}\n\n/**\n * Collects and aggregates sync metrics for diagnostics and DevTools integration.\n *\n * Tracks connection timing, RTT percentiles, throughput counters, queue depth,\n * initial sync progress, errors, and connection quality. Periodically emits\n * a `sync:diagnostics` event with a full snapshot.\n */\nexport class SyncMetricsCollector {\n\tprivate readonly rttWindow: SlidingWindowPercentile\n\tprivate readonly inboundBandwidth: BandwidthEstimator\n\tprivate readonly outboundBandwidth: BandwidthEstimator\n\tprivate readonly timeSource: TimeSource\n\tprivate readonly diagnosticsInterval: number\n\n\t// Connection\n\tprivate connectedAt: number | null = null\n\tprivate disconnectedAt: number | null = null\n\tprivate reconnectAttempts = 0\n\n\t// Throughput\n\tprivate operationsSent = 0\n\tprivate operationsReceived = 0\n\tprivate bytesSent = 0\n\tprivate bytesReceived = 0\n\n\t// Queue\n\tprivate pendingOperations = 0\n\tprivate outboundQueueSize = 0\n\n\t// Sync progress\n\tprivate lastSyncedAt: number | null = null\n\tprivate syncStartTime: number | null = null\n\tprivate syncDuration: number | null = null\n\tprivate initialSyncComplete = false\n\tprivate initialSyncTotalBatches = 0\n\tprivate initialSyncReceivedBatches = 0\n\n\t// Errors\n\tprivate lastError: string | null = null\n\tprivate errorCount = 0\n\n\t// Status\n\tprivate currentStatus: SyncStatus = 'offline'\n\tprivate currentQuality: ConnectionQuality = 'offline'\n\n\t// Periodic emission\n\tprivate emitter: KoraEventEmitter | null = null\n\tprivate periodicTimer: ReturnType<typeof setInterval> | null = null\n\n\tconstructor(config?: MetricsCollectorConfig) {\n\t\tthis.rttWindow = new SlidingWindowPercentile(config?.rttWindowSize ?? 100)\n\t\tthis.inboundBandwidth = new BandwidthEstimator(\n\t\t\tconfig?.bandwidthWindowSize ?? 20,\n\t\t\tconfig?.timeSource,\n\t\t)\n\t\tthis.outboundBandwidth = new BandwidthEstimator(\n\t\t\tconfig?.bandwidthWindowSize ?? 20,\n\t\t\tconfig?.timeSource,\n\t\t)\n\t\tthis.timeSource = config?.timeSource ?? { now: () => Date.now() }\n\t\tthis.diagnosticsInterval = config?.diagnosticsInterval ?? 5000\n\t}\n\n\t/**\n\t * Attach an event emitter for periodic diagnostics emission.\n\t * Starts emitting `sync:diagnostics` events at the configured interval\n\t * while connected.\n\t */\n\tattachEmitter(emitter: KoraEventEmitter): void {\n\t\tthis.emitter = emitter\n\t}\n\n\t/**\n\t * Record that a connection has been established.\n\t */\n\trecordConnected(): void {\n\t\tthis.connectedAt = this.timeSource.now()\n\t\tthis.reconnectAttempts = 0\n\t\tthis.startPeriodicEmission()\n\t}\n\n\t/**\n\t * Record that the connection has been lost.\n\t */\n\trecordDisconnected(): void {\n\t\tthis.disconnectedAt = this.timeSource.now()\n\t\tthis.stopPeriodicEmission()\n\t}\n\n\t/**\n\t * Record a reconnection attempt.\n\t */\n\trecordReconnectAttempt(): void {\n\t\tthis.reconnectAttempts++\n\t}\n\n\t/**\n\t * Record a round-trip time measurement.\n\t */\n\trecordRtt(ms: number): void {\n\t\tthis.rttWindow.addSample(ms)\n\t}\n\n\t/**\n\t * Record operations sent with their serialized byte size.\n\t */\n\trecordSent(operationCount: number, byteSize: number, durationMs: number): void {\n\t\tthis.operationsSent += operationCount\n\t\tthis.bytesSent += byteSize\n\t\tif (durationMs > 0 && byteSize > 0) {\n\t\t\tthis.outboundBandwidth.recordTransfer(byteSize, durationMs)\n\t\t\tthis.emitBandwidthEvent('out')\n\t\t}\n\t}\n\n\t/**\n\t * Record operations received with their serialized byte size.\n\t */\n\trecordReceived(operationCount: number, byteSize: number, durationMs: number): void {\n\t\tthis.operationsReceived += operationCount\n\t\tthis.bytesReceived += byteSize\n\t\tif (durationMs > 0 && byteSize > 0) {\n\t\t\tthis.inboundBandwidth.recordTransfer(byteSize, durationMs)\n\t\t\tthis.emitBandwidthEvent('in')\n\t\t}\n\t}\n\n\t/**\n\t * Update the pending operations count and estimated outbound queue size.\n\t */\n\tupdateQueue(pendingOps: number, estimatedBytes: number): void {\n\t\tthis.pendingOperations = pendingOps\n\t\tthis.outboundQueueSize = estimatedBytes\n\t}\n\n\t/**\n\t * Record that sync has started (for measuring sync duration).\n\t */\n\trecordSyncStarted(): void {\n\t\tthis.syncStartTime = this.timeSource.now()\n\t}\n\n\t/**\n\t * Record that a sync cycle has completed.\n\t */\n\trecordSyncCompleted(): void {\n\t\tif (this.syncStartTime !== null) {\n\t\t\tthis.syncDuration = this.timeSource.now() - this.syncStartTime\n\t\t\tthis.syncStartTime = null\n\t\t}\n\t\tthis.lastSyncedAt = this.timeSource.now()\n\t\tif (!this.initialSyncComplete) {\n\t\t\tthis.initialSyncComplete = true\n\t\t}\n\t}\n\n\t/**\n\t * Update initial sync progress.\n\t * @param receivedBatches - Number of delta batches received so far.\n\t * @param totalBatches - Estimated total batches (0 if unknown).\n\t */\n\tupdateInitialSyncProgress(receivedBatches: number, totalBatches: number): void {\n\t\tthis.initialSyncReceivedBatches = receivedBatches\n\t\tthis.initialSyncTotalBatches = totalBatches\n\n\t\tconst progress =\n\t\t\ttotalBatches > 0 ? Math.min(1, receivedBatches / totalBatches) : receivedBatches > 0 ? 0.5 : 0\n\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:initial-sync-progress',\n\t\t\tprogress,\n\t\t\ttotalBatches,\n\t\t\treceivedBatches,\n\t\t})\n\t}\n\n\t/**\n\t * Record an error.\n\t */\n\trecordError(message: string): void {\n\t\tthis.lastError = message\n\t\tthis.errorCount++\n\t}\n\n\t/**\n\t * Update the current developer-facing status.\n\t */\n\tupdateStatus(status: SyncStatus): void {\n\t\tthis.currentStatus = status\n\t}\n\n\t/**\n\t * Update the connection quality.\n\t */\n\tupdateQuality(quality: ConnectionQuality): void {\n\t\tthis.currentQuality = quality\n\t}\n\n\t/**\n\t * Compute and return a full diagnostics snapshot.\n\t */\n\tgetSnapshot(): SyncDiagnosticsSnapshot {\n\t\treturn {\n\t\t\t// Connection\n\t\t\tstatus: this.currentStatus,\n\t\t\tconnectedAt: this.connectedAt,\n\t\t\tdisconnectedAt: this.disconnectedAt,\n\t\t\treconnectAttempts: this.reconnectAttempts,\n\n\t\t\t// Latency\n\t\t\trttMs: this.rttWindow.latest(),\n\t\t\trttP50Ms: this.rttWindow.percentile(50),\n\t\t\trttP95Ms: this.rttWindow.percentile(95),\n\t\t\trttP99Ms: this.rttWindow.percentile(99),\n\n\t\t\t// Throughput\n\t\t\toperationsSent: this.operationsSent,\n\t\t\toperationsReceived: this.operationsReceived,\n\t\t\tbytesSent: this.bytesSent,\n\t\t\tbytesReceived: this.bytesReceived,\n\n\t\t\t// Queue\n\t\t\tpendingOperations: this.pendingOperations,\n\t\t\toutboundQueueSize: this.outboundQueueSize,\n\n\t\t\t// Sync Progress\n\t\t\tlastSyncedAt: this.lastSyncedAt,\n\t\t\tsyncDuration: this.syncDuration,\n\t\t\tinitialSyncComplete: this.initialSyncComplete,\n\t\t\tinitialSyncProgress: this.computeInitialSyncProgress(),\n\n\t\t\t// Errors\n\t\t\tlastError: this.lastError,\n\t\t\terrorCount: this.errorCount,\n\n\t\t\t// Connection Quality\n\t\t\tquality: this.currentQuality,\n\t\t\teffectiveBandwidth: this.computeEffectiveBandwidth(),\n\t\t}\n\t}\n\n\t/**\n\t * Assess connection quality from current metrics.\n\t *\n\t * Quality is derived from RTT percentiles, error rate, and bandwidth:\n\t * - excellent: p95 < 100ms, no errors\n\t * - good: p95 < 300ms, errorCount <= 1\n\t * - fair: p95 < 1000ms, errorCount <= 3\n\t * - poor: p95 >= 1000ms or errorCount > 3\n\t * - offline: no connection\n\t */\n\tassessQuality(): ConnectionQuality {\n\t\tif (this.currentStatus === 'offline') return 'offline'\n\n\t\tconst p95 = this.rttWindow.percentile(95)\n\t\tconst hasSamples = this.rttWindow.size() > 0\n\n\t\tif (!hasSamples) {\n\t\t\t// No RTT data yet, fall back to error-based assessment\n\t\t\tif (this.errorCount > 3) return 'poor'\n\t\t\tif (this.errorCount > 0) return 'fair'\n\t\t\treturn 'good'\n\t\t}\n\n\t\tif (this.errorCount > 3) return 'poor'\n\t\tif (p95 < 100 && this.errorCount === 0) return 'excellent'\n\t\tif (p95 < 300 && this.errorCount <= 1) return 'good'\n\t\tif (p95 < 1000 && this.errorCount <= 3) return 'fair'\n\t\treturn 'poor'\n\t}\n\n\t/**\n\t * Reset all collected metrics. Call when starting a new session.\n\t */\n\treset(): void {\n\t\tthis.rttWindow.reset()\n\t\tthis.inboundBandwidth.reset()\n\t\tthis.outboundBandwidth.reset()\n\n\t\tthis.connectedAt = null\n\t\tthis.disconnectedAt = null\n\t\tthis.reconnectAttempts = 0\n\n\t\tthis.operationsSent = 0\n\t\tthis.operationsReceived = 0\n\t\tthis.bytesSent = 0\n\t\tthis.bytesReceived = 0\n\n\t\tthis.pendingOperations = 0\n\t\tthis.outboundQueueSize = 0\n\n\t\tthis.lastSyncedAt = null\n\t\tthis.syncStartTime = null\n\t\tthis.syncDuration = null\n\t\tthis.initialSyncComplete = false\n\t\tthis.initialSyncTotalBatches = 0\n\t\tthis.initialSyncReceivedBatches = 0\n\n\t\tthis.lastError = null\n\t\tthis.errorCount = 0\n\n\t\tthis.currentStatus = 'offline'\n\t\tthis.currentQuality = 'offline'\n\n\t\tthis.stopPeriodicEmission()\n\t}\n\n\t/**\n\t * Stop periodic emission and clean up resources.\n\t */\n\tdispose(): void {\n\t\tthis.stopPeriodicEmission()\n\t\tthis.emitter = null\n\t}\n\n\t// --- Private helpers ---\n\n\tprivate computeInitialSyncProgress(): number {\n\t\tif (this.initialSyncComplete) return 1\n\t\tif (this.initialSyncTotalBatches > 0) {\n\t\t\treturn Math.min(1, this.initialSyncReceivedBatches / this.initialSyncTotalBatches)\n\t\t}\n\t\t// Unknown total: if we have received some batches, report 0.5 as indeterminate progress\n\t\treturn this.initialSyncReceivedBatches > 0 ? 0.5 : 0\n\t}\n\n\tprivate computeEffectiveBandwidth(): number | null {\n\t\t// Use the lower of inbound and outbound as effective bandwidth,\n\t\t// since the bottleneck determines the effective rate.\n\t\tconst inbound = this.inboundBandwidth.estimate()\n\t\tconst outbound = this.outboundBandwidth.estimate()\n\n\t\tif (inbound === null && outbound === null) return null\n\t\tif (inbound === null) return outbound\n\t\tif (outbound === null) return inbound\n\t\treturn Math.min(inbound, outbound)\n\t}\n\n\tprivate startPeriodicEmission(): void {\n\t\tif (this.periodicTimer !== null) return\n\t\tif (!this.emitter) return\n\n\t\tthis.periodicTimer = setInterval(() => {\n\t\t\tthis.emitDiagnostics()\n\t\t}, this.diagnosticsInterval)\n\t}\n\n\tprivate stopPeriodicEmission(): void {\n\t\tif (this.periodicTimer !== null) {\n\t\t\tclearInterval(this.periodicTimer)\n\t\t\tthis.periodicTimer = null\n\t\t}\n\t}\n\n\tprivate emitDiagnostics(): void {\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'sync:diagnostics',\n\t\t\tdiagnostics: this.getSnapshot(),\n\t\t})\n\t}\n\n\tprivate emitBandwidthEvent(direction: 'in' | 'out'): void {\n\t\tconst estimator = direction === 'in' ? this.inboundBandwidth : this.outboundBandwidth\n\t\tconst bps = estimator.estimate()\n\t\tif (bps !== null) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:bandwidth',\n\t\t\t\tbytesPerSecond: bps,\n\t\t\t\tdirection,\n\t\t\t})\n\t\t}\n\t}\n}\n","import { SyncError } from '@korajs/core'\nimport type { CachedKeyring, KeyCache } from './key-cache'\nimport { MemoryKeyCache } from './key-cache'\nimport { DEFAULT_PBKDF2_ITERATIONS } from './key-derivation'\nimport type { WrappedDataKey, WrappedKeyRecord } from './key-record'\nimport {\n\tDEFAULT_KEYRING,\n\tKEY_RECORD_FORMAT,\n\tcanonicalJson,\n\tisKeyId,\n\tisMasterId,\n\tvalidateKeyRecord,\n} from './key-record'\nimport type { MasterKeys } from './keyring-crypto'\nimport {\n\tKEK_SALT_BYTES,\n\tKeyUnwrapError,\n\tbytesEqual,\n\tcreateRecoveryKeyPair,\n\tderiveKeyEncryptionKey,\n\tformatRecoveryKey,\n\tfromBase64,\n\tgenerateDataKey,\n\tgenerateMasterKey,\n\timportMasterKey,\n\tnewKeyId,\n\tnewRingId,\n\tparseRecoveryKey,\n\trandomBytes,\n\trecoveryAnchor,\n\tsealRecord,\n\ttoBase64,\n\ttoNonExtractable,\n\tunwrapDataKey,\n\tunwrapMasterKey,\n\tunwrapMasterWithRecovery,\n\tverifyRecordMac,\n\twrapDataKey,\n\twrapMasterForRecovery,\n\twrapMasterKey,\n} from './keyring-crypto'\nimport type { EncryptorOptions } from './sync-encryptor'\nimport { SyncEncryptor } from './sync-encryptor'\nimport type { VersionedKey } from './types'\n\n/** Lock state of an encryption keyring. */\nexport type EncryptionLockState = 'locked' | 'unlocking' | 'unlocked' | 'error'\n\n/** Why a keyring is locked or in error (or, while unlocked, what needs attention). */\nexport type EncryptionStatusCode =\n\t| 'NO_PASSPHRASE'\n\t| 'AWAITING_SERVER'\n\t| 'LOCKED_BY_APP'\n\t| 'WRONG_PASSPHRASE'\n\t| 'PASSPHRASE_REQUIRED'\n\t| 'KEY_RECORD_INVALID'\n\t| 'KEY_RECORD_ROLLBACK'\n\t| 'KEY_RECORD_MISSING'\n\t| 'KEY_RING_FORK'\n\t| 'KEY_SERVICE_FORBIDDEN'\n\t| 'KEY_SERVICE_UNSUPPORTED'\n\t| 'RECOVERY_FAILED'\n\n/** Status of an encryption keyring, as `app.encryption.getStatus()` reports it. */\nexport interface EncryptionStatus {\n\tstate: EncryptionLockState\n\tkeyring: string\n\t/** Key version new operations are encrypted with, or null while locked. */\n\tkeyVersion: number | null\n\t/** Key id of that version, or null while locked. */\n\tkeyId: string | null\n\t/** Every key version this device can decrypt. */\n\tavailableVersions: number[]\n\t/** Where unlocked keys are kept between starts. */\n\tcache: KeyCache['kind']\n\t/**\n\t * Set while locked or in error. While `unlocked`, `PASSPHRASE_REQUIRED` means the\n\t * passphrase changed on another device: this device keeps working with the keys it\n\t * holds, and needs the current passphrase for key management.\n\t */\n\tcode?: EncryptionStatusCode\n\tmessage?: string\n}\n\n/** The server's answer to a key-record fetch or write. */\nexport interface KeyServiceReply {\n\t/** 'conflict': the write lost a compare-and-set; `record` is the current one. */\n\tstatus: 'ok' | 'conflict'\n\trecord: WrappedKeyRecord | null\n\t/**\n\t * With no record: key ids the server's stored operations of this owner are encrypted\n\t * with (a sample). Non-empty means encrypted history exists, so the record was lost\n\t * and a device that holds the ring must re-upload it; nobody may start a new ring.\n\t */\n\tknownKeyIds?: string[]\n}\n\n/** How a keyring reaches the server's key service (the sync connection). */\nexport interface KeyServiceChannel {\n\tfetch(keyring: string): Promise<KeyServiceReply>\n\tput(keyring: string, record: WrappedKeyRecord, expectedRevision: number): Promise<KeyServiceReply>\n}\n\n/** Options of {@link EncryptionKeyring}. */\nexport interface EncryptionKeyringOptions {\n\t/** Keyring name (one per user by default). */\n\tkeyring?: string\n\t/** Passphrase source; without one the keyring starts locked until `unlock()`. */\n\tpassphrase?: string | (() => Promise<string>)\n\t/** Where unlocked keys are kept. Defaults to memory (the app picks IndexedDB in browsers). */\n\tcache?: KeyCache\n\t/**\n\t * PBKDF2 iterations for new records, and the minimum accepted from the server\n\t * (a server cannot downgrade the KDF). Defaults to 600,000. Lower only in tests.\n\t */\n\tkdfIterations?: number\n\t/** Encryptor options (cleartext scope fields, plaintext migration window). */\n\tencryptor?: EncryptorOptions\n}\n\n/** Thrown by keyring management calls. `context.code` names the reason. */\nexport class EncryptionKeyError extends SyncError {\n\tconstructor(message: string, context: Record<string, unknown> & { code: string }) {\n\t\tsuper(message, { ...context, errorType: 'ENCRYPTION_KEY_ERROR' })\n\t\tthis.name = 'EncryptionKeyError'\n\t}\n\n\t/** The reason code (`context.code`), e.g. WRONG_PASSPHRASE. */\n\tget keyCode(): string {\n\t\treturn String(this.context?.code ?? '')\n\t}\n}\n\n/** Failed unlocks before the client-side backoff starts. */\nconst FREE_UNLOCK_ATTEMPTS = 3\nconst MAX_UNLOCK_BACKOFF_MS = 60_000\n/** Compare-and-set retries of one key-record write (and nested adopt/merge rounds). */\nconst MAX_WRITE_ATTEMPTS = 4\n\n/** A record this device authenticated: the master keys that verify it, and its KEK if known. */\ninterface OpenedRing {\n\tmaster: MasterKeys\n\t/** The KEK of the record's own salt, when this device derived (or holds) it. */\n\tkek: CryptoKey | null\n}\n\n/**\n * A device's view of one encryption keyring (ENC-1, decision D4b; record format 2): the\n * server-stored, passphrase-wrapped ring, opened and authenticated locally.\n *\n * - **Shared key material.** Every device of a user opens the same record, so every\n * device holds the same data keys and decrypts everything.\n * - **Authenticated record.** The record carries an HMAC under a key derived from the\n * ring's master key; a device acts only on records it verified (RT-95). The server\n * can store, withhold or replay records, nothing more.\n * - **Forward only.** A device pins the ring id and highest revision it accepted and\n * refuses a lower revision (`KEY_RECORD_ROLLBACK`, RT-96), keeping its keys and\n * re-uploading its newer record when connected.\n * - **Writes re-read.** Every compare-and-set retry adopts the server's current record\n * first and wraps under that record's master key only (RT-97).\n * - **Lost records.** A device holding the ring re-uploads it unchanged; a new device\n * that finds no record but encrypted history waits (`KEY_RECORD_MISSING`); two rings\n * that exist anyway are merged by the first device holding both (RT-104).\n * - **Offline.** Once unlocked, keys are cached (non-extractable) and the keyring needs\n * no server; a cached record also lets `unlock()` work offline.\n */\nexport class EncryptionKeyring {\n\treadonly name: string\n\tprivate passphraseSource: string | (() => Promise<string>) | null\n\tprivate readonly cache: KeyCache\n\tprivate readonly kdfIterations: number\n\tprivate readonly encryptorOptions: EncryptorOptions\n\tprivate loadedPrincipal: string | null | undefined = undefined\n\t/** The last record this device authenticated and accepted: the pin. */\n\tprivate record: WrappedKeyRecord | null = null\n\t/** Keys derived from the pinned record's master key. */\n\tprivate master: MasterKeys | null = null\n\tprivate kek: CryptoKey | null = null\n\t/** Salt (base64) the KEK was derived with. */\n\tprivate kekSalt: string | null = null\n\t/** Held data keys by key id. Invariant: every held key id is in `record`. */\n\tprivate readonly dataKeys = new Map<string, CryptoKey>()\n\t/**\n\t * Ids of master keys a passphrase change retired, from every record this device\n\t * authenticated (persisted with the cache): a record authenticated only by one of\n\t * them is refused (RT-107).\n\t */\n\tprivate readonly retiredMasters = new Set<string>()\n\t/**\n\t * Ids of master keys this device held when the server served a newer revision of the\n\t * same ring that they do not authenticate (most likely a passphrase change made\n\t * elsewhere). A record authenticated only by one of them is not adopted without an\n\t * explicit `unlock(passphrase)`: after a genuine change, no later genuine record is\n\t * authenticated by the old master key (RT-107).\n\t */\n\tprivate readonly supersededMasters = new Set<string>()\n\tprivate encryptor: SyncEncryptor | null = null\n\tprivate status: EncryptionStatus\n\tprivate readonly listeners = new Set<(status: EncryptionStatus) => void>()\n\tprivate lockedByApp = false\n\tprivate pendingPassphrase: string | null = null\n\t/** A recovery requested while offline, run at the next handshake. */\n\tprivate pendingRecovery: { recoveryKey: string; newPassphrase: string } | null = null\n\t/** The configured passphrase that failed for a salt (not retried for that salt). */\n\tprivate failedSource: { salt: string; passphrase: string } | null = null\n\t/** The current lock is a server-side condition a later session can clear (no user action). */\n\tprivate retryable = false\n\t/** startNewKeyring() was called: a missing record with encrypted history may be replaced. */\n\tprivate allowNewRing = false\n\tprivate failedUnlocks = 0\n\tprivate nextUnlockAt = 0\n\tprivate mutex: Promise<unknown> = Promise.resolve()\n\n\tconstructor(options: EncryptionKeyringOptions = {}) {\n\t\tthis.name = options.keyring ?? DEFAULT_KEYRING\n\t\tthis.passphraseSource = options.passphrase ?? null\n\t\tthis.cache = options.cache ?? new MemoryKeyCache()\n\t\tthis.kdfIterations = options.kdfIterations ?? DEFAULT_PBKDF2_ITERATIONS\n\t\tif (!Number.isSafeInteger(this.kdfIterations) || this.kdfIterations < 1) {\n\t\t\tthrow new EncryptionKeyError('encryption.kdfIterations must be a positive integer.', {\n\t\t\t\tcode: 'INVALID_CONFIG',\n\t\t\t})\n\t\t}\n\t\tthis.encryptorOptions = options.encryptor ?? {}\n\t\tthis.status = this.makeStatus('locked', 'NO_PASSPHRASE', 'The keyring has not been opened.')\n\t}\n\n\t/** The encryptor of the unlocked keyring, or null while locked. */\n\tgetEncryptor(): SyncEncryptor | null {\n\t\treturn this.encryptor\n\t}\n\n\t/** Current status. */\n\tgetStatus(): EncryptionStatus {\n\t\treturn this.status\n\t}\n\n\t/** Whether this device holds the data key with this key id. */\n\tholdsKeyId(keyId: string): boolean {\n\t\treturn this.dataKeys.has(keyId)\n\t}\n\n\t/**\n\t * Whether the current lock is a server-side condition (a rolled-back, missing,\n\t * malformed or forked record) that a later sync session can clear without the user:\n\t * sync should reconnect with backoff rather than wait for `unlock()`.\n\t */\n\tisRetryableLock(): boolean {\n\t\treturn this.retryable && this.status.state !== 'unlocked'\n\t}\n\n\t/** Subscribe to status changes. Returns an unsubscribe function. */\n\tonStatusChange(listener: (status: EncryptionStatus) => void): () => void {\n\t\tthis.listeners.add(listener)\n\t\treturn () => this.listeners.delete(listener)\n\t}\n\n\t/**\n\t * Whether sync may connect: the keyring is unlocked, or a passphrase is available to\n\t * open (or create) the server record during the handshake.\n\t */\n\tcanConnect(): boolean {\n\t\tif (this.encryptor) return true\n\t\tif (this.pendingPassphrase !== null || this.pendingRecovery !== null) return true\n\t\treturn !this.lockedByApp && this.passphraseSource !== null\n\t}\n\n\t/**\n\t * Load the cached keyring of a principal (the signed-in user, or null). A principal\n\t * change drops everything held for the previous one first, so one user's keys never\n\t * encrypt another user's operations.\n\t */\n\tload(principal: string | null): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(() => this.loadLocked(principal))\n\t}\n\n\t/**\n\t * Open the keyring against the server's record during a sync handshake: fetch it,\n\t * create it on a user's first device, adopt a newer revision, re-upload a lost or\n\t * rolled-back one, or merge a forked ring.\n\t *\n\t * @returns 'ready' when unlocked, 'locked' when a passphrase is needed (or wrong), or\n\t * when the server's record must change first ({@link isRetryableLock})\n\t * @throws When the key service fails (the session is retried later)\n\t */\n\tsynchronize(channel: KeyServiceChannel, principal: string | null): Promise<'ready' | 'locked'> {\n\t\treturn this.exclusive(async () => {\n\t\t\tawait this.loadLocked(principal)\n\t\t\tif (this.encryptor === null) this.setStatus(this.makeStatus('unlocking'))\n\t\t\tconst recovery = this.pendingRecovery\n\t\t\tif (recovery !== null) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.recoverLocked(recovery.recoveryKey, recovery.newPassphrase, channel)\n\t\t\t\t\treturn 'ready'\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof EncryptionKeyError || error instanceof KeyUnwrapError) {\n\t\t\t\t\t\tthis.lockWith('error', 'RECOVERY_FAILED', errorMessage(error))\n\t\t\t\t\t\treturn 'locked'\n\t\t\t\t\t}\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst reply = await channel.fetch(this.name)\n\t\t\t\tif (reply.record === null) {\n\t\t\t\t\treturn await this.createOrRestoreLocked(channel, reply.knownKeyIds ?? [])\n\t\t\t\t}\n\t\t\t\treturn await this.adoptLocked(reply.record, null, channel)\n\t\t\t} catch (error) {\n\t\t\t\t// Permanent refusals: retrying the session would not change them.\n\t\t\t\tif (\n\t\t\t\t\terror instanceof EncryptionKeyError &&\n\t\t\t\t\t(error.keyCode === 'KEY_SERVICE_FORBIDDEN' || error.keyCode === 'KEY_SERVICE_UNSUPPORTED')\n\t\t\t\t) {\n\t\t\t\t\tthis.lockWith('error', error.keyCode, error.message)\n\t\t\t\t\treturn 'locked'\n\t\t\t\t}\n\t\t\t\tif (this.encryptor === null && this.status.state === 'unlocking') {\n\t\t\t\t\tthis.setStatus(this.makeStatus('locked', 'AWAITING_SERVER', errorMessage(error)))\n\t\t\t\t}\n\t\t\t\tthrow error\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Adopt a record the server pushed (another device rotated keys, changed the\n\t * passphrase or set up recovery). Without a connection to write to, a rollback or a\n\t * fork is reported and resolved at the next handshake.\n\t *\n\t * @returns 'ready' when still unlocked, 'locked' when the passphrase is needed or the\n\t * record was refused\n\t */\n\tadoptPushed(record: WrappedKeyRecord): Promise<'ready' | 'locked'> {\n\t\treturn this.exclusive(() => this.adoptLocked(record, null, null))\n\t}\n\n\t/**\n\t * Unlock with a passphrase. With a cached record this works offline; otherwise the\n\t * passphrase is kept until the next handshake opens (or creates) the server record,\n\t * and the returned promise settles then (pass `channel` when connected).\n\t *\n\t * Repeated wrong passphrases are slowed down on the device (backoff after three).\n\t * This is a usability guard, not a security boundary: whoever holds the record can\n\t * try passphrases offline, so passphrase strength and PBKDF2 are the protection.\n\t *\n\t * @throws {EncryptionKeyError} WRONG_PASSPHRASE, UNLOCK_THROTTLED, KEY_RECORD_MISSING\n\t */\n\tunlock(passphrase: string, channel: KeyServiceChannel | null): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(async () => {\n\t\t\tconst now = Date.now()\n\t\t\tif (now < this.nextUnlockAt) {\n\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t`Too many wrong passphrases on this device. Try again in ${Math.ceil((this.nextUnlockAt - now) / 1000)}s.`,\n\t\t\t\t\t{ code: 'UNLOCK_THROTTLED', retryAfterMs: this.nextUnlockAt - now },\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (passphrase.length === 0) {\n\t\t\t\tthrow new EncryptionKeyError('The passphrase must not be empty.', {\n\t\t\t\t\tcode: 'EMPTY_PASSPHRASE',\n\t\t\t\t})\n\t\t\t}\n\t\t\tthis.lockedByApp = false\n\t\t\tlet record = this.record\n\t\t\tif (channel) {\n\t\t\t\tconst reply = await channel.fetch(this.name)\n\t\t\t\tif (reply.record === null) {\n\t\t\t\t\t// Kept for the next handshake when the record is missing (KEY_RECORD_MISSING).\n\t\t\t\t\tthis.pendingPassphrase = passphrase\n\t\t\t\t\tconst outcome = await this.createOrRestoreLocked(channel, reply.knownKeyIds ?? [])\n\t\t\t\t\tif (outcome !== 'ready') throw this.statusError()\n\t\t\t\t\treturn this.status\n\t\t\t\t}\n\t\t\t\trecord = reply.record\n\t\t\t}\n\t\t\tif (record === null) {\n\t\t\t\t// No record known yet: open it at the next handshake.\n\t\t\t\tthis.pendingPassphrase = passphrase\n\t\t\t\tthis.setStatus(\n\t\t\t\t\tthis.makeStatus(\n\t\t\t\t\t\t'locked',\n\t\t\t\t\t\t'AWAITING_SERVER',\n\t\t\t\t\t\t'The passphrase is kept until the sync server provides the key record.',\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn this.status\n\t\t\t}\n\t\t\tthis.pendingPassphrase = passphrase\n\t\t\tconst outcome = await this.adoptLocked(record, passphrase, channel)\n\t\t\tif (outcome !== 'ready') {\n\t\t\t\tthis.pendingPassphrase = null\n\t\t\t\tthrow this.statusError()\n\t\t\t}\n\t\t\treturn this.status\n\t\t})\n\t}\n\n\t/**\n\t * Lock: forget the data keys, the master keys and the KEK on this device (cache\n\t * included). Sync stops until `unlock()`; local data stays readable (it is not\n\t * encrypted at rest by this layer). The pinned record is kept (it is the rollback\n\t * floor). The configured passphrase is not used again until `unlock()`.\n\t */\n\tlock(): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(async () => {\n\t\t\tthis.lockedByApp = true\n\t\t\tthis.pendingPassphrase = null\n\t\t\tthis.dropKeys()\n\t\t\tconst record = this.record\n\t\t\tif (record) {\n\t\t\t\tawait this.cache.save(this.cacheId(), {\n\t\t\t\t\trecord,\n\t\t\t\t\tkek: null,\n\t\t\t\t\tmaster: null,\n\t\t\t\t\tkeys: [],\n\t\t\t\t\tretiredMasters: [...this.retiredMasters],\n\t\t\t\t\tsupersededMasters: [...this.supersededMasters],\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tawait this.cache.clear(this.cacheId())\n\t\t\t}\n\t\t\tthis.lockWith('locked', 'LOCKED_BY_APP', 'Locked by the app. Call unlock(passphrase).')\n\t\t\treturn this.status\n\t\t})\n\t}\n\n\t/**\n\t * Rotate: create a new data key version. New operations use it; old versions stay\n\t * in the record so history decrypts. Needs the server (compare-and-set). Each retry\n\t * adopts the server's current record first and wraps under ITS master key only.\n\t */\n\trotate(channel: KeyServiceChannel): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(async () => {\n\t\t\tfor (let attempt = 0; ; attempt++) {\n\t\t\t\tconst record = this.requireRecord()\n\t\t\t\tconst master = this.requireMaster('rotate the key')\n\t\t\t\tconst dataKey = await generateDataKey()\n\t\t\t\tconst keyVersion = record.currentVersion + 1\n\t\t\t\tconst keyId = newKeyId()\n\t\t\t\tconst next = await sealRecord(\n\t\t\t\t\t{\n\t\t\t\t\t\t...record,\n\t\t\t\t\t\trevision: record.revision + 1,\n\t\t\t\t\t\tcurrentVersion: keyVersion,\n\t\t\t\t\t\tkeys: [\n\t\t\t\t\t\t\t...record.keys,\n\t\t\t\t\t\t\tawait wrapDataKey(dataKey, master.wrapKey, this.name, keyVersion, keyId),\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\tmaster.macKey,\n\t\t\t\t)\n\t\t\t\tconst reply = await channel.put(this.name, next, record.revision)\n\t\t\t\tif (reply.status === 'ok') {\n\t\t\t\t\tthis.dataKeys.set(keyId, await toNonExtractable(dataKey))\n\t\t\t\t\tawait this.commitLocked(next)\n\t\t\t\t\treturn this.status\n\t\t\t\t}\n\t\t\t\tawait this.followConflictLocked(reply, attempt, null, channel)\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Change the passphrase: a NEW master key, sealed under a KEK derived from the new\n\t * passphrase and a new salt, re-wraps every data key version (and the recovery wrap,\n\t * if any). No operation is re-encrypted. The record lists the old master key as\n\t * retired (`retiredMasters`): a device that authenticated this record (or a later\n\t * one) refuses every record authenticated only by the old master key, however it is\n\t * opened. Other devices keep working with their cached data keys (status\n\t * `PASSPHRASE_REQUIRED` while unlocked); they need the new passphrase for a key they\n\t * do not hold and for key management. A device that was served the new record but\n\t * could not read it no longer adopts, without an explicit `unlock()`, a record that\n\t * only its old master key authenticates.\n\t *\n\t * **A passphrase change alone does not contain a leaked passphrase.** The ring is a\n\t * shared secret: a device that still holds only the OLD master key cannot tell a\n\t * record the server forged under it (with a data key of the attacker's) from a\n\t * genuine one, and the server decides which records it forwards. After a suspected\n\t * leak: change the passphrase, `rotate()`, call `enableRecovery()` again (a recovery\n\t * key made before the change can be steered to a ring the old passphrase built) and\n\t * re-unlock EVERY other device with the new passphrase (`lock()`, then\n\t * `unlock(newPassphrase)`, and update any configured `key`; never type the old\n\t * passphrase again: an explicit unlock with it is the user's decision). A device that\n\t * is not re-unlocked, or an old recovery key that is still used, remains exposed.\n\t *\n\t * @param newPassphrase - The new passphrase\n\t * @param channel - The key service\n\t * @param currentPassphrase - Optional: verified against the record first\n\t */\n\tchangePassphrase(\n\t\tnewPassphrase: string,\n\t\tchannel: KeyServiceChannel,\n\t\tcurrentPassphrase?: string,\n\t): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(async () => {\n\t\t\tif (newPassphrase.length === 0) {\n\t\t\t\tthrow new EncryptionKeyError('The new passphrase must not be empty.', {\n\t\t\t\t\tcode: 'EMPTY_PASSPHRASE',\n\t\t\t\t})\n\t\t\t}\n\t\t\tfor (let attempt = 0; ; attempt++) {\n\t\t\t\tconst record = this.requireRecord()\n\t\t\t\tconst master =\n\t\t\t\t\tcurrentPassphrase !== undefined\n\t\t\t\t\t\t? (await this.openWithPassphrase(record, currentPassphrase)).master\n\t\t\t\t\t\t: this.requireMaster('change the passphrase')\n\t\t\t\tconst raw = generateMasterKey()\n\t\t\t\tconst salt = randomBytes(KEK_SALT_BYTES)\n\t\t\t\tlet next: WrappedKeyRecord\n\t\t\t\tlet newMaster: MasterKeys\n\t\t\t\tlet newKek: CryptoKey\n\t\t\t\ttry {\n\t\t\t\t\tnewMaster = await importMasterKey(raw)\n\t\t\t\t\tnewKek = await deriveKeyEncryptionKey(newPassphrase, salt, this.kdfIterations)\n\t\t\t\t\tconst keys: WrappedDataKey[] = []\n\t\t\t\t\tfor (const entry of record.keys) {\n\t\t\t\t\t\tconst dataKey = await unwrapDataKey(entry, master.wrapKey, this.name, true)\n\t\t\t\t\t\tkeys.push(\n\t\t\t\t\t\t\tawait wrapDataKey(\n\t\t\t\t\t\t\t\tdataKey,\n\t\t\t\t\t\t\t\tnewMaster.wrapKey,\n\t\t\t\t\t\t\t\tthis.name,\n\t\t\t\t\t\t\t\tentry.keyVersion,\n\t\t\t\t\t\t\t\tentry.keyId,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tconst { recovery: previousRecovery, ...rest } = record\n\t\t\t\t\tnext = await sealRecord(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t...rest,\n\t\t\t\t\t\t\trevision: record.revision + 1,\n\t\t\t\t\t\t\tkdf: {\n\t\t\t\t\t\t\t\tname: 'PBKDF2',\n\t\t\t\t\t\t\t\thash: 'SHA-256',\n\t\t\t\t\t\t\t\titerations: this.kdfIterations,\n\t\t\t\t\t\t\t\tsalt: toBase64(salt),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tmaster: await wrapMasterKey(raw, newKek, this.name, record.ringId),\n\t\t\t\t\t\t\tkeys,\n\t\t\t\t\t\t\t// Authenticated under the NEW master: a device that accepts this record\n\t\t\t\t\t\t\t// never again accepts one authenticated only by the old one (RT-107).\n\t\t\t\t\t\t\tretiredMasters: [...new Set([...(record.retiredMasters ?? []), master.id])],\n\t\t\t\t\t\t\t// The recovery public key was authenticated with the record; the new\n\t\t\t\t\t\t\t// master key is wrapped to it, so the recovery key keeps working.\n\t\t\t\t\t\t\t...(previousRecovery\n\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\trecovery: await wrapMasterForRecovery(\n\t\t\t\t\t\t\t\t\t\t\traw,\n\t\t\t\t\t\t\t\t\t\t\tpreviousRecovery.publicKey,\n\t\t\t\t\t\t\t\t\t\t\tthis.name,\n\t\t\t\t\t\t\t\t\t\t\trecord.ringId,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnewMaster.macKey,\n\t\t\t\t\t)\n\t\t\t\t} finally {\n\t\t\t\t\traw.fill(0)\n\t\t\t\t}\n\t\t\t\tconst reply = await channel.put(this.name, next, record.revision)\n\t\t\t\tif (reply.status === 'ok') {\n\t\t\t\t\tthis.master = newMaster\n\t\t\t\t\tthis.kek = newKek\n\t\t\t\t\tthis.kekSalt = next.kdf.salt\n\t\t\t\t\tthis.failedSource = null\n\t\t\t\t\tawait this.commitLocked(next)\n\t\t\t\t\treturn this.status\n\t\t\t\t}\n\t\t\t\tawait this.followConflictLocked(reply, attempt, currentPassphrase ?? null, channel)\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Set up (or replace) the recovery key: the ring's master key is also wrapped to a\n\t * new recovery public key, and the private half is returned ONCE. Store it offline;\n\t * it recovers the data after a lost passphrase. Without it, a lost passphrase means\n\t * the encrypted data is unrecoverable. Needs the passphrase key of the CURRENT\n\t * record (the recovery block is authenticated with the record, so only a holder of\n\t * the master key can add or replace it).\n\t *\n\t * The same write adds a new data key version (a rotation) that anchors the recovery\n\t * key to this ring: `recover()` accepts only a ring holding that key, which no older\n\t * passphrase ever opened (RT-107). After a suspected passphrase leak, call this again\n\t * once the passphrase was changed, and destroy the previous recovery key.\n\t *\n\t * @returns The recovery key (`kora-rk3-<key>.<anchor>`)\n\t */\n\tenableRecovery(channel: KeyServiceChannel): Promise<string> {\n\t\treturn this.exclusive(async () => {\n\t\t\tfor (let attempt = 0; ; attempt++) {\n\t\t\t\tconst record = this.requireRecord()\n\t\t\t\tconst master = this.requireMaster('set up a recovery key')\n\t\t\t\tconst kek = this.requireKek(record, 'set up a recovery key')\n\t\t\t\tconst raw = await unwrapMasterKey(record.master, kek, this.name, record.ringId).catch(\n\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t\t\t'This device cannot open the current key record with its passphrase key. Unlock with the current passphrase, then try again.',\n\t\t\t\t\t\t\t{ code: 'PASSPHRASE_REQUIRED', cause: errorMessage(error) },\n\t\t\t\t\t\t)\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\t// The anchor key: a new data key version created in this write under the\n\t\t\t\t// current master key only. No earlier passphrase or master key ever opened it,\n\t\t\t\t// so nobody holding an older (leaked) passphrase can build a ring the recovery\n\t\t\t\t// key accepts (RT-107). It also becomes the key new operations use.\n\t\t\t\tconst anchorKey = await generateDataKey()\n\t\t\t\tconst keyVersion = record.currentVersion + 1\n\t\t\t\tconst keyId = newKeyId()\n\t\t\t\tlet next: WrappedKeyRecord\n\t\t\t\tlet recoveryKey: string\n\t\t\t\ttry {\n\t\t\t\t\tconst { publicKey, d } = await createRecoveryKeyPair()\n\t\t\t\t\trecoveryKey = formatRecoveryKey(\n\t\t\t\t\t\td,\n\t\t\t\t\t\tawait recoveryAnchor(anchorKey, this.name, record.ringId, keyId, publicKey),\n\t\t\t\t\t)\n\t\t\t\t\tnext = await sealRecord(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t...record,\n\t\t\t\t\t\t\trevision: record.revision + 1,\n\t\t\t\t\t\t\tcurrentVersion: keyVersion,\n\t\t\t\t\t\t\tkeys: [\n\t\t\t\t\t\t\t\t...record.keys,\n\t\t\t\t\t\t\t\tawait wrapDataKey(anchorKey, master.wrapKey, this.name, keyVersion, keyId),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\trecovery: await wrapMasterForRecovery(raw, publicKey, this.name, record.ringId),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmaster.macKey,\n\t\t\t\t\t)\n\t\t\t\t} finally {\n\t\t\t\t\traw.fill(0)\n\t\t\t\t}\n\t\t\t\tconst reply = await channel.put(this.name, next, record.revision)\n\t\t\t\tif (reply.status === 'ok') {\n\t\t\t\t\tthis.dataKeys.set(keyId, await toNonExtractable(anchorKey))\n\t\t\t\t\tawait this.commitLocked(next)\n\t\t\t\t\treturn recoveryKey\n\t\t\t\t}\n\t\t\t\tawait this.followConflictLocked(reply, attempt, null, channel)\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Recover after a lost passphrase: open the ring's master key with the recovery key,\n\t * verify the whole record with it, and seal the master key under a new passphrase.\n\t * Unlocks this device. Without a channel (sync not connected) the recovery runs at\n\t * the next handshake. Devices that hold the ring keep working without a prompt (the\n\t * master key is unchanged).\n\t */\n\trecover(\n\t\trecoveryKey: string,\n\t\tnewPassphrase: string,\n\t\tchannel: KeyServiceChannel | null,\n\t): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(async () => {\n\t\t\tif (newPassphrase.length === 0) {\n\t\t\t\tthrow new EncryptionKeyError('The new passphrase must not be empty.', {\n\t\t\t\t\tcode: 'EMPTY_PASSPHRASE',\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (channel === null) {\n\t\t\t\tthis.pendingRecovery = { recoveryKey, newPassphrase }\n\t\t\t\tthis.setStatus(\n\t\t\t\t\tthis.makeStatus(\n\t\t\t\t\t\t'locked',\n\t\t\t\t\t\t'AWAITING_SERVER',\n\t\t\t\t\t\t'Recovery runs when the sync server provides the key record.',\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn this.status\n\t\t\t}\n\t\t\treturn this.recoverLocked(recoveryKey, newPassphrase, channel)\n\t\t})\n\t}\n\n\t/**\n\t * Last resort after the server lost the key record and no device that holds the ring\n\t * will come back (`KEY_RECORD_MISSING`): allow this device to create a new ring.\n\t * Operations encrypted under the lost ring stay unreadable on this device. If a device\n\t * holding the old ring reconnects later, it merges both rings and the history becomes\n\t * readable again. Without a channel (sync is not connected: a missing record ends the\n\t * session) the ring is created at the next handshake.\n\t *\n\t * @throws {EncryptionKeyError} KEY_RECORD_EXISTS when the server has a record (unlock it)\n\t */\n\tstartNewKeyring(channel: KeyServiceChannel | null): Promise<EncryptionStatus> {\n\t\treturn this.exclusive(async () => {\n\t\t\tthis.allowNewRing = true\n\t\t\tif (channel === null) {\n\t\t\t\tthis.retryable = false\n\t\t\t\tthis.setStatus(\n\t\t\t\t\tthis.makeStatus(\n\t\t\t\t\t\t'locked',\n\t\t\t\t\t\t'AWAITING_SERVER',\n\t\t\t\t\t\t'A new keyring is created when the sync server is reached (unless it holds a record).',\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\treturn this.status\n\t\t\t}\n\t\t\tconst reply = await channel.fetch(this.name)\n\t\t\tif (reply.record !== null) {\n\t\t\t\tthis.allowNewRing = false\n\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t'The sync server holds a key record for this keyring: unlock it with the passphrase instead.',\n\t\t\t\t\t{ code: 'KEY_RECORD_EXISTS' },\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst outcome = await this.createOrRestoreLocked(channel, reply.knownKeyIds ?? [])\n\t\t\tif (outcome !== 'ready') throw this.statusError()\n\t\t\treturn this.status\n\t\t})\n\t}\n\n\tprivate async recoverLocked(\n\t\trecoveryKey: string,\n\t\tnewPassphrase: string,\n\t\tchannel: KeyServiceChannel,\n\t): Promise<EncryptionStatus> {\n\t\tthis.pendingRecovery = null\n\t\tfor (let attempt = 0; ; attempt++) {\n\t\t\tconst reply = await channel.fetch(this.name)\n\t\t\tconst record = reply.record\n\t\t\tif (record === null || !validateKeyRecord(record, this.name).ok) {\n\t\t\t\tthrow new EncryptionKeyError('The server has no key record to recover.', {\n\t\t\t\t\tcode: 'KEY_RECORD_MISSING',\n\t\t\t\t})\n\t\t\t}\n\t\t\tconst recovery = record.recovery\n\t\t\tif (!recovery) {\n\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t'This keyring has no recovery key. Without the passphrase its data cannot be decrypted.',\n\t\t\t\t\t{ code: 'NO_RECOVERY_KEY' },\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst pinned = this.record\n\t\t\tif (pinned && pinned.ringId === record.ringId && record.revision < pinned.revision) {\n\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t`The server served revision ${record.revision} of the key record, older than revision ${pinned.revision} this device accepted. Recovery is refused on a rolled-back record.`,\n\t\t\t\t\t{ code: 'KEY_RECORD_ROLLBACK' },\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst raw = await unwrapMasterWithRecovery(recovery, recoveryKey, this.name, record.ringId)\n\t\t\tconst salt = randomBytes(KEK_SALT_BYTES)\n\t\t\tlet master: MasterKeys\n\t\t\tlet newKek: CryptoKey\n\t\t\tlet masterWrap: WrappedKeyRecord['master']\n\t\t\ttry {\n\t\t\t\tmaster = await importMasterKey(raw)\n\t\t\t\tif (!(await verifyRecordMac(record, master.macKey))) {\n\t\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t\t'The key record does not authenticate under the recovered master key: it was modified by someone without the keys. Recovery is refused.',\n\t\t\t\t\t\t{ code: 'KEY_RECORD_INVALID' },\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tif (this.isRetiredMaster(record, master)) {\n\t\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t\t'The key record is authenticated by a master key a passphrase change retired. Recovery is refused.',\n\t\t\t\t\t\t{ code: 'KEY_RECORD_INVALID' },\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\t// The recovery PUBLIC key is no secret: anyone can wrap a master key of their own\n\t\t\t\t// to it and seal a record under that master. Only the user's own ring holds the\n\t\t\t\t// anchor key created with the recovery key, which no older passphrase opened.\n\t\t\t\tif (\n\t\t\t\t\t!(await this.holdsAnchor(\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tmaster,\n\t\t\t\t\t\trecovery.publicKey,\n\t\t\t\t\t\tparseRecoveryKey(recoveryKey).anchor,\n\t\t\t\t\t))\n\t\t\t\t) {\n\t\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t\t'The key record opens with this recovery key but is not the keyring the recovery key was made for: it lacks the data key the recovery key is anchored to (the server substituted another keyring). Recovery is refused.',\n\t\t\t\t\t\t{ code: 'KEY_RECORD_INVALID' },\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tnewKek = await deriveKeyEncryptionKey(newPassphrase, salt, this.kdfIterations)\n\t\t\t\tmasterWrap = await wrapMasterKey(raw, newKek, this.name, record.ringId)\n\t\t\t} finally {\n\t\t\t\traw.fill(0)\n\t\t\t}\n\t\t\tconst opened = await this.openDataKeys(record, master)\n\t\t\tconst absorbed = await this.absorbHeldKeys(record, master)\n\t\t\tconst next = await sealRecord(\n\t\t\t\t{\n\t\t\t\t\t...record,\n\t\t\t\t\trevision: this.nextRevision(record),\n\t\t\t\t\tkdf: {\n\t\t\t\t\t\tname: 'PBKDF2',\n\t\t\t\t\t\thash: 'SHA-256',\n\t\t\t\t\t\titerations: this.kdfIterations,\n\t\t\t\t\t\tsalt: toBase64(salt),\n\t\t\t\t\t},\n\t\t\t\t\tmaster: masterWrap,\n\t\t\t\t\tkeys: [...record.keys, ...absorbed],\n\t\t\t\t\tcurrentVersion: record.currentVersion + absorbed.length,\n\t\t\t\t},\n\t\t\t\tmaster.macKey,\n\t\t\t)\n\t\t\tconst put = await channel.put(this.name, next, record.revision)\n\t\t\tif (put.status === 'ok') {\n\t\t\t\tthis.lockedByApp = false\n\t\t\t\tthis.master = master\n\t\t\t\tthis.kek = newKek\n\t\t\t\tthis.kekSalt = next.kdf.salt\n\t\t\t\tfor (const [keyId, key] of opened) this.dataKeys.set(keyId, key)\n\t\t\t\tawait this.commitLocked(next)\n\t\t\t\treturn this.status\n\t\t\t}\n\t\t\tif (attempt + 1 >= MAX_WRITE_ATTEMPTS) throw conflictError()\n\t\t}\n\t}\n\n\t// --- internals (all run under the mutex) ---\n\n\tprivate async loadLocked(principal: string | null): Promise<EncryptionStatus> {\n\t\tif (this.loadedPrincipal === principal) return this.status\n\t\tthis.dropKeys()\n\t\tthis.record = null\n\t\tthis.retiredMasters.clear()\n\t\tthis.supersededMasters.clear()\n\t\tthis.failedSource = null\n\t\tthis.allowNewRing = false\n\t\t// A passphrase given before the first load (unlock() at start-up) is kept; one given\n\t\t// for another principal is not.\n\t\tif (this.loadedPrincipal !== undefined) this.pendingPassphrase = null\n\t\tthis.loadedPrincipal = principal\n\t\tlet cached: CachedKeyring | null = null\n\t\ttry {\n\t\t\tcached = await this.cache.load(this.cacheId())\n\t\t} catch {\n\t\t\tcached = null\n\t\t}\n\t\tif (cached && validateKeyRecord(cached.record, this.name).ok) {\n\t\t\tconst record = cached.record\n\t\t\tthis.record = record\n\t\t\tfor (const id of [...(cached.retiredMasters ?? []), ...(record.retiredMasters ?? [])]) {\n\t\t\t\tif (isMasterId(id)) this.retiredMasters.add(id)\n\t\t\t}\n\t\t\tfor (const id of cached.supersededMasters ?? []) {\n\t\t\t\tif (isMasterId(id)) this.supersededMasters.add(id)\n\t\t\t}\n\t\t\tconst known = new Set(record.keys.map((key) => key.keyId))\n\t\t\tfor (const entry of cached.keys) {\n\t\t\t\tif (known.has(entry.keyId)) this.dataKeys.set(entry.keyId, entry.key)\n\t\t\t}\n\t\t\tconst current = record.keys.find((key) => key.keyVersion === record.currentVersion)\n\t\t\t// A release-candidate cache keeps master keys without an id: unlock again.\n\t\t\tif (\n\t\t\t\tcached.master &&\n\t\t\t\ttypeof cached.master.id === 'string' &&\n\t\t\t\tcurrent &&\n\t\t\t\tthis.dataKeys.has(current.keyId)\n\t\t\t) {\n\t\t\t\tthis.master = cached.master\n\t\t\t\tthis.kek = cached.kek\n\t\t\t\tthis.kekSalt = cached.kek ? record.kdf.salt : null\n\t\t\t\tthis.rebuildEncryptor()\n\t\t\t\tthis.setStatus(this.makeStatus('unlocked'))\n\t\t\t\treturn this.status\n\t\t\t}\n\t\t\tthis.dropKeys()\n\t\t}\n\t\tthis.setStatus(\n\t\t\tthis.makeStatus(\n\t\t\t\t'locked',\n\t\t\t\tthis.lockedByApp ? 'LOCKED_BY_APP' : 'NO_PASSPHRASE',\n\t\t\t\t'Unlock with the encryption passphrase.',\n\t\t\t),\n\t\t)\n\t\treturn this.status\n\t}\n\n\t/**\n\t * No record on the server. A device that holds a ring re-uploads it unchanged (same\n\t * revision and MAC, so every device's pin still accepts it). A device without one\n\t * creates the first ring, unless the server reports encrypted history: then the\n\t * record was lost, and it waits for a device that holds the ring.\n\t */\n\tprivate async createOrRestoreLocked(\n\t\tchannel: KeyServiceChannel,\n\t\tknownKeyIds: string[],\n\t): Promise<'ready' | 'locked'> {\n\t\tif (this.record !== null) {\n\t\t\tconst restored = this.record\n\t\t\tconst reply = await channel.put(this.name, restored, 0)\n\t\t\treturn this.adoptLocked(reply.status === 'ok' ? restored : reply.record, null, channel)\n\t\t}\n\t\tconst history = knownKeyIds.filter((keyId) => isKeyId(keyId))\n\t\tif (history.length > 0 && !this.allowNewRing) {\n\t\t\tthis.holdFor(\n\t\t\t\t'locked',\n\t\t\t\t'KEY_RECORD_MISSING',\n\t\t\t\t`The sync server has no key record for keyring \"${this.name}\", but it stores operations encrypted with it: the record was lost (for example a server restored without its key table). Waiting for a device that holds the keyring to reconnect and re-upload it. If no such device exists, startNewKeyring() starts a new keyring; the old history then stays unreadable.`,\n\t\t\t)\n\t\t\treturn 'locked'\n\t\t}\n\t\treturn this.createRingLocked(channel)\n\t}\n\n\t/** Create a brand-new ring (the user's first device). */\n\tprivate async createRingLocked(channel: KeyServiceChannel): Promise<'ready' | 'locked'> {\n\t\tconst passphrase = await this.availablePassphrase()\n\t\tif (passphrase === null) {\n\t\t\tthis.lockWith(\n\t\t\t\t'locked',\n\t\t\t\t'NO_PASSPHRASE',\n\t\t\t\t'No key record exists yet; unlock with a passphrase to create it.',\n\t\t\t)\n\t\t\treturn 'locked'\n\t\t}\n\t\tconst ringId = newRingId()\n\t\tconst salt = randomBytes(KEK_SALT_BYTES)\n\t\tconst kek = await deriveKeyEncryptionKey(passphrase, salt, this.kdfIterations)\n\t\tconst raw = generateMasterKey()\n\t\tlet master: MasterKeys\n\t\tlet masterWrap: WrappedKeyRecord['master']\n\t\ttry {\n\t\t\tmaster = await importMasterKey(raw)\n\t\t\tmasterWrap = await wrapMasterKey(raw, kek, this.name, ringId)\n\t\t} finally {\n\t\t\traw.fill(0)\n\t\t}\n\t\tconst dataKey = await generateDataKey()\n\t\tconst keyId = newKeyId()\n\t\tconst record = await sealRecord(\n\t\t\t{\n\t\t\t\tformat: KEY_RECORD_FORMAT,\n\t\t\t\tkeyring: this.name,\n\t\t\t\tringId,\n\t\t\t\trevision: 1,\n\t\t\t\tcurrentVersion: 1,\n\t\t\t\tkdf: {\n\t\t\t\t\tname: 'PBKDF2',\n\t\t\t\t\thash: 'SHA-256',\n\t\t\t\t\titerations: this.kdfIterations,\n\t\t\t\t\tsalt: toBase64(salt),\n\t\t\t\t},\n\t\t\t\tmaster: masterWrap,\n\t\t\t\tkeys: [await wrapDataKey(dataKey, master.wrapKey, this.name, 1, keyId)],\n\t\t\t},\n\t\t\tmaster.macKey,\n\t\t)\n\t\tconst reply = await channel.put(this.name, record, 0)\n\t\tif (reply.status === 'ok') {\n\t\t\t// Only the confirmed record is used: a key that lost the creation race would\n\t\t\t// fork the user's data into a key no other device has.\n\t\t\tthis.allowNewRing = false\n\t\t\tthis.master = master\n\t\t\tthis.kek = kek\n\t\t\tthis.kekSalt = record.kdf.salt\n\t\t\tthis.dataKeys.set(keyId, await toNonExtractable(dataKey))\n\t\t\tthis.pendingPassphrase = null\n\t\t\tawait this.commitLocked(record)\n\t\t\treturn 'ready'\n\t\t}\n\t\tif (reply.record === null) {\n\t\t\tthrow new EncryptionKeyError('The key service refused the first record without a reason.', {\n\t\t\t\tcode: 'KEY_RECORD_CONFLICT',\n\t\t\t})\n\t\t}\n\t\t// Another device created the record first: open that one.\n\t\treturn this.adoptLocked(reply.record, passphrase, channel)\n\t}\n\n\t/**\n\t * Accept a server record, after authenticating it.\n\t *\n\t * - Lower revision of the pinned ring: refused (rollback); re-upload the pin.\n\t * - Authenticated by the held master key, or by the passphrase (explicit, else the\n\t * configured one): forward when it keeps every key this device holds, else merge.\n\t * - Not authenticated: nothing is adopted. A device that holds every key of a newer\n\t * revision of its ring keeps working (the passphrase changed elsewhere); otherwise\n\t * it needs the passphrase.\n\t */\n\tprivate async adoptLocked(\n\t\tincoming: WrappedKeyRecord | null,\n\t\tpassphrase: string | null,\n\t\tchannel: KeyServiceChannel | null,\n\t\tdepth = 0,\n\t): Promise<'ready' | 'locked'> {\n\t\tif (incoming === null) {\n\t\t\treturn this.refuseRecord('KEY_RECORD_INVALID', 'The key service returned no record.')\n\t\t}\n\t\tconst validation = validateKeyRecord(incoming, this.name)\n\t\tif (!validation.ok) {\n\t\t\treturn this.refuseRecord(\n\t\t\t\t'KEY_RECORD_INVALID',\n\t\t\t\t`The server's key record is malformed (${validation.reason}).`,\n\t\t\t)\n\t\t}\n\t\tif (incoming.kdf.iterations < this.kdfIterations) {\n\t\t\t// A server must not be able to make devices derive a weaker KEK.\n\t\t\treturn this.refuseRecord(\n\t\t\t\t'KEY_RECORD_INVALID',\n\t\t\t\t`The server's key record uses ${incoming.kdf.iterations} PBKDF2 iterations, below this app's minimum of ${this.kdfIterations}.`,\n\t\t\t)\n\t\t}\n\t\tconst pinned = this.record\n\t\tconst explicit = passphrase !== null\n\t\tif (pinned !== null && incoming.ringId === pinned.ringId) {\n\t\t\tif (incoming.revision < pinned.revision) {\n\t\t\t\treturn this.rollbackLocked(incoming, passphrase, channel, depth)\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!explicit &&\n\t\t\t\tincoming.revision === pinned.revision &&\n\t\t\t\tcanonicalJson(incoming) === canonicalJson(pinned) &&\n\t\t\t\tthis.master !== null &&\n\t\t\t\tthis.encryptor !== null\n\t\t\t) {\n\t\t\t\tif (this.status.state !== 'unlocked' || this.status.code !== undefined) {\n\t\t\t\t\tthis.retryable = false\n\t\t\t\t\tthis.setStatus(this.makeStatus('unlocked'))\n\t\t\t\t}\n\t\t\t\treturn 'ready'\n\t\t\t}\n\t\t}\n\n\t\tlet opened = await this.authenticateHeld(incoming)\n\t\tlet sourceFailed = false\n\t\tif (opened === null || explicit) {\n\t\t\tconst source = passphrase ?? (await this.sourcePassphraseFor(incoming))\n\t\t\tif (source !== null) {\n\t\t\t\ttry {\n\t\t\t\t\topened = await this.openWithPassphrase(incoming, source)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!(error instanceof EncryptionKeyError)) throw error\n\t\t\t\t\tif (error.keyCode === 'KEY_RECORD_INVALID') {\n\t\t\t\t\t\treturn this.refuseRecord('KEY_RECORD_INVALID', error.message)\n\t\t\t\t\t}\n\t\t\t\t\tif (explicit) {\n\t\t\t\t\t\tthis.noteFailedUnlock()\n\t\t\t\t\t\t// A mistyped unlock() on an unlocked device changes nothing.\n\t\t\t\t\t\tif (this.encryptor !== null) throw error\n\t\t\t\t\t\tthis.lockWith('error', 'WRONG_PASSPHRASE', error.message)\n\t\t\t\t\t\treturn 'locked'\n\t\t\t\t\t}\n\t\t\t\t\tthis.failedSource = { salt: incoming.kdf.salt, passphrase: source }\n\t\t\t\t\tsourceFailed = true\n\t\t\t\t}\n\t\t\t} else if (this.failedSource?.salt === incoming.kdf.salt) {\n\t\t\t\tsourceFailed = true\n\t\t\t}\n\t\t}\n\t\tif (opened === null) return this.unverifiableLocked(incoming, sourceFailed)\n\t\tif (this.isRetiredMaster(incoming, opened.master)) return this.refuseRetired()\n\t\tif (this.isSupersededMaster(opened.master, explicit)) return this.refuseSuperseded()\n\t\treturn this.acceptLocked(incoming, opened, passphrase, channel, depth)\n\t}\n\n\t/** An authenticated record: move forward to it, or merge it with the held ring. */\n\tprivate async acceptLocked(\n\t\tincoming: WrappedKeyRecord,\n\t\topened: OpenedRing,\n\t\tpassphrase: string | null,\n\t\tchannel: KeyServiceChannel | null,\n\t\tdepth: number,\n\t): Promise<'ready' | 'locked'> {\n\t\tconst incomingIds = new Set(incoming.keys.map((key) => key.keyId))\n\t\tconst uncovered = [...this.dataKeys.keys()].some((keyId) => !incomingIds.has(keyId))\n\t\tif (uncovered) {\n\t\t\t// Another ring (the server lost the record and a device created a new one), or a\n\t\t\t// diverged revision: it lacks keys this device holds. Merge, never drop them.\n\t\t\tif (channel === null) {\n\t\t\t\tthis.holdFor(\n\t\t\t\t\t'error',\n\t\t\t\t\t'KEY_RING_FORK',\n\t\t\t\t\t'The server holds a key ring that lacks keys this device holds. Both rings are merged at the next sync session.',\n\t\t\t\t)\n\t\t\t\treturn 'locked'\n\t\t\t}\n\t\t\treturn this.mergeLocked(incoming, opened, passphrase, channel, depth)\n\t\t}\n\t\tlet keys: Map<string, CryptoKey>\n\t\ttry {\n\t\t\tkeys = await this.openDataKeys(incoming, opened.master)\n\t\t} catch (error) {\n\t\t\treturn this.refuseRecord(\n\t\t\t\t'KEY_RECORD_INVALID',\n\t\t\t\t`An authenticated key record holds a data-key wrap its master key does not open (${errorMessage(error)}).`,\n\t\t\t)\n\t\t}\n\t\tfor (const [keyId, key] of keys) this.dataKeys.set(keyId, key)\n\t\tthis.master = opened.master\n\t\tif (opened.kek !== null) {\n\t\t\tthis.kek = opened.kek\n\t\t\tthis.kekSalt = incoming.kdf.salt\n\t\t} else if (this.kekSalt !== incoming.kdf.salt) {\n\t\t\tthis.kek = null\n\t\t\tthis.kekSalt = null\n\t\t}\n\t\tif (passphrase !== null) {\n\t\t\tthis.failedUnlocks = 0\n\t\t\tthis.nextUnlockAt = 0\n\t\t}\n\t\tthis.pendingPassphrase = null\n\t\tawait this.commitLocked(incoming)\n\t\treturn 'ready'\n\t}\n\n\t/** A lower revision of the pinned ring: refuse it, keep the keys, re-upload the pin. */\n\tprivate async rollbackLocked(\n\t\tincoming: WrappedKeyRecord,\n\t\tpassphrase: string | null,\n\t\tchannel: KeyServiceChannel | null,\n\t\tdepth: number,\n\t): Promise<'ready' | 'locked'> {\n\t\tconst pinned = this.record as WrappedKeyRecord\n\t\tthis.holdFor(\n\t\t\t'error',\n\t\t\t'KEY_RECORD_ROLLBACK',\n\t\t\t`The server served revision ${incoming.revision} of the key record, older than revision ${pinned.revision} this device accepted (a rollback, or a server restored from an old backup). It is refused; this device keeps its keys${channel ? ' and re-uploads its newer record' : ' and re-uploads its newer record at the next sync session'}.`,\n\t\t)\n\t\tif (channel === null || depth >= MAX_WRITE_ATTEMPTS) return 'locked'\n\t\tconst pinnedVersions = new Map(pinned.keys.map((key) => [key.keyVersion, key.keyId]))\n\t\tconst subset = incoming.keys.every((key) => pinnedVersions.get(key.keyVersion) === key.keyId)\n\t\tif (subset) {\n\t\t\t// The pin keeps everything the older revision has: put it back as it is.\n\t\t\tconst reply = await channel.put(this.name, pinned, incoming.revision)\n\t\t\treturn this.adoptLocked(\n\t\t\t\treply.status === 'ok' ? pinned : reply.record,\n\t\t\t\tpassphrase,\n\t\t\t\tchannel,\n\t\t\t\tdepth + 1,\n\t\t\t)\n\t\t}\n\t\t// The older revision has keys this device lacks (the ring diverged after a\n\t\t// restore): merge both into a revision above either, once it is authenticated.\n\t\tlet opened = await this.authenticateHeld(incoming)\n\t\tif (opened === null) {\n\t\t\tconst source = passphrase ?? (await this.sourcePassphraseFor(incoming))\n\t\t\tif (source !== null) {\n\t\t\t\topened = await this.openWithPassphrase(incoming, source).catch(() => null)\n\t\t\t}\n\t\t}\n\t\tif (opened === null) return 'locked'\n\t\tif (this.isRetiredMaster(incoming, opened.master)) return this.refuseRetired()\n\t\tif (this.isSupersededMaster(opened.master, passphrase !== null)) {\n\t\t\treturn this.refuseSuperseded()\n\t\t}\n\t\treturn this.mergeLocked(incoming, opened, passphrase, channel, depth)\n\t}\n\n\t/**\n\t * A record authenticated only by a master key a passphrase change retired: whoever\n\t * wrote it knew an OLD passphrase (or master key), not the current one (RT-107).\n\t */\n\tprivate refuseRetired(): 'locked' {\n\t\treturn this.refuseRecord(\n\t\t\t'KEY_RECORD_INVALID',\n\t\t\t\"The server's key record is authenticated only by a master key that a passphrase change retired: it was written with an old passphrase. It is refused; this device keeps its keys.\",\n\t\t)\n\t}\n\n\t/**\n\t * A record authenticated only by a master key this device held when the server served\n\t * a newer revision of the ring that the key does not authenticate (RT-107).\n\t */\n\tprivate refuseSuperseded(): 'locked' {\n\t\treturn this.refuseRecord(\n\t\t\t'KEY_RECORD_INVALID',\n\t\t\t\"The server's key record is authenticated only by this device's previous master key, after the server served a newer revision that key does not open (the passphrase changed on another device). It is not adopted automatically: unlock with the current passphrase.\",\n\t\t)\n\t}\n\n\t/**\n\t * Whether `master` was superseded on this device. An explicit `unlock(passphrase)` is\n\t * the user's decision and clears the mark (a server that served a junk revision can\n\t * delay, never lock out, a device).\n\t */\n\tprivate isSupersededMaster(master: MasterKeys, explicit: boolean): boolean {\n\t\tif (!this.supersededMasters.has(master.id)) return false\n\t\tif (!explicit) return true\n\t\tthis.supersededMasters.delete(master.id)\n\t\treturn false\n\t}\n\n\t/** A record this device cannot authenticate: adopt nothing. */\n\tprivate async unverifiableLocked(\n\t\tincoming: WrappedKeyRecord,\n\t\tsourceFailed: boolean,\n\t): Promise<'ready' | 'locked'> {\n\t\tconst pinned = this.record\n\t\tif (\n\t\t\tthis.master !== null &&\n\t\t\tpinned !== null &&\n\t\t\tincoming.ringId === pinned.ringId &&\n\t\t\tincoming.revision > pinned.revision &&\n\t\t\t!this.supersededMasters.has(this.master.id)\n\t\t) {\n\t\t\t// A newer revision of this ring that the held master key does not authenticate:\n\t\t\t// a passphrase change elsewhere (or a forgery). After a genuine change, no later\n\t\t\t// genuine record is authenticated by the held key, so a record that only it\n\t\t\t// authenticates is never adopted automatically again, even after a restart.\n\t\t\tthis.supersededMasters.add(this.master.id)\n\t\t\tawait this.saveCacheLocked(pinned)\n\t\t}\n\t\tif (\n\t\t\tpinned !== null &&\n\t\t\tthis.encryptor !== null &&\n\t\t\tincoming.ringId === pinned.ringId &&\n\t\t\tincoming.revision === pinned.revision\n\t\t) {\n\t\t\t// Another body under the revision this device accepted, and it does not\n\t\t\t// authenticate: tampered. The held keys are unaffected.\n\t\t\treturn this.refuseRecord(\n\t\t\t\t'KEY_RECORD_INVALID',\n\t\t\t\t`The server's key record differs from revision ${pinned.revision} this device accepted and does not authenticate. It is refused.`,\n\t\t\t)\n\t\t}\n\t\tif (\n\t\t\tpinned !== null &&\n\t\t\tthis.encryptor !== null &&\n\t\t\tincoming.ringId === pinned.ringId &&\n\t\t\tincoming.revision > pinned.revision &&\n\t\t\tincoming.keys.every((key) => this.dataKeys.has(key.keyId))\n\t\t) {\n\t\t\t// The passphrase changed on another device (new master key), or the server forged\n\t\t\t// a record: either way nothing in it is used. The held keys are the ring's keys.\n\t\t\tthis.retryable = false\n\t\t\tthis.setStatus(\n\t\t\t\tthis.makeStatus(\n\t\t\t\t\t'unlocked',\n\t\t\t\t\t'PASSPHRASE_REQUIRED',\n\t\t\t\t\t'The key record changed on another device (passphrase change). This device keeps using the keys it holds; enter the current passphrase to rotate keys, change the passphrase or set up recovery here.',\n\t\t\t\t),\n\t\t\t)\n\t\t\treturn 'ready'\n\t\t}\n\t\tif (sourceFailed) {\n\t\t\tthis.lockWith(\n\t\t\t\t'error',\n\t\t\t\t'WRONG_PASSPHRASE',\n\t\t\t\t`The configured passphrase does not open keyring \"${this.name}\". Check the passphrase.`,\n\t\t\t)\n\t\t\treturn 'locked'\n\t\t}\n\t\tif (pinned !== null && incoming.ringId !== pinned.ringId) {\n\t\t\tthis.lockWith(\n\t\t\t\t'locked',\n\t\t\t\t'KEY_RING_FORK',\n\t\t\t\t'The server holds another key ring for this keyring (it lost the record and another device created a new one). Enter the passphrase: both rings are merged and every operation stays readable.',\n\t\t\t)\n\t\t\treturn 'locked'\n\t\t}\n\t\tthis.lockWith(\n\t\t\t'locked',\n\t\t\tpinned === null ? 'NO_PASSPHRASE' : 'PASSPHRASE_REQUIRED',\n\t\t\t'Enter the encryption passphrase to open the key record.',\n\t\t)\n\t\treturn 'locked'\n\t}\n\n\t/**\n\t * Merge the held ring into `base` (the server's current record, which survives: the\n\t * server only accepts append-only successors of what it stores). Keys this device\n\t * holds that `base` lacks are appended as new versions; operations keep naming them\n\t * by key id. The result has a higher revision than both and is written with\n\t * compare-and-set; a lost race adopts (or merges) the winner.\n\t */\n\tprivate async mergeLocked(\n\t\tbase: WrappedKeyRecord,\n\t\topened: OpenedRing,\n\t\tpassphrase: string | null,\n\t\tchannel: KeyServiceChannel,\n\t\tdepth: number,\n\t): Promise<'ready' | 'locked'> {\n\t\tif (depth >= MAX_WRITE_ATTEMPTS) throw conflictError()\n\t\tconst absorbed = await this.absorbHeldKeys(base, opened.master)\n\t\tconst keys = await this.openDataKeys(base, opened.master)\n\t\tconst merged = await sealRecord(\n\t\t\t{\n\t\t\t\t...base,\n\t\t\t\trevision: this.nextRevision(base),\n\t\t\t\tkeys: [...base.keys, ...absorbed],\n\t\t\t\tcurrentVersion: base.currentVersion + absorbed.length,\n\t\t\t},\n\t\t\topened.master.macKey,\n\t\t)\n\t\tconst reply = await channel.put(this.name, merged, base.revision)\n\t\tif (reply.status !== 'ok') return this.adoptLocked(reply.record, passphrase, channel, depth + 1)\n\t\tfor (const [keyId, key] of keys) this.dataKeys.set(keyId, key)\n\t\tthis.master = opened.master\n\t\tif (opened.kek !== null) {\n\t\t\tthis.kek = opened.kek\n\t\t\tthis.kekSalt = merged.kdf.salt\n\t\t} else if (this.kekSalt !== merged.kdf.salt) {\n\t\t\tthis.kek = null\n\t\t\tthis.kekSalt = null\n\t\t}\n\t\tthis.pendingPassphrase = null\n\t\tawait this.commitLocked(merged)\n\t\treturn 'ready'\n\t}\n\n\t/**\n\t * Re-wrap the held data keys `base` lacks under `target` (appended after base's\n\t * highest version). They are extracted from the pinned record with the held master.\n\t */\n\tprivate async absorbHeldKeys(\n\t\tbase: WrappedKeyRecord,\n\t\ttarget: MasterKeys,\n\t): Promise<WrappedDataKey[]> {\n\t\tconst baseIds = new Set(base.keys.map((key) => key.keyId))\n\t\tconst pinned = this.record\n\t\tconst missing = (pinned?.keys ?? []).filter(\n\t\t\t(key) => this.dataKeys.has(key.keyId) && !baseIds.has(key.keyId),\n\t\t)\n\t\tif (missing.length === 0) return []\n\t\tconst held = this.master\n\t\tif (pinned === null || held === null) {\n\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t'This device holds keys of another ring but not its master key; unlock it with the passphrase first.',\n\t\t\t\t{ code: 'KEY_RING_FORK' },\n\t\t\t)\n\t\t}\n\t\tconst out: WrappedDataKey[] = []\n\t\tlet version = base.currentVersion\n\t\tfor (const entry of [...missing].sort((a, b) => a.keyVersion - b.keyVersion)) {\n\t\t\tconst dataKey = await unwrapDataKey(entry, held.wrapKey, this.name, true)\n\t\t\tversion++\n\t\t\tout.push(await wrapDataKey(dataKey, target.wrapKey, this.name, version, entry.keyId))\n\t\t}\n\t\treturn out\n\t}\n\n\t/** Open every data key of `record` this device does not hold yet. */\n\tprivate async openDataKeys(\n\t\trecord: WrappedKeyRecord,\n\t\tmaster: MasterKeys,\n\t): Promise<Map<string, CryptoKey>> {\n\t\tconst opened = new Map<string, CryptoKey>()\n\t\tfor (const entry of record.keys) {\n\t\t\tif (this.dataKeys.has(entry.keyId)) continue\n\t\t\topened.set(entry.keyId, await unwrapDataKey(entry, master.wrapKey, this.name))\n\t\t}\n\t\treturn opened\n\t}\n\n\t/**\n\t * Whether `record` holds the anchor key of a recovery key: data keys are never removed\n\t * or re-keyed (a passphrase change, recovery or merge re-wraps them under the same key\n\t * id), so the anchor stays valid for the ring.\n\t */\n\tprivate async holdsAnchor(\n\t\trecord: WrappedKeyRecord,\n\t\tmaster: MasterKeys,\n\t\tpublicKey: { x: string; y: string },\n\t\tanchor: Uint8Array,\n\t): Promise<boolean> {\n\t\tfor (const entry of record.keys) {\n\t\t\tlet dataKey: CryptoKey\n\t\t\ttry {\n\t\t\t\tdataKey = await unwrapDataKey(entry, master.wrapKey, this.name, true)\n\t\t\t} catch {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst tag = await recoveryAnchor(dataKey, this.name, record.ringId, entry.keyId, publicKey)\n\t\t\tif (bytesEqual(tag, anchor)) return true\n\t\t}\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether `master` (which authenticated `record`) is a master key a passphrase change\n\t * retired: listed in `record` itself or in any record this device authenticated.\n\t */\n\tprivate isRetiredMaster(record: WrappedKeyRecord, master: MasterKeys): boolean {\n\t\treturn this.retiredMasters.has(master.id) || (record.retiredMasters ?? []).includes(master.id)\n\t}\n\n\t/** The next revision after `base`, above the pinned one when it is the same ring. */\n\tprivate nextRevision(base: WrappedKeyRecord): number {\n\t\tconst pinned = this.record\n\t\tconst floor = pinned !== null && pinned.ringId === base.ringId ? pinned.revision : 0\n\t\treturn Math.max(base.revision, floor) + 1\n\t}\n\n\t/** Authenticate a record with the held master key (no passphrase needed). */\n\tprivate async authenticateHeld(record: WrappedKeyRecord): Promise<OpenedRing | null> {\n\t\tif (this.master === null) return null\n\t\tif (!(await verifyRecordMac(record, this.master.macKey))) return null\n\t\treturn {\n\t\t\tmaster: this.master,\n\t\t\tkek: this.kek !== null && this.kekSalt === record.kdf.salt ? this.kek : null,\n\t\t}\n\t}\n\n\t/**\n\t * Open and authenticate a record with a passphrase.\n\t *\n\t * @throws {EncryptionKeyError} WRONG_PASSPHRASE, or KEY_RECORD_INVALID when the master\n\t * key opens but the record does not authenticate under it (tampered)\n\t */\n\tprivate async openWithPassphrase(\n\t\trecord: WrappedKeyRecord,\n\t\tpassphrase: string,\n\t): Promise<OpenedRing> {\n\t\tconst kek = await deriveKeyEncryptionKey(\n\t\t\tpassphrase,\n\t\t\tfromBase64(record.kdf.salt),\n\t\t\trecord.kdf.iterations,\n\t\t)\n\t\tlet raw: Uint8Array\n\t\ttry {\n\t\t\traw = await unwrapMasterKey(record.master, kek, this.name, record.ringId)\n\t\t} catch (error) {\n\t\t\tthrow new EncryptionKeyError(errorMessage(error), { code: 'WRONG_PASSPHRASE' })\n\t\t}\n\t\tlet master: MasterKeys\n\t\ttry {\n\t\t\tmaster = await importMasterKey(raw)\n\t\t} finally {\n\t\t\traw.fill(0)\n\t\t}\n\t\tif (!(await verifyRecordMac(record, master.macKey))) {\n\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\"The server's key record does not authenticate under its master key: it was modified by someone without the keys (wraps swapped, a recovery key or version injected). It is refused.\",\n\t\t\t\t{ code: 'KEY_RECORD_INVALID' },\n\t\t\t)\n\t\t}\n\t\treturn { master, kek }\n\t}\n\n\t/** The configured or pending passphrase, unless it already failed for this salt. */\n\tprivate async sourcePassphraseFor(record: WrappedKeyRecord): Promise<string | null> {\n\t\tconst source = await this.availablePassphrase()\n\t\tif (source === null) return null\n\t\tconst failed = this.failedSource\n\t\tif (failed !== null && failed.salt === record.kdf.salt && failed.passphrase === source) {\n\t\t\treturn null\n\t\t}\n\t\treturn source\n\t}\n\n\tprivate async followConflictLocked(\n\t\treply: KeyServiceReply,\n\t\tattempt: number,\n\t\tpassphrase: string | null,\n\t\tchannel: KeyServiceChannel,\n\t): Promise<void> {\n\t\tif (attempt + 1 >= MAX_WRITE_ATTEMPTS) throw conflictError()\n\t\tconst current = reply.record\n\t\tif (current === null) {\n\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t'The sync server lost the key record during the write. Reconnect: a device holding it re-uploads it.',\n\t\t\t\t{ code: 'KEY_RECORD_MISSING' },\n\t\t\t)\n\t\t}\n\t\tconst outcome = await this.adoptLocked(current, passphrase, channel)\n\t\tif (outcome !== 'ready') throw this.statusError()\n\t\tconst pinned = this.record\n\t\tif (\n\t\t\tpinned === null ||\n\t\t\t(pinned.ringId === current.ringId && pinned.revision < current.revision)\n\t\t) {\n\t\t\t// Nothing in the newer record could be authenticated: never write over it with\n\t\t\t// keys of an older master key.\n\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t'The key record changed on another device (its passphrase changed) and this device cannot authenticate the new one. Unlock with the current passphrase, then try again.',\n\t\t\t\t{ code: 'PASSPHRASE_REQUIRED' },\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate async commitLocked(record: WrappedKeyRecord): Promise<void> {\n\t\tconst known = new Set(record.keys.map((key) => key.keyId))\n\t\tfor (const keyId of this.dataKeys.keys()) {\n\t\t\tif (!known.has(keyId)) {\n\t\t\t\t// Every path that adopts a record first absorbs the held keys it lacks.\n\t\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t\t`Internal error: key ${keyId} would be dropped by revision ${record.revision}.`,\n\t\t\t\t\t{ code: 'KEY_RECORD_INVARIANT' },\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tthis.record = record\n\t\tfor (const id of record.retiredMasters ?? []) this.retiredMasters.add(id)\n\t\tthis.retryable = false\n\t\tthis.rebuildEncryptor()\n\t\tthis.setStatus(this.makeStatus('unlocked'))\n\t\tawait this.saveCacheLocked(record)\n\t}\n\n\t/** Persist the pinned record, the held keys and the master marks (RT-107). */\n\tprivate async saveCacheLocked(record: WrappedKeyRecord): Promise<void> {\n\t\ttry {\n\t\t\tawait this.cache.save(this.cacheId(), {\n\t\t\t\trecord,\n\t\t\t\tretiredMasters: [...this.retiredMasters],\n\t\t\t\tsupersededMasters: [...this.supersededMasters],\n\t\t\t\tkek: this.kek,\n\t\t\t\tmaster: this.master,\n\t\t\t\tkeys: record.keys.flatMap((entry) => {\n\t\t\t\t\tconst key = this.dataKeys.get(entry.keyId)\n\t\t\t\t\treturn key ? [{ keyVersion: entry.keyVersion, keyId: entry.keyId, key }] : []\n\t\t\t\t}),\n\t\t\t})\n\t\t} catch {\n\t\t\t// The cache is a convenience: the keyring works for this session without it.\n\t\t}\n\t}\n\n\tprivate rebuildEncryptor(): void {\n\t\tconst record = this.record\n\t\tconst versions: VersionedKey[] = []\n\t\tfor (const entry of record?.keys ?? []) {\n\t\t\tconst key = this.dataKeys.get(entry.keyId)\n\t\t\tif (key) versions.push({ version: entry.keyVersion, key, keyId: entry.keyId })\n\t\t}\n\t\tif (versions.length === 0 || record === null || this.master === null) {\n\t\t\tthis.encryptor = null\n\t\t\treturn\n\t\t}\n\t\tthis.encryptor = SyncEncryptor.fromKeys(versions, this.encryptorOptions)\n\t}\n\n\tprivate async availablePassphrase(): Promise<string | null> {\n\t\tif (this.pendingPassphrase !== null) return this.pendingPassphrase\n\t\tif (this.lockedByApp || this.passphraseSource === null) return null\n\t\tconst value =\n\t\t\ttypeof this.passphraseSource === 'function'\n\t\t\t\t? await this.passphraseSource()\n\t\t\t\t: this.passphraseSource\n\t\treturn value.length > 0 ? value : null\n\t}\n\n\tprivate requireMaster(action: string): MasterKeys {\n\t\tif (this.master === null || this.encryptor === null) {\n\t\t\tthrow new EncryptionKeyError(`Unlock the keyring with the passphrase to ${action}.`, {\n\t\t\t\tcode: 'PASSPHRASE_REQUIRED',\n\t\t\t})\n\t\t}\n\t\treturn this.master\n\t}\n\n\t/** The KEK of `record`'s own salt (never one derived for another record's salt). */\n\tprivate requireKek(record: WrappedKeyRecord, action: string): CryptoKey {\n\t\tif (this.kek === null || this.kekSalt !== record.kdf.salt || this.encryptor === null) {\n\t\t\tthrow new EncryptionKeyError(\n\t\t\t\t`Unlock the keyring with the passphrase to ${action}: this device holds the data keys but not the key of the current passphrase (it changed on another device, or the keyring is locked).`,\n\t\t\t\t{ code: 'PASSPHRASE_REQUIRED' },\n\t\t\t)\n\t\t}\n\t\treturn this.kek\n\t}\n\n\tprivate requireRecord(): WrappedKeyRecord {\n\t\tif (this.record === null) {\n\t\t\tthrow new EncryptionKeyError('The keyring has no record yet.', { code: 'KEY_RECORD_MISSING' })\n\t\t}\n\t\treturn this.record\n\t}\n\n\tprivate noteFailedUnlock(): void {\n\t\tthis.failedUnlocks++\n\t\tif (this.failedUnlocks > FREE_UNLOCK_ATTEMPTS) {\n\t\t\tconst delay = Math.min(\n\t\t\t\tMAX_UNLOCK_BACKOFF_MS,\n\t\t\t\t1000 * 2 ** (this.failedUnlocks - FREE_UNLOCK_ATTEMPTS - 1),\n\t\t\t)\n\t\t\tthis.nextUnlockAt = Date.now() + delay\n\t\t}\n\t}\n\n\tprivate dropKeys(): void {\n\t\tthis.dataKeys.clear()\n\t\tthis.master = null\n\t\tthis.kek = null\n\t\tthis.kekSalt = null\n\t\tthis.encryptor = null\n\t}\n\n\t/** A record refused for what the server sent: the held keys stay, nothing is adopted. */\n\tprivate refuseRecord(code: EncryptionStatusCode, message: string): 'locked' {\n\t\tthis.holdFor('error', code, message)\n\t\treturn 'locked'\n\t}\n\n\t/** A server-side condition: keys kept, a later session (or a push) can clear it. */\n\tprivate holdFor(state: 'locked' | 'error', code: EncryptionStatusCode, message: string): void {\n\t\tthis.retryable = true\n\t\tthis.setStatus(this.makeStatus(state, code, message))\n\t}\n\n\tprivate lockWith(state: 'locked' | 'error', code: EncryptionStatusCode, message: string): void {\n\t\tthis.retryable = false\n\t\tthis.dropKeysIfUnusable(code)\n\t\tthis.setStatus(this.makeStatus(state, code, message))\n\t}\n\n\t/** Locks that need the user stop encrypting; records refused keep held keys. */\n\tprivate dropKeysIfUnusable(code: EncryptionStatusCode): void {\n\t\tif (\n\t\t\tcode === 'LOCKED_BY_APP' ||\n\t\t\tcode === 'KEY_SERVICE_FORBIDDEN' ||\n\t\t\tcode === 'KEY_SERVICE_UNSUPPORTED' ||\n\t\t\tcode === 'PASSPHRASE_REQUIRED' ||\n\t\t\tcode === 'WRONG_PASSPHRASE' ||\n\t\t\tcode === 'KEY_RING_FORK'\n\t\t) {\n\t\t\tthis.encryptor = null\n\t\t}\n\t}\n\n\tprivate statusError(): EncryptionKeyError {\n\t\treturn new EncryptionKeyError(this.status.message ?? 'The keyring is locked.', {\n\t\t\tcode: this.status.code ?? 'LOCKED',\n\t\t})\n\t}\n\n\tprivate cacheId(): string {\n\t\treturn `${this.loadedPrincipal ?? ''}\\u0000${this.name}`\n\t}\n\n\tprivate makeStatus(\n\t\tstate: EncryptionLockState,\n\t\tcode?: EncryptionStatusCode,\n\t\tmessage?: string,\n\t): EncryptionStatus {\n\t\tconst unlocked = state === 'unlocked' && this.encryptor !== null\n\t\tconst keyVersion = unlocked ? (this.encryptor?.getCurrentKeyVersion() ?? null) : null\n\t\tconst record = this.record\n\t\tconst held = (record?.keys ?? []).filter((key) => this.dataKeys.has(key.keyId))\n\t\treturn {\n\t\t\tstate,\n\t\t\tkeyring: this.name,\n\t\t\tkeyVersion,\n\t\t\tkeyId:\n\t\t\t\tkeyVersion !== null\n\t\t\t\t\t? (held.find((key) => key.keyVersion === keyVersion)?.keyId ?? null)\n\t\t\t\t\t: null,\n\t\t\tavailableVersions: unlocked ? held.map((key) => key.keyVersion).sort((a, b) => a - b) : [],\n\t\t\tcache: this.cache.kind,\n\t\t\t...(code ? { code } : {}),\n\t\t\t...(message ? { message } : {}),\n\t\t}\n\t}\n\n\tprivate setStatus(status: EncryptionStatus): void {\n\t\tconst previous = this.status\n\t\tthis.status = status\n\t\tif (\n\t\t\tprevious?.state === status.state &&\n\t\t\tprevious.code === status.code &&\n\t\t\tprevious.keyVersion === status.keyVersion &&\n\t\t\tprevious.keyId === status.keyId &&\n\t\t\tprevious.availableVersions?.length === status.availableVersions.length\n\t\t) {\n\t\t\treturn\n\t\t}\n\t\tfor (const listener of this.listeners) {\n\t\t\ttry {\n\t\t\t\tlistener(status)\n\t\t\t} catch {\n\t\t\t\t// A listener error must not break the keyring.\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate exclusive<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst run = this.mutex.then(fn, fn)\n\t\tthis.mutex = run.catch(() => undefined)\n\t\treturn run\n\t}\n}\n\nfunction conflictError(): EncryptionKeyError {\n\treturn new EncryptionKeyError(\n\t\t'The key record kept changing on the server while this device updated it. Try again.',\n\t\t{ code: 'KEY_RECORD_CONFLICT' },\n\t)\n}\n\nfunction errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error)\n}\n","import type { WrappedKeyRecord } from './key-record'\nimport type { MasterKeys } from './keyring-crypto'\n\n/**\n * What a device keeps of an unlocked keyring: the last key record it authenticated and\n * accepted (also the pin: its ring id and revision are the floor for every later\n * record), the passphrase KEK when known, the keys derived from the ring's master key\n * (to authenticate later records and wrap new data keys), and every data key it\n * opened. Keys are NON-EXTRACTABLE CryptoKeys: script on the page can use them while it\n * runs, but cannot read their bytes. A locked keyring keeps the record (the pin) and\n * nothing else.\n */\nexport interface CachedKeyring {\n\trecord: WrappedKeyRecord\n\tkek: CryptoKey | null\n\t/** Derived from the ring's master key; null while locked. */\n\tmaster?: MasterKeys | null\n\tkeys: Array<{ keyVersion: number; keyId: string; key: CryptoKey }>\n\t/**\n\t * Ids of master keys a passphrase change retired, from every record this device\n\t * authenticated (RT-107). Kept when locked: records authenticated only by one of them\n\t * stay refused across restarts.\n\t */\n\tretiredMasters?: string[]\n\t/**\n\t * Ids of master keys this device held when the server served a NEWER revision of the\n\t * same ring that they do not authenticate (a passphrase change this device could not\n\t * read yet). Records authenticated only by one of them are not adopted automatically;\n\t * an explicit `unlock(passphrase)` decides (RT-107).\n\t */\n\tsupersededMasters?: string[]\n}\n\n/** Where a device keeps unlocked keyrings between app starts. */\nexport interface KeyCache {\n\t/** Which kind of cache this is (for status and docs). */\n\treadonly kind: 'indexeddb' | 'memory' | 'none'\n\tload(id: string): Promise<CachedKeyring | null>\n\tsave(id: string, entry: CachedKeyring): Promise<void>\n\tclear(id: string): Promise<void>\n}\n\n/** Keeps unlocked keyrings for the lifetime of the process only. */\nexport class MemoryKeyCache implements KeyCache {\n\treadonly kind = 'memory' as const\n\tprivate readonly entries = new Map<string, CachedKeyring>()\n\n\tasync load(id: string): Promise<CachedKeyring | null> {\n\t\treturn this.entries.get(id) ?? null\n\t}\n\n\tasync save(id: string, entry: CachedKeyring): Promise<void> {\n\t\tthis.entries.set(id, entry)\n\t}\n\n\tasync clear(id: string): Promise<void> {\n\t\tthis.entries.delete(id)\n\t}\n}\n\n/** Keeps nothing: the device asks for the passphrase on every start. */\nexport class NoKeyCache implements KeyCache {\n\treadonly kind = 'none' as const\n\tasync load(): Promise<CachedKeyring | null> {\n\t\treturn null\n\t}\n\tasync save(): Promise<void> {}\n\tasync clear(): Promise<void> {}\n}\n\nconst IDB_STORE = 'keyrings'\n\n/**\n * Keeps unlocked keyrings in IndexedDB (structured clone keeps a CryptoKey\n * non-extractable). Survives reloads, so a device unlocks once and then works offline.\n */\nexport class IndexedDbKeyCache implements KeyCache {\n\treadonly kind = 'indexeddb' as const\n\tprivate db: Promise<IDBDatabase> | null = null\n\n\t/** @param databaseName - IndexedDB database name (one per app database) */\n\tconstructor(private readonly databaseName: string) {}\n\n\tasync load(id: string): Promise<CachedKeyring | null> {\n\t\tconst db = await this.open()\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst request = db.transaction(IDB_STORE, 'readonly').objectStore(IDB_STORE).get(id)\n\t\t\trequest.onsuccess = () => resolve((request.result as CachedKeyring | undefined) ?? null)\n\t\t\trequest.onerror = () => reject(request.error)\n\t\t})\n\t}\n\n\tasync save(id: string, entry: CachedKeyring): Promise<void> {\n\t\tconst db = await this.open()\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = db.transaction(IDB_STORE, 'readwrite')\n\t\t\ttx.objectStore(IDB_STORE).put(entry, id)\n\t\t\ttx.oncomplete = () => resolve()\n\t\t\ttx.onerror = () => reject(tx.error)\n\t\t\ttx.onabort = () => reject(tx.error)\n\t\t})\n\t}\n\n\tasync clear(id: string): Promise<void> {\n\t\tconst db = await this.open()\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = db.transaction(IDB_STORE, 'readwrite')\n\t\t\ttx.objectStore(IDB_STORE).delete(id)\n\t\t\ttx.oncomplete = () => resolve()\n\t\t\ttx.onerror = () => reject(tx.error)\n\t\t\ttx.onabort = () => reject(tx.error)\n\t\t})\n\t}\n\n\tprivate open(): Promise<IDBDatabase> {\n\t\tif (!this.db) {\n\t\t\tthis.db = new Promise((resolve, reject) => {\n\t\t\t\tconst request = indexedDB.open(this.databaseName, 1)\n\t\t\t\trequest.onupgradeneeded = () => {\n\t\t\t\t\tif (!request.result.objectStoreNames.contains(IDB_STORE)) {\n\t\t\t\t\t\trequest.result.createObjectStore(IDB_STORE)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trequest.onsuccess = () => resolve(request.result)\n\t\t\t\trequest.onerror = () => reject(request.error)\n\t\t\t})\n\t\t}\n\t\treturn this.db\n\t}\n}\n\n/**\n * The default cache: IndexedDB where it exists (browsers), memory otherwise (Node,\n * tests). See the encryption guide for what each means for offline unlock.\n *\n * @param mode - 'auto' (default), 'indexeddb', 'memory' or 'none'\n * @param databaseName - IndexedDB database name\n */\nexport function createKeyCache(\n\tmode: 'auto' | 'indexeddb' | 'memory' | 'none' = 'auto',\n\tdatabaseName = 'kora-keyring',\n): KeyCache {\n\tif (mode === 'none') return new NoKeyCache()\n\tif (mode === 'memory') return new MemoryKeyCache()\n\tconst hasIndexedDb = typeof (globalThis as { indexedDB?: unknown }).indexedDB !== 'undefined'\n\tif (mode === 'indexeddb' || hasIndexedDb) {\n\t\treturn hasIndexedDb ? new IndexedDbKeyCache(databaseName) : new MemoryKeyCache()\n\t}\n\treturn new MemoryKeyCache()\n}\n","import { SyncError } from '@korajs/core'\nimport type { VersionedKey } from './types'\n\n/**\n * Thrown when key derivation fails.\n */\nexport class KeyDerivationError extends SyncError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, { ...context, errorType: 'KEY_DERIVATION_ERROR' })\n\t\tthis.name = 'KeyDerivationError'\n\t}\n}\n\n/** Salt length in bytes. 32 bytes (256 bits) provides sufficient randomness. */\nconst SALT_LENGTH = 32\n\n/**\n * PBKDF2 iteration count. 600,000 iterations is the OWASP-recommended minimum\n * for SHA-256 as of 2024, providing strong resistance against brute-force attacks.\n *\n * This is the production default. It can be overridden per-call via the\n * `iterations` argument on {@link deriveKey}/{@link deriveVersionedKey} — used\n * by tests to keep the real crypto path exercised without paying the full\n * brute-force-resistant cost on every derivation. The default is never lowered.\n */\nexport const DEFAULT_PBKDF2_ITERATIONS = 600_000\n\n/** Derived key length in bits (AES-256). */\nconst DERIVED_KEY_LENGTH = 256\n\n/**\n * Asserts that `crypto.subtle` is available, throwing a clear error if not.\n */\nfunction assertCryptoAvailable(): void {\n\tif (typeof globalThis.crypto === 'undefined' || typeof globalThis.crypto.subtle === 'undefined') {\n\t\tthrow new KeyDerivationError(\n\t\t\t'Web Crypto API (crypto.subtle) is not available in this environment. ' +\n\t\t\t\t'Sync encryption requires crypto.subtle, which is available in modern browsers and Node.js 20+.',\n\t\t)\n\t}\n}\n\n/**\n * Generates a cryptographically random salt for PBKDF2 key derivation.\n *\n * @returns A random 32-byte Uint8Array\n * @throws {KeyDerivationError} If crypto.getRandomValues is unavailable\n */\nexport function generateSalt(): Uint8Array {\n\tif (typeof globalThis.crypto === 'undefined') {\n\t\tthrow new KeyDerivationError(\n\t\t\t'Web Crypto API (crypto) is not available. Cannot generate random salt.',\n\t\t)\n\t}\n\n\treturn globalThis.crypto.getRandomValues(new Uint8Array(SALT_LENGTH))\n}\n\n/**\n * Derives an AES-256-GCM encryption key from a passphrase using PBKDF2.\n *\n * Uses PBKDF2 with SHA-256 and 600,000 iterations (OWASP-recommended minimum)\n * to derive a 256-bit key. The derived key is deterministic: the same passphrase\n * and salt always produce the same key.\n *\n * @param passphrase - The user's passphrase (must be non-empty)\n * @param salt - Optional salt bytes. If omitted, a random 32-byte salt is generated.\n * @param iterations - Optional PBKDF2 iteration count. Defaults to\n * {@link DEFAULT_PBKDF2_ITERATIONS}. Lower it only in tests; production must\n * keep the default so derived keys stay brute-force resistant.\n * @returns The derived CryptoKey and the salt used\n * @throws {KeyDerivationError} If the passphrase is empty, crypto is unavailable, or derivation fails\n *\n * @example\n * ```typescript\n * // First time: derive key with a new salt\n * const { key, salt } = await deriveKey('my-secure-passphrase')\n *\n * // Later: re-derive the same key using the stored salt\n * const { key: sameKey } = await deriveKey('my-secure-passphrase', salt)\n * ```\n */\nexport async function deriveKey(\n\tpassphrase: string,\n\tsalt?: Uint8Array,\n\titerations: number = DEFAULT_PBKDF2_ITERATIONS,\n): Promise<{ key: CryptoKey; salt: Uint8Array }> {\n\tassertCryptoAvailable()\n\n\tif (passphrase.length === 0) {\n\t\tthrow new KeyDerivationError(\n\t\t\t'Passphrase must not be empty. Provide a non-empty string for encryption key derivation.',\n\t\t)\n\t}\n\n\tif (!Number.isInteger(iterations) || iterations < 1) {\n\t\tthrow new KeyDerivationError(\n\t\t\t`PBKDF2 iteration count must be a positive integer, received: ${iterations}`,\n\t\t\t{ iterations },\n\t\t)\n\t}\n\n\tconst usedSalt = salt ?? generateSalt()\n\n\ttry {\n\t\t// Import the passphrase as raw key material for PBKDF2\n\t\tconst passphraseBytes = new TextEncoder().encode(passphrase)\n\t\tconst baseKey = await globalThis.crypto.subtle.importKey(\n\t\t\t'raw',\n\t\t\tpassphraseBytes,\n\t\t\t'PBKDF2',\n\t\t\tfalse,\n\t\t\t['deriveBits', 'deriveKey'],\n\t\t)\n\n\t\t// Derive an AES-256-GCM key using PBKDF2 with SHA-256\n\t\tconst derivedKey = await globalThis.crypto.subtle.deriveKey(\n\t\t\t{\n\t\t\t\tname: 'PBKDF2',\n\t\t\t\tsalt: usedSalt as unknown as ArrayBuffer,\n\t\t\t\titerations,\n\t\t\t\thash: 'SHA-256',\n\t\t\t},\n\t\t\tbaseKey,\n\t\t\t{ name: 'AES-GCM', length: DERIVED_KEY_LENGTH },\n\t\t\t// extractable: true so the derived key can be exported for testing/debugging\n\t\t\ttrue,\n\t\t\t['encrypt', 'decrypt'],\n\t\t)\n\n\t\treturn { key: derivedKey, salt: usedSalt }\n\t} catch (cause) {\n\t\tif (cause instanceof KeyDerivationError) {\n\t\t\tthrow cause\n\t\t}\n\n\t\tthrow new KeyDerivationError(\n\t\t\t'Failed to derive encryption key from passphrase using PBKDF2. ' +\n\t\t\t\t'Ensure the runtime supports PBKDF2 with SHA-256.',\n\t\t\t{ cause: cause instanceof Error ? cause.message : String(cause) },\n\t\t)\n\t}\n}\n\n/**\n * Derives a versioned encryption key from a passphrase.\n *\n * Wraps {@link deriveKey} with a version number for key rotation support.\n * When the passphrase changes, create a new versioned key with a higher\n * version number.\n *\n * @param passphrase - The user's passphrase\n * @param version - Key version number (must be a positive integer)\n * @param salt - Optional salt bytes. If omitted, a random salt is generated.\n * @param iterations - Optional PBKDF2 iteration count. Defaults to\n * {@link DEFAULT_PBKDF2_ITERATIONS}. Lower it only in tests.\n * @returns A {@link VersionedKey} containing the key, version, and salt\n * @throws {KeyDerivationError} If parameters are invalid or derivation fails\n */\nexport async function deriveVersionedKey(\n\tpassphrase: string,\n\tversion: number,\n\tsalt?: Uint8Array,\n\titerations: number = DEFAULT_PBKDF2_ITERATIONS,\n): Promise<VersionedKey> {\n\tif (!Number.isInteger(version) || version < 1) {\n\t\tthrow new KeyDerivationError(`Key version must be a positive integer, received: ${version}`, {\n\t\t\tversion,\n\t\t})\n\t}\n\n\tconst { key, salt: usedSalt } = await deriveKey(passphrase, salt, iterations)\n\n\treturn { version, key, salt: usedSalt }\n}\n","/**\n * The server-stored, passphrase-wrapped key record (ENC-1, decision D4b; format 2 since\n * the final RC red team, RT-95/RT-96/RT-97/RT-104).\n *\n * Key hierarchy of one keyring (\"ring\"):\n *\n * passphrase --PBKDF2-SHA256(salt, iterations)--> KEK\n * KEK --AES-GCM--> master key (random 256 bits, the ring's secret)\n * recovery public key --ECDH P-256 + HKDF + AES-GCM--> master key (optional)\n * master key --HKDF \"data-key-wrap\"--> wrapping key --AES-GCM--> data key v1, v2, ...\n * master key --HKDF \"record-mac\"--> MAC key --HMAC-SHA256--> `mac` over the whole record\n *\n * The MAC covers every field except itself (format, keyring, ring id, revision, current\n * version, KDF parameters, the master wrap, every data-key wrap, the recovery block), so\n * a server cannot add or swap a recovery key, relabel or drop a version, or serve\n * another record under the same revision without the device noticing. Only a holder of\n * the master key (the passphrase, a device that opened the ring, or the recovery key)\n * can write a record devices accept. The server stores the record opaquely: it never\n * sees the passphrase, the KEK, the master key or a data key.\n *\n * Shared by the client keyring (which creates, opens and authenticates records) and the\n * sync server (which validates their structure and stores them with compare-and-set).\n */\n\n/** Current record format. Format 1 (beta.13 release candidates only) is not read. */\nexport const KEY_RECORD_FORMAT = 2 as const\n\n/** Name of the default keyring (one per user). */\nexport const DEFAULT_KEYRING = 'default'\n\n/** Maximum serialized size of a key record the server accepts. */\nexport const MAX_KEY_RECORD_BYTES = 64 * 1024\n\n/** Maximum number of key versions in one record. */\nexport const MAX_KEY_VERSIONS = 256\n\n/** Maximum keyring name length. */\nexport const MAX_KEYRING_NAME_LENGTH = 128\n\n/**\n * Maximum PBKDF2 iterations a device accepts. A record above it would make an unlock\n * never finish (the floor is the app's own `kdfIterations`).\n */\nexport const MAX_KDF_ITERATIONS = 10_000_000\n\n/** Recovery wrap algorithm of format 2. */\nexport const RECOVERY_ALGORITHM = 'ECDH-P256+HKDF-SHA256+AES-GCM' as const\n\n/** Key-derivation parameters of a record's passphrase KEK. */\nexport interface KeyRecordKdf {\n\tname: 'PBKDF2'\n\thash: 'SHA-256'\n\t/** PBKDF2 iteration count (600,000 by default). */\n\titerations: number\n\t/** Base64 random 32-byte salt, generated per passphrase. */\n\tsalt: string\n}\n\n/** The ring's master key, wrapped by the passphrase KEK (AES-256-GCM). */\nexport interface WrappedMasterKey {\n\t/** Base64 12-byte AES-GCM IV. */\n\tiv: string\n\t/** Base64 AES-GCM ciphertext of the 32-byte master key (with tag: 48 bytes). */\n\twrappedKey: string\n}\n\n/** One data key version, wrapped by the master key's wrapping key. */\nexport interface WrappedDataKey {\n\t/** Key version (1, 2, ...) in this ring. */\n\tkeyVersion: number\n\t/** Random key id: the envelope's `keyId`. Never derived from key material. */\n\tkeyId: string\n\t/** Base64 12-byte AES-GCM IV of the wrap. */\n\tiv: string\n\t/** Base64 AES-GCM wrap of the raw 32-byte data key (ciphertext and tag). */\n\twrappedKey: string\n}\n\n/**\n * Optional recovery: the master key, wrapped to a recovery public key (ephemeral-static\n * ECDH P-256, HKDF-SHA256 over the shared secret and both public keys, AES-256-GCM). The\n * private half is the user's recovery key; it opens the master key, hence every data key.\n */\nexport interface KeyRecordRecovery {\n\talg: typeof RECOVERY_ALGORITHM\n\t/** Recovery public key (JWK x/y, base64url). */\n\tpublicKey: { x: string; y: string }\n\t/** Ephemeral public key of the wrap (JWK x/y, base64url). */\n\tephemeralPublicKey: { x: string; y: string }\n\tiv: string\n\twrappedKey: string\n}\n\n/**\n * The record the sync server stores per (user, keyring). Opaque to the server apart\n * from its structure: it cannot unwrap any key or forge the MAC.\n */\nexport interface WrappedKeyRecord {\n\tformat: typeof KEY_RECORD_FORMAT\n\tkeyring: string\n\t/** Random id of this ring (`r-` and 32 hex digits). Two rings of one keyring are a fork. */\n\tringId: string\n\t/** Compare-and-set counter: grows on every write; devices never accept a lower one. */\n\trevision: number\n\t/** The key version new operations are encrypted with (the highest one). */\n\tcurrentVersion: number\n\tkdf: KeyRecordKdf\n\tmaster: WrappedMasterKey\n\t/** Every key version ever created. Never shrinks: old history must stay readable. */\n\tkeys: WrappedDataKey[]\n\trecovery?: KeyRecordRecovery\n\t/**\n\t * Ids (`m-` and 32 hex digits) of master keys a passphrase change retired (RT-107).\n\t * Append-only. A device that authenticated a record listing them refuses every later\n\t * record authenticated only by one of them, however it was opened (held master key,\n\t * a stale configured passphrase, the old passphrase typed again). Absent until the\n\t * first passphrase change.\n\t */\n\tretiredMasters?: string[]\n\t/** Base64 HMAC-SHA256 of every other field (see {@link keyRecordMacInput}). */\n\tmac: string\n}\n\n/** Result of a structural validation. */\nexport type KeyRecordValidation = { ok: true } | { ok: false; reason: string }\n\nconst RECORD_FIELDS: ReadonlySet<string> = new Set([\n\t'format',\n\t'keyring',\n\t'ringId',\n\t'revision',\n\t'currentVersion',\n\t'kdf',\n\t'master',\n\t'keys',\n\t'recovery',\n\t'retiredMasters',\n\t'mac',\n])\n\n/**\n * Structural validation of a key record (server and client). It does not and cannot\n * check the MAC or that the wraps open: only a holder of the master key can.\n *\n * @param value - A decoded record\n * @param keyring - The keyring the record must belong to\n */\nexport function validateKeyRecord(value: unknown, keyring: string): KeyRecordValidation {\n\tif (!isObject(value)) return fail('record is not an object')\n\tif (value.format !== KEY_RECORD_FORMAT) return fail(`unsupported format ${String(value.format)}`)\n\tfor (const field of Object.keys(value)) {\n\t\t// Strict: every field is covered by the MAC, and an unknown one has no meaning.\n\t\tif (!RECORD_FIELDS.has(field)) return fail(`unknown field \"${field}\"`)\n\t}\n\tif (value.keyring !== keyring) return fail('keyring does not match')\n\tif (!isRingId(value.ringId)) return fail('ringId is malformed')\n\tif (!isPositiveInteger(value.revision)) return fail('revision must be a positive integer')\n\tif (!isPositiveInteger(value.currentVersion)) return fail('currentVersion must be positive')\n\tconst kdf = value.kdf\n\tif (!isObject(kdf) || kdf.name !== 'PBKDF2' || kdf.hash !== 'SHA-256') {\n\t\treturn fail('kdf must be PBKDF2 with SHA-256')\n\t}\n\tif (!isPositiveInteger(kdf.iterations) || kdf.iterations > MAX_KDF_ITERATIONS) {\n\t\treturn fail(`kdf.iterations must be 1 to ${MAX_KDF_ITERATIONS}`)\n\t}\n\tif (!isBase64(kdf.salt, 16, 64)) return fail('kdf.salt must be 16 to 64 base64 bytes')\n\tconst master = value.master\n\tif (!isObject(master) || !isBase64(master.iv, 12, 12) || !isBase64(master.wrappedKey, 48, 48)) {\n\t\treturn fail('master wrap is malformed')\n\t}\n\tif (!Array.isArray(value.keys) || value.keys.length === 0) return fail('keys must not be empty')\n\tif (value.keys.length > MAX_KEY_VERSIONS) return fail('too many key versions')\n\tconst versions = new Set<number>()\n\tconst ids = new Set<string>()\n\tlet max = 0\n\tfor (const key of value.keys) {\n\t\tif (!isObject(key)) return fail('key entry is not an object')\n\t\tif (!isPositiveInteger(key.keyVersion)) return fail('keyVersion must be positive')\n\t\tif (!isKeyId(key.keyId)) return fail('keyId is malformed')\n\t\tif (!isBase64(key.iv, 12, 12)) return fail('iv must be 12 base64 bytes')\n\t\tif (!isBase64(key.wrappedKey, 48, 48)) return fail('wrappedKey must be 48 base64 bytes')\n\t\tif (versions.has(key.keyVersion) || ids.has(key.keyId)) return fail('duplicate key version')\n\t\tversions.add(key.keyVersion)\n\t\tids.add(key.keyId)\n\t\tmax = Math.max(max, key.keyVersion)\n\t}\n\tif (value.currentVersion !== max) return fail('currentVersion must be the highest key version')\n\tif (value.recovery !== undefined) {\n\t\tconst recovery = value.recovery\n\t\tif (!isObject(recovery) || recovery.alg !== RECOVERY_ALGORITHM) {\n\t\t\treturn fail(`recovery.alg must be ${RECOVERY_ALGORITHM}`)\n\t\t}\n\t\tif (!isPoint(recovery.publicKey)) return fail('recovery.publicKey is malformed')\n\t\tif (!isPoint(recovery.ephemeralPublicKey)) return fail('recovery ephemeral key is malformed')\n\t\tif (!isBase64(recovery.iv, 12, 12) || !isBase64(recovery.wrappedKey, 48, 48)) {\n\t\t\treturn fail('recovery wrap is malformed')\n\t\t}\n\t}\n\tif (value.retiredMasters !== undefined) {\n\t\tconst retired = value.retiredMasters\n\t\tif (!Array.isArray(retired) || retired.length > MAX_KEY_VERSIONS) {\n\t\t\treturn fail(`retiredMasters must be an array of at most ${MAX_KEY_VERSIONS} ids`)\n\t\t}\n\t\tif (!retired.every((id) => isMasterId(id)) || new Set(retired).size !== retired.length) {\n\t\t\treturn fail('retiredMasters must hold distinct master ids')\n\t\t}\n\t}\n\tif (!isBase64(value.mac, 32, 32)) return fail('mac must be 32 base64 bytes')\n\treturn { ok: true }\n}\n\n/**\n * Whether `next` may replace `previous` (the server's write rule): the revision grows,\n * the ring id stays, and every key version of `previous` is still present under the\n * same key id, so no history becomes unreadable. Wraps may change (a passphrase change\n * re-wraps every version). A first record (or one re-uploaded after the server lost\n * it) may carry any revision: devices pin the highest revision they accepted, so a\n * restore must keep the revision it had.\n *\n * @param previous - The stored record, or null when none exists\n * @param next - The proposed record\n */\nexport function isKeyRecordSuccessor(\n\tprevious: WrappedKeyRecord | null,\n\tnext: WrappedKeyRecord,\n): KeyRecordValidation {\n\tif (previous === null) return { ok: true }\n\tif (next.revision <= previous.revision) {\n\t\treturn fail(`revision must be greater than ${previous.revision}`)\n\t}\n\tif (next.ringId !== previous.ringId) return fail('ringId must not change')\n\tconst nextIds = new Map(next.keys.map((key) => [key.keyVersion, key.keyId]))\n\tfor (const key of previous.keys) {\n\t\tif (nextIds.get(key.keyVersion) !== key.keyId) {\n\t\t\treturn fail(`key version ${key.keyVersion} must be kept`)\n\t\t}\n\t}\n\tconst nextRetired = new Set(next.retiredMasters ?? [])\n\tfor (const id of previous.retiredMasters ?? []) {\n\t\tif (!nextRetired.has(id)) return fail(`retired master ${id} must be kept`)\n\t}\n\treturn { ok: true }\n}\n\n/**\n * Additional authenticated data of one data-key wrap: binds a wrapped data key to its\n * keyring, version and key id, so a wrap cannot be relabelled as another version's.\n *\n * @param keyring - Keyring name\n * @param keyVersion - Key version\n * @param keyId - Key id\n */\nexport function wrapAdditionalData(keyring: string, keyVersion: number, keyId: string): Uint8Array {\n\treturn new TextEncoder().encode(\n\t\tJSON.stringify(['kora-key-wrap', KEY_RECORD_FORMAT, 'data-key', keyring, keyVersion, keyId]),\n\t)\n}\n\n/**\n * Additional authenticated data of a master-key wrap (passphrase or recovery): binds it\n * to the keyring and ring, and a recovery wrap to its recipient public key.\n */\nexport function masterWrapAdditionalData(\n\tkeyring: string,\n\tringId: string,\n\tpurpose: 'passphrase' | 'recovery',\n\trecipient?: { x: string; y: string },\n): Uint8Array {\n\treturn new TextEncoder().encode(\n\t\tJSON.stringify([\n\t\t\t'kora-master-wrap',\n\t\t\tKEY_RECORD_FORMAT,\n\t\t\tpurpose,\n\t\t\tkeyring,\n\t\t\tringId,\n\t\t\trecipient?.x ?? null,\n\t\t\trecipient?.y ?? null,\n\t\t]),\n\t)\n}\n\n/**\n * The bytes the record MAC covers: a domain prefix and the canonical JSON (sorted keys,\n * no whitespace) of every field except `mac`.\n *\n * @param record - The record (its `mac`, if any, is ignored)\n */\nexport function keyRecordMacInput(\n\trecord: WrappedKeyRecord | Omit<WrappedKeyRecord, 'mac'>,\n): Uint8Array {\n\tconst { mac: _mac, ...rest } = record as WrappedKeyRecord\n\treturn new TextEncoder().encode(\n\t\t`kora-key-record-mac\\u0000${KEY_RECORD_FORMAT}\\u0000${canonicalJson(rest)}`,\n\t)\n}\n\n/** Deterministic JSON: object keys sorted, undefined members dropped, no whitespace. */\nexport function canonicalJson(value: unknown): string {\n\tif (value === null || typeof value !== 'object') {\n\t\tconst encoded = JSON.stringify(value)\n\t\treturn encoded === undefined ? 'null' : encoded\n\t}\n\tif (Array.isArray(value)) return `[${value.map((item) => canonicalJson(item)).join(',')}]`\n\tconst entries = Object.keys(value as Record<string, unknown>)\n\t\t.filter((key) => (value as Record<string, unknown>)[key] !== undefined)\n\t\t.sort()\n\t\t.map(\n\t\t\t(key) => `${JSON.stringify(key)}:${canonicalJson((value as Record<string, unknown>)[key])}`,\n\t\t)\n\treturn `{${entries.join(',')}}`\n}\n\n/** Parse a record from JSON, returning null when it is not one. */\nexport function parseKeyRecord(json: string, keyring: string): WrappedKeyRecord | null {\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(json)\n\t} catch {\n\t\treturn null\n\t}\n\treturn validateKeyRecord(parsed, keyring).ok ? (parsed as WrappedKeyRecord) : null\n}\n\n/** Whether a keyring name is acceptable. */\nexport function isValidKeyringName(value: unknown): value is string {\n\treturn (\n\t\ttypeof value === 'string' &&\n\t\tvalue.length > 0 &&\n\t\tvalue.length <= MAX_KEYRING_NAME_LENGTH &&\n\t\t/^[A-Za-z0-9._:-]+$/.test(value)\n\t)\n}\n\n/** Whether a value is a key id (`k2-` and 32 hex digits). */\nexport function isKeyId(value: unknown): value is string {\n\treturn typeof value === 'string' && /^k2-[0-9a-f]{32}$/.test(value)\n}\n\n/** Whether a value is a master id (`m-` and 32 hex digits). */\nexport function isMasterId(value: unknown): value is string {\n\treturn typeof value === 'string' && /^m-[0-9a-f]{32}$/.test(value)\n}\n\nfunction fail(reason: string): KeyRecordValidation {\n\treturn { ok: false, reason }\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isPositiveInteger(value: unknown): value is number {\n\treturn typeof value === 'number' && Number.isSafeInteger(value) && value > 0\n}\n\nfunction isRingId(value: unknown): value is string {\n\treturn typeof value === 'string' && /^r-[0-9a-f]{32}$/.test(value)\n}\n\nfunction isBase64(value: unknown, minBytes: number, maxBytes: number): boolean {\n\tif (typeof value !== 'string' || !/^[A-Za-z0-9+/]*={0,2}$/.test(value)) return false\n\tif (value.length % 4 !== 0) return false\n\tconst padding = value.endsWith('==') ? 2 : value.endsWith('=') ? 1 : 0\n\tconst bytes = (value.length / 4) * 3 - padding\n\treturn bytes >= minBytes && bytes <= maxBytes\n}\n\nfunction isPoint(value: unknown): value is { x: string; y: string } {\n\treturn (\n\t\tisObject(value) &&\n\t\ttypeof value.x === 'string' &&\n\t\ttypeof value.y === 'string' &&\n\t\t/^[A-Za-z0-9_-]{43}$/.test(value.x) &&\n\t\t/^[A-Za-z0-9_-]{43}$/.test(value.y)\n\t)\n}\n","import { SyncError } from '@korajs/core'\nimport type {\n\tKeyRecordRecovery,\n\tWrappedDataKey,\n\tWrappedKeyRecord,\n\tWrappedMasterKey,\n} from './key-record'\nimport {\n\tRECOVERY_ALGORITHM,\n\tkeyRecordMacInput,\n\tmasterWrapAdditionalData,\n\twrapAdditionalData,\n} from './key-record'\n\n/**\n * WebCrypto primitives of the key hierarchy (ENC-1, D4b, record format 2):\n *\n * passphrase --PBKDF2-SHA256(salt, iterations)--> KEK (AES-256-GCM, non-extractable)\n * KEK --AES-GCM (AAD: keyring, ring)--> master key (32 random bytes)\n * recovery public key --ECDH P-256, HKDF-SHA256, AES-GCM--> master key (optional)\n * master key --HKDF-SHA256 \"data-key-wrap\"--> wrapping key (AES-256-GCM)\n * master key --HKDF-SHA256 \"record-mac\"--> MAC key (HMAC-SHA256)\n * wrapping key --AES-GCM wrap (AAD: keyring, version, keyId)--> data key v1, v2, ...\n * master key --HKDF-SHA256 \"master-id\"--> master id (names a master a passphrase change retires)\n * recovery anchor key (a data key created WITH the recovery key) --HMAC-SHA256 over\n * (keyring, ring id, key id, recovery public key)--> anchor (16 bytes, in the recovery key)\n *\n * Raw master-key bytes exist only inside the calls that create, re-wrap or open the\n * master key, and are zeroed afterwards. What a device keeps is the two derived keys\n * ({@link MasterKeys}), both non-extractable. Data keys are extractable only for the\n * moment they are wrapped; the copy a device encrypts with is non-extractable.\n */\n\n/** Thrown when a key record cannot be opened with the given passphrase or recovery key. */\nexport class KeyUnwrapError extends SyncError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, { ...context, errorType: 'KEY_UNWRAP_ERROR' })\n\t\tthis.name = 'KeyUnwrapError'\n\t}\n}\n\n/** PBKDF2 salt length (bytes). */\nexport const KEK_SALT_BYTES = 32\n\n/** Master key length (bytes). */\nexport const MASTER_KEY_BYTES = 32\n\nconst GCM_IV_BYTES = 12\nconst RECOVERY_KEY_PREFIX = 'kora-rk3-'\n/** Bytes of a recovery key's ring anchor (see {@link recoveryAnchor}). */\nconst RECOVERY_ANCHOR_BYTES = 16\nconst HKDF_DATA_KEY_WRAP = 'kora/key-record/2/data-key-wrap'\nconst HKDF_RECORD_MAC = 'kora/key-record/2/record-mac'\nconst HKDF_MASTER_ID = 'kora/key-record/2/master-id'\nconst HKDF_RECOVERY_WRAP = 'kora/key-record/2/recovery-wrap'\nconst RECOVERY_ANCHOR_INFO = 'kora/key-record/2/recovery-anchor/3'\n\n/** The keys a device derives from (and keeps instead of) a ring's master key. */\nexport interface MasterKeys {\n\t/** Wraps and unwraps the ring's data keys. */\n\twrapKey: CryptoKey\n\t/** Authenticates the whole key record. */\n\tmacKey: CryptoKey\n\t/**\n\t * Public name of the master key (`m-` and 32 hex digits, an HKDF output of the master\n\t * key): what a record's `retiredMasters` lists after a passphrase change. Reveals\n\t * nothing about the key; only a holder of the master key can compute it.\n\t */\n\tid: string\n}\n\nfunction subtle(): SubtleCrypto {\n\tconst api = globalThis.crypto?.subtle\n\tif (!api) {\n\t\tthrow new SyncError(\n\t\t\t'Web Crypto API (crypto.subtle) is not available. Sync encryption requires a secure context (HTTPS or localhost) in browsers, or Node.js 20+.',\n\t\t\t{ code: 'CRYPTO_UNAVAILABLE' },\n\t\t)\n\t}\n\treturn api\n}\n\nfunction buffer(bytes: Uint8Array): ArrayBuffer {\n\treturn bytes as unknown as ArrayBuffer\n}\n\n/** Cryptographically random bytes. */\nexport function randomBytes(length: number): Uint8Array {\n\treturn globalThis.crypto.getRandomValues(new Uint8Array(length))\n}\n\nfunction hex(bytes: Uint8Array): string {\n\treturn Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n/** A fresh random key id (`k2-` and 32 hex digits). Names a key; reveals nothing about it. */\nexport function newKeyId(): string {\n\treturn `k2-${hex(randomBytes(16))}`\n}\n\n/** A fresh random ring id (`r-` and 32 hex digits). */\nexport function newRingId(): string {\n\treturn `r-${hex(randomBytes(16))}`\n}\n\n/**\n * Derive the key-encryption key from a passphrase. Non-extractable; usable only to\n * seal and open the ring's master key.\n *\n * @param passphrase - The user's passphrase (non-empty)\n * @param salt - The record's random salt\n * @param iterations - PBKDF2 iterations\n */\nexport async function deriveKeyEncryptionKey(\n\tpassphrase: string,\n\tsalt: Uint8Array,\n\titerations: number,\n): Promise<CryptoKey> {\n\tif (passphrase.length === 0) {\n\t\tthrow new KeyUnwrapError('The encryption passphrase must not be empty.', {\n\t\t\tcode: 'EMPTY_PASSPHRASE',\n\t\t})\n\t}\n\tconst base = await subtle().importKey(\n\t\t'raw',\n\t\tbuffer(new TextEncoder().encode(passphrase)),\n\t\t'PBKDF2',\n\t\tfalse,\n\t\t['deriveKey'],\n\t)\n\treturn subtle().deriveKey(\n\t\t{ name: 'PBKDF2', salt: buffer(salt), iterations, hash: 'SHA-256' },\n\t\tbase,\n\t\t{ name: 'AES-GCM', length: 256 },\n\t\tfalse,\n\t\t['encrypt', 'decrypt'],\n\t)\n}\n\n/** New random master-key bytes. The caller zeroes them once wrapped and imported. */\nexport function generateMasterKey(): Uint8Array {\n\treturn randomBytes(MASTER_KEY_BYTES)\n}\n\n/**\n * Derive the non-extractable wrapping and MAC keys of a master key (HKDF-SHA256 with\n * distinct info strings).\n */\nexport async function importMasterKey(raw: Uint8Array): Promise<MasterKeys> {\n\tconst base = await subtle().importKey('raw', buffer(raw), 'HKDF', false, [\n\t\t'deriveKey',\n\t\t'deriveBits',\n\t])\n\tconst hkdf = (info: string): HkdfParams => ({\n\t\tname: 'HKDF',\n\t\thash: 'SHA-256',\n\t\tsalt: new ArrayBuffer(0),\n\t\tinfo: buffer(new TextEncoder().encode(info)),\n\t})\n\tconst [wrapKey, macKey, idBits] = await Promise.all([\n\t\tsubtle().deriveKey(hkdf(HKDF_DATA_KEY_WRAP), base, { name: 'AES-GCM', length: 256 }, false, [\n\t\t\t'wrapKey',\n\t\t\t'unwrapKey',\n\t\t]),\n\t\tsubtle().deriveKey(\n\t\t\thkdf(HKDF_RECORD_MAC),\n\t\t\tbase,\n\t\t\t{ name: 'HMAC', hash: 'SHA-256', length: 256 },\n\t\t\tfalse,\n\t\t\t['sign', 'verify'],\n\t\t),\n\t\tsubtle().deriveBits(hkdf(HKDF_MASTER_ID), base, 128),\n\t])\n\treturn { wrapKey, macKey, id: `m-${hex(new Uint8Array(idBits))}` }\n}\n\n/** Seal the master key under the passphrase KEK. */\nexport async function wrapMasterKey(\n\traw: Uint8Array,\n\tkek: CryptoKey,\n\tkeyring: string,\n\tringId: string,\n): Promise<WrappedMasterKey> {\n\tconst iv = randomBytes(GCM_IV_BYTES)\n\tconst sealed = await subtle().encrypt(\n\t\t{\n\t\t\tname: 'AES-GCM',\n\t\t\tiv: buffer(iv),\n\t\t\tadditionalData: buffer(masterWrapAdditionalData(keyring, ringId, 'passphrase')),\n\t\t},\n\t\tkek,\n\t\tbuffer(raw),\n\t)\n\treturn { iv: toBase64(iv), wrappedKey: toBase64(new Uint8Array(sealed)) }\n}\n\n/**\n * Open the master key with the passphrase KEK. The returned bytes must be zeroed by\n * the caller once imported (or re-wrapped).\n *\n * @throws {KeyUnwrapError} WRONG_PASSPHRASE when the wrap does not authenticate\n */\nexport async function unwrapMasterKey(\n\twrap: WrappedMasterKey,\n\tkek: CryptoKey,\n\tkeyring: string,\n\tringId: string,\n): Promise<Uint8Array> {\n\ttry {\n\t\tconst raw = await subtle().decrypt(\n\t\t\t{\n\t\t\t\tname: 'AES-GCM',\n\t\t\t\tiv: buffer(fromBase64(wrap.iv)),\n\t\t\t\tadditionalData: buffer(masterWrapAdditionalData(keyring, ringId, 'passphrase')),\n\t\t\t},\n\t\t\tkek,\n\t\t\tbuffer(fromBase64(wrap.wrappedKey)),\n\t\t)\n\t\treturn new Uint8Array(raw)\n\t} catch (cause) {\n\t\tthrow new KeyUnwrapError(\n\t\t\t`The passphrase does not open keyring \"${keyring}\". Check the passphrase.`,\n\t\t\t{\n\t\t\t\tcode: 'WRONG_PASSPHRASE',\n\t\t\t\tcause: cause instanceof Error ? cause.message : String(cause),\n\t\t\t},\n\t\t)\n\t}\n}\n\n/**\n * Wrap a data key with the ring's wrapping key.\n *\n * @param dataKey - An extractable data key\n * @param wrapKey - The master key's wrapping key\n * @param keyring - Keyring name (bound as AAD)\n * @param keyVersion - Key version (bound as AAD)\n * @param keyId - Key id (bound as AAD)\n */\nexport async function wrapDataKey(\n\tdataKey: CryptoKey,\n\twrapKey: CryptoKey,\n\tkeyring: string,\n\tkeyVersion: number,\n\tkeyId: string,\n): Promise<WrappedDataKey> {\n\tconst iv = randomBytes(GCM_IV_BYTES)\n\tconst wrapped = await subtle().wrapKey('raw', dataKey, wrapKey, {\n\t\tname: 'AES-GCM',\n\t\tiv: buffer(iv),\n\t\tadditionalData: buffer(wrapAdditionalData(keyring, keyVersion, keyId)),\n\t})\n\treturn { keyVersion, keyId, iv: toBase64(iv), wrappedKey: toBase64(new Uint8Array(wrapped)) }\n}\n\n/**\n * Unwrap a data key with the ring's wrapping key. A tampered wrap, or one relabelled to\n * another version, fails authentication.\n *\n * @param entry - The wrapped key\n * @param wrapKey - The master key's wrapping key\n * @param keyring - Keyring name\n * @param extractable - True only to re-wrap it right away; the kept copy is non-extractable\n * @throws {KeyUnwrapError} When the wrap does not authenticate under this key\n */\nexport async function unwrapDataKey(\n\tentry: WrappedDataKey,\n\twrapKey: CryptoKey,\n\tkeyring: string,\n\textractable = false,\n): Promise<CryptoKey> {\n\ttry {\n\t\treturn await subtle().unwrapKey(\n\t\t\t'raw',\n\t\t\tbuffer(fromBase64(entry.wrappedKey)),\n\t\t\twrapKey,\n\t\t\t{\n\t\t\t\tname: 'AES-GCM',\n\t\t\t\tiv: buffer(fromBase64(entry.iv)),\n\t\t\t\tadditionalData: buffer(wrapAdditionalData(keyring, entry.keyVersion, entry.keyId)),\n\t\t\t},\n\t\t\t{ name: 'AES-GCM', length: 256 },\n\t\t\textractable,\n\t\t\t['encrypt', 'decrypt'],\n\t\t)\n\t} catch (cause) {\n\t\tthrow new KeyUnwrapError(\n\t\t\t`Key version ${entry.keyVersion} of keyring \"${keyring}\" does not open under this ring's master key.`,\n\t\t\t{\n\t\t\t\tcode: 'KEY_UNWRAP_FAILED',\n\t\t\t\tkeyVersion: entry.keyVersion,\n\t\t\t\tcause: cause instanceof Error ? cause.message : String(cause),\n\t\t\t},\n\t\t)\n\t}\n}\n\n/** The record's MAC (base64 HMAC-SHA256) under a master key's MAC key. */\nexport async function computeRecordMac(\n\trecord: Omit<WrappedKeyRecord, 'mac'> | WrappedKeyRecord,\n\tmacKey: CryptoKey,\n): Promise<string> {\n\tconst mac = await subtle().sign('HMAC', macKey, buffer(keyRecordMacInput(record)))\n\treturn toBase64(new Uint8Array(mac))\n}\n\n/** A copy of `record` with its MAC computed under `macKey`. */\nexport async function sealRecord(\n\trecord: Omit<WrappedKeyRecord, 'mac'> | WrappedKeyRecord,\n\tmacKey: CryptoKey,\n): Promise<WrappedKeyRecord> {\n\tconst { mac: _old, ...body } = record as WrappedKeyRecord\n\treturn { ...body, mac: await computeRecordMac(body, macKey) } as WrappedKeyRecord\n}\n\n/** Whether the record's MAC verifies under `macKey` (constant-time, WebCrypto verify). */\nexport async function verifyRecordMac(\n\trecord: WrappedKeyRecord,\n\tmacKey: CryptoKey,\n): Promise<boolean> {\n\ttry {\n\t\treturn await subtle().verify(\n\t\t\t'HMAC',\n\t\t\tmacKey,\n\t\t\tbuffer(fromBase64(record.mac)),\n\t\t\tbuffer(keyRecordMacInput(record)),\n\t\t)\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * A new recovery key pair: the public half goes in the record, the private scalar `d`\n * into the recovery key ({@link formatRecoveryKey}) together with the ring anchor.\n */\nexport async function createRecoveryKeyPair(): Promise<{\n\tpublicKey: { x: string; y: string }\n\td: string\n}> {\n\tconst pair = (await subtle().generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, [\n\t\t'deriveBits',\n\t])) as CryptoKeyPair\n\tconst jwk = await subtle().exportKey('jwk', pair.privateKey)\n\tif (typeof jwk.d !== 'string' || typeof jwk.x !== 'string' || typeof jwk.y !== 'string') {\n\t\tthrow new SyncError('Failed to export the recovery key.', { code: 'RECOVERY_KEY_EXPORT' })\n\t}\n\treturn { publicKey: { x: jwk.x, y: jwk.y }, d: jwk.d }\n}\n\n/** The recovery key string the user stores: `kora-rk3-<d>.<anchor>`. */\nexport function formatRecoveryKey(d: string, anchor: Uint8Array): string {\n\tif (anchor.length !== RECOVERY_ANCHOR_BYTES) {\n\t\tthrow new SyncError('A recovery key anchor is 16 bytes.', { code: 'RECOVERY_KEY_EXPORT' })\n\t}\n\treturn `${RECOVERY_KEY_PREFIX}${d}.${toBase64Url(anchor)}`\n}\n\n/**\n * A new recovery key pair with a given (or, for tests, random) anchor.\n *\n * @param anchor - The ring anchor ({@link recoveryAnchor}); random when omitted\n */\nexport async function generateRecoveryKeyPair(\n\tanchor: Uint8Array = randomBytes(RECOVERY_ANCHOR_BYTES),\n): Promise<{\n\tpublicKey: { x: string; y: string }\n\trecoveryKey: string\n}> {\n\tconst { publicKey, d } = await createRecoveryKeyPair()\n\treturn { publicKey, recoveryKey: formatRecoveryKey(d, anchor) }\n}\n\n/**\n * The ring anchor of a recovery key (RT-107): the first 16 bytes of HMAC-SHA256 keyed\n * with the raw bytes of the ring's recovery ANCHOR KEY over the keyring, the ring id,\n * the anchor key's id and the recovery public key.\n *\n * Anyone can wrap a master key of their own to the (public) recovery key, so opening a\n * master key with the recovery key proves nothing about whose ring it is. The anchor key\n * is a data key created in the same write as the recovery key, under the ring's current\n * master key: no earlier passphrase or master key ever opened it, so neither the server\n * nor a holder of an OLDER (leaked) passphrase can build a ring that matches the anchor.\n * The tag reveals nothing about the key; matching it requires the key itself.\n *\n * @param dataKey - An extractable data key\n * @param keyring - Keyring name\n * @param ringId - Ring id\n * @param keyId - The data key's key id\n * @param publicKey - The recovery public key the anchor belongs to\n */\nexport async function recoveryAnchor(\n\tdataKey: CryptoKey,\n\tkeyring: string,\n\tringId: string,\n\tkeyId: string,\n\tpublicKey: { x: string; y: string },\n): Promise<Uint8Array> {\n\tconst raw = new Uint8Array(await subtle().exportKey('raw', dataKey))\n\ttry {\n\t\tconst hmac = await subtle().importKey(\n\t\t\t'raw',\n\t\t\tbuffer(raw),\n\t\t\t{ name: 'HMAC', hash: 'SHA-256' },\n\t\t\tfalse,\n\t\t\t['sign'],\n\t\t)\n\t\tconst input = JSON.stringify([\n\t\t\tRECOVERY_ANCHOR_INFO,\n\t\t\tkeyring,\n\t\t\tringId,\n\t\t\tkeyId,\n\t\t\tpublicKey.x,\n\t\t\tpublicKey.y,\n\t\t])\n\t\tconst tag = await subtle().sign('HMAC', hmac, buffer(new TextEncoder().encode(input)))\n\t\treturn new Uint8Array(tag).slice(0, RECOVERY_ANCHOR_BYTES)\n\t} finally {\n\t\traw.fill(0)\n\t}\n}\n\n/**\n * Split a recovery key into its private scalar and ring anchor.\n *\n * @throws {KeyUnwrapError} WRONG_RECOVERY_KEY when it is not a `kora-rk3-` recovery key.\n * Keys of the beta.13 release candidates (`kora-rk1-`, `kora-rk2-`) are refused with\n * `reason: 'RECOVERY_KEY_RETIRED'`: a `kora-rk2-` anchor is the ring's first data key,\n * which an older (possibly leaked) passphrase opens (RT-107).\n */\nexport function parseRecoveryKey(recoveryKey: string): { d: string; anchor: Uint8Array } {\n\tconst trimmed = recoveryKey.trim()\n\tif (/^kora-rk[12]-/.test(trimmed)) {\n\t\tthrow new KeyUnwrapError(\n\t\t\t'This recovery key comes from a 1.0.0-beta.13 release candidate and is no longer accepted: its anchor can be matched by a keyring built with an older passphrase. Call enableRecovery() again on a device unlocked with the current passphrase and store the new \"kora-rk3-\" key.',\n\t\t\t{ code: 'WRONG_RECOVERY_KEY', reason: 'RECOVERY_KEY_RETIRED' },\n\t\t)\n\t}\n\tconst match = /^kora-rk3-([A-Za-z0-9_-]{43})\\.([A-Za-z0-9_-]{22})$/.exec(trimmed)\n\tif (match === null) {\n\t\tthrow new KeyUnwrapError(\n\t\t\t`A recovery key has the form \"${RECOVERY_KEY_PREFIX}<key>.<anchor>\", as enableRecovery() returned it.`,\n\t\t\t{ code: 'WRONG_RECOVERY_KEY' },\n\t\t)\n\t}\n\treturn { d: match[1] as string, anchor: fromBase64Url(match[2] as string) }\n}\n\n/** Whether two byte strings are equal (no early exit). */\nexport function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n\tif (a.length !== b.length) return false\n\tlet diff = 0\n\tfor (let i = 0; i < a.length; i++) diff |= (a[i] as number) ^ (b[i] as number)\n\treturn diff === 0\n}\n\n/**\n * HKDF-SHA256 over the ECDH shared secret, with both public keys in the info, to the\n * AES-GCM key of a recovery wrap (never the raw shared secret as a key).\n */\nasync function recoveryWrappingKey(\n\tprivateKey: CryptoKey,\n\tpeer: CryptoKey,\n\tephemeral: { x: string; y: string },\n\trecipient: { x: string; y: string },\n\tusage: 'encrypt' | 'decrypt',\n): Promise<CryptoKey> {\n\tconst secret = new Uint8Array(\n\t\tawait subtle().deriveBits({ name: 'ECDH', public: peer }, privateKey, 256),\n\t)\n\ttry {\n\t\tconst base = await subtle().importKey('raw', buffer(secret), 'HKDF', false, ['deriveKey'])\n\t\treturn await subtle().deriveKey(\n\t\t\t{\n\t\t\t\tname: 'HKDF',\n\t\t\t\thash: 'SHA-256',\n\t\t\t\tsalt: new ArrayBuffer(0),\n\t\t\t\tinfo: buffer(\n\t\t\t\t\tnew TextEncoder().encode(\n\t\t\t\t\t\tJSON.stringify([\n\t\t\t\t\t\t\tHKDF_RECOVERY_WRAP,\n\t\t\t\t\t\t\tephemeral.x,\n\t\t\t\t\t\t\tephemeral.y,\n\t\t\t\t\t\t\trecipient.x,\n\t\t\t\t\t\t\trecipient.y,\n\t\t\t\t\t\t]),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t},\n\t\t\tbase,\n\t\t\t{ name: 'AES-GCM', length: 256 },\n\t\t\tfalse,\n\t\t\t[usage],\n\t\t)\n\t} finally {\n\t\tsecret.fill(0)\n\t}\n}\n\n/**\n * Wrap the master key for the recovery public key (ephemeral-static ECDH P-256).\n * Needs only the public key.\n */\nexport async function wrapMasterForRecovery(\n\traw: Uint8Array,\n\trecoveryPublicKey: { x: string; y: string },\n\tkeyring: string,\n\tringId: string,\n): Promise<KeyRecordRecovery> {\n\tconst recipient = await importPoint(recoveryPublicKey)\n\tconst ephemeral = (await subtle().generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, [\n\t\t'deriveBits',\n\t])) as CryptoKeyPair\n\tconst ephemeralJwk = await subtle().exportKey('jwk', ephemeral.publicKey)\n\tconst ephemeralPublicKey = { x: String(ephemeralJwk.x), y: String(ephemeralJwk.y) }\n\tconst key = await recoveryWrappingKey(\n\t\tephemeral.privateKey,\n\t\trecipient,\n\t\tephemeralPublicKey,\n\t\trecoveryPublicKey,\n\t\t'encrypt',\n\t)\n\tconst iv = randomBytes(GCM_IV_BYTES)\n\tconst sealed = await subtle().encrypt(\n\t\t{\n\t\t\tname: 'AES-GCM',\n\t\t\tiv: buffer(iv),\n\t\t\tadditionalData: buffer(\n\t\t\t\tmasterWrapAdditionalData(keyring, ringId, 'recovery', recoveryPublicKey),\n\t\t\t),\n\t\t},\n\t\tkey,\n\t\tbuffer(raw),\n\t)\n\treturn {\n\t\talg: RECOVERY_ALGORITHM,\n\t\tpublicKey: { x: recoveryPublicKey.x, y: recoveryPublicKey.y },\n\t\tephemeralPublicKey,\n\t\tiv: toBase64(iv),\n\t\twrappedKey: toBase64(new Uint8Array(sealed)),\n\t}\n}\n\n/**\n * Open the master key with the recovery key. The caller zeroes the returned bytes.\n *\n * @throws {KeyUnwrapError} WRONG_RECOVERY_KEY when the key is malformed or does not open it\n */\nexport async function unwrapMasterWithRecovery(\n\trecovery: KeyRecordRecovery,\n\trecoveryKey: string,\n\tkeyring: string,\n\tringId: string,\n): Promise<Uint8Array> {\n\tconst { d } = parseRecoveryKey(recoveryKey)\n\ttry {\n\t\tconst privateKey = await subtle().importKey(\n\t\t\t'jwk',\n\t\t\t{\n\t\t\t\tkty: 'EC',\n\t\t\t\tcrv: 'P-256',\n\t\t\t\tx: recovery.publicKey.x,\n\t\t\t\ty: recovery.publicKey.y,\n\t\t\t\td,\n\t\t\t\text: false,\n\t\t\t},\n\t\t\t{ name: 'ECDH', namedCurve: 'P-256' },\n\t\t\tfalse,\n\t\t\t['deriveBits'],\n\t\t)\n\t\tconst ephemeral = await importPoint(recovery.ephemeralPublicKey)\n\t\tconst key = await recoveryWrappingKey(\n\t\t\tprivateKey,\n\t\t\tephemeral,\n\t\t\trecovery.ephemeralPublicKey,\n\t\t\trecovery.publicKey,\n\t\t\t'decrypt',\n\t\t)\n\t\tconst raw = await subtle().decrypt(\n\t\t\t{\n\t\t\t\tname: 'AES-GCM',\n\t\t\t\tiv: buffer(fromBase64(recovery.iv)),\n\t\t\t\tadditionalData: buffer(\n\t\t\t\t\tmasterWrapAdditionalData(keyring, ringId, 'recovery', recovery.publicKey),\n\t\t\t\t),\n\t\t\t},\n\t\t\tkey,\n\t\t\tbuffer(fromBase64(recovery.wrappedKey)),\n\t\t)\n\t\treturn new Uint8Array(raw)\n\t} catch (cause) {\n\t\tthrow new KeyUnwrapError(`The recovery key does not open keyring \"${keyring}\".`, {\n\t\t\tcode: 'WRONG_RECOVERY_KEY',\n\t\t\tcause: cause instanceof Error ? cause.message : String(cause),\n\t\t})\n\t}\n}\n\n/** A new random data key, extractable so it can be wrapped (then discarded). */\nexport async function generateDataKey(): Promise<CryptoKey> {\n\treturn subtle().generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt'])\n}\n\n/**\n * Re-import an extractable data key as a non-extractable one (the copy a device\n * keeps). Exports the raw bytes transiently, inside this call only.\n */\nexport async function toNonExtractable(dataKey: CryptoKey): Promise<CryptoKey> {\n\tif (!dataKey.extractable) return dataKey\n\tconst raw = await subtle().exportKey('raw', dataKey)\n\ttry {\n\t\treturn await subtle().importKey('raw', raw, { name: 'AES-GCM', length: 256 }, false, [\n\t\t\t'encrypt',\n\t\t\t'decrypt',\n\t\t])\n\t} finally {\n\t\tnew Uint8Array(raw).fill(0)\n\t}\n}\n\nasync function importPoint(point: { x: string; y: string }): Promise<CryptoKey> {\n\treturn subtle().importKey(\n\t\t'jwk',\n\t\t{ kty: 'EC', crv: 'P-256', x: point.x, y: point.y, ext: true },\n\t\t{ name: 'ECDH', namedCurve: 'P-256' },\n\t\ttrue,\n\t\t[],\n\t)\n}\n\n/** Standard base64 of bytes. */\nexport function toBase64(bytes: Uint8Array): string {\n\tlet binary = ''\n\tfor (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i] as number)\n\treturn btoa(binary)\n}\n\nfunction toBase64Url(bytes: Uint8Array): string {\n\treturn toBase64(bytes).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction fromBase64Url(value: string): Uint8Array {\n\tconst padded = value.replace(/-/g, '+').replace(/_/g, '/')\n\treturn fromBase64(padded + '='.repeat((4 - (padded.length % 4)) % 4))\n}\n\n/** Bytes of standard base64. */\nexport function fromBase64(value: string): Uint8Array {\n\tconst binary = atob(value)\n\tconst bytes = new Uint8Array(binary.length)\n\tfor (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)\n\treturn bytes\n}\n","import { SyncError } from '@korajs/core'\nimport type {\n\tAtomicOp,\n\tEncryptedEnvelopeField,\n\tEncryptedOperationEnvelope,\n\tHLCTimestamp,\n\tOperation,\n\tOperationType,\n} from '@korajs/core'\nimport { canonicalize } from '@korajs/core/internal'\nimport type { SerializedOperation } from '../protocol/messages'\nimport { deriveVersionedKey } from './key-derivation'\nimport type { SyncEncryptionConfig, VersionedKey } from './types'\n\n/**\n * Thrown when encryption of operation data fails.\n */\nexport class EncryptionError extends SyncError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, { ...context, errorType: 'ENCRYPTION_ERROR' })\n\t\tthis.name = 'EncryptionError'\n\t}\n}\n\n/**\n * Thrown when decryption of operation data fails.\n * This typically indicates a wrong key, tampered ciphertext, or corrupted data.\n */\nexport class DecryptionError extends SyncError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, { ...context, errorType: 'DECRYPTION_ERROR' })\n\t\tthis.name = 'DecryptionError'\n\t}\n}\n\n/** AES-GCM initialization vector length in bytes (96 bits). NIST recommended. */\nconst IV_LENGTH = 12\n\n/** Marker of the protocol-1 in-`data` payload (no integrity binding; no longer accepted). */\nconst LEGACY_ENCRYPTED_MARKER = '__kora_e2e_encrypted' as const\n\n/** The members of an operation the envelope protects. */\ntype EnvelopeMember = 'data' | 'previousData' | 'atomicOps'\n\n/**\n * The operation shape the encryptor works on: a domain {@link Operation} or its wire\n * form {@link SerializedOperation} (same fields).\n */\ninterface EncryptableOperation {\n\tid: string\n\tnodeId: string\n\ttype: OperationType\n\tcollection: string\n\trecordId: string\n\tdata: Record<string, unknown> | null\n\tpreviousData: Record<string, unknown> | null\n\ttimestamp: HLCTimestamp\n\tsequenceNumber: number\n\tschemaVersion: number\n\tatomicOps?: Record<string, AtomicOp>\n\thashVersion?: 1 | 2\n\tencrypted?: EncryptedOperationEnvelope\n}\n\n/**\n * End-to-end encryption of operations, envelope v2 (protocol v2; ENC-3, NEW-ENC-1).\n *\n * **Envelope.** The plaintext `data`, `previousData` and `atomicOps` move into\n * `op.encrypted` ({@link EncryptedOperationEnvelope}: alg, keyId, keyVersion and one\n * AES-256-GCM ciphertext per member). On the wire `data` is null, or holds only the\n * collection's documented cleartext scope fields (`cleartextFields`), so a schema-aware\n * server stores the operation opaquely and can still evaluate scopes. Metadata (id,\n * nodeId, collection, recordId, timestamp, sequence, causalDeps, schemaVersion) stays in\n * cleartext so the server can route, deduplicate and order.\n *\n * **Binding (ENC-3).** Every ciphertext is authenticated with AES-GCM additional data\n * canonical(nodeId, collection, recordId, type, timestamp, sequenceNumber, field,\n * keyVersion, hashVersion): a ciphertext moved to another operation, record or member,\n * or an envelope whose metadata was rewritten, fails authentication. `data` and\n * `previousData` are always encrypted, even when null, so a delete is authenticated too.\n * The operation id (the version-2 content hash of the PLAINTEXT) is verified after\n * decryption by the sync engine (`verify-inbound.ts`).\n *\n * **Plaintext.** With encryption enabled an operation without an envelope is refused\n * (a server must not be able to inject unauthenticated writes), unless the config opens\n * a migration window (`allowPlaintextMigration`). Protocol-1 payloads (ciphertext inside\n * `data`, no binding) are refused the same way.\n *\n * **Key id.** The envelope names its key material (`keyId`: the keyring's random key\n * id, ENC-1), so a device holding different material reports a diagnosable\n * `KEY_ID_MISMATCH` rather than a bare authentication failure.\n *\n * @example\n * ```typescript\n * const encryptor = SyncEncryptor.fromKeys([{ version: 1, key: dataKey, keyId }])\n * const sealed = await encryptor.encryptOperation(operation)\n * const opened = await encryptor.decryptOperation(sealed)\n * ```\n */\nexport class SyncEncryptor {\n\t/**\n\t * Map of key version -> VersionedKey. The current (latest) version is used\n\t * for encryption. All versions are available for decryption (key rotation).\n\t */\n\tprivate readonly keys: Map<number, VersionedKey>\n\t/** The current key version used for encryption. */\n\tprivate currentVersion: number\n\t/** Fingerprint of each key version's material (cached). */\n\tprivate readonly keyIds = new Map<number, Promise<string>>()\n\tprivate readonly cleartextFields: Readonly<Record<string, readonly string[]>>\n\tprivate readonly allowPlaintextMigration: boolean\n\n\tprivate constructor(\n\t\tkeys: Map<number, VersionedKey>,\n\t\tcurrentVersion: number,\n\t\toptions: EncryptorOptions = {},\n\t) {\n\t\tthis.keys = keys\n\t\tthis.currentVersion = currentVersion\n\t\tthis.cleartextFields = options.cleartextFields ?? {}\n\t\tthis.allowPlaintextMigration = options.allowPlaintextMigration === true\n\t}\n\n\t/**\n\t * Creates a SyncEncryptor from a passphrase and an explicit, shared salt.\n\t *\n\t * Low-level: apps use the keyring (`createApp` with `sync.encryption`), which\n\t * distributes random data keys wrapped by the passphrase. This derives the data key\n\t * directly from the passphrase, so every device must pass the same salt; there is\n\t * no random-salt default any more (ENC-1: a per-process random salt made every\n\t * device derive a different key).\n\t *\n\t * @param config - Encryption configuration with the passphrase in `key`\n\t * @param salt - The key-derivation salt, shared by every device (16 bytes or more)\n\t * @param iterations - Optional PBKDF2 iteration count. Lower it only in tests.\n\t * @returns A configured SyncEncryptor instance\n\t * @throws {EncryptionError} If configuration is invalid or the salt is missing\n\t * @throws {KeyDerivationError} If key derivation fails\n\t */\n\tstatic async create(\n\t\tconfig: SyncEncryptionConfig,\n\t\tsalt: Uint8Array,\n\t\titerations?: number,\n\t): Promise<SyncEncryptor> {\n\t\tif (!config.enabled) {\n\t\t\tthrow new EncryptionError(\n\t\t\t\t'Cannot create SyncEncryptor with encryption disabled. ' +\n\t\t\t\t\t'Set enabled: true in the encryption config.',\n\t\t\t)\n\t\t}\n\t\tif (!(salt instanceof Uint8Array) || salt.length < 16) {\n\t\t\tthrow new EncryptionError(\n\t\t\t\t'SyncEncryptor.create needs the shared key-derivation salt (16 bytes or more). Devices that derive with different salts cannot read each other; use the keyring (sync.encryption in createApp) for shared keys.',\n\t\t\t\t{ code: 'SALT_REQUIRED' },\n\t\t\t)\n\t\t}\n\n\t\tconst passphrase = typeof config.key === 'function' ? await config.key() : (config.key ?? '')\n\n\t\tif (passphrase.length === 0) {\n\t\t\tthrow new EncryptionError(\n\t\t\t\t'Encryption key/passphrase must not be empty. ' +\n\t\t\t\t\t'Provide a non-empty string or key provider function.',\n\t\t\t)\n\t\t}\n\n\t\tconst versionedKey = await deriveVersionedKey(passphrase, 1, salt, iterations)\n\t\tconst keys = new Map<number, VersionedKey>()\n\t\tkeys.set(1, versionedKey)\n\n\t\treturn new SyncEncryptor(keys, 1, {\n\t\t\t...(config.cleartextFields ? { cleartextFields: config.cleartextFields } : {}),\n\t\t\t...(config.allowPlaintextMigration ? { allowPlaintextMigration: true } : {}),\n\t\t})\n\t}\n\n\t/**\n\t * Creates a SyncEncryptor from pre-derived versioned keys.\n\t *\n\t * Use this when you need to support multiple key versions for key rotation,\n\t * or when you have already derived the keys externally.\n\t *\n\t * @param versionedKeys - Array of versioned keys. The highest version is used for encryption.\n\t * @param options - Cleartext scope fields and the plaintext migration window\n\t * @returns A configured SyncEncryptor instance\n\t * @throws {EncryptionError} If no keys are provided\n\t */\n\tstatic fromKeys(versionedKeys: VersionedKey[], options: EncryptorOptions = {}): SyncEncryptor {\n\t\tif (versionedKeys.length === 0) {\n\t\t\tthrow new EncryptionError('At least one versioned key must be provided.')\n\t\t}\n\n\t\tconst keys = new Map<number, VersionedKey>()\n\t\tlet maxVersion = 0\n\n\t\tfor (const vk of versionedKeys) {\n\t\t\tkeys.set(vk.version, vk)\n\t\t\tif (vk.version > maxVersion) {\n\t\t\t\tmaxVersion = vk.version\n\t\t\t}\n\t\t}\n\n\t\treturn new SyncEncryptor(keys, maxVersion, options)\n\t}\n\n\t/**\n\t * Add a new key version for key rotation.\n\t *\n\t * After adding, the new key becomes the current version used for encryption\n\t * if its version number is higher than the current version. Previously-versioned\n\t * keys remain available for decrypting older operations.\n\t *\n\t * @param key - The new versioned key to add\n\t * @throws {EncryptionError} If the key version already exists\n\t */\n\taddKey(key: VersionedKey): void {\n\t\tif (this.keys.has(key.version)) {\n\t\t\tthrow new EncryptionError(\n\t\t\t\t`Key version ${key.version} already exists. Use a higher version number for rotation.`,\n\t\t\t\t{ existingVersion: key.version },\n\t\t\t)\n\t\t}\n\n\t\tthis.keys.set(key.version, key)\n\t\tif (key.version > this.currentVersion) {\n\t\t\tthis.currentVersion = key.version\n\t\t}\n\t}\n\n\t/** The key registered under an explicit key id (keyring keys), if any. */\n\tprivate keyById(keyId: string): VersionedKey | undefined {\n\t\tfor (const key of this.keys.values()) {\n\t\t\tif (key.keyId === keyId) return key\n\t\t}\n\t\treturn undefined\n\t}\n\n\t/**\n\t * Get the current encryption key version number.\n\t */\n\tgetCurrentKeyVersion(): number {\n\t\treturn this.currentVersion\n\t}\n\n\t/**\n\t * The key id written into envelopes for a key version: the keyring's random key id,\n\t * or for a passphrase-derived key a fingerprint of its salt (never the key itself).\n\t *\n\t * @param version - Key version (defaults to the current one)\n\t */\n\tasync getKeyId(version: number = this.currentVersion): Promise<string> {\n\t\tconst key = this.keys.get(version)\n\t\tif (!key) {\n\t\t\tthrow new EncryptionError(`Encryption key version ${version} not found.`, { version })\n\t\t}\n\t\tif (key.keyId !== undefined) return key.keyId\n\t\tlet cached = this.keyIds.get(version)\n\t\tif (!cached) {\n\t\t\tif (!key.salt) {\n\t\t\t\tthrow new EncryptionError(\n\t\t\t\t\t`Encryption key version ${version} has neither a key id nor a salt to name it.`,\n\t\t\t\t\t{ version },\n\t\t\t\t)\n\t\t\t}\n\t\t\tcached = keyFingerprint(key.salt)\n\t\t\tthis.keyIds.set(version, cached)\n\t\t}\n\t\treturn cached\n\t}\n\n\t/**\n\t * Seal an operation into an envelope v2.\n\t *\n\t * Returns a new operation (operations are immutable): `encrypted` holds the\n\t * ciphertexts, `data` is null or the collection's cleartext scope fields, and\n\t * `previousData`/`atomicOps` are removed. An operation that already carries an\n\t * envelope is returned unchanged.\n\t *\n\t * @param operation - The plaintext operation\n\t * @returns The sealed operation\n\t * @throws {EncryptionError} If encryption fails\n\t */\n\tasync encryptOperation(operation: Operation): Promise<Operation> {\n\t\treturn this.seal(operation)\n\t}\n\n\t/**\n\t * Open an envelope v2 and restore the plaintext `data`, `previousData` and\n\t * `atomicOps`. The returned operation has no `encrypted` member.\n\t *\n\t * @param operation - The sealed operation\n\t * @returns The plaintext operation (its id is NOT verified here; the caller does)\n\t * @throws {DecryptionError} On a plaintext operation (unless the migration window\n\t * is open), a protocol-1 payload, an unknown key, a key-id mismatch, or a\n\t * ciphertext that fails authentication (tampered, transplanted, wrong key)\n\t */\n\tasync decryptOperation(operation: Operation): Promise<Operation> {\n\t\treturn this.open(operation)\n\t}\n\n\t/**\n\t * Same as {@link encryptOperation} for the wire form.\n\t *\n\t * @param serialized - The serialized operation to encrypt\n\t * @returns The sealed serialized operation\n\t */\n\tasync encryptSerializedOperation(serialized: SerializedOperation): Promise<SerializedOperation> {\n\t\treturn this.seal(serialized)\n\t}\n\n\t/**\n\t * Same as {@link decryptOperation} for the wire form.\n\t *\n\t * @param serialized - The serialized operation to decrypt\n\t * @returns The plaintext serialized operation\n\t */\n\tasync decryptSerializedOperation(serialized: SerializedOperation): Promise<SerializedOperation> {\n\t\treturn this.open(serialized)\n\t}\n\n\t/**\n\t * Encrypt a batch of operations in parallel.\n\t *\n\t * @param operations - Operations to encrypt\n\t * @returns New operations with encrypted data fields\n\t */\n\tasync encryptBatch(operations: Operation[]): Promise<Operation[]> {\n\t\treturn Promise.all(operations.map((op) => this.encryptOperation(op)))\n\t}\n\n\t/**\n\t * Decrypt a batch of operations in parallel.\n\t *\n\t * @param operations - Operations to decrypt\n\t * @returns New operations with decrypted data fields\n\t */\n\tasync decryptBatch(operations: Operation[]): Promise<Operation[]> {\n\t\treturn Promise.all(operations.map((op) => this.decryptOperation(op)))\n\t}\n\n\t/**\n\t * Check if an operation `data` field holds a protocol-1 encrypted payload (the\n\t * pre-envelope format, refused since protocol v2).\n\t *\n\t * @param field - An operation's `data` or `previousData` field\n\t */\n\tstatic isEncryptedPayload(field: Record<string, unknown> | null): boolean {\n\t\treturn isEncryptedPayload(field)\n\t}\n\n\t/**\n\t * Whether a plaintext operation touches only data the server can already read: its\n\t * `data` (and, except for a delete, its `previousData`) names only the collection's\n\t * cleartext scope fields, and it has no atomic ops. The sync engine accepts such an\n\t * operation from a server-authored node (a cascade, a set-null of a cleartext\n\t * reference, a constraint correction of a cleartext field) under end-to-end\n\t * encryption: the server cannot seal it (it has no key), and it reveals or forges\n\t * nothing the server does not already hold in cleartext. Any sealed field makes it\n\t * false, so the operation is refused as plaintext.\n\t *\n\t * @param operation - A delivered operation without an envelope\n\t */\n\tisCleartextOnly(operation: Operation): boolean {\n\t\tif (operation.encrypted !== undefined) return false\n\t\tif (operation.atomicOps !== undefined && Object.keys(operation.atomicOps).length > 0) {\n\t\t\treturn false\n\t\t}\n\t\tconst allowed = new Set(this.cleartextFields[operation.collection] ?? [])\n\t\tconst within = (value: Record<string, unknown> | null): boolean =>\n\t\t\tvalue === null || Object.keys(value).every((field) => allowed.has(field))\n\t\tif (!within(operation.data)) return false\n\t\t// A delete's previousData is informational: the fold never reads it.\n\t\treturn operation.type === 'delete' || within(operation.previousData)\n\t}\n\n\t/** Whether an operation carries an envelope v2. */\n\tstatic isEncryptedOperation(operation: { encrypted?: unknown }): boolean {\n\t\treturn operation.encrypted !== undefined && operation.encrypted !== null\n\t}\n\n\t// --- Private helpers ---\n\n\tprivate async seal<T extends EncryptableOperation>(operation: T): Promise<T> {\n\t\tif (operation.encrypted !== undefined) return operation\n\t\tconst keyVersion = this.currentVersion\n\t\tconst key = this.keys.get(keyVersion)\n\t\tif (!key) {\n\t\t\tthrow new EncryptionError(`Current encryption key version ${keyVersion} not found.`, {\n\t\t\t\toperationId: operation.id,\n\t\t\t})\n\t\t}\n\t\tconst keyId = await this.getKeyId(keyVersion)\n\t\tconst hasAtomicOps =\n\t\t\toperation.atomicOps !== undefined && Object.keys(operation.atomicOps).length > 0\n\t\tconst [data, previousData, atomicOps] = await Promise.all([\n\t\t\tthis.encryptMember(operation, 'data', operation.data, key, keyVersion),\n\t\t\tthis.encryptMember(operation, 'previousData', operation.previousData, key, keyVersion),\n\t\t\thasAtomicOps\n\t\t\t\t? this.encryptMember(operation, 'atomicOps', operation.atomicOps ?? null, key, keyVersion)\n\t\t\t\t: Promise.resolve(null),\n\t\t])\n\t\tconst envelope: EncryptedOperationEnvelope = {\n\t\t\tv: 2,\n\t\t\talg: 'aes-256-gcm',\n\t\t\tkeyId,\n\t\t\tkeyVersion,\n\t\t\tdata,\n\t\t\tpreviousData,\n\t\t\t...(atomicOps !== null ? { atomicOps } : {}),\n\t\t}\n\t\tconst { atomicOps: _plainAtomic, ...rest } = operation\n\t\treturn {\n\t\t\t...rest,\n\t\t\tdata: this.cleartextScope(operation),\n\t\t\tpreviousData: null,\n\t\t\tencrypted: envelope,\n\t\t} as T\n\t}\n\n\tprivate async open<T extends EncryptableOperation>(operation: T): Promise<T> {\n\t\tconst envelope = operation.encrypted\n\t\tif (envelope === undefined || envelope === null) {\n\t\t\tif (isEncryptedPayload(operation.data) || isEncryptedPayload(operation.previousData)) {\n\t\t\t\tthrow new DecryptionError(\n\t\t\t\t\t`Operation ${operation.id} carries a protocol-1 encrypted payload (ciphertext inside data, not bound to the operation). Protocol v2 accepts only the encryption envelope; re-sync from an upgraded client.`,\n\t\t\t\t\t{ operationId: operation.id, code: 'LEGACY_ENCRYPTED_PAYLOAD' },\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (this.allowPlaintextMigration) return operation\n\t\t\tthrow new DecryptionError(\n\t\t\t\t`Operation ${operation.id} is not encrypted, but end-to-end encryption is enabled: a plaintext operation could have been written by anyone who can reach the sync server. It is refused. During a migration from plaintext sync, set encryption.allowPlaintextMigration.`,\n\t\t\t\t{ operationId: operation.id, code: 'PLAINTEXT_REJECTED' },\n\t\t\t)\n\t\t}\n\t\tif (envelope.v !== 2 || envelope.alg !== 'aes-256-gcm') {\n\t\t\tthrow new DecryptionError(\n\t\t\t\t`Unsupported encryption envelope (v${String(envelope.v)}, ${String(envelope.alg)}) on operation ${operation.id}. Update @korajs/sync.`,\n\t\t\t\t{ operationId: operation.id },\n\t\t\t)\n\t\t}\n\t\t// Keyring keys are found by key id: after two forked rings are merged (RT-104) one\n\t\t// ring holds keys that sealed operations under another ring's version numbers. The\n\t\t// envelope's own keyVersion is still what the AAD binds.\n\t\tconst key = this.keyById(envelope.keyId) ?? this.keys.get(envelope.keyVersion)\n\t\tif (!key) {\n\t\t\tthrow new DecryptionError(\n\t\t\t\t`No encryption key available for version ${envelope.keyVersion} (key id ${envelope.keyId}). This operation was encrypted with a key that is not registered. If you rotated keys, ensure all previous key versions are provided.`,\n\t\t\t\t{\n\t\t\t\t\toperationId: operation.id,\n\t\t\t\t\tkeyVersion: envelope.keyVersion,\n\t\t\t\t\tkeyId: envelope.keyId,\n\t\t\t\t\tavailableVersions: [...this.keys.keys()],\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t\tconst localKeyId = key.keyId ?? (await this.getKeyId(envelope.keyVersion))\n\t\tif (localKeyId !== envelope.keyId) {\n\t\t\tthrow new DecryptionError(\n\t\t\t\t`Operation ${operation.id} was encrypted with key material \"${envelope.keyId}\" (version ${envelope.keyVersion}), but this device holds \"${localKeyId}\". The devices hold different keys: they must open the same keyring (the same user and keyring name) on the same sync server.`,\n\t\t\t\t{\n\t\t\t\t\toperationId: operation.id,\n\t\t\t\t\tcode: 'KEY_ID_MISMATCH',\n\t\t\t\t\tkeyId: envelope.keyId,\n\t\t\t\t\tlocalKeyId,\n\t\t\t\t\tkeyVersion: envelope.keyVersion,\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t\tif (envelope.data === null || envelope.previousData === null) {\n\t\t\tthrow new DecryptionError(\n\t\t\t\t`Operation ${operation.id} has an incomplete encryption envelope (data and previousData must both be sealed).`,\n\t\t\t\t{ operationId: operation.id },\n\t\t\t)\n\t\t}\n\t\tconst [data, previousData, atomicOps] = await Promise.all([\n\t\t\tthis.decryptMember(operation, 'data', envelope.data, key, envelope.keyVersion),\n\t\t\tthis.decryptMember(\n\t\t\t\toperation,\n\t\t\t\t'previousData',\n\t\t\t\tenvelope.previousData,\n\t\t\t\tkey,\n\t\t\t\tenvelope.keyVersion,\n\t\t\t),\n\t\t\tenvelope.atomicOps\n\t\t\t\t? this.decryptMember(operation, 'atomicOps', envelope.atomicOps, key, envelope.keyVersion)\n\t\t\t\t: Promise.resolve(null),\n\t\t])\n\t\tconst { encrypted: _sealed, atomicOps: _unsealedAtomic, ...rest } = operation\n\t\treturn {\n\t\t\t...rest,\n\t\t\tdata,\n\t\t\tpreviousData,\n\t\t\t...(atomicOps !== null ? { atomicOps: atomicOps as Record<string, AtomicOp> } : {}),\n\t\t} as T\n\t}\n\n\t/** The collection's documented cleartext scope fields, or null when none. */\n\tprivate cleartextScope(operation: EncryptableOperation): Record<string, unknown> | null {\n\t\tconst fields = this.cleartextFields[operation.collection]\n\t\tif (!fields || fields.length === 0 || operation.data === null) return null\n\t\tconst scope: Record<string, unknown> = {}\n\t\tfor (const field of fields) {\n\t\t\tif (field in operation.data) scope[field] = operation.data[field]\n\t\t}\n\t\treturn Object.keys(scope).length > 0 ? scope : null\n\t}\n\n\tprivate async encryptMember(\n\t\toperation: EncryptableOperation,\n\t\tmember: EnvelopeMember,\n\t\tvalue: Record<string, unknown> | null,\n\t\tkey: VersionedKey,\n\t\tkeyVersion: number,\n\t): Promise<EncryptedEnvelopeField> {\n\t\ttry {\n\t\t\tconst plaintext = new TextEncoder().encode(JSON.stringify(value))\n\t\t\t// A fresh random IV per member encryption (NIST SP 800-38D, 96-bit IV).\n\t\t\tconst iv = globalThis.crypto.getRandomValues(new Uint8Array(IV_LENGTH))\n\t\t\tconst ciphertext = await globalThis.crypto.subtle.encrypt(\n\t\t\t\t{\n\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\tiv: iv as unknown as ArrayBuffer,\n\t\t\t\t\tadditionalData: additionalData(operation, member, keyVersion) as unknown as ArrayBuffer,\n\t\t\t\t},\n\t\t\t\tkey.key,\n\t\t\t\tplaintext as unknown as ArrayBuffer,\n\t\t\t)\n\t\t\treturn { iv: toBase64(iv), ct: toBase64(new Uint8Array(ciphertext)) }\n\t\t} catch (cause) {\n\t\t\tthrow new EncryptionError(\n\t\t\t\t`Failed to encrypt operation ${member}. Ensure the encryption key is valid and crypto.subtle is available.`,\n\t\t\t\t{\n\t\t\t\t\toperationId: operation.id,\n\t\t\t\t\tfieldName: member,\n\t\t\t\t\tcause: cause instanceof Error ? cause.message : String(cause),\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate async decryptMember(\n\t\toperation: EncryptableOperation,\n\t\tmember: EnvelopeMember,\n\t\tsealed: EncryptedEnvelopeField,\n\t\tkey: VersionedKey,\n\t\tkeyVersion: number,\n\t): Promise<Record<string, unknown> | null> {\n\t\tlet parsed: unknown\n\t\ttry {\n\t\t\tconst plaintext = await globalThis.crypto.subtle.decrypt(\n\t\t\t\t{\n\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\tiv: fromBase64(sealed.iv) as unknown as ArrayBuffer,\n\t\t\t\t\tadditionalData: additionalData(operation, member, keyVersion) as unknown as ArrayBuffer,\n\t\t\t\t},\n\t\t\t\tkey.key,\n\t\t\t\tfromBase64(sealed.ct) as unknown as ArrayBuffer,\n\t\t\t)\n\t\t\tparsed = JSON.parse(new TextDecoder().decode(plaintext))\n\t\t} catch (cause) {\n\t\t\tthrow new DecryptionError(\n\t\t\t\t`Failed to decrypt operation ${member}: the ciphertext does not authenticate against this operation. It was tampered with, moved from another operation, record or field, or encrypted with a different key.`,\n\t\t\t\t{\n\t\t\t\t\toperationId: operation.id,\n\t\t\t\t\tfieldName: member,\n\t\t\t\t\tkeyVersion,\n\t\t\t\t\tcause: cause instanceof Error ? cause.message : String(cause),\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t\tif (parsed === null) return null\n\t\tif (typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\tthrow new DecryptionError(`Decrypted ${member} is not a valid record object.`, {\n\t\t\t\toperationId: operation.id,\n\t\t\t\tfieldName: member,\n\t\t\t})\n\t\t}\n\t\treturn parsed as Record<string, unknown>\n\t}\n}\n\n/** Options of {@link SyncEncryptor.fromKeys}. */\nexport interface EncryptorOptions {\n\t/** Per collection, the data fields kept in cleartext beside the envelope (for scopes). */\n\tcleartextFields?: Readonly<Record<string, readonly string[]>>\n\t/** Accept plaintext (envelope-less) inbound operations during a migration window. */\n\tallowPlaintextMigration?: boolean\n}\n\n/**\n * AES-GCM additional authenticated data of one envelope member (ENC-3): canonical\n * JSON of (nodeId, collection, recordId, type, timestamp, sequenceNumber, field,\n * keyVersion, hashVersion). The declared hash version is bound too, so an envelope's\n * id cannot be downgraded to the weaker version-1 hash in transit.\n */\nfunction additionalData(\n\toperation: EncryptableOperation,\n\tmember: EnvelopeMember,\n\tkeyVersion: number,\n): Uint8Array {\n\treturn new TextEncoder().encode(\n\t\tcanonicalize({\n\t\t\tnodeId: operation.nodeId,\n\t\t\tcollection: operation.collection,\n\t\t\trecordId: operation.recordId,\n\t\t\ttype: operation.type,\n\t\t\ttimestamp: {\n\t\t\t\twallTime: operation.timestamp.wallTime,\n\t\t\t\tlogical: operation.timestamp.logical,\n\t\t\t\tnodeId: operation.timestamp.nodeId,\n\t\t\t},\n\t\t\tsequenceNumber: operation.sequenceNumber,\n\t\t\tfield: member,\n\t\t\tkeyVersion,\n\t\t\thashVersion: operation.hashVersion ?? 1,\n\t\t}),\n\t)\n}\n\n/** First 16 hex chars of SHA-256(\"kora-key-id\" || salt): names key material, reveals nothing. */\nasync function keyFingerprint(salt: Uint8Array): Promise<string> {\n\tconst prefix = new TextEncoder().encode('kora-key-id\\u0000')\n\tconst input = new Uint8Array(prefix.length + salt.length)\n\tinput.set(prefix, 0)\n\tinput.set(salt, prefix.length)\n\tconst digest = new Uint8Array(\n\t\tawait globalThis.crypto.subtle.digest('SHA-256', input as unknown as ArrayBuffer),\n\t)\n\treturn `k1-${Array.from(digest.slice(0, 8), (b) => b.toString(16).padStart(2, '0')).join('')}`\n}\n\n// --- Utility functions ---\n\n/**\n * Check if a field value contains a protocol-1 encrypted payload (ciphertext inside\n * `data`). Protocol v2 refuses such payloads; the envelope lives in `op.encrypted`.\n *\n * @param field - An operation's `data` or `previousData` field\n * @returns true if the field contains a protocol-1 encrypted payload\n */\nexport function isEncryptedPayload(field: Record<string, unknown> | null): boolean {\n\tif (field === null || typeof field !== 'object') {\n\t\treturn false\n\t}\n\treturn (\n\t\tfield[LEGACY_ENCRYPTED_MARKER] === true &&\n\t\ttypeof field.v === 'number' &&\n\t\ttypeof field.iv === 'string' &&\n\t\ttypeof field.ct === 'string' &&\n\t\ttypeof field.alg === 'string'\n\t)\n}\n\n// --- Base64 helpers ---\n\nfunction toBase64(bytes: Uint8Array): string {\n\tlet binary = ''\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\tbinary += String.fromCharCode(bytes[i] as number)\n\t}\n\treturn btoa(binary)\n}\n\nfunction fromBase64(str: string): Uint8Array {\n\tconst binary = atob(str)\n\tconst bytes = new Uint8Array(binary.length)\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tbytes[i] = binary.charCodeAt(i)\n\t}\n\treturn bytes\n}\n","import { generateUUIDv7 } from '@korajs/core'\nimport type { YjsDocUpdateMessage } from '../protocol/messages'\nimport { decodeYjsUpdate, encodeYjsUpdate, richtextDocKey } from './doc-channel-wire'\n\n/** Default snapshot size above which the doc channel is preferred. */\nexport const DEFAULT_RICHTEXT_DOC_CHANNEL_THRESHOLD = 4096\n\nexport type RichtextDocListener = (update: Uint8Array) => void\n\nexport interface RichtextDocChannelOptions {\n\t/** Snapshot byte length at which incremental channel sync is recommended. */\n\tlargeDocThreshold?: number\n\t/** Called when a local update should be sent on the wire. */\n\tonSend?: (message: YjsDocUpdateMessage) => void\n}\n\n/**\n * Side channel for incremental Yjs updates on large richtext fields.\n * Ephemeral — not persisted in the operation log (durable state still flows via ops).\n */\nexport class RichtextDocChannel {\n\tprivate readonly threshold: number\n\tprivate readonly onSend: ((message: YjsDocUpdateMessage) => void) | null\n\tprivate readonly listeners = new Map<string, Set<RichtextDocListener>>()\n\n\tconstructor(options?: RichtextDocChannelOptions) {\n\t\tthis.threshold = options?.largeDocThreshold ?? DEFAULT_RICHTEXT_DOC_CHANNEL_THRESHOLD\n\t\tthis.onSend = options?.onSend ?? null\n\t}\n\n\t/**\n\t * Whether the doc channel should be used for a field.\n\t * @param preference - `true` forces on, `false` forces off, `undefined` uses size threshold\n\t */\n\tshouldUseChannel(snapshotByteLength: number, preference?: boolean): boolean {\n\t\tif (preference === false) {\n\t\t\treturn false\n\t\t}\n\t\tif (preference === true) {\n\t\t\treturn true\n\t\t}\n\t\treturn snapshotByteLength >= this.threshold\n\t}\n\n\tgetThreshold(): number {\n\t\treturn this.threshold\n\t}\n\n\t/**\n\t * Subscribe to incremental updates for a document key.\n\t */\n\tsubscribe(\n\t\tcollection: string,\n\t\trecordId: string,\n\t\tfield: string,\n\t\tlistener: RichtextDocListener,\n\t): () => void {\n\t\tconst key = richtextDocKey(collection, recordId, field)\n\t\tlet set = this.listeners.get(key)\n\t\tif (!set) {\n\t\t\tset = new Set()\n\t\t\tthis.listeners.set(key, set)\n\t\t}\n\t\tset.add(listener)\n\n\t\treturn () => {\n\t\t\tconst current = this.listeners.get(key)\n\t\t\tif (!current) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcurrent.delete(listener)\n\t\t\tif (current.size === 0) {\n\t\t\t\tthis.listeners.delete(key)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Publish a local incremental update to peers.\n\t */\n\tsend(collection: string, recordId: string, field: string, update: Uint8Array): void {\n\t\tif (!this.onSend || update.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tthis.onSend({\n\t\t\ttype: 'yjs-doc-update',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tcollection,\n\t\t\trecordId,\n\t\t\tfield,\n\t\t\tupdate: encodeYjsUpdate(update),\n\t\t})\n\t}\n\n\t/**\n\t * Deliver a remote incremental update to local subscribers.\n\t */\n\tdeliver(message: YjsDocUpdateMessage): void {\n\t\tconst key = richtextDocKey(message.collection, message.recordId, message.field)\n\t\tconst set = this.listeners.get(key)\n\t\tif (!set || set.size === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst update = decodeYjsUpdate(message.update)\n\t\tfor (const listener of set) {\n\t\t\tlistener(update)\n\t\t}\n\t}\n}\n","const WIRE_BYTES_KEY = '__kora_bytes__'\n\n/**\n * Encode a Yjs update for the doc channel wire (base64).\n */\nexport function encodeYjsUpdate(update: Uint8Array): string {\n\tlet binary = ''\n\tfor (let i = 0; i < update.length; i++) {\n\t\tbinary += String.fromCharCode(update[i] as number)\n\t}\n\treturn btoa(binary)\n}\n\n/**\n * Decode a base64 Yjs update from the doc channel wire.\n */\nexport function decodeYjsUpdate(encoded: string): Uint8Array {\n\tconst binary = atob(encoded)\n\tconst bytes = new Uint8Array(binary.length)\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tbytes[i] = binary.charCodeAt(i)\n\t}\n\treturn bytes\n}\n\n/**\n * Build a stable key for a richtext field document.\n */\nexport function richtextDocKey(collection: string, recordId: string, field: string): string {\n\treturn `${collection}:${recordId}:${field}`\n}\n\nexport { WIRE_BYTES_KEY }\n","/**\n * Canonical key of a downlink scope map (SYNC-11). The client keys its per-view\n * delivery watermark by the scope the server ACCEPTED and reports that scope's key at\n * the next handshake; the server computes the key of the scope it resolves now and\n * resumes the delivery stream from the reported watermark only when the two match.\n * Both sides must therefore compute it the same way, from scopes that may have been\n * re-ordered or round-tripped through JSON: object keys are sorted, `undefined` entries\n * dropped, and `$in` lists de-duplicated and sorted. No scope (undefined or null) is\n * the empty string.\n */\nexport function scopeViewKey(\n\tscope: Record<string, Record<string, unknown>> | null | undefined,\n): string {\n\tif (scope === null || scope === undefined) return ''\n\treturn canonicalJson(normalizeValue(scope))\n}\n\nfunction normalizeValue(value: unknown): unknown {\n\tif (Array.isArray(value)) return value.map(normalizeValue)\n\tif (value === null || typeof value !== 'object') return value\n\tconst record = value as Record<string, unknown>\n\tconst out: Record<string, unknown> = {}\n\tfor (const key of Object.keys(record)) {\n\t\tconst entry = record[key]\n\t\tif (entry === undefined) continue\n\t\tif (key === '$in' && Array.isArray(entry)) {\n\t\t\tconst byKey = new Map<string, unknown>()\n\t\t\tfor (const item of entry) {\n\t\t\t\tconst normalized = normalizeValue(item)\n\t\t\t\tbyKey.set(canonicalJson(normalized), normalized)\n\t\t\t}\n\t\t\tout[key] = [...byKey.keys()].sort().map((k) => byKey.get(k))\n\t\t\tcontinue\n\t\t}\n\t\tout[key] = normalizeValue(entry)\n\t}\n\treturn out\n}\n\nfunction canonicalJson(value: unknown): string {\n\tif (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null'\n\tif (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`\n\tconst record = value as Record<string, unknown>\n\tconst entries = Object.keys(record)\n\t\t.sort()\n\t\t.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)\n\treturn `{${entries.join(',')}}`\n}\n","import type { Operation } from '@korajs/core'\nimport type { QueueStorage } from '../types'\n\n/**\n * In-memory QueueStorage implementation for testing.\n * Operations are lost on process restart — production should use\n * IndexedDB or OPFS-backed storage.\n */\nexport class MemoryQueueStorage implements QueueStorage {\n\tprivate operations: Map<string, Operation> = new Map()\n\tprivate readonly sent = new Set<string>()\n\n\tasync load(): Promise<Operation[]> {\n\t\treturn [...this.operations.values()]\n\t}\n\n\tasync enqueue(op: Operation): Promise<void> {\n\t\tthis.operations.set(op.id, op)\n\t}\n\n\tasync dequeue(ids: string[]): Promise<void> {\n\t\tfor (const id of ids) {\n\t\t\tthis.operations.delete(id)\n\t\t\tthis.sent.delete(id)\n\t\t}\n\t}\n\n\tasync markSent(ops: Operation[]): Promise<void> {\n\t\tfor (const op of ops) {\n\t\t\tif (this.operations.has(op.id)) this.sent.add(op.id)\n\t\t}\n\t}\n\n\tasync loadSentIds(): Promise<string[]> {\n\t\treturn [...this.sent]\n\t}\n\n\tasync count(): Promise<number> {\n\t\treturn this.operations.size\n\t}\n}\n","import type { Operation } from '@korajs/core'\nimport { topologicalSort } from '@korajs/core/internal'\nimport type { QueueStorage } from '../types'\n\n/**\n * A batch of operations taken from the queue for sending.\n */\nexport interface OutboundBatch {\n\t/** Unique identifier for this batch */\n\tbatchId: string\n\t/** Operations in this batch, in causal order */\n\toperations: Operation[]\n}\n\n/**\n * Outbound operation queue with pluggable persistence.\n * Manages operations waiting to be sent to the sync server.\n *\n * Operations are deduplicated by ID (content-addressed) and maintained\n * in causal order via topological sort.\n */\nexport class OutboundQueue {\n\tprivate queue: Operation[] = []\n\tprivate readonly seen: Set<string> = new Set()\n\tprivate readonly inFlight: Map<string, Operation[]> = new Map()\n\t/**\n\t * Ids of queued operations that were put on the wire at least once. Such an op may\n\t * already be stored on the server (its ack can be lost), so a clock rebase must never\n\t * re-stamp it (W3 step 4). Persisted through the storage when it supports it.\n\t */\n\tprivate readonly sent: Set<string> = new Set()\n\tprivate nextBatchId = 0\n\tprivate initialized = false\n\n\tconstructor(private readonly storage: QueueStorage) {}\n\n\t/**\n\t * Load persisted operations from storage.\n\t * Must be called before using the queue.\n\t */\n\tasync initialize(): Promise<void> {\n\t\tconst stored = await this.storage.load()\n\t\tfor (const op of stored) {\n\t\t\tif (!this.seen.has(op.id)) {\n\t\t\t\tthis.seen.add(op.id)\n\t\t\t\tthis.queue.push(op)\n\t\t\t}\n\t\t}\n\t\t// Ensure causal order\n\t\tif (this.queue.length > 1) {\n\t\t\tthis.queue = topologicalSort(this.queue)\n\t\t}\n\t\tif (this.storage.loadSentIds) {\n\t\t\tfor (const id of await this.storage.loadSentIds()) {\n\t\t\t\tif (this.seen.has(id)) this.sent.add(id)\n\t\t\t}\n\t\t}\n\t\tthis.initialized = true\n\t}\n\n\t/**\n\t * Add an operation to the outbound queue.\n\t * Deduplicates by operation ID. Persists to storage.\n\t */\n\tasync enqueue(op: Operation): Promise<void> {\n\t\tif (this.seen.has(op.id)) return\n\n\t\tthis.seen.add(op.id)\n\t\tthis.queue.push(op)\n\t\tawait this.storage.enqueue(op)\n\n\t\t// Re-sort to maintain causal order when new ops arrive\n\t\tif (this.queue.length > 1) {\n\t\t\tthis.queue = topologicalSort(this.queue)\n\t\t}\n\t}\n\n\t/**\n\t * Add several operations at once: deduplicated by id, persisted, and causally\n\t * re-sorted once (rather than once per operation).\n\t */\n\tasync enqueueMany(ops: Operation[]): Promise<void> {\n\t\tlet added = false\n\t\tfor (const op of ops) {\n\t\t\tif (this.seen.has(op.id)) continue\n\t\t\tthis.seen.add(op.id)\n\t\t\tthis.queue.push(op)\n\t\t\tawait this.storage.enqueue(op)\n\t\t\tadded = true\n\t\t}\n\t\tif (added && this.queue.length > 1) {\n\t\t\tthis.queue = topologicalSort(this.queue)\n\t\t}\n\t}\n\n\t/**\n\t * Take a batch of operations from the front of the queue.\n\t * Moves them to in-flight status. Returns null if queue is empty.\n\t *\n\t * @param batchSize - Maximum number of operations in the batch\n\t * @param accept - Only operations it accepts are taken (in queue order); the others\n\t * stay queued. The engine passes \"authored under this session's node id\": a session\n\t * may only upload its own node's operations (RT-38, RT-40).\n\t */\n\ttakeBatch(batchSize: number, accept?: (op: Operation) => boolean): OutboundBatch | null {\n\t\tif (this.queue.length === 0) return null\n\n\t\tlet ops: Operation[]\n\t\tif (accept) {\n\t\t\tops = []\n\t\t\tconst kept: Operation[] = []\n\t\t\tfor (const op of this.queue) {\n\t\t\t\tif (ops.length < batchSize && accept(op)) ops.push(op)\n\t\t\t\telse kept.push(op)\n\t\t\t}\n\t\t\tif (ops.length === 0) return null\n\t\t\tthis.queue = kept\n\t\t} else {\n\t\t\tops = this.queue.splice(0, batchSize)\n\t\t}\n\t\tconst batchId = `batch-${this.nextBatchId++}`\n\t\tthis.inFlight.set(batchId, ops)\n\n\t\treturn { batchId, operations: ops }\n\t}\n\n\t/** Whether any queued (not in-flight) operation satisfies `accept`. */\n\thasOperationsMatching(accept: (op: Operation) => boolean): boolean {\n\t\treturn this.queue.some(accept)\n\t}\n\n\t/** Number of queued (not in-flight) operations that satisfy `accept`. */\n\tcountMatching(accept: (op: Operation) => boolean): number {\n\t\tlet count = 0\n\t\tfor (const op of this.queue) if (accept(op)) count++\n\t\treturn count\n\t}\n\n\t/**\n\t * Acknowledge a batch, removing its operations permanently.\n\t */\n\tasync acknowledge(batchId: string): Promise<void> {\n\t\tconst ops = this.inFlight.get(batchId)\n\t\tif (!ops) return\n\n\t\tthis.inFlight.delete(batchId)\n\t\tconst ids = ops.map((op) => op.id)\n\t\t// Release the ids (SYNC-6): the seen set must only hold ops still owned by the\n\t\t// queue, or it grows forever and an acked op could never be re-enqueued (for\n\t\t// example when a restored server needs it again).\n\t\tfor (const id of ids) {\n\t\t\tthis.seen.delete(id)\n\t\t\tthis.sent.delete(id)\n\t\t}\n\t\tawait this.storage.dequeue(ids)\n\t}\n\n\t/**\n\t * Acknowledge only operations in an in-flight batch whose local sequence number is\n\t * covered by the server's ack. Any later operations return to the front of the\n\t * queue for retry. This is required when the server accepts a prefix but rejects a\n\t * later operation retriably (for example a stale-scope push after auth changed).\n\t */\n\tasync acknowledgeThrough(\n\t\tbatchId: string,\n\t\tlastSequenceNumber: number,\n\t): Promise<{ acknowledged: Operation[]; returned: Operation[] }> {\n\t\tconst ops = this.inFlight.get(batchId)\n\t\tif (!ops) return { acknowledged: [], returned: [] }\n\n\t\tthis.inFlight.delete(batchId)\n\t\tconst acknowledged = ops.filter((op) => op.sequenceNumber <= lastSequenceNumber)\n\t\tconst retry = ops.filter((op) => op.sequenceNumber > lastSequenceNumber)\n\n\t\tif (retry.length > 0) {\n\t\t\tthis.queue.unshift(...retry)\n\t\t\tif (this.queue.length > 1) {\n\t\t\t\tthis.queue = topologicalSort(this.queue)\n\t\t\t}\n\t\t}\n\n\t\tif (acknowledged.length > 0) {\n\t\t\tconst ids = acknowledged.map((op) => op.id)\n\t\t\tfor (const id of ids) {\n\t\t\t\tthis.seen.delete(id)\n\t\t\t\tthis.sent.delete(id)\n\t\t\t}\n\t\t\tawait this.storage.dequeue(ids)\n\t\t}\n\t\treturn { acknowledged, returned: retry }\n\t}\n\n\t/**\n\t * Record that a taken batch was put on the wire. Its operations may now be stored on\n\t * the server even if no ack ever arrives, so they are excluded from clock rebases.\n\t */\n\tasync markSent(batchId: string): Promise<void> {\n\t\tconst ops = this.inFlight.get(batchId)\n\t\tif (!ops) return\n\t\tconst fresh = ops.filter((op) => !this.sent.has(op.id))\n\t\tif (fresh.length === 0) return\n\t\tfor (const op of fresh) this.sent.add(op.id)\n\t\tawait this.storage.markSent?.(fresh)\n\t}\n\n\t/** Whether this queued operation was ever put on the wire. */\n\twasSent(opId: string): boolean {\n\t\treturn this.sent.has(opId)\n\t}\n\n\t/**\n\t * Forget the sent flag of operations the server explicitly refused without storing\n\t * (for example a batch refused with INVALID_TIMESTAMP), so a clock rebase may re-stamp\n\t * them. In memory only: after a restart they are conservatively treated as sent.\n\t */\n\tclearSent(opIds: string[]): void {\n\t\tfor (const id of opIds) this.sent.delete(id)\n\t}\n\n\t/** Whether the queue owns this operation (queued or in flight). */\n\thas(opId: string): boolean {\n\t\treturn this.seen.has(opId)\n\t}\n\n\t/** Every operation currently in flight, across batches. */\n\tgetInFlight(): Operation[] {\n\t\treturn [...this.inFlight.values()].flat()\n\t}\n\n\t/**\n\t * Replace some queued (not in-flight) operations by rewritten ones, for example after\n\t * a clock rebase re-stamped never-sent operations under new content-addressed ids.\n\t * Operations not named in `oldIds` are untouched.\n\t */\n\tasync replace(oldIds: string[], newOps: Operation[]): Promise<void> {\n\t\tconst remove = new Set(oldIds)\n\t\tthis.queue = this.queue.filter((op) => !remove.has(op.id))\n\t\tfor (const id of oldIds) {\n\t\t\tthis.seen.delete(id)\n\t\t\tthis.sent.delete(id)\n\t\t}\n\t\tif (oldIds.length > 0) {\n\t\t\tawait this.storage.dequeue(oldIds)\n\t\t}\n\t\tfor (const op of newOps) {\n\t\t\tif (this.seen.has(op.id)) continue\n\t\t\tthis.seen.add(op.id)\n\t\t\tthis.queue.push(op)\n\t\t\tawait this.storage.enqueue(op)\n\t\t}\n\t\tif (this.queue.length > 1) {\n\t\t\tthis.queue = topologicalSort(this.queue)\n\t\t}\n\t}\n\n\t/**\n\t * Return a failed batch to the front of the queue for retry.\n\t * Prepends the operations to maintain priority.\n\t */\n\treturnBatch(batchId: string): void {\n\t\tconst ops = this.inFlight.get(batchId)\n\t\tif (!ops) return\n\n\t\tthis.inFlight.delete(batchId)\n\t\t// Prepend returned ops, then re-sort for causal order\n\t\tthis.queue.unshift(...ops)\n\t\tif (this.queue.length > 1) {\n\t\t\tthis.queue = topologicalSort(this.queue)\n\t\t}\n\t}\n\n\t/**\n\t * Number of operations waiting in the queue (not counting in-flight).\n\t */\n\tget size(): number {\n\t\treturn this.queue.length\n\t}\n\n\t/**\n\t * Total operations including in-flight.\n\t */\n\tget totalPending(): number {\n\t\tlet inFlightCount = 0\n\t\tfor (const ops of this.inFlight.values()) {\n\t\t\tinFlightCount += ops.length\n\t\t}\n\t\treturn this.queue.length + inFlightCount\n\t}\n\n\t/**\n\t * Whether the queue has any operations to send.\n\t */\n\tget hasOperations(): boolean {\n\t\treturn this.queue.length > 0\n\t}\n\n\t/**\n\t * Peek at the first `count` operations without removing them.\n\t */\n\tpeek(count: number): Operation[] {\n\t\treturn this.queue.slice(0, count)\n\t}\n\n\t/**\n\t * All queued operations (not counting in-flight), in causal order.\n\t */\n\tgetAll(): Operation[] {\n\t\treturn [...this.queue]\n\t}\n\n\t/**\n\t * Atomically replace the entire queue with a new set of operations.\n\t *\n\t * Used after a timestamp rebase rewrote the queued operations under new\n\t * content-addressed ids: the old entries must vanish from memory AND from\n\t * persistent storage in one step, or a page refresh could resurrect\n\t * stale-stamped ops the server would reject. Resets the seen set to exactly\n\t * the new ids so the rewritten ops are not treated as duplicates.\n\t */\n\tasync replaceAll(ops: Operation[]): Promise<void> {\n\t\tconst removeIds: string[] = this.queue.map((op) => op.id)\n\t\tfor (const batch of this.inFlight.values()) {\n\t\t\tfor (const op of batch) {\n\t\t\t\tremoveIds.push(op.id)\n\t\t\t}\n\t\t}\n\t\tthis.inFlight.clear()\n\n\t\tthis.queue = ops.length > 1 ? topologicalSort([...ops]) : [...ops]\n\t\tthis.seen.clear()\n\t\tthis.sent.clear()\n\t\tfor (const op of this.queue) {\n\t\t\tthis.seen.add(op.id)\n\t\t}\n\n\t\tif (removeIds.length > 0) {\n\t\t\tawait this.storage.dequeue(removeIds)\n\t\t}\n\t\tfor (const op of this.queue) {\n\t\t\tawait this.storage.enqueue(op)\n\t\t}\n\t}\n\n\t/**\n\t * Whether initialize() has been called.\n\t */\n\tget isInitialized(): boolean {\n\t\treturn this.initialized\n\t}\n\n\t/**\n\t * Remove a single operation the server permanently rejected — from the queue,\n\t * from any in-flight batch, and from durable storage — so a later batch ack or\n\t * a reconnect `returnBatch` can never resend or resurrect it. Returns the\n\t * removed operation so the caller can record it in a durable rejected store,\n\t * or null if it was not present (already acknowledged and removed).\n\t */\n\tasync reject(opId: string): Promise<Operation | null> {\n\t\tlet removed: Operation | null = null\n\n\t\t// Common case: the op was just sent and sits in an in-flight batch.\n\t\tfor (const [batchId, ops] of this.inFlight) {\n\t\t\tconst idx = ops.findIndex((op) => op.id === opId)\n\t\t\tif (idx !== -1) {\n\t\t\t\tremoved = ops[idx] ?? null\n\t\t\t\tops.splice(idx, 1)\n\t\t\t\tif (ops.length === 0) {\n\t\t\t\t\tthis.inFlight.delete(batchId)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\t// Otherwise it may still be queued (not yet sent).\n\t\tif (!removed) {\n\t\t\tconst idx = this.queue.findIndex((op) => op.id === opId)\n\t\t\tif (idx !== -1) {\n\t\t\t\tremoved = this.queue[idx] ?? null\n\t\t\t\tthis.queue.splice(idx, 1)\n\t\t\t}\n\t\t}\n\n\t\tthis.seen.delete(opId)\n\t\tthis.sent.delete(opId)\n\t\tawait this.storage.dequeue([opId])\n\t\treturn removed\n\t}\n\n\t/** Remove every pending/in-flight operation for a record so authorization loss cannot upload it. */\n\tasync rejectRecord(collection: string, recordId: string): Promise<Operation[]> {\n\t\t// An operation already put on the wire may be stored by the server whatever this\n\t\t// device decides now: it stays in flight and its ack (stored) or rejection\n\t\t// resolves it. Quarantining it here would exclude from this device's fold a write\n\t\t// every other replica folds (merge breakage found in Phase 3: RT-27 retract case).\n\t\tconst matching = [...this.queue, ...[...this.inFlight.values()].flat()].filter(\n\t\t\t(op) => op.collection === collection && op.recordId === recordId && !this.sent.has(op.id),\n\t\t)\n\t\tconst removed: Operation[] = []\n\t\tfor (const operation of matching) {\n\t\t\tconst rejected = await this.reject(operation.id)\n\t\t\tif (rejected) removed.push(rejected)\n\t\t}\n\t\treturn removed\n\t}\n\n\t/**\n\t * Remove operations by id from queue and persistent storage.\n\t * Used when ops were already sent during handshake delta exchange.\n\t */\n\tasync removeByIds(ids: string[]): Promise<void> {\n\t\tif (ids.length === 0) return\n\t\tconst idSet = new Set(ids)\n\t\tthis.queue = this.queue.filter((op) => !idSet.has(op.id))\n\t\tfor (const id of ids) {\n\t\t\tthis.seen.delete(id)\n\t\t\tthis.sent.delete(id)\n\t\t}\n\t\tawait this.storage.dequeue(ids)\n\t}\n}\n","import {\n\tbase64ToBytes,\n\tbytesToBase64,\n\tcanonicalizeLegacyOperation,\n\tisKoraBytesValue,\n\tverifyOperationId,\n} from '@korajs/core'\nimport type { FieldDescriptor, Operation, SchemaDefinition } from '@korajs/core'\nimport { INVALID_OPERATION_ID } from '../protocol/protocol-version'\n\n/** Prefix of node ids reserved for Kora itself (server-authored and server-synthesized). */\nconst RESERVED_NODE_PREFIX = 'kora:'\n\n/** Node of server-synthesized scope-entry operations (ids derived from the trigger op). */\nconst SCOPE_ENTRY_NODE_ID = 'kora:scope-entry'\n\n/**\n * Whether an operation's id is exempt from content verification: it is not a content\n * hash by construction. Only two kinds of operation qualify, both on Kora's reserved\n * nodes, which no device can claim:\n * - scope entries (`kora:scope-entry`): the id names the triggering operation, and the\n * data is the record's current row;\n * - server-derived operations (cascades, set-nulls, constraint corrections) of a\n * `kora:server:` node: the id is a keyed HMAC (RT-64) and they declare no hash version.\n *\n * A reserved-node operation that declares a hash version (a server route write, built\n * with `createOperation`) claims a content-addressed id and is verified like any other.\n */\nfunction isExemptFromIdVerification(op: Operation): boolean {\n\tif (!op.nodeId.startsWith(RESERVED_NODE_PREFIX)) return false\n\treturn op.nodeId === SCOPE_ENTRY_NODE_ID || op.hashVersion === undefined\n}\n\n/** Outcome of {@link verifyInboundOperation}. */\nexport type InboundVerification =\n\t| {\n\t\t\tok: true\n\t\t\t/** Whether the id was checked (false: not content-addressed, or not declared). */\n\t\t\tverified: boolean\n\t\t\t/**\n\t\t\t * The hash version the id matched, when it declared none (`'verify-ids'`): 1, or 2\n\t\t\t * for a version-2 id whose declaration was lost on the way.\n\t\t\t */\n\t\t\tmatchedVersion?: 1 | 2\n\t\t\t/**\n\t\t\t * False when the id matched only through the schema-dependent rebuild of a\n\t\t\t * beta.12 hash (declared nested object members that were `undefined`): a\n\t\t\t * receiver without the schema cannot repeat that check, so the server must not\n\t\t\t * declare the matched version on the stored copy. Absent: true.\n\t\t\t */\n\t\t\tdeclarable?: boolean\n\t\t\t/**\n\t\t\t * The update's data with each top-level member beta.12 hashed as `undefined`\n\t\t\t * restored as `null` (RT-71): beta.12 cleared those fields locally, and `null`\n\t\t\t * hashes identically under version 1, so the stored copy carries the write the\n\t\t\t * beta.12 client made, under the same verified id. Present only when the id\n\t\t\t * matched through that rebuild.\n\t\t\t */\n\t\t\trestoredData?: Operation['data']\n\t }\n\t| { ok: false; code: typeof INVALID_OPERATION_ID; message: string }\n\n/** How {@link verifyInboundOperation} treats an operation that declares no hash version. */\nexport type AbsentHashVersionPolicy =\n\t/**\n\t * Not checked (clients). The server declares the version of every id it verified\n\t * (1 or 2) when it stores the operation; an absent version marks an id the server\n\t * could not verify: a log row from before beta.13, a server-side schema transform\n\t * (which rewrites data under the original id), or a protocol-1 encrypted payload.\n\t */\n\t| 'skip'\n\t/**\n\t * Checked (the server, on upload: omitting the version skips nothing): the id must be\n\t * the operation's version-1 or version-2 content hash.\n\t */\n\t| 'verify-ids'\n\n/**\n * Verify a delivered (or uploaded) operation's content-addressed id (CORE-1, RT-64).\n * Runs AFTER decryption, on the plaintext the id was computed over, and before any\n * transform or apply. A mismatch means the operation was altered after it was created\n * (in transit, by a relay, or a ciphertext swap that kept its id), or that its id was\n * chosen rather than computed (squatting an id the server derives).\n *\n * Rules:\n * - Scope entries and server-derived operations (reserved `kora:` nodes, no declared hash\n * version: keyed HMAC ids) are not checked. A reserved-node operation that declares a\n * hash version (a server route write) is checked like any other.\n * - An operation that arrived in an encryption envelope is always checked, against\n * the hash version it declares (bound into the envelope's authenticated data).\n * - A plaintext operation declaring hash version 2 or 1 is checked against that version.\n * Version 1 covers type, collection, recordId, data, timestamp, nodeId and atomicOps;\n * binary values are accepted in either of their two forms (bytes, or the canonical\n * `{ $koraBytes }` form a JSON round trip produces), since a version-1 hash was\n * computed over whichever form the writer held.\n * - A plaintext operation declaring no version follows `absentVersion`: skipped, or\n * accepted when its id is its version-1 or version-2 content hash.\n * - An unknown declared version fails closed.\n *\n * @param op - The operation, decrypted\n * @param context - Whether it arrived in an encryption envelope, and the policy for an\n * operation that declares no hash version (default `'skip'`)\n * @returns ok, or the refusal with a code and an explanation\n */\nexport async function verifyInboundOperation(\n\top: Operation,\n\tcontext: {\n\t\tencrypted: boolean\n\t\tabsentVersion?: AbsentHashVersionPolicy\n\t\t/** The schema, for rebuilding beta.12 hashes of `undefined` nested members (RT-71). */\n\t\tschema?: SchemaDefinition | null\n\t},\n): Promise<InboundVerification> {\n\tif (isExemptFromIdVerification(op)) return { ok: true, verified: false }\n\tconst declared = op.hashVersion\n\tif (declared !== undefined && declared !== 1 && declared !== 2) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tcode: INVALID_OPERATION_ID,\n\t\t\tmessage: `Operation \"${op.id}\" declares unknown content-hash version ${String(declared)}; it cannot be verified and is not applied. Upgrade this client.`,\n\t\t}\n\t}\n\tif (\n\t\t!context.encrypted &&\n\t\tdeclared === undefined &&\n\t\t(context.absentVersion ?? 'skip') === 'skip'\n\t) {\n\t\treturn { ok: true, verified: false }\n\t}\n\tif (declared === undefined && !context.encrypted) {\n\t\tif (await verifyOperationId(op)) return { ok: true, verified: true, matchedVersion: 1 }\n\t\tif (await verifyOperationId({ ...op, hashVersion: 2 })) {\n\t\t\treturn { ok: true, verified: true, matchedVersion: 2 }\n\t\t}\n\t\tconst match = await matchOperationId(op, context.schema ?? null)\n\t\tif (match !== null) {\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tverified: true,\n\t\t\t\tmatchedVersion: 1,\n\t\t\t\t// Not declarable: a receiver without the schema cannot repeat a nested rebuild,\n\t\t\t\t// and a Date-form match never bound the Date's value (RT-90), so the stored\n\t\t\t\t// copy must not claim a verified version.\n\t\t\t\tdeclarable: !match.usedSchema && match.usedDateForm !== true,\n\t\t\t\t...(match.usedPrevious ? { restoredData: restoreUndefinedFromPrevious(op) } : {}),\n\t\t\t}\n\t\t}\n\t} else if ((await matchOperationId(op, context.schema ?? null)) !== null) {\n\t\treturn { ok: true, verified: true }\n\t}\n\treturn {\n\t\tok: false,\n\t\tcode: INVALID_OPERATION_ID,\n\t\tmessage: `Operation \"${op.id}\" from node \"${op.nodeId}\" does not match its content hash (hash version ${String(declared ?? 1)}${context.encrypted ? ', after decryption' : ''}): it was altered after it was created, or its id was not computed from its content. It is refused, not applied.`,\n\t}\n}\n\n/**\n * Whether the operation's id is its content hash under its declared version (absent:\n * 1). A version-1 id is also accepted over the other form of its binary values, and\n * over the beta.12 form of `undefined` members (see {@link matchOperationId}).\n *\n * @param op - The operation (plaintext)\n * @param schema - Optional schema, to rebuild declared nested members\n * @returns true when the id is the content hash\n */\nexport async function operationIdMatches(\n\top: Operation,\n\tschema: SchemaDefinition | null = null,\n): Promise<boolean> {\n\treturn (await matchOperationId(op, schema)) !== null\n}\n\n/** How an id matched: which beta.12 rebuilds (if any) its content needed. */\nexport interface OperationIdMatch {\n\t/** Top-level members restored from an update's `previousData` keys. */\n\tusedPrevious: boolean\n\t/** Declared nested members restored (needs the schema). */\n\tusedSchema: boolean\n\t/**\n\t * Strings in `Date#toISOString()` form hashed as `{}` (RT-90): beta.12 hashed a Date as\n\t * `{}`, so such an id never covered the Date's value.\n\t */\n\tusedDateForm?: boolean\n}\n\n/**\n * Match an operation's id against its content hash (RT-64, RT-71).\n *\n * Version 2 is checked as is: its canonical form is the JSON form (an `undefined`\n * member is absent, RT-72), so what was hashed is what arrives.\n *\n * Version 1 (beta.12 and earlier) hashed the in-memory value with `canonicalize`,\n * which writes an `undefined` object member as `\"key\":null`; the op log and the wire\n * are JSON, so the member is gone on arrival. The forms beta.12 actually produced are\n * rebuilt where they are recoverable:\n * - an update: beta.12 writes `previousData[key]` for every key it validated, so every\n * `previousData` key absent from `data` was an `undefined` value in `data`\n * (`update(id, { assignee: undefined })`);\n * - an object field (needs the schema): declared nested members absent from the value\n * (`{ a: 1, b: undefined }`), recursively for declared nested objects;\n * - a `Date` inside a json value (RT-90): `canonicalize` walks `Object.keys`, so it\n * hashed as `{}`, while the JSON log and wire hold its `toISOString()` string. Strings\n * in exactly that form are tried as `{}` (see {@link dateFormCandidates}).\n * Each form is also tried with the other form of its binary values (bytes, or the\n * canonical `{ $koraBytes }` form a JSON round trip produces).\n *\n * @param op - The operation (plaintext)\n * @param schema - The schema, or null (no nested rebuild)\n * @returns how the id matched, or null when it is not the content hash\n */\nexport async function matchOperationId(\n\top: Operation,\n\tschema: SchemaDefinition | null,\n): Promise<OperationIdMatch | null> {\n\tif (await verifyOperationId(op)) return { usedPrevious: false, usedSchema: false }\n\tif ((op.hashVersion ?? 1) !== 1) return null\n\tconst candidates: Array<{ data: Operation['data']; kind: OperationIdMatch }> = [\n\t\t{ data: op.data, kind: { usedPrevious: false, usedSchema: false } },\n\t]\n\tconst filled = restoreUndefinedFromPrevious(op)\n\tif (filled !== op.data) {\n\t\tcandidates.push({ data: filled, kind: { usedPrevious: true, usedSchema: false } })\n\t}\n\tif (schema) {\n\t\tfor (const candidate of [...candidates]) {\n\t\t\tconst nested = fillDeclaredNestedMembers(candidate.data, op.collection, schema)\n\t\t\tif (nested !== null) {\n\t\t\t\tcandidates.push({ data: nested, kind: { ...candidate.kind, usedSchema: true } })\n\t\t\t}\n\t\t}\n\t}\n\t// beta.12 (and older) hashed a Date inside a json value as `{}` (RT-90).\n\tfor (const candidate of [...candidates]) {\n\t\tfor (const data of dateFormCandidates(candidate.data)) {\n\t\t\tcandidates.push({ data, kind: { ...candidate.kind, usedDateForm: true } })\n\t\t}\n\t}\n\tfor (const candidate of candidates) {\n\t\tif (candidate.data !== op.data && (await verifyOperationId({ ...op, data: candidate.data }))) {\n\t\t\treturn candidate.kind\n\t\t}\n\t\tfor (const convert of [toBytesForm, toKoraBytesForm]) {\n\t\t\tconst data = convert(candidate.data)\n\t\t\tconst atomicOps = op.atomicOps === undefined ? undefined : convert(op.atomicOps)\n\t\t\tif (data.changed || atomicOps?.changed) {\n\t\t\t\tconst variant: Operation = {\n\t\t\t\t\t...op,\n\t\t\t\t\tdata: data.value as Operation['data'],\n\t\t\t\t\t...(atomicOps ? { atomicOps: atomicOps.value as Operation['atomicOps'] } : {}),\n\t\t\t\t}\n\t\t\t\tif (await verifyOperationId(variant)) return candidate.kind\n\t\t\t}\n\t\t}\n\t}\n\treturn null\n}\n\n/**\n * A beta.12 update's data with every `previousData` key it lacks restored as `null`\n * (RT-71). beta.12 writes `previousData[key]` for every key it validated, so such a key\n * held `undefined` in `data`: hashed as `null`, applied locally as a cleared field, and\n * dropped by the JSON upload. Returns `op.data` itself when there is none to restore.\n *\n * @param op - A plaintext operation\n * @returns The restored data, or `op.data` unchanged\n */\nexport function restoreUndefinedFromPrevious(op: Operation): Operation['data'] {\n\t// One rule for every replica: core's legacy canonical body (RT-71, RT-83).\n\treturn canonicalizeLegacyOperation({ ...op, hashVersion: undefined }).data\n}\n\n/**\n * The data with each declared nested member an object value lacks restored as `null`\n * (recursively), or null when nothing was restored.\n */\nfunction fillDeclaredNestedMembers(\n\tdata: Operation['data'],\n\tcollection: string,\n\tschema: SchemaDefinition,\n): Operation['data'] {\n\tconst fields = schema.collections[collection]?.fields\n\tif (data === null || fields === undefined) return null\n\tlet changed = false\n\tconst out: Record<string, unknown> = { ...data }\n\tfor (const [name, value] of Object.entries(data)) {\n\t\tconst descriptor = fields[name]\n\t\tif (descriptor === undefined) continue\n\t\tconst filled = fillNested(value, descriptor)\n\t\tif (filled !== value) {\n\t\t\tout[name] = filled\n\t\t\tchanged = true\n\t\t}\n\t}\n\treturn changed ? out : null\n}\n\nfunction fillNested(value: unknown, descriptor: FieldDescriptor): unknown {\n\tconst nested = descriptor.nestedFields\n\tif (descriptor.kind !== 'object' || !nested) return value\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return value\n\tconst record = value as Record<string, unknown>\n\tlet changed = false\n\tconst out: Record<string, unknown> = { ...record }\n\tfor (const [name, child] of Object.entries(nested)) {\n\t\tif (!(name in record)) {\n\t\t\tout[name] = null\n\t\t\tchanged = true\n\t\t\tcontinue\n\t\t}\n\t\tconst filled = fillNested(record[name], child)\n\t\tif (filled !== record[name]) {\n\t\t\tout[name] = filled\n\t\t\tchanged = true\n\t\t}\n\t}\n\treturn changed ? out : value\n}\n\n/** `Date#toISOString()` output, including its extended-year form. */\nconst ISO_DATE_STRING = /^(?:\\d{4}|[+-]\\d{6})-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$/\n\n/**\n * Most ISO strings per operation whose every Date/string combination is tried; above\n * it only \"all of them were Dates\" is tried (bounds the hashing work to 2^6 - 1).\n */\nconst MAX_DATE_FORM_STRINGS = 6\n\n/**\n * The data with strings in `Date#toISOString()` form replaced by `{}`: how beta.12 (and\n * older) hashed a `Date` held in a json value (RT-90). Which of the strings were Dates\n * is unknown, so every non-empty subset is returned (all of them only, above\n * {@link MAX_DATE_FORM_STRINGS}). Empty when the data holds no such string.\n *\n * The stored data keeps the string, which is what beta.12 wrote to its row and applied.\n * Such an id never covered the Date's value (beta.12's hash did not either), so a match\n * through this rebuild is not declarable: the server stores the operation without a\n * declared hash version, as it stored every unverifiable protocol-1 id.\n */\nfunction dateFormCandidates(data: Operation['data']): Array<Operation['data']> {\n\tif (data === null) return []\n\tconst paths: Array<Array<string | number>> = []\n\tconst collect = (value: unknown, path: Array<string | number>): void => {\n\t\tif (typeof value === 'string') {\n\t\t\tif (ISO_DATE_STRING.test(value)) paths.push(path)\n\t\t\treturn\n\t\t}\n\t\tif (Array.isArray(value)) {\n\t\t\tvalue.forEach((item, index) => collect(item, [...path, index]))\n\t\t\treturn\n\t\t}\n\t\tif (value !== null && typeof value === 'object' && !(value instanceof Uint8Array)) {\n\t\t\tfor (const [key, item] of Object.entries(value)) collect(item, [...path, key])\n\t\t}\n\t}\n\tfor (const [field, value] of Object.entries(data)) collect(value, [field])\n\tif (paths.length === 0) return []\n\tconst subsets: Array<Array<Array<string | number>>> = []\n\tif (paths.length > MAX_DATE_FORM_STRINGS) {\n\t\tsubsets.push(paths)\n\t} else {\n\t\tfor (let mask = 1; mask < 1 << paths.length; mask++) {\n\t\t\tsubsets.push(paths.filter((_path, index) => (mask & (1 << index)) !== 0))\n\t\t}\n\t}\n\treturn subsets.map((subset) => {\n\t\tlet out: unknown = data\n\t\tfor (const path of subset) out = replaceAt(out, path, {})\n\t\treturn out as Operation['data']\n\t})\n}\n\n/** A copy of `value` with the member at `path` replaced (copy on write). */\nfunction replaceAt(\n\tvalue: unknown,\n\tpath: ReadonlyArray<string | number>,\n\treplacement: unknown,\n): unknown {\n\tconst [head, ...rest] = path\n\tif (head === undefined) return replacement\n\tif (Array.isArray(value)) {\n\t\tconst copy = [...value]\n\t\tcopy[head as number] = replaceAt(copy[head as number], rest, replacement)\n\t\treturn copy\n\t}\n\tconst record = value as Record<string, unknown>\n\treturn { ...record, [head]: replaceAt(record[head as string], rest, replacement) }\n}\n\ninterface Converted {\n\tvalue: unknown\n\tchanged: boolean\n}\n\n/** `{ $koraBytes }` values replaced by the bytes they encode. */\nfunction toBytesForm(value: unknown): Converted {\n\tif (isKoraBytesValue(value)) {\n\t\ttry {\n\t\t\treturn { value: base64ToBytes(value.$koraBytes), changed: true }\n\t\t} catch {\n\t\t\treturn { value, changed: false }\n\t\t}\n\t}\n\treturn mapMembers(value, toBytesForm)\n}\n\n/** Byte values replaced by their canonical `{ $koraBytes }` form. */\nfunction toKoraBytesForm(value: unknown): Converted {\n\tif (value instanceof Uint8Array)\n\t\treturn { value: { $koraBytes: bytesToBase64(value) }, changed: true }\n\treturn mapMembers(value, toKoraBytesForm)\n}\n\nfunction mapMembers(value: unknown, convert: (member: unknown) => Converted): Converted {\n\tif (Array.isArray(value)) {\n\t\tlet changed = false\n\t\tconst out = value.map((member) => {\n\t\t\tconst result = convert(member)\n\t\t\tchanged ||= result.changed\n\t\t\treturn result.value\n\t\t})\n\t\treturn changed ? { value: out, changed } : { value, changed: false }\n\t}\n\tif (value !== null && typeof value === 'object' && !(value instanceof Uint8Array)) {\n\t\tlet changed = false\n\t\tconst out: Record<string, unknown> = {}\n\t\tfor (const [key, member] of Object.entries(value)) {\n\t\t\tconst result = convert(member)\n\t\t\tchanged ||= result.changed\n\t\t\tout[key] = result.value\n\t\t}\n\t\treturn changed ? { value: out, changed } : { value, changed: false }\n\t}\n\treturn { value, changed: false }\n}\n","import type { ConnectionQuality, TimeSource } from '@korajs/core'\n\n/**\n * Configuration for the connection monitor.\n */\nexport interface ConnectionMonitorConfig {\n\t/** Number of latency samples to keep in the rolling window. Defaults to 20. */\n\twindowSize?: number\n\t/** Time in ms after which the connection is considered stale. Defaults to 30000. */\n\tstaleThreshold?: number\n\t/** Injectable time source for deterministic testing */\n\ttimeSource?: TimeSource\n}\n\n/**\n * Monitors connection quality based on RTT latency samples,\n * missed acknowledgments, and activity timestamps.\n */\nexport class ConnectionMonitor {\n\tprivate readonly windowSize: number\n\tprivate readonly staleThreshold: number\n\tprivate readonly timeSource: TimeSource\n\n\tprivate latencies: number[] = []\n\tprivate missedAcks = 0\n\tprivate lastActivityTime: number\n\n\tconstructor(config?: ConnectionMonitorConfig) {\n\t\tthis.windowSize = config?.windowSize ?? 20\n\t\tthis.staleThreshold = config?.staleThreshold ?? 30000\n\t\tthis.timeSource = config?.timeSource ?? { now: () => Date.now() }\n\t\tthis.lastActivityTime = this.timeSource.now()\n\t}\n\n\t/**\n\t * Record a round-trip time sample.\n\t */\n\trecordLatency(ms: number): void {\n\t\tthis.latencies.push(ms)\n\t\tif (this.latencies.length > this.windowSize) {\n\t\t\tthis.latencies.shift()\n\t\t}\n\t\tthis.lastActivityTime = this.timeSource.now()\n\t\t// Successful response resets missed acks\n\t\tthis.missedAcks = 0\n\t}\n\n\t/**\n\t * Record a missed acknowledgment (message sent but no response received).\n\t */\n\trecordMissedAck(): void {\n\t\tthis.missedAcks++\n\t}\n\n\t/**\n\t * Record any activity (message sent or received).\n\t */\n\trecordActivity(): void {\n\t\tthis.lastActivityTime = this.timeSource.now()\n\t}\n\n\t/**\n\t * Assess current connection quality based on collected metrics.\n\t *\n\t * Quality thresholds (average RTT):\n\t * - excellent: < 100ms, 0 missed acks\n\t * - good: < 300ms, ≤ 1 missed ack\n\t * - fair: < 1000ms, ≤ 3 missed acks\n\t * - poor: < 5000ms or > 3 missed acks\n\t * - offline: no activity for staleThreshold ms\n\t */\n\tgetQuality(): ConnectionQuality {\n\t\tconst elapsed = this.timeSource.now() - this.lastActivityTime\n\t\tif (elapsed > this.staleThreshold) return 'offline'\n\n\t\tif (this.latencies.length === 0) {\n\t\t\t// No data yet — assume good until proven otherwise\n\t\t\treturn this.missedAcks > 3 ? 'poor' : 'good'\n\t\t}\n\n\t\tconst avgLatency = this.latencies.reduce((sum, l) => sum + l, 0) / this.latencies.length\n\n\t\tif (this.missedAcks > 3) return 'poor'\n\t\tif (avgLatency < 100 && this.missedAcks === 0) return 'excellent'\n\t\tif (avgLatency < 300 && this.missedAcks <= 1) return 'good'\n\t\tif (avgLatency < 1000 && this.missedAcks <= 3) return 'fair'\n\t\treturn 'poor'\n\t}\n\n\t/**\n\t * Reset all metrics. Call on disconnect.\n\t */\n\treset(): void {\n\t\tthis.latencies = []\n\t\tthis.missedAcks = 0\n\t\tthis.lastActivityTime = this.timeSource.now()\n\t}\n\n\t/**\n\t * Get the current average latency in ms. Returns null if no samples.\n\t */\n\tgetAverageLatency(): number | null {\n\t\tif (this.latencies.length === 0) return null\n\t\treturn this.latencies.reduce((sum, l) => sum + l, 0) / this.latencies.length\n\t}\n\n\t/**\n\t * Get the number of missed acks.\n\t */\n\tgetMissedAcks(): number {\n\t\treturn this.missedAcks\n\t}\n}\n","import type { TimeSource } from '@korajs/core'\n\n/**\n * Configuration for the reconnection manager.\n */\nexport interface ReconnectionConfig {\n\t/** Initial delay in ms before first reconnection attempt. Defaults to 1000. */\n\tinitialDelay?: number\n\t/** Maximum delay in ms between attempts. Defaults to 30000. */\n\tmaxDelay?: number\n\t/** Multiplier for exponential backoff. Defaults to 2. */\n\tmultiplier?: number\n\t/** Maximum number of reconnection attempts. 0 means unlimited. Defaults to 0. */\n\tmaxAttempts?: number\n\t/** Jitter factor (0-1). Random variation applied to delay. Defaults to 0.25. */\n\tjitter?: number\n\t/**\n\t * How long a connection must stay up (see {@link ReconnectionManager.reportConnected})\n\t * before the backoff resets to the initial delay, in ms (SYNC-8). A server that\n\t * accepts sessions and drops them right away therefore keeps backing off instead of\n\t * being retried at the initial interval forever. Defaults to 10000.\n\t */\n\tstableAfterMs?: number\n\t/** Injectable time source for deterministic testing. */\n\ttimeSource?: TimeSource\n\t/** Injectable random source for deterministic jitter. Returns value in [0, 1). */\n\trandomSource?: () => number\n}\n\n/**\n * Manages reconnection attempts with exponential backoff and jitter.\n *\n * Formula: min(initialDelay * multiplier^attempt, maxDelay) * (1 + jitter * (random - 0.5) * 2)\n *\n * The attempt counter belongs to the manager, not to one `start()` run (SYNC-8): it\n * keeps growing across runs and resets only once a connection stayed up for\n * `stableAfterMs` ({@link reportConnected}), or on an explicit {@link reset}.\n */\nexport class ReconnectionManager {\n\tprivate readonly initialDelay: number\n\tprivate readonly maxDelay: number\n\tprivate readonly multiplier: number\n\tprivate readonly maxAttempts: number\n\tprivate readonly jitter: number\n\tprivate readonly stableAfterMs: number\n\tprivate readonly random: () => number\n\n\tprivate attempt = 0\n\tprivate timer: ReturnType<typeof setTimeout> | null = null\n\tprivate stableTimer: ReturnType<typeof setTimeout> | null = null\n\tprivate stopped = false\n\tprivate running = false\n\t/** A disconnect was reported while an attempt was in flight (NEW-SYNC-2). */\n\tprivate pendingRetry = false\n\tprivate waitResolve: (() => void) | null = null\n\n\tconstructor(config?: ReconnectionConfig) {\n\t\tthis.initialDelay = config?.initialDelay ?? 1000\n\t\tthis.maxDelay = config?.maxDelay ?? 30000\n\t\tthis.multiplier = config?.multiplier ?? 2\n\t\tthis.maxAttempts = config?.maxAttempts ?? 0\n\t\tthis.jitter = config?.jitter ?? 0.25\n\t\tthis.stableAfterMs = config?.stableAfterMs ?? 10_000\n\t\tthis.random = config?.randomSource ?? Math.random\n\t}\n\n\t/**\n\t * Start reconnection attempts. Calls `onReconnect` with exponential backoff.\n\t *\n\t * An attempt counts as successful only when `onReconnect` returns true AND no\n\t * disconnect was reported ({@link requestRetry}) while it ran: a session that\n\t * drops before the attempt returns is retried, never silently lost (NEW-SYNC-2).\n\t *\n\t * @param onReconnect - Called on each attempt. Return `true` if reconnection succeeded\n\t * (the caller decides what success means; Kora waits for the `streaming` state).\n\t * @returns Promise that resolves when reconnection succeeds or maxAttempts reached.\n\t */\n\tasync start(onReconnect: () => Promise<boolean>): Promise<boolean> {\n\t\tthis.stopped = false\n\t\tthis.running = true\n\t\tthis.pendingRetry = false\n\t\tthis.clearStableTimer()\n\n\t\ttry {\n\t\t\twhile (!this.stopped) {\n\t\t\t\tif (this.maxAttempts > 0 && this.attempt >= this.maxAttempts) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\n\t\t\t\tconst delay = this.getNextDelay()\n\t\t\t\tthis.attempt++\n\n\t\t\t\tawait this.wait(delay)\n\n\t\t\t\tif (this.stopped) return false\n\n\t\t\t\tthis.pendingRetry = false\n\t\t\t\ttry {\n\t\t\t\t\tconst success = await onReconnect()\n\t\t\t\t\tif (this.stopped) return false\n\t\t\t\t\tif (success && !this.pendingRetry) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Continue retrying on failure\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false\n\t\t} finally {\n\t\t\tthis.running = false\n\t\t\tthis.pendingRetry = false\n\t\t}\n\t}\n\n\t/**\n\t * Report a disconnect. While a run is in flight it makes the current attempt count\n\t * as failed, so the loop retries (with backoff) instead of exiting on a session\n\t * that already dropped. Returns true when a running loop took the request; false\n\t * means the caller should start a new run.\n\t */\n\trequestRetry(): boolean {\n\t\tthis.clearStableTimer()\n\t\tif (!this.running) return false\n\t\tthis.pendingRetry = true\n\t\treturn true\n\t}\n\n\t/**\n\t * Report that a connection is up (it reached streaming). If it is still up after\n\t * `stableAfterMs`, the backoff resets to the initial delay. A disconnect reported\n\t * before then ({@link requestRetry} or {@link reportDisconnected}) cancels the reset.\n\t */\n\treportConnected(): void {\n\t\tthis.clearStableTimer()\n\t\tif (this.stableAfterMs <= 0) {\n\t\t\tthis.attempt = 0\n\t\t\treturn\n\t\t}\n\t\tthis.stableTimer = setTimeout(() => {\n\t\t\tthis.stableTimer = null\n\t\t\tthis.attempt = 0\n\t\t}, this.stableAfterMs)\n\t\tconst timer = this.stableTimer as { unref?: () => void }\n\t\ttimer.unref?.()\n\t}\n\n\t/** Report that the connection dropped: a pending backoff reset is cancelled. */\n\treportDisconnected(): void {\n\t\tthis.clearStableTimer()\n\t}\n\n\t/**\n\t * Cancel the current backoff wait and proceed to the next reconnect attempt immediately.\n\t * No-op if not waiting.\n\t */\n\twake(): void {\n\t\tif (this.timer !== null) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tthis.timer = null\n\t\t}\n\t\tif (this.waitResolve) {\n\t\t\tthis.waitResolve()\n\t\t\tthis.waitResolve = null\n\t\t}\n\t}\n\n\t/**\n\t * Stop any pending reconnection attempt.\n\t */\n\tstop(): void {\n\t\tthis.stopped = true\n\t\tthis.clearStableTimer()\n\t\tif (this.timer !== null) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tthis.timer = null\n\t\t}\n\t\t// Resolve the pending wait promise so start() loop can exit\n\t\tif (this.waitResolve) {\n\t\t\tthis.waitResolve()\n\t\t\tthis.waitResolve = null\n\t\t}\n\t}\n\n\t/**\n\t * Reset the attempt counter to the initial delay. Call after a deliberate\n\t * (user-initiated) reconnection. It never clears a {@link stop}: a stopped loop\n\t * stays stopped, so a stop racing a reset cannot resurrect it (SYNC-8).\n\t */\n\treset(): void {\n\t\tthis.attempt = 0\n\t}\n\n\t/**\n\t * Compute the next delay for the current attempt.\n\t * Exposed for testing purposes.\n\t */\n\tgetNextDelay(): number {\n\t\tconst baseDelay = Math.min(this.initialDelay * this.multiplier ** this.attempt, this.maxDelay)\n\n\t\t// Apply jitter: varies the delay by ±jitter factor\n\t\tconst jitterRange = baseDelay * this.jitter\n\t\tconst jitterOffset = (this.random() - 0.5) * 2 * jitterRange\n\t\treturn Math.max(0, Math.round(baseDelay + jitterOffset))\n\t}\n\n\t/**\n\t * Whether the reconnection loop is currently running.\n\t */\n\tisRunning(): boolean {\n\t\treturn this.running\n\t}\n\n\t/**\n\t * Current attempt number (for testing).\n\t */\n\tgetAttemptCount(): number {\n\t\treturn this.attempt\n\t}\n\n\tprivate clearStableTimer(): void {\n\t\tif (this.stableTimer !== null) {\n\t\t\tclearTimeout(this.stableTimer)\n\t\t\tthis.stableTimer = null\n\t\t}\n\t}\n\n\tprivate wait(ms: number): Promise<void> {\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.waitResolve = resolve\n\t\t\tthis.timer = setTimeout(() => {\n\t\t\t\tthis.timer = null\n\t\t\t\tthis.waitResolve = null\n\t\t\t\tresolve()\n\t\t\t}, ms)\n\t\t})\n\t}\n}\n","import { KoraError } from '@korajs/core'\nimport type { SchemaDefinition } from '@korajs/core'\n\n/** The part of the sync encryption config that decides what the server can read. */\nexport interface EncryptedRelationConfig {\n\tenabled?: boolean\n\tcleartextFields?: Readonly<Record<string, readonly string[]>>\n}\n\n/**\n * Thrown at app initialization when end-to-end encryption would seal the foreign-key\n * field of a relation with a referential `onDelete` policy.\n */\nexport class SealedRelationFieldError extends KoraError {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly relation: string,\n\t\tpublic readonly collection: string,\n\t\tpublic readonly field: string,\n\t) {\n\t\tsuper(message, 'SEALED_RELATION_FIELD', {\n\t\t\trelation,\n\t\t\tcollection,\n\t\t\tfield,\n\t\t\tfix: `Add \"${field}\" to sync.encryption.cleartextFields.${collection}.`,\n\t\t})\n\t\tthis.name = 'SealedRelationFieldError'\n\t}\n}\n\n/**\n * Refuse a configuration where end-to-end encryption seals the foreign key of a\n * relation whose `onDelete` is `cascade`, `set-null` or `restrict`.\n *\n * Referential policies are enforced by the sync server, the one replica that sees every\n * child: it cascades (or nulls, or refuses) for children no single device knows about,\n * and relays the result. It can only do so when it can read the foreign key. With the\n * key sealed, no replica can enforce the policy for everyone, and every device-side\n * substitute either loses a cascade or deletes a child another device moved away\n * (RT-74, RT-78, RT-82). So the foreign key of such a relation must travel in cleartext:\n * it reveals which parent a child belongs to, never the child's other fields.\n *\n * Relations with `onDelete: 'no-action'` (or none) are not affected: nothing is\n * enforced on delete, so their foreign key may stay sealed.\n *\n * @param schema - The app schema\n * @param encryption - The sync encryption config (none, or disabled: nothing to check)\n * @throws {SealedRelationFieldError} Naming the relation, the field and the fix\n */\nexport function validateEncryptedRelations(\n\tschema: SchemaDefinition,\n\tencryption: EncryptedRelationConfig | null | undefined,\n): void {\n\tif (!encryption || encryption.enabled !== true) return\n\tfor (const [name, relation] of Object.entries(schema.relations ?? {})) {\n\t\tconst policy = relation.onDelete\n\t\tif (policy !== 'cascade' && policy !== 'set-null' && policy !== 'restrict') continue\n\t\tconst cleartext = encryption.cleartextFields?.[relation.from] ?? []\n\t\tif (cleartext.includes(relation.field)) continue\n\t\tthrow new SealedRelationFieldError(\n\t\t\t`Relation \"${name}\" (${relation.from}.${relation.field} -> ${relation.to}, onDelete: '${policy}') needs its foreign key in cleartext under end-to-end sync encryption: the sync server enforces '${policy}' for every device and cannot read a sealed \"${relation.field}\". Add it to sync.encryption.cleartextFields: { ${relation.from}: ['${relation.field}'] } (the server then sees which ${relation.to} record each ${relation.from} record belongs to, nothing else), or set onDelete: 'no-action'.`,\n\t\t\tname,\n\t\t\trelation.from,\n\t\t\trelation.field,\n\t\t)\n\t}\n}\n","import type { KoraEventEmitter, KoraEventType } from '@korajs/core'\nimport type { HeldNodeInfo, SyncStatusInfo } from '../types'\n\nconst NO_HELD_NODES: HeldNodeInfo[] = Object.freeze([]) as unknown as HeldNodeInfo[]\n\n/** Default status when sync is not configured or the engine is unavailable. */\nexport const OFFLINE_SYNC_STATUS: SyncStatusInfo = Object.freeze({\n\tstatus: 'offline',\n\tphase: 'offline',\n\treconnecting: false,\n\tpendingOperations: 0,\n\tlastSyncedAt: null,\n\tlastSuccessfulPush: null,\n\tlastSuccessfulPull: null,\n\tconflicts: 0,\n\theldOperations: 0,\n\theldNodes: NO_HELD_NODES,\n\tlocalDurability: 'durable',\n\tserverProtocolVersion: null,\n\tprotocolDeprecated: false,\n\tclockSkewMs: null,\n\tinFlightUploadOperations: 0,\n\thasInFlightDeliveryBatch: false,\n\tactiveViewId: '',\n\tactiveViewComplete: false,\n\tinitialSync: { complete: false, receivedBatches: 0, totalBatches: null, progress: null },\n\tdeliveryWatermark: 0,\n\tserverFrontier: null,\n\tblockedFailure: null,\n})\n\n/**\n * Returns `next` with every value unchanged since `previous` carried over by\n * reference, and the optional Phase 2/3 fields defaulted, so UI bindings see the\n * same object (and the same nested `heldNodes`, `initialSync`, `blockedFailure`)\n * until something actually changes (DX-5). Returns `previous` itself when nothing did.\n */\nexport function stabilizeSyncStatus(\n\tnext: SyncStatusInfo,\n\tprevious: SyncStatusInfo | null,\n): SyncStatusInfo {\n\tconst normalized: SyncStatusInfo = {\n\t\t...next,\n\t\theldOperations: next.heldOperations ?? 0,\n\t\theldNodes: next.heldNodes && next.heldNodes.length > 0 ? next.heldNodes : NO_HELD_NODES,\n\t\tlocalDurability: next.localDurability ?? 'durable',\n\t\tserverProtocolVersion: next.serverProtocolVersion ?? null,\n\t\tprotocolDeprecated: next.protocolDeprecated ?? false,\n\t}\n\tif (previous === null) return Object.freeze(normalized)\n\n\tconst nextRecord = normalized as unknown as Record<string, unknown>\n\tconst prevRecord = previous as unknown as Record<string, unknown>\n\tlet changed = Object.keys(prevRecord).some((key) => !(key in nextRecord))\n\tconst merged: Record<string, unknown> = {}\n\tfor (const [key, value] of Object.entries(nextRecord)) {\n\t\tconst before = prevRecord[key]\n\t\tif (key in prevRecord && (value === before || sameJson(value, before))) {\n\t\t\tmerged[key] = before\n\t\t} else {\n\t\t\tmerged[key] = value\n\t\t\tchanged = true\n\t\t}\n\t}\n\treturn changed ? (Object.freeze(merged) as unknown as SyncStatusInfo) : previous\n}\n\nfunction sameJson(a: unknown, b: unknown): boolean {\n\tif (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {\n\t\treturn false\n\t}\n\treturn JSON.stringify(a) === JSON.stringify(b)\n}\n\nconst SYNC_STATUS_EVENT_TYPES = [\n\t'sync:connected',\n\t'sync:clock-skew',\n\t'sync:disconnected',\n\t'sync:schema-mismatch',\n\t'sync:auth-failed',\n\t'sync:sent',\n\t'sync:received',\n\t'sync:acknowledged',\n\t'sync:apply-failed',\n\t'sync:suspended',\n\t'sync:apply-blocked',\n\t'sync:apply-retrying',\n\t'sync:apply-recovered',\n\t'sync:delivery-gap',\n\t'sync:diagnostics',\n\t'sync:initial-sync-progress',\n\t'sync:durability-degraded',\n\t'sync:durability-restored',\n\t'sync:local-node',\n] as const satisfies readonly KoraEventType[]\n\nexport interface SyncStatusControllerOptions {\n\tsyncEngine?: { getStatus(): SyncStatusInfo } | null\n\tgetSyncEngine?: () => { getStatus(): SyncStatusInfo } | null\n\tsubscribeSyncStatus: ((listener: (status: SyncStatusInfo) => void) => () => void) | null\n\tevents: KoraEventEmitter | null\n}\n\nexport interface SyncStatusController {\n\tgetSnapshot(): SyncStatusInfo\n\tsubscribe(listener: () => void): () => void\n\trefresh(): void\n\tdestroy(): void\n}\n\n/**\n * Framework-agnostic sync status subscription with live reads when no bridge exists.\n */\nexport function createSyncStatusController(\n\toptions: SyncStatusControllerOptions,\n): SyncStatusController {\n\tconst useLiveSnapshot = options.subscribeSyncStatus === null && options.events === null\n\tlet snapshot = OFFLINE_SYNC_STATUS\n\tconst listeners = new Set<() => void>()\n\tlet cleanup: (() => void) | null = null\n\n\tconst resolveEngine = (): { getStatus(): SyncStatusInfo } | null => {\n\t\treturn options.getSyncEngine?.() ?? options.syncEngine ?? null\n\t}\n\n\tconst notify = (): void => {\n\t\tfor (const listener of listeners) {\n\t\t\tlistener()\n\t\t}\n\t}\n\n\tconst setSnapshot = (next: SyncStatusInfo): void => {\n\t\tconst stable = stabilizeSyncStatus(next, snapshot)\n\t\tif (stable === snapshot) {\n\t\t\treturn\n\t\t}\n\t\tsnapshot = stable\n\t\tnotify()\n\t}\n\n\tconst refresh = (): void => {\n\t\tconst engine = resolveEngine()\n\t\tconst next = engine ? engine.getStatus() : OFFLINE_SYNC_STATUS\n\t\tsetSnapshot(next)\n\t}\n\n\tconst attach = (): void => {\n\t\tcleanup?.()\n\n\t\tif (options.subscribeSyncStatus) {\n\t\t\tcleanup = options.subscribeSyncStatus(setSnapshot)\n\t\t\treturn\n\t\t}\n\n\t\tif (!resolveEngine()) {\n\t\t\tsetSnapshot(OFFLINE_SYNC_STATUS)\n\t\t\tcleanup = () => {}\n\t\t\treturn\n\t\t}\n\n\t\tif (options.events) {\n\t\t\tconst unsubs = SYNC_STATUS_EVENT_TYPES.map((type) => options.events?.on(type, refresh))\n\t\t\trefresh()\n\t\t\tcleanup = () => {\n\t\t\t\tfor (const unsub of unsubs) {\n\t\t\t\t\tunsub?.()\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\trefresh()\n\t\tcleanup = () => {}\n\t}\n\n\tattach()\n\n\treturn {\n\t\tgetSnapshot(): SyncStatusInfo {\n\t\t\tif (useLiveSnapshot) {\n\t\t\t\t// The engine builds a new object per call: keep the previous one while equal,\n\t\t\t\t// or useSyncExternalStore would see a change on every read and loop.\n\t\t\t\tconst engine = resolveEngine()\n\t\t\t\tsnapshot = stabilizeSyncStatus(engine ? engine.getStatus() : OFFLINE_SYNC_STATUS, snapshot)\n\t\t\t}\n\t\t\treturn snapshot\n\t\t},\n\t\tsubscribe(listener: () => void): () => void {\n\t\t\tlisteners.add(listener)\n\t\t\tlistener()\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener)\n\t\t\t}\n\t\t},\n\t\trefresh,\n\t\tdestroy(): void {\n\t\t\tcleanup?.()\n\t\t\tcleanup = null\n\t\t\tlisteners.clear()\n\t\t},\n\t}\n}\n","import type { AwarenessManager } from '../awareness/awareness-manager'\nimport type { AwarenessState } from '../awareness/types'\n\nconst EMPTY_COLLABORATORS: AwarenessState[] = []\n\nfunction awarenessStatesEqual(left: AwarenessState[], right: AwarenessState[]): boolean {\n\tif (left.length !== right.length) {\n\t\treturn false\n\t}\n\n\tfor (let index = 0; index < left.length; index++) {\n\t\tconst a = left[index]\n\t\tconst b = right[index]\n\t\tif (!a || !b) {\n\t\t\treturn false\n\t\t}\n\t\tif (a.user.name !== b.user.name || a.user.color !== b.user.color) {\n\t\t\treturn false\n\t\t}\n\t\tconst aCursor = a.cursor\n\t\tconst bCursor = b.cursor\n\t\tif (aCursor === undefined && bCursor === undefined) {\n\t\t\tcontinue\n\t\t}\n\t\tif (!aCursor || !bCursor) {\n\t\t\treturn false\n\t\t}\n\t\tif (\n\t\t\taCursor.collection !== bCursor.collection ||\n\t\t\taCursor.recordId !== bCursor.recordId ||\n\t\t\taCursor.field !== bCursor.field ||\n\t\t\taCursor.anchor !== bCursor.anchor ||\n\t\t\taCursor.head !== bCursor.head\n\t\t) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\n/**\n * Returns remote collaborators' awareness states (excludes the local client).\n */\nexport function getRemoteAwarenessStates(awareness: AwarenessManager): AwarenessState[] {\n\tconst localClientId = awareness.clientId\n\tconst remoteStates: AwarenessState[] = []\n\n\tfor (const [clientId, state] of awareness.getStates()) {\n\t\tif (clientId === localClientId) continue\n\t\tremoteStates.push(state)\n\t}\n\n\treturn remoteStates\n}\n\n/**\n * Subscribes to awareness changes and invokes `listener` only when the remote\n * collaborator set changes.\n */\nexport function subscribeRemoteAwarenessStates(\n\tawareness: AwarenessManager,\n\tlistener: (states: AwarenessState[]) => void,\n): () => void {\n\tlet snapshot = EMPTY_COLLABORATORS\n\n\tconst emitIfChanged = (): void => {\n\t\tconst next = getRemoteAwarenessStates(awareness)\n\t\tif (awarenessStatesEqual(snapshot, next)) {\n\t\t\treturn\n\t\t}\n\t\tsnapshot = next\n\t\tlistener(snapshot)\n\t}\n\n\temitIfChanged()\n\treturn awareness.on('change', emitIfChanged)\n}\n"],"mappings":"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","names":["import_core","protobuf","import_core","import_core","buffer","buffer","import_core","import_core","import_core","import_core","import_core","toBase64","fromBase64","toBase64","fromBase64","import_core","toBase64","fromBase64","outcome","toBase64","fromBase64","import_core","canonicalJson","import_internal","import_core","stableStringify","op","import_core"]}
|