loro-repo 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"websocket.js","names":["auth: Uint8Array | undefined","session","subscription: TransportSubscription","session: MetadataSession","session: DocSession"],"sources":["../../src/internal/debug.ts","../../src/transport/websocket.ts"],"sourcesContent":["type EnvRecord = Record<string, string | undefined>;\n\nconst getEnv = (): EnvRecord | undefined => {\n if (typeof globalThis !== \"object\" || globalThis === null) {\n return undefined;\n }\n const processLike = (globalThis as { process?: { env?: EnvRecord } }).process;\n return processLike?.env;\n};\n\nconst rawNamespaceConfig = (getEnv()?.LORO_REPO_DEBUG ?? \"\").trim();\n\nconst normalizedNamespaces =\n rawNamespaceConfig.length > 0\n ? rawNamespaceConfig\n .split(/[\\s,]+/)\n .map((token) => token.toLowerCase())\n .filter(Boolean)\n : [];\n\nconst wildcardTokens = new Set([\"*\", \"1\", \"true\", \"all\"]);\nconst namespaceSet = new Set(normalizedNamespaces);\nconst hasWildcard =\n namespaceSet.size > 0 &&\n normalizedNamespaces.some((token) => wildcardTokens.has(token));\n\nexport const isDebugEnabled = (namespace?: string): boolean => {\n if (!namespaceSet.size) {\n return false;\n }\n if (!namespace) {\n return hasWildcard;\n }\n const normalized = namespace.toLowerCase();\n if (hasWildcard) {\n return true;\n }\n if (namespaceSet.has(normalized)) {\n return true;\n }\n const [root] = normalized.split(\":\");\n return namespaceSet.has(root);\n};\n\nexport type DebugLogger = (...args: unknown[]) => void;\n\nexport const createDebugLogger = (namespace: string): DebugLogger => {\n const normalized = namespace.toLowerCase();\n return (...args: unknown[]) => {\n if (!isDebugEnabled(normalized)) {\n return;\n }\n const prefix = `[loro-repo:${namespace}]`;\n if (args.length === 0) {\n console.info(prefix);\n return;\n }\n console.info(prefix, ...args);\n };\n};\n","import { Flock } from \"@loro-dev/flock\";\nimport { LoroDoc } from \"loro-crdt\";\nimport { type CrdtDocAdaptor } from \"loro-adaptors\"\nimport { LoroAdaptor, LoroAdaptorConfig } from \"loro-adaptors/loro\";\nimport { bytesToHex } from \"loro-protocol\";\nimport {\n LoroWebsocketClient,\n type LoroWebsocketClientOptions,\n type LoroWebsocketClientRoom,\n} from \"loro-websocket\";\nimport { createDebugLogger } from \"../internal/debug\";\n\nimport type {\n RepoSyncOptions,\n TransportAdapter,\n TransportJoinParams,\n TransportSubscription,\n TransportSyncResult,\n} from \"../types\";\nimport { FlockAdaptor } from \"loro-adaptors/flock\";\n\ntype MetadataSession = {\n adaptor: CrdtDocAdaptor;\n room: LoroWebsocketClientRoom;\n firstSynced: Promise<void>;\n flock: Flock;\n roomId: string;\n auth?: Uint8Array;\n refCount: number;\n};\n\ntype DocSession = {\n adaptor: CrdtDocAdaptor;\n room: LoroWebsocketClientRoom;\n firstSynced: Promise<void>;\n doc: LoroDoc;\n roomId: string;\n refCount: number;\n};\n\nconst debug = createDebugLogger(\"transport:websocket\");\n\nfunction withTimeout<T>(promise: Promise<T>, timeoutMs?: number): Promise<T> {\n if (!timeoutMs || timeoutMs <= 0) {\n return promise;\n }\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(`Operation timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n promise\n .then((value) => {\n clearTimeout(timer);\n resolve(value);\n })\n .catch((error) => {\n clearTimeout(timer);\n reject(error);\n });\n });\n}\n\nfunction normalizeRoomId(roomId: unknown, fallback: string): string {\n if (typeof roomId === \"string\" && roomId.length > 0) {\n return roomId;\n }\n if (roomId instanceof Uint8Array && roomId.length > 0) {\n try {\n return bytesToHex(roomId);\n } catch {\n return fallback;\n }\n }\n return fallback;\n}\n\nexport interface WebSocketTransportOptions {\n /**\n * WebSocket endpoint provided to the loro-websocket client.\n */\n readonly url: string;\n /**\n * Metadata room identifier. Defaults to \"repo:meta\".\n */\n readonly metadataRoomId?: string;\n /**\n * Optional loro-websocket client configuration.\n */\n readonly client?: Omit<LoroWebsocketClientOptions, \"url\">;\n /**\n * Optional adaptor configuration for metadata joins.\n */\n readonly metadataAdaptorConfig?: LoroAdaptorConfig;\n /**\n * Static auth payload for metadata joins.\n */\n readonly metadataAuth?: Uint8Array | (() => Promise<Uint8Array>);\n /**\n * Factory that maps document ids to room identifiers. Defaults to the doc id.\n */\n readonly docRoomId?: (docId: string) => string;\n /**\n * Optional auth provider for document joins.\n */\n readonly docAuth?: (docId: string) => Promise<Uint8Array>;\n}\n\nfunction bytesEqual(a?: Uint8Array, b?: Uint8Array): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i += 1) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * loro-websocket backed {@link TransportAdapter} implementation for LoroRepo. \n * It uses loro-protocol as the underlying protocol for the transport.\n */\nexport class WebSocketTransportAdapter implements TransportAdapter {\n private readonly options: WebSocketTransportOptions;\n private client?: LoroWebsocketClient;\n private metadataSession?: MetadataSession;\n private readonly docSessions = new Map<string, DocSession>();\n\n constructor(options: WebSocketTransportOptions) {\n this.options = options;\n }\n\n async connect(_options?: { timeout?: number }): Promise<void> {\n const client = this.ensureClient();\n debug(\"connect requested\", { status: client.getStatus() });\n try {\n await client.connect();\n debug(\"client.connect resolved\");\n await client.waitConnected();\n debug(\"client.waitConnected resolved\", { status: client.getStatus() });\n } catch (error) {\n debug(\"connect failed\", error);\n throw error;\n }\n }\n\n async close(): Promise<void> {\n debug(\"close requested\", {\n docSessions: this.docSessions.size,\n metadataSession: Boolean(this.metadataSession),\n });\n for (const [docId] of this.docSessions) {\n await this.leaveDocSession(docId).catch(() => { });\n }\n this.docSessions.clear();\n\n await this.teardownMetadataSession().catch(() => { });\n\n if (this.client) {\n const client = this.client;\n this.client = undefined;\n client.destroy();\n debug(\"websocket client destroyed\");\n }\n debug(\"close completed\");\n }\n\n isConnected(): boolean {\n return this.client?.getStatus() === \"connected\";\n }\n\n async syncMeta(\n flock: Flock,\n options?: { timeout?: number },\n ): Promise<TransportSyncResult> {\n debug(\"syncMeta requested\", { roomId: this.options.metadataRoomId });\n try {\n let auth: Uint8Array | undefined;\n if (this.options.metadataAuth) {\n if (typeof this.options.metadataAuth === \"function\") {\n auth = await this.options.metadataAuth();\n } else {\n auth = this.options.metadataAuth;\n }\n }\n const session = await this.ensureMetadataSession(flock, {\n roomId: this.options.metadataRoomId ?? \"repo:meta\",\n auth: auth\n });\n await withTimeout(session.firstSynced, options?.timeout);\n debug(\"syncMeta completed\", { roomId: this.options.metadataRoomId });\n return { ok: true };\n } catch (error) {\n debug(\"syncMeta failed\", error);\n return { ok: false };\n }\n }\n\n joinMetaRoom(\n flock: Flock,\n params?: TransportJoinParams,\n ): TransportSubscription {\n const fallback = this.options.metadataRoomId ?? \"\";\n const roomId = normalizeRoomId(params?.roomId, fallback);\n if (!roomId) {\n throw new Error(\"Metadata room id not configured\");\n }\n\n const session = (async () => {\n let auth: Uint8Array | undefined;\n const authWay = params?.auth ?? this.options.metadataAuth;\n if (typeof authWay === \"function\") {\n auth = await authWay();\n } else {\n auth = authWay;\n }\n debug(\"joinMetaRoom requested\", {\n roomId,\n hasAuth: Boolean(auth && auth.length),\n });\n const ensure = this.ensureMetadataSession(flock, {\n roomId,\n auth,\n });\n return ensure;\n })();\n const firstSynced = session.then((session) => session.firstSynced);\n const getConnected = () => this.isConnected();\n const subscription: TransportSubscription = {\n unsubscribe: () => {\n void session.then((session) => {\n session.refCount = Math.max(0, session.refCount - 1);\n debug(\"metadata session refCount decremented\", {\n roomId: session.roomId,\n refCount: session.refCount,\n });\n if (session.refCount === 0) {\n debug(\"tearing down metadata session due to refCount=0\", {\n roomId: session.roomId,\n });\n void this.teardownMetadataSession(session).catch(() => { });\n }\n });\n },\n firstSyncedWithRemote: firstSynced,\n get connected() {\n return getConnected();\n },\n };\n\n void session.then((session) => {\n session.refCount += 1;\n debug(\"metadata session refCount incremented\", {\n roomId: session.roomId,\n refCount: session.refCount,\n });\n });\n return subscription;\n }\n\n async syncDoc(\n docId: string,\n doc: LoroDoc,\n options?: { timeout?: number },\n ): Promise<TransportSyncResult> {\n debug(\"syncDoc requested\", { docId });\n try {\n const session = await this.ensureDocSession(docId, doc, {});\n await withTimeout(session.firstSynced, options?.timeout);\n debug(\"syncDoc completed\", { docId, roomId: session.roomId });\n return { ok: true };\n } catch (error) {\n debug(\"syncDoc failed\", { docId, error });\n return { ok: false };\n }\n }\n\n joinDocRoom(\n docId: string,\n doc: LoroDoc,\n params?: TransportJoinParams,\n ): TransportSubscription {\n debug(\"joinDocRoom requested\", {\n docId,\n roomParamType: params?.roomId\n ? typeof params.roomId === \"string\"\n ? \"string\"\n : \"uint8array\"\n : undefined,\n hasAuthOverride: Boolean(params?.auth && params.auth.length),\n });\n const ensure = this.ensureDocSession(docId, doc, params ?? {});\n const firstSynced = ensure.then((session) => session.firstSynced);\n const getConnected = () => this.isConnected();\n const subscription: TransportSubscription = {\n unsubscribe: () => {\n void ensure.then((session) => {\n session.refCount = Math.max(0, session.refCount - 1);\n debug(\"doc session refCount decremented\", {\n docId,\n roomId: session.roomId,\n refCount: session.refCount,\n });\n if (session.refCount === 0) {\n void this.leaveDocSession(docId).catch(() => { });\n }\n });\n },\n firstSyncedWithRemote: firstSynced,\n get connected() {\n return getConnected();\n },\n };\n void ensure.then((session) => {\n session.refCount += 1;\n debug(\"doc session refCount incremented\", {\n docId,\n roomId: session.roomId,\n refCount: session.refCount,\n });\n });\n return subscription;\n }\n\n private ensureClient(): LoroWebsocketClient {\n if (this.client) {\n debug(\"reusing websocket client\", { status: this.client.getStatus() });\n return this.client;\n }\n const { url, client: clientOptions } = this.options;\n debug(\"creating websocket client\", {\n url,\n clientOptionsKeys: clientOptions ? Object.keys(clientOptions) : [],\n });\n const client = new LoroWebsocketClient({\n url,\n ...clientOptions,\n });\n this.client = client;\n return client;\n }\n\n private async ensureMetadataSession(\n flock: Flock,\n params: { roomId: string; auth?: Uint8Array },\n ): Promise<MetadataSession> {\n debug(\"ensureMetadataSession invoked\", {\n roomId: params.roomId,\n hasAuth: Boolean(params.auth && params.auth.length),\n });\n const client = this.ensureClient();\n await client.waitConnected();\n debug(\"websocket client ready for metadata session\", {\n status: client.getStatus(),\n });\n\n if (\n this.metadataSession &&\n this.metadataSession.flock === flock &&\n this.metadataSession.roomId === params.roomId &&\n bytesEqual(this.metadataSession.auth, params.auth)\n ) {\n debug(\"reusing metadata session\", {\n roomId: this.metadataSession.roomId,\n refCount: this.metadataSession.refCount,\n });\n return this.metadataSession;\n }\n\n if (this.metadataSession) {\n debug(\"tearing down previous metadata session\", {\n roomId: this.metadataSession.roomId,\n });\n await this.teardownMetadataSession(this.metadataSession).catch(() => { });\n }\n\n const adaptor = new FlockAdaptor(\n flock,\n this.options.metadataAdaptorConfig\n );\n debug(\"joining metadata room\", {\n roomId: params.roomId,\n hasAuth: Boolean(params.auth && params.auth.length),\n });\n const room = await client.join({\n roomId: params.roomId,\n crdtAdaptor: adaptor,\n auth: params.auth,\n });\n const firstSynced = room.waitForReachingServerVersion();\n firstSynced.then(\n () => {\n debug(\"metadata session firstSynced resolved\", {\n roomId: params.roomId,\n });\n },\n (error) => {\n debug(\"metadata session firstSynced rejected\", {\n roomId: params.roomId,\n error,\n });\n },\n );\n const session: MetadataSession = {\n adaptor,\n room,\n firstSynced,\n flock,\n roomId: params.roomId,\n auth: params.auth,\n refCount: 0,\n };\n this.metadataSession = session;\n return session;\n }\n\n private async teardownMetadataSession(\n session?: MetadataSession,\n ): Promise<void> {\n const target = session ?? this.metadataSession;\n if (!target) return;\n debug(\"teardownMetadataSession invoked\", { roomId: target.roomId });\n if (this.metadataSession === target) {\n this.metadataSession = undefined;\n }\n const { adaptor, room } = target;\n try {\n await room.leave();\n debug(\"metadata room left\", { roomId: target.roomId });\n } catch (error) {\n debug(\"metadata room leave failed; destroying\", {\n roomId: target.roomId,\n error,\n });\n await room.destroy().catch(() => { });\n }\n adaptor.destroy();\n debug(\"metadata session destroyed\", { roomId: target.roomId });\n }\n\n private async ensureDocSession(\n docId: string,\n doc: LoroDoc,\n params: TransportJoinParams,\n ): Promise<DocSession> {\n debug(\"ensureDocSession invoked\", { docId });\n const client = this.ensureClient();\n await client.waitConnected();\n debug(\"websocket client ready for doc session\", {\n docId,\n status: client.getStatus(),\n });\n\n const existing = this.docSessions.get(docId);\n const derivedRoomId = this.options.docRoomId?.(docId) ?? docId;\n const roomId = normalizeRoomId(params.roomId, derivedRoomId);\n let auth: Uint8Array | undefined;\n const authWay = params.auth ?? this.options.docAuth?.(docId);\n auth = await authWay;\n debug(\"doc session params resolved\", {\n docId,\n roomId,\n hasAuth: Boolean(auth && auth.length),\n });\n\n if (existing && existing.doc === doc && existing.roomId === roomId) {\n debug(\"reusing doc session\", {\n docId,\n roomId,\n refCount: existing.refCount,\n });\n return existing;\n }\n\n if (existing) {\n debug(\"doc session mismatch; leaving existing session\", {\n docId,\n previousRoomId: existing.roomId,\n nextRoomId: roomId,\n });\n await this.leaveDocSession(docId).catch(() => { });\n }\n\n const adaptor = new LoroAdaptor(doc);\n debug(\"joining doc room\", {\n docId,\n roomId,\n hasAuth: Boolean(auth && auth.length),\n });\n const room = await client.join({\n roomId,\n crdtAdaptor: adaptor,\n auth,\n });\n const firstSynced = room.waitForReachingServerVersion();\n firstSynced.then(\n () => {\n debug(\"doc session firstSynced resolved\", { docId, roomId });\n },\n (error) => {\n debug(\"doc session firstSynced rejected\", { docId, roomId, error });\n },\n );\n const session: DocSession = {\n adaptor,\n room,\n firstSynced,\n doc,\n roomId,\n refCount: 0,\n };\n this.docSessions.set(docId, session);\n return session;\n }\n\n private async leaveDocSession(docId: string): Promise<void> {\n const session = this.docSessions.get(docId);\n if (!session) {\n debug(\"leaveDocSession invoked but no session found\", { docId });\n return;\n }\n this.docSessions.delete(docId);\n debug(\"leaving doc session\", { docId, roomId: session.roomId });\n try {\n await session.room.leave();\n debug(\"doc room left\", { docId, roomId: session.roomId });\n } catch (error) {\n debug(\"doc room leave failed; destroying\", {\n docId,\n roomId: session.roomId,\n error,\n });\n await session.room.destroy().catch(() => { });\n }\n session.adaptor.destroy();\n debug(\"doc session destroyed\", { docId, roomId: session.roomId });\n }\n}\n\nexport type {\n TransportJoinParams,\n TransportSubscription,\n TransportSyncResult,\n RepoSyncOptions,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"websocket.js","names":["auth: Uint8Array | undefined","statusEmitterRef: StatusEmitter | undefined","session","subscription: TransportSubscription","subscription: TransportSubscription & { store: EphemeralStore }","session: MetadataSession","session: DocSession","session: EphemeralSession"],"sources":["../../src/internal/debug.ts","../../src/transport/websocket.ts"],"sourcesContent":["type EnvRecord = Record<string, string | undefined>;\n\nconst getEnv = (): EnvRecord | undefined => {\n if (typeof globalThis !== \"object\" || globalThis === null) {\n return undefined;\n }\n const processLike = (globalThis as { process?: { env?: EnvRecord } }).process;\n return processLike?.env;\n};\n\nconst rawNamespaceConfig = (getEnv()?.LORO_REPO_DEBUG ?? \"\").trim();\n\nconst normalizedNamespaces =\n rawNamespaceConfig.length > 0\n ? rawNamespaceConfig\n .split(/[\\s,]+/)\n .map((token) => token.toLowerCase())\n .filter(Boolean)\n : [];\n\nconst wildcardTokens = new Set([\"*\", \"1\", \"true\", \"all\"]);\nconst namespaceSet = new Set(normalizedNamespaces);\nconst hasWildcard =\n namespaceSet.size > 0 &&\n normalizedNamespaces.some((token) => wildcardTokens.has(token));\n\nexport const isDebugEnabled = (namespace?: string): boolean => {\n if (!namespaceSet.size) {\n return false;\n }\n if (!namespace) {\n return hasWildcard;\n }\n const normalized = namespace.toLowerCase();\n if (hasWildcard) {\n return true;\n }\n if (namespaceSet.has(normalized)) {\n return true;\n }\n const [root] = normalized.split(\":\");\n return namespaceSet.has(root);\n};\n\nexport type DebugLogger = (...args: unknown[]) => void;\n\nexport const createDebugLogger = (namespace: string): DebugLogger => {\n const normalized = namespace.toLowerCase();\n return (...args: unknown[]) => {\n if (!isDebugEnabled(normalized)) {\n return;\n }\n const prefix = `[loro-repo:${namespace}]`;\n if (args.length === 0) {\n console.info(prefix);\n return;\n }\n console.info(prefix, ...args);\n };\n};\n","import { Flock } from \"@loro-dev/flock\";\nimport { EphemeralStore, LoroDoc } from \"loro-crdt\";\nimport { type CrdtDocAdaptor } from \"loro-adaptors\";\nimport {\n LoroAdaptor,\n LoroAdaptorConfig,\n LoroEphemeralAdaptor,\n} from \"loro-adaptors/loro\";\nimport {\n LoroWebsocketClient,\n type LoroWebsocketClientOptions,\n type LoroWebsocketClientRoom,\n type ClientStatusValue,\n} from \"loro-websocket\";\nimport { createDebugLogger } from \"../internal/debug\";\n\nimport type {\n RepoSyncOptions,\n TransportAdapter,\n TransportJoinParams,\n TransportSubscription,\n TransportSyncResult,\n TransportRoomStatus,\n TransportConnectionStatus,\n} from \"../types\";\nimport { FlockAdaptor } from \"loro-adaptors/flock\";\n\ntype RoomJoinStatusValue = TransportRoomStatus;\nexport type WebSocketRoomKind = \"meta\" | \"doc\" | \"ephemeral\";\nexport interface WebSocketRoomStatusEvent {\n kind: WebSocketRoomKind;\n roomId: string;\n docId?: string;\n status: TransportRoomStatus;\n}\ntype WebSocketRoomStatusEventHandler = (event: WebSocketRoomStatusEvent) => void;\n\ntype MetadataSession = {\n adaptor: CrdtDocAdaptor;\n room: LoroWebsocketClientRoom;\n firstSynced: Promise<void>;\n flock: Flock;\n roomId: string;\n auth?: Uint8Array;\n refCount: number;\n statusEmitter: StatusEmitter;\n statusListener?: (status: TransportRoomStatus) => void;\n};\n\ntype DocSession = {\n adaptor: CrdtDocAdaptor;\n room: LoroWebsocketClientRoom;\n firstSynced: Promise<void>;\n doc: LoroDoc;\n roomId: string;\n docId: string;\n refCount: number;\n statusEmitter: StatusEmitter;\n statusListener?: (status: TransportRoomStatus) => void;\n};\n\ntype EphemeralSession = {\n adaptor: LoroEphemeralAdaptor;\n room?: LoroWebsocketClientRoom;\n firstSynced: Promise<void>;\n store: EphemeralStore;\n roomId: string;\n refCount: number;\n statusEmitter: StatusEmitter;\n};\n\nconst debug = createDebugLogger(\"transport:websocket\");\n\nfunction withTimeout<T>(promise: Promise<T>, timeoutMs?: number): Promise<T> {\n if (!timeoutMs || timeoutMs <= 0) {\n return promise;\n }\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(`Operation timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n promise\n .then((value) => {\n clearTimeout(timer);\n resolve(value);\n })\n .catch((error) => {\n clearTimeout(timer);\n reject(error);\n });\n });\n}\n\ntype StatusEmitter = {\n status: TransportRoomStatus;\n listeners: Set<(status: TransportRoomStatus) => void>;\n};\n\nfunction createStatusEmitter(\n initial: RoomJoinStatusValue = \"connecting\",\n): StatusEmitter {\n return { status: initial, listeners: new Set() };\n}\n\nfunction emitStatus(\n emitter: StatusEmitter | undefined,\n status: TransportRoomStatus,\n): void {\n if (!emitter) return;\n emitter.status = status;\n const listeners = Array.from(emitter.listeners);\n for (const cb of listeners) {\n try {\n cb(status as TransportRoomStatus);\n } catch (error) {\n debug(\"status listener error\", error);\n }\n }\n}\n\nfunction mapClientStatusToRoom(status: ClientStatusValue): TransportRoomStatus {\n switch (status) {\n case \"connected\":\n return \"joined\";\n case \"connecting\":\n return \"reconnecting\";\n default:\n return \"disconnected\";\n }\n}\n\nexport interface WebSocketTransportOptions {\n /**\n * WebSocket endpoint provided to the loro-websocket client.\n */\n readonly url: string;\n /**\n * Metadata room identifier. Defaults to \"repo:meta\".\n */\n readonly metadataRoomId?: string;\n /**\n * Optional loro-websocket client configuration.\n */\n readonly client?: Omit<LoroWebsocketClientOptions, \"url\">;\n /**\n * Optional adaptor configuration for metadata joins.\n */\n readonly metadataAdaptorConfig?: LoroAdaptorConfig;\n /**\n * Static auth payload for metadata joins.\n */\n readonly metadataAuth?: Uint8Array | (() => Promise<Uint8Array>);\n /**\n * Factory that maps document ids to room identifiers. Defaults to the doc id.\n */\n readonly docRoomId?: (docId: string) => string;\n /**\n * Optional auth provider for document joins.\n */\n readonly docAuth?: (docId: string) => Promise<Uint8Array>;\n /**\n * Client-level status listener (connecting/connected/disconnected).\n * Invoked immediately with the current status when registered.\n */\n readonly onStatusChange?: (status: ClientStatusValue) => void;\n /**\n * Listener fed by ping/pong RTT measurements from the underlying websocket client.\n */\n readonly onLatency?: (latencyMs: number) => void;\n /**\n * Global room status listener invoked for metadata, document, and ephemeral joins.\n */\n readonly onRoomStatusChange?: WebSocketRoomStatusEventHandler;\n}\n\nfunction bytesEqual(a?: Uint8Array, b?: Uint8Array): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i += 1) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * loro-websocket backed {@link TransportAdapter} implementation for LoroRepo. \n * It uses loro-protocol as the underlying protocol for the transport.\n */\nexport class WebSocketTransportAdapter implements TransportAdapter {\n private readonly options: WebSocketTransportOptions;\n private client?: LoroWebsocketClient;\n private readonly clientListeners: Array<() => void> = [];\n private metadataSession?: MetadataSession;\n private readonly docSessions = new Map<string, DocSession>();\n private readonly ephemeralSessions = new Map<string, EphemeralSession>();\n\n constructor(options: WebSocketTransportOptions) {\n this.options = options;\n }\n\n private trackClientListener(unsubscribe: () => void): void {\n this.clientListeners.push(unsubscribe);\n }\n\n private propagateClientStatus(status: ClientStatusValue): void {\n const mapped = mapClientStatusToRoom(status);\n const meta = this.metadataSession;\n if (meta) {\n this.dispatchRoomStatus(\n \"meta\",\n { roomId: meta.roomId },\n mapped,\n meta.statusEmitter,\n meta.statusListener,\n );\n }\n for (const session of this.docSessions.values()) {\n this.dispatchRoomStatus(\n \"doc\",\n { roomId: session.roomId, docId: session.docId },\n mapped,\n session.statusEmitter,\n session.statusListener,\n );\n }\n for (const session of this.ephemeralSessions.values()) {\n this.dispatchRoomStatus(\n \"ephemeral\",\n { roomId: session.roomId },\n mapped,\n session.statusEmitter,\n undefined,\n );\n }\n }\n\n private dispatchRoomStatus(\n kind: WebSocketRoomKind,\n payload: { roomId: string; docId?: string },\n status: TransportRoomStatus,\n emitter?: StatusEmitter,\n listener?: (status: TransportRoomStatus) => void,\n ): void {\n emitStatus(emitter, status);\n try {\n listener?.(status);\n } catch (error) {\n debug(\"room listener error\", error);\n }\n try {\n this.options.onRoomStatusChange?.({ kind, ...payload, status });\n } catch (error) {\n debug(\"global room status listener error\", error);\n }\n }\n\n private cleanupClientListeners(): void {\n while (this.clientListeners.length > 0) {\n const unsubscribe = this.clientListeners.pop();\n try {\n unsubscribe?.();\n } catch {\n /* noop */\n }\n }\n }\n\n async connect(options?: { timeout?: number; resetBackoff?: boolean }): Promise<void> {\n const client = this.ensureClient();\n debug(\"connect requested\", {\n status: client.getStatus(),\n resetBackoff: Boolean(options?.resetBackoff),\n });\n try {\n const connectFn = (client as unknown as {\n connect: (opts?: unknown) => Promise<void>;\n }).connect;\n await connectFn.call(\n client,\n options?.resetBackoff ? { resetBackoff: options.resetBackoff } : undefined,\n );\n debug(\"client.connect resolved\");\n await withTimeout(client.waitConnected(), options?.timeout);\n debug(\"client.waitConnected resolved\", { status: client.getStatus() });\n } catch (error) {\n debug(\"connect failed\", error);\n throw error;\n }\n }\n\n async reconnect(options?: { timeout?: number; resetBackoff?: boolean }): Promise<void> {\n const client = this.ensureClient();\n debug(\"reconnect requested\", {\n status: client.getStatus(),\n resetBackoff: Boolean(options?.resetBackoff),\n });\n try {\n const connectFn = (client as unknown as {\n connect: (opts?: unknown) => Promise<void>;\n retryNow?: () => Promise<void>;\n }).connect;\n if (options?.resetBackoff) {\n await connectFn.call(client, { resetBackoff: true });\n } else if ((client as unknown as { retryNow?: () => Promise<void> }).retryNow) {\n await (client as unknown as { retryNow: () => Promise<void> }).retryNow.call(client);\n } else {\n await connectFn.call(client);\n }\n await withTimeout(client.waitConnected(), options?.timeout);\n debug(\"reconnect completed\", { status: client.getStatus() });\n } catch (error) {\n debug(\"reconnect failed\", error);\n throw error;\n }\n }\n\n async close(): Promise<void> {\n debug(\"close requested\", {\n docSessions: this.docSessions.size,\n metadataSession: Boolean(this.metadataSession),\n });\n for (const [docId] of this.docSessions) {\n await this.leaveDocSession(docId).catch(() => { });\n }\n this.docSessions.clear();\n for (const [roomId] of this.ephemeralSessions) {\n await this.leaveEphemeralSession(roomId).catch(() => { });\n }\n this.ephemeralSessions.clear();\n\n await this.teardownMetadataSession().catch(() => { });\n\n if (this.client) {\n const client = this.client;\n this.client = undefined;\n this.cleanupClientListeners();\n client.destroy();\n debug(\"websocket client destroyed\");\n }\n debug(\"close completed\");\n }\n\n isConnected(): boolean {\n return this.client?.getStatus() === \"connected\";\n }\n\n getStatus(): TransportConnectionStatus {\n return this.ensureClient().getStatus();\n }\n\n getLatency(): number | undefined {\n return this.ensureClient().getLatency?.();\n }\n\n onStatusChange(\n listener: (status: TransportConnectionStatus) => void,\n ): () => void {\n const unsubscribe = this.ensureClient().onStatusChange(listener);\n this.trackClientListener(unsubscribe);\n return () => {\n unsubscribe();\n const idx = this.clientListeners.indexOf(unsubscribe);\n if (idx >= 0) {\n this.clientListeners.splice(idx, 1);\n }\n };\n }\n\n onLatency(listener: (latencyMs: number) => void): () => void {\n const unsubscribe = this.ensureClient().onLatency(listener);\n this.trackClientListener(unsubscribe);\n return () => {\n unsubscribe();\n const idx = this.clientListeners.indexOf(unsubscribe);\n if (idx >= 0) {\n this.clientListeners.splice(idx, 1);\n }\n };\n }\n\n async syncMeta(\n flock: Flock,\n options?: { timeout?: number },\n ): Promise<TransportSyncResult> {\n debug(\"syncMeta requested\", { roomId: this.options.metadataRoomId });\n try {\n let auth: Uint8Array | undefined;\n if (this.options.metadataAuth) {\n if (typeof this.options.metadataAuth === \"function\") {\n auth = await this.options.metadataAuth();\n } else {\n auth = this.options.metadataAuth;\n }\n }\n const session = await this.ensureMetadataSession(flock, {\n roomId: this.options.metadataRoomId ?? \"repo:meta\",\n auth: auth\n });\n await withTimeout(session.firstSynced, options?.timeout);\n debug(\"syncMeta completed\", { roomId: this.options.metadataRoomId });\n return { ok: true };\n } catch (error) {\n debug(\"syncMeta failed\", error);\n return { ok: false };\n }\n }\n\n joinMetaRoom(\n flock: Flock,\n params?: TransportJoinParams,\n ): TransportSubscription {\n const roomId = this.options.metadataRoomId ?? \"repo:meta\";\n let statusEmitterRef: StatusEmitter | undefined;\n const session = (async () => {\n let auth: Uint8Array | undefined;\n const authWay = params?.auth ?? this.options.metadataAuth;\n if (typeof authWay === \"function\") {\n auth = await authWay();\n } else {\n auth = authWay;\n }\n debug(\"joinMetaRoom requested\", {\n roomId,\n hasAuth: Boolean(auth && auth.length),\n });\n const ensure = this.ensureMetadataSession(flock, {\n roomId,\n auth,\n onStatusChange: params?.onStatusChange,\n });\n const resolved = await ensure;\n statusEmitterRef = resolved.statusEmitter;\n return ensure;\n })();\n const firstSynced = session.then((session) => session.firstSynced);\n const getConnected = () => this.isConnected();\n const subscription: TransportSubscription = {\n unsubscribe: () => {\n void session.then((session) => {\n session.refCount = Math.max(0, session.refCount - 1);\n debug(\"metadata session refCount decremented\", {\n roomId: session.roomId,\n refCount: session.refCount,\n });\n if (session.refCount === 0) {\n debug(\"tearing down metadata session due to refCount=0\", {\n roomId: session.roomId,\n });\n void this.teardownMetadataSession(session).catch(() => { });\n }\n });\n },\n firstSyncedWithRemote: firstSynced,\n get connected() {\n return getConnected();\n },\n get status() {\n return (\n statusEmitterRef?.status ?? \"connecting\"\n ) as TransportRoomStatus;\n },\n onStatusChange: (listener) => {\n const attach = (emitter: StatusEmitter): (() => void) => {\n emitter.listeners.add(listener);\n try {\n listener(emitter.status as TransportRoomStatus);\n } catch (error) {\n debug(\"metadata onStatusChange listener error\", error);\n }\n return () => emitter.listeners.delete(listener);\n };\n if (statusEmitterRef) {\n const cleanup = attach(statusEmitterRef);\n return () => cleanup();\n }\n let unsubscribed = false;\n const cleanupPromise = session.then((resolved) =>\n attach(resolved.statusEmitter),\n );\n return () => {\n if (unsubscribed) return;\n unsubscribed = true;\n void cleanupPromise.then((cleanup) => cleanup()).catch(() => { });\n };\n },\n };\n\n void session.then((session) => {\n session.refCount += 1;\n debug(\"metadata session refCount incremented\", {\n roomId: session.roomId,\n refCount: session.refCount,\n });\n });\n return subscription;\n }\n\n async syncDoc(\n docId: string,\n doc: LoroDoc,\n options?: { timeout?: number },\n ): Promise<TransportSyncResult> {\n debug(\"syncDoc requested\", { docId });\n try {\n const session = await this.ensureDocSession(docId, doc, {});\n await withTimeout(session.firstSynced, options?.timeout);\n debug(\"syncDoc completed\", { docId, roomId: session.roomId });\n return { ok: true };\n } catch (error) {\n debug(\"syncDoc failed\", { docId, error });\n return { ok: false };\n }\n }\n\n joinDocRoom(\n docId: string,\n doc: LoroDoc,\n params?: TransportJoinParams,\n ): TransportSubscription {\n debug(\"joinDocRoom requested\", {\n docId,\n hasAuthOverride: Boolean(params?.auth && params.auth.length),\n });\n let statusEmitterRef: StatusEmitter | undefined;\n const ensure = this.ensureDocSession(docId, doc, params ?? {}).then(\n (session) => {\n statusEmitterRef = session.statusEmitter;\n return session;\n },\n );\n const firstSynced = ensure.then((session) => session.firstSynced);\n const getConnected = () => this.isConnected();\n const subscription: TransportSubscription = {\n unsubscribe: () => {\n void ensure.then((session) => {\n session.refCount = Math.max(0, session.refCount - 1);\n debug(\"doc session refCount decremented\", {\n docId,\n roomId: session.roomId,\n refCount: session.refCount,\n });\n if (session.refCount === 0) {\n void this.leaveDocSession(docId).catch(() => { });\n }\n });\n },\n firstSyncedWithRemote: firstSynced,\n get connected() {\n return getConnected();\n },\n get status() {\n return (\n statusEmitterRef?.status ?? \"connecting\"\n ) as TransportRoomStatus;\n },\n onStatusChange: (listener) => {\n const attach = (emitter: StatusEmitter): (() => void) => {\n emitter.listeners.add(listener);\n try {\n listener(emitter.status as TransportRoomStatus);\n } catch (error) {\n debug(\"doc onStatusChange listener error\", error);\n }\n return () => emitter.listeners.delete(listener);\n };\n if (statusEmitterRef) {\n const cleanup = attach(statusEmitterRef);\n return () => cleanup();\n }\n let unsubscribed = false;\n const cleanupPromise = ensure.then((session) =>\n attach(session.statusEmitter),\n );\n return () => {\n if (unsubscribed) return;\n unsubscribed = true;\n void cleanupPromise.then((cleanup) => cleanup()).catch(() => { });\n };\n },\n };\n void ensure.then((session) => {\n session.refCount += 1;\n debug(\"doc session refCount incremented\", {\n docId,\n roomId: session.roomId,\n refCount: session.refCount,\n });\n });\n return subscription;\n }\n\n joinEphemeralRoom(\n roomId: string,\n ): TransportSubscription & { store: EphemeralStore } {\n debug(\"joinEphemeralRoom requested\", { roomId });\n let statusEmitterRef: StatusEmitter | undefined;\n const ensure = this.ensureEphemeralSession(roomId).then((session) => {\n statusEmitterRef = session.statusEmitter;\n return session;\n });\n const session = this.ephemeralSessions.get(roomId);\n const store = session?.store;\n if (!store) {\n throw new Error(\"Failed to initialize ephemeral session\");\n }\n const firstSynced = ensure.then((session) => session.firstSynced);\n const getConnected = () => this.isConnected();\n const subscription: TransportSubscription & { store: EphemeralStore } = {\n store,\n unsubscribe: () => {\n void ensure.then((session) => {\n session.refCount = Math.max(0, session.refCount - 1);\n debug(\"ephemeral session refCount decremented\", {\n roomId,\n refCount: session.refCount,\n });\n if (session.refCount === 0) {\n void this.leaveEphemeralSession(roomId).catch(() => { });\n }\n });\n },\n firstSyncedWithRemote: firstSynced,\n get connected() {\n return getConnected();\n },\n get status() {\n return (\n statusEmitterRef?.status ?? \"connecting\"\n ) as TransportRoomStatus;\n },\n onStatusChange: (listener) => {\n const attach = (emitter: StatusEmitter): (() => void) => {\n emitter.listeners.add(listener);\n try {\n listener(emitter.status as TransportRoomStatus);\n } catch (error) {\n debug(\"ephemeral onStatusChange listener error\", error);\n }\n return () => emitter.listeners.delete(listener);\n };\n if (statusEmitterRef) {\n const cleanup = attach(statusEmitterRef);\n return () => cleanup();\n }\n let unsubscribed = false;\n const cleanupPromise = ensure.then((session) =>\n attach(session.statusEmitter),\n );\n return () => {\n if (unsubscribed) return;\n unsubscribed = true;\n void cleanupPromise.then((cleanup) => cleanup()).catch(() => { });\n };\n },\n };\n void ensure.then((session) => {\n subscription.store = session.store;\n session.refCount += 1;\n debug(\"ephemeral session refCount incremented\", {\n roomId,\n refCount: session.refCount,\n });\n });\n return subscription;\n }\n\n private ensureClient(): LoroWebsocketClient {\n if (this.client) {\n debug(\"reusing websocket client\", { status: this.client.getStatus() });\n return this.client;\n }\n const { url, client: clientOptions } = this.options;\n debug(\"creating websocket client\", {\n url,\n clientOptionsKeys: clientOptions ? Object.keys(clientOptions) : [],\n });\n const client = new LoroWebsocketClient({\n url,\n ...clientOptions,\n });\n this.trackClientListener(\n client.onStatusChange((status) => {\n this.propagateClientStatus(status);\n this.options.onStatusChange?.(status);\n }),\n );\n if (this.options.onLatency) {\n this.trackClientListener(client.onLatency(this.options.onLatency));\n }\n this.client = client;\n // seed current status to sessions\n this.propagateClientStatus(client.getStatus());\n return client;\n }\n\n public get websocketClient(): LoroWebsocketClient {\n return this.ensureClient();\n }\n\n private async ensureMetadataSession(\n flock: Flock,\n params: {\n roomId: string;\n auth?: Uint8Array;\n onStatusChange?: TransportJoinParams[\"onStatusChange\"];\n },\n ): Promise<MetadataSession> {\n debug(\"ensureMetadataSession invoked\", {\n roomId: params.roomId,\n hasAuth: Boolean(params.auth && params.auth.length),\n });\n const client = this.ensureClient();\n await client.waitConnected();\n debug(\"websocket client ready for metadata session\", {\n status: client.getStatus(),\n });\n\n if (\n this.metadataSession &&\n this.metadataSession.flock === flock &&\n this.metadataSession.roomId === params.roomId &&\n bytesEqual(this.metadataSession.auth, params.auth)\n ) {\n debug(\"reusing metadata session\", {\n roomId: this.metadataSession.roomId,\n refCount: this.metadataSession.refCount,\n });\n return this.metadataSession;\n }\n\n if (this.metadataSession) {\n debug(\"tearing down previous metadata session\", {\n roomId: this.metadataSession.roomId,\n });\n await this.teardownMetadataSession(this.metadataSession).catch(() => { });\n }\n\n const adaptor = new FlockAdaptor(flock, this.options.metadataAdaptorConfig);\n const statusEmitter = createStatusEmitter(\"connecting\");\n if (params.onStatusChange) {\n statusEmitter.listeners.add(params.onStatusChange);\n }\n this.dispatchRoomStatus(\n \"meta\",\n { roomId: params.roomId },\n \"connecting\",\n statusEmitter,\n params.onStatusChange,\n );\n debug(\"joining metadata room\", {\n roomId: params.roomId,\n hasAuth: Boolean(params.auth && params.auth.length),\n });\n const room = await client.join({\n roomId: params.roomId,\n crdtAdaptor: adaptor,\n auth: params.auth,\n });\n this.dispatchRoomStatus(\n \"meta\",\n { roomId: params.roomId },\n \"joined\",\n statusEmitter,\n params.onStatusChange,\n );\n const firstSynced = room.waitForReachingServerVersion();\n firstSynced.then(\n () => {\n debug(\"metadata session firstSynced resolved\", {\n roomId: params.roomId,\n });\n },\n (error) => {\n debug(\"metadata session firstSynced rejected\", {\n roomId: params.roomId,\n error,\n });\n },\n );\n const session: MetadataSession = {\n adaptor,\n room,\n firstSynced,\n flock,\n roomId: params.roomId,\n auth: params.auth,\n refCount: 0,\n statusEmitter,\n statusListener: params.onStatusChange,\n };\n this.metadataSession = session;\n return session;\n }\n\n private async teardownMetadataSession(\n session?: MetadataSession,\n ): Promise<void> {\n const target = session ?? this.metadataSession;\n if (!target) return;\n debug(\"teardownMetadataSession invoked\", { roomId: target.roomId });\n if (this.metadataSession === target) {\n this.metadataSession = undefined;\n }\n const { adaptor, room } = target;\n try {\n await room.leave();\n debug(\"metadata room left\", { roomId: target.roomId });\n } catch (error) {\n debug(\"metadata room leave failed; destroying\", {\n roomId: target.roomId,\n error,\n });\n await room.destroy().catch(() => { });\n }\n adaptor.destroy();\n this.dispatchRoomStatus(\n \"meta\",\n { roomId: target.roomId },\n \"disconnected\",\n target.statusEmitter,\n target.statusListener,\n );\n debug(\"metadata session destroyed\", { roomId: target.roomId });\n }\n\n private async ensureDocSession(\n docId: string,\n doc: LoroDoc,\n params: TransportJoinParams,\n ): Promise<DocSession> {\n debug(\"ensureDocSession invoked\", { docId });\n const client = this.ensureClient();\n await client.waitConnected();\n debug(\"websocket client ready for doc session\", {\n docId,\n status: client.getStatus(),\n });\n\n const existing = this.docSessions.get(docId);\n const roomId = this.options.docRoomId?.(docId) ?? docId;\n let auth: Uint8Array | undefined;\n const authWay = params.auth ?? this.options.docAuth?.(docId);\n auth = await authWay;\n debug(\"doc session params resolved\", {\n docId,\n roomId,\n hasAuth: Boolean(auth && auth.length),\n });\n\n if (existing && existing.doc === doc && existing.roomId === roomId) {\n debug(\"reusing doc session\", {\n docId,\n roomId,\n refCount: existing.refCount,\n });\n return existing;\n }\n\n if (existing) {\n debug(\"doc session mismatch; leaving existing session\", {\n docId,\n previousRoomId: existing.roomId,\n nextRoomId: roomId,\n });\n await this.leaveDocSession(docId).catch(() => { });\n }\n\n const adaptor = new LoroAdaptor(doc);\n const statusEmitter = createStatusEmitter(\"connecting\");\n if (params.onStatusChange) {\n statusEmitter.listeners.add(params.onStatusChange);\n }\n this.dispatchRoomStatus(\n \"doc\",\n { roomId, docId },\n \"connecting\",\n statusEmitter,\n params.onStatusChange,\n );\n debug(\"joining doc room\", {\n docId,\n roomId,\n hasAuth: Boolean(auth && auth.length),\n });\n const room = await client.join({\n roomId,\n crdtAdaptor: adaptor,\n auth,\n });\n this.dispatchRoomStatus(\n \"doc\",\n { roomId, docId },\n \"joined\",\n statusEmitter,\n params.onStatusChange,\n );\n const firstSynced = room.waitForReachingServerVersion();\n firstSynced.then(\n () => {\n debug(\"doc session firstSynced resolved\", { docId, roomId });\n },\n (error) => {\n debug(\"doc session firstSynced rejected\", { docId, roomId, error });\n },\n );\n const session: DocSession = {\n adaptor,\n room,\n firstSynced,\n doc,\n roomId,\n docId,\n refCount: 0,\n statusEmitter,\n statusListener: params.onStatusChange,\n };\n this.docSessions.set(docId, session);\n return session;\n }\n\n private async ensureEphemeralSession(\n roomId: string,\n ): Promise<EphemeralSession> {\n debug(\"ensureEphemeralSession invoked\", { roomId });\n const existing = this.ephemeralSessions.get(roomId);\n if (existing) {\n debug(\"reusing ephemeral session\", { roomId, refCount: existing.refCount });\n return existing;\n }\n\n const adaptor = new LoroEphemeralAdaptor();\n const statusEmitter = createStatusEmitter(\"connecting\");\n this.dispatchRoomStatus(\n \"ephemeral\",\n { roomId },\n \"connecting\",\n statusEmitter,\n undefined,\n );\n const store = adaptor.getStore();\n const session: EphemeralSession = {\n adaptor,\n store,\n roomId,\n firstSynced: Promise.resolve(),\n refCount: 0,\n statusEmitter,\n };\n this.ephemeralSessions.set(roomId, session);\n\n const client = this.ensureClient();\n await client.waitConnected();\n debug(\"websocket client ready for ephemeral session\", {\n roomId,\n status: client.getStatus(),\n });\n\n try {\n const room = await client.join({\n roomId,\n crdtAdaptor: adaptor,\n });\n this.dispatchRoomStatus(\n \"ephemeral\",\n { roomId },\n \"joined\",\n statusEmitter,\n undefined,\n );\n const firstSynced = room.waitForReachingServerVersion();\n firstSynced.then(\n () => {\n debug(\"ephemeral session firstSynced resolved\", { roomId });\n },\n (error) => {\n debug(\"ephemeral session firstSynced rejected\", { roomId, error });\n },\n );\n session.room = room;\n session.firstSynced = firstSynced;\n return session;\n } catch (error) {\n this.ephemeralSessions.delete(roomId);\n adaptor.destroy();\n throw error;\n }\n }\n\n private async leaveDocSession(docId: string): Promise<void> {\n const session = this.docSessions.get(docId);\n if (!session) {\n debug(\"leaveDocSession invoked but no session found\", { docId });\n return;\n }\n this.docSessions.delete(docId);\n debug(\"leaving doc session\", { docId, roomId: session.roomId });\n try {\n await session.room.leave();\n debug(\"doc room left\", { docId, roomId: session.roomId });\n } catch (error) {\n debug(\"doc room leave failed; destroying\", {\n docId,\n roomId: session.roomId,\n error,\n });\n await session.room.destroy().catch(() => { });\n }\n session.adaptor.destroy();\n this.dispatchRoomStatus(\n \"doc\",\n { roomId: session.roomId, docId: session.docId },\n \"disconnected\",\n session.statusEmitter,\n session.statusListener,\n );\n debug(\"doc session destroyed\", { docId, roomId: session.roomId });\n }\n\n private async leaveEphemeralSession(roomId: string): Promise<void> {\n const session = this.ephemeralSessions.get(roomId);\n if (!session) {\n debug(\"leaveEphemeralSession invoked but no session found\", { roomId });\n return;\n }\n this.ephemeralSessions.delete(roomId);\n debug(\"leaving ephemeral session\", { roomId });\n try {\n await session.room?.leave();\n debug(\"ephemeral room left\", { roomId });\n } catch (error) {\n debug(\"ephemeral room leave failed; destroying\", { roomId, error });\n await session.room?.destroy().catch(() => { });\n }\n session.adaptor.destroy();\n this.dispatchRoomStatus(\n \"ephemeral\",\n { roomId: session.roomId },\n \"disconnected\",\n session.statusEmitter,\n undefined,\n );\n debug(\"ephemeral session destroyed\", { roomId });\n }\n}\n\nexport type {\n TransportJoinParams,\n TransportSubscription,\n TransportSyncResult,\n RepoSyncOptions,\n};\n"],"mappings":"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"}
package/dist/types.d.cts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Flock } from "@loro-dev/flock";
2
- import { Frontiers, LoroDoc } from "loro-crdt";
3
- import { RoomId } from "loro-protocol";
2
+ import { EphemeralStore, Frontiers, LoroDoc } from "loro-crdt";
4
3
 
5
4
  //#region src/types.d.ts
6
5
 
@@ -26,6 +25,16 @@ interface LoroRepoOptions {
26
25
  * after local edits. Defaults to 1000 ms when omitted.
27
26
  */
28
27
  readonly docFrontierDebounceMs?: number;
28
+ /**
29
+ * Debounce duration in milliseconds before persisting metadata (Flock) updates.
30
+ * Defaults to 5000 ms when omitted.
31
+ */
32
+ readonly metaPersistDebounceMs?: number;
33
+ /**
34
+ * Minimum duration in milliseconds to retain tombstoned docs before purging.
35
+ * Defaults to 30 days when omitted.
36
+ */
37
+ readonly deletedDocKeepMs?: number;
29
38
  }
30
39
  /**
31
40
  * Persists metadata, documents, and asset payloads on behalf of the repo.
@@ -45,6 +54,10 @@ interface StorageAdapter {
45
54
  * Loads a stored document by hydrating the persisted snapshot and replaying any queued updates.
46
55
  */
47
56
  loadDoc(docId: string): Promise<LoroDoc | undefined>;
57
+ /**
58
+ * Optionally deletes all persisted data for a document when purging.
59
+ */
60
+ deleteDoc?(docId: string): Promise<void>;
48
61
  /**
49
62
  * Loads the repository-level metadata replica, if present.
50
63
  */
@@ -120,12 +133,24 @@ interface TransportSyncRequest {
120
133
  interface TransportSyncResult {
121
134
  readonly ok: boolean;
122
135
  }
136
+ /**
137
+ * High-level status values emitted by transport implementations while joining rooms.
138
+ */
139
+ type TransportRoomStatus = "connecting" | "joined" | "reconnecting" | "disconnected" | "error";
140
+ /**
141
+ * High-level connection status surfaced by transport adapters.
142
+ */
143
+ type TransportConnectionStatus = "connecting" | "connected" | "disconnected";
123
144
  /**
124
145
  * Parameters used when joining live collaboration rooms.
125
146
  */
126
147
  interface TransportJoinParams {
127
- readonly roomId?: RoomId;
128
148
  readonly auth?: Uint8Array;
149
+ /**
150
+ * Optional listener invoked when the underlying transport reports a room-level status
151
+ * change (connecting, joined, reconnecting, disconnected, or error).
152
+ */
153
+ readonly onStatusChange?: (status: TransportRoomStatus) => void;
129
154
  }
130
155
  /**
131
156
  * Subscription handle returned by transport adapters after joining a room.
@@ -134,6 +159,14 @@ interface TransportSubscription {
134
159
  readonly unsubscribe: () => void;
135
160
  readonly firstSyncedWithRemote: Promise<void>;
136
161
  readonly connected: boolean;
162
+ /**
163
+ * Latest observed room status.
164
+ */
165
+ readonly status: TransportRoomStatus;
166
+ /**
167
+ * Subscribe to room status transitions; returns an unsubscribe callback.
168
+ */
169
+ readonly onStatusChange: (listener: (status: TransportRoomStatus) => void) => () => void;
137
170
  }
138
171
  /**
139
172
  * Abstracts the transport channel that exchanges metadata and document bundles.
@@ -144,6 +177,7 @@ interface TransportAdapter {
144
177
  */
145
178
  connect(options?: {
146
179
  timeout?: number;
180
+ resetBackoff?: boolean;
147
181
  }): Promise<void>;
148
182
  /**
149
183
  * Tears down network resources and stops background polling.
@@ -153,6 +187,29 @@ interface TransportAdapter {
153
187
  * Indicates whether the adapter currently has an active transport connection.
154
188
  */
155
189
  isConnected(): boolean;
190
+ /**
191
+ * Optional accessor for the adapter's connection status.
192
+ */
193
+ getStatus(): TransportConnectionStatus;
194
+ /**
195
+ * Subscribe to adapter-level connection status changes; returns an unsubscribe callback.
196
+ */
197
+ onStatusChange(listener: (status: TransportConnectionStatus) => void): () => void;
198
+ /**
199
+ * Optional latency metric surfaced by the transport (e.g., websocket ping RTT).
200
+ */
201
+ getLatency?(): number | undefined;
202
+ /**
203
+ * Subscribe to adapter-level latency updates; returns an unsubscribe callback.
204
+ */
205
+ onLatency?(listener: (latencyMs: number) => void): () => void;
206
+ /**
207
+ * Manually trigger a reconnect attempt (optional).
208
+ */
209
+ reconnect?(options?: {
210
+ timeout?: number;
211
+ resetBackoff?: boolean;
212
+ }): Promise<void>;
156
213
  /**
157
214
  * Pulls metadata updates and merges them into the supplied Flock replica. If the sync fails, the adapter should throw an error.
158
215
  */
@@ -173,6 +230,12 @@ interface TransportAdapter {
173
230
  * Subscribes to live document updates, returning a cleanup handle.
174
231
  */
175
232
  joinDocRoom(docId: string, loroDoc: LoroDoc, params?: TransportJoinParams): TransportSubscription;
233
+ /**
234
+ * Joins an ephemeral room for non-persistent CRDT state, returning a live store plus a cleanup handle.
235
+ */
236
+ joinEphemeralRoom(roomId: string): TransportSubscription & {
237
+ store: EphemeralStore;
238
+ };
176
239
  }
177
240
  /**
178
241
  * Union describing the kinds of payloads persisted via the storage adapter.
@@ -286,6 +349,32 @@ interface GarbageCollectionOptions {
286
349
  */
287
350
  readonly batchSize?: number;
288
351
  }
352
+ /**
353
+ * Options for marking a document as deleted while leaving it available for a retention window.
354
+ */
355
+ interface DeleteDocOptions {
356
+ /**
357
+ * Timestamp used as the deletion marker. Defaults to Date.now().
358
+ */
359
+ readonly deletedAt?: number;
360
+ /**
361
+ * Force re-marking even if the doc is already tombstoned.
362
+ */
363
+ readonly force?: boolean;
364
+ }
365
+ /**
366
+ * Options for sweeping tombstoned documents whose retention window expired.
367
+ */
368
+ interface GcDeletedDocOptions {
369
+ /**
370
+ * Minimum retention in milliseconds; defaults to repo-level `deletedDocKeepMs`.
371
+ */
372
+ readonly minKeepMs?: number;
373
+ /**
374
+ * Override the timestamp used to evaluate expiration (useful for tests).
375
+ */
376
+ readonly now?: number;
377
+ }
289
378
  /**
290
379
  * Metadata entry returned by `listDoc`.
291
380
  */
@@ -294,11 +383,22 @@ interface RepoDocMeta<Meta extends JsonObject = JsonObject> {
294
383
  * Globally unique identifier for the document (e.g. `note:<uuid>`).
295
384
  */
296
385
  readonly docId: string;
386
+ /**
387
+ * Timestamp (ms) when the doc was soft-deleted, if present.
388
+ */
389
+ readonly deletedAtMs?: number;
297
390
  /**
298
391
  * Merged metadata snapshot respecting the `Meta` contract.
299
392
  */
300
393
  readonly meta: Meta;
301
394
  }
395
+ /**
396
+ * Snapshot returned by `getDocMeta`.
397
+ */
398
+ interface RepoDocSnapshot<Meta extends JsonObject = JsonObject> {
399
+ readonly meta: Meta;
400
+ readonly deletedAtMs?: number;
401
+ }
302
402
  /**
303
403
  * Range-scan parameters for `listDoc`.
304
404
  */
@@ -334,6 +434,11 @@ interface RepoDocHandle {
334
434
  readonly syncOnce: () => Promise<void>;
335
435
  /**
336
436
  * Join the realtime collaboration room.
437
+ *
438
+ * - Idempotent: repeated calls for the same doc reuse the existing join session instead of
439
+ * sending extra join requests.
440
+ * - Reference-counted: each returned subscription increments an internal counter and the room is
441
+ * only left after every caller has invoked `unsubscribe`.
337
442
  */
338
443
  readonly joinRoom: (auth?: Uint8Array) => Promise<TransportSubscription>;
339
444
  }
@@ -345,6 +450,18 @@ type RepoEventBy = "local" | "sync" | "live";
345
450
  * Unified event envelope types emitted by `watch`.
346
451
  */
347
452
  type RepoEventType<Meta extends JsonObject = JsonObject> = {
453
+ "doc-soft-deleted": {
454
+ readonly kind: "doc-soft-deleted";
455
+ readonly docId: string;
456
+ readonly deletedAtMs?: number;
457
+ readonly by: RepoEventBy;
458
+ };
459
+ "doc-purging": {
460
+ readonly kind: "doc-purging";
461
+ readonly docId: string;
462
+ readonly deletedAtMs?: number;
463
+ readonly by: RepoEventBy;
464
+ };
348
465
  "doc-metadata": {
349
466
  readonly kind: "doc-metadata";
350
467
  readonly docId: string;
@@ -415,5 +532,5 @@ interface RepoEventFilter<Meta extends JsonObject = JsonObject> {
415
532
  readonly by?: ReadonlyArray<RepoEventBy>;
416
533
  }
417
534
  //#endregion
418
- export { TransportSyncResult as A, StorageAdapter as C, TransportJoinParams as D, TransportAdapter as E, TransportSubscription as O, RepoWatchHandle as S, SyncScope as T, RepoEventFilter as _, AssetUploadMetadata as a, RepoEventType as b, JsonValue as c, LoroRepoOptions as d, RepoAssetMetadata as f, RepoEventBy as g, RepoEvent as h, AssetTransportAdapter as i, UploadAssetOptions as j, TransportSyncRequest as k, LinkAssetOptions as l, RepoDocMeta as m, AssetDownload as n, GarbageCollectionOptions as o, RepoDocHandle as p, AssetId as r, JsonObject as s, AssetContent as t, ListDocQuery as u, RepoEventKind as v, StorageSavePayload as w, RepoSyncOptions as x, RepoEventListener as y };
535
+ export { TransportConnectionStatus as A, RepoEventType as C, StorageSavePayload as D, StorageAdapter as E, TransportSyncResult as F, UploadAssetOptions as I, TransportRoomStatus as M, TransportSubscription as N, SyncScope as O, TransportSyncRequest as P, RepoEventListener as S, RepoWatchHandle as T, RepoDocSnapshot as _, AssetUploadMetadata as a, RepoEventFilter as b, GcDeletedDocOptions as c, LinkAssetOptions as d, ListDocQuery as f, RepoDocMeta as g, RepoDocHandle as h, AssetTransportAdapter as i, TransportJoinParams as j, TransportAdapter as k, JsonObject as l, RepoAssetMetadata as m, AssetDownload as n, DeleteDocOptions as o, LoroRepoOptions as p, AssetId as r, GarbageCollectionOptions as s, AssetContent as t, JsonValue as u, RepoEvent as v, RepoSyncOptions as w, RepoEventKind as x, RepoEventBy as y };
419
536
  //# sourceMappingURL=types.d.cts.map
package/dist/types.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Flock } from "@loro-dev/flock";
2
- import { Frontiers, LoroDoc } from "loro-crdt";
3
- import { RoomId } from "loro-protocol";
2
+ import { EphemeralStore, Frontiers, LoroDoc } from "loro-crdt";
4
3
 
5
4
  //#region src/types.d.ts
6
5
 
@@ -26,6 +25,16 @@ interface LoroRepoOptions {
26
25
  * after local edits. Defaults to 1000 ms when omitted.
27
26
  */
28
27
  readonly docFrontierDebounceMs?: number;
28
+ /**
29
+ * Debounce duration in milliseconds before persisting metadata (Flock) updates.
30
+ * Defaults to 5000 ms when omitted.
31
+ */
32
+ readonly metaPersistDebounceMs?: number;
33
+ /**
34
+ * Minimum duration in milliseconds to retain tombstoned docs before purging.
35
+ * Defaults to 30 days when omitted.
36
+ */
37
+ readonly deletedDocKeepMs?: number;
29
38
  }
30
39
  /**
31
40
  * Persists metadata, documents, and asset payloads on behalf of the repo.
@@ -45,6 +54,10 @@ interface StorageAdapter {
45
54
  * Loads a stored document by hydrating the persisted snapshot and replaying any queued updates.
46
55
  */
47
56
  loadDoc(docId: string): Promise<LoroDoc | undefined>;
57
+ /**
58
+ * Optionally deletes all persisted data for a document when purging.
59
+ */
60
+ deleteDoc?(docId: string): Promise<void>;
48
61
  /**
49
62
  * Loads the repository-level metadata replica, if present.
50
63
  */
@@ -120,12 +133,24 @@ interface TransportSyncRequest {
120
133
  interface TransportSyncResult {
121
134
  readonly ok: boolean;
122
135
  }
136
+ /**
137
+ * High-level status values emitted by transport implementations while joining rooms.
138
+ */
139
+ type TransportRoomStatus = "connecting" | "joined" | "reconnecting" | "disconnected" | "error";
140
+ /**
141
+ * High-level connection status surfaced by transport adapters.
142
+ */
143
+ type TransportConnectionStatus = "connecting" | "connected" | "disconnected";
123
144
  /**
124
145
  * Parameters used when joining live collaboration rooms.
125
146
  */
126
147
  interface TransportJoinParams {
127
- readonly roomId?: RoomId;
128
148
  readonly auth?: Uint8Array;
149
+ /**
150
+ * Optional listener invoked when the underlying transport reports a room-level status
151
+ * change (connecting, joined, reconnecting, disconnected, or error).
152
+ */
153
+ readonly onStatusChange?: (status: TransportRoomStatus) => void;
129
154
  }
130
155
  /**
131
156
  * Subscription handle returned by transport adapters after joining a room.
@@ -134,6 +159,14 @@ interface TransportSubscription {
134
159
  readonly unsubscribe: () => void;
135
160
  readonly firstSyncedWithRemote: Promise<void>;
136
161
  readonly connected: boolean;
162
+ /**
163
+ * Latest observed room status.
164
+ */
165
+ readonly status: TransportRoomStatus;
166
+ /**
167
+ * Subscribe to room status transitions; returns an unsubscribe callback.
168
+ */
169
+ readonly onStatusChange: (listener: (status: TransportRoomStatus) => void) => () => void;
137
170
  }
138
171
  /**
139
172
  * Abstracts the transport channel that exchanges metadata and document bundles.
@@ -144,6 +177,7 @@ interface TransportAdapter {
144
177
  */
145
178
  connect(options?: {
146
179
  timeout?: number;
180
+ resetBackoff?: boolean;
147
181
  }): Promise<void>;
148
182
  /**
149
183
  * Tears down network resources and stops background polling.
@@ -153,6 +187,29 @@ interface TransportAdapter {
153
187
  * Indicates whether the adapter currently has an active transport connection.
154
188
  */
155
189
  isConnected(): boolean;
190
+ /**
191
+ * Optional accessor for the adapter's connection status.
192
+ */
193
+ getStatus(): TransportConnectionStatus;
194
+ /**
195
+ * Subscribe to adapter-level connection status changes; returns an unsubscribe callback.
196
+ */
197
+ onStatusChange(listener: (status: TransportConnectionStatus) => void): () => void;
198
+ /**
199
+ * Optional latency metric surfaced by the transport (e.g., websocket ping RTT).
200
+ */
201
+ getLatency?(): number | undefined;
202
+ /**
203
+ * Subscribe to adapter-level latency updates; returns an unsubscribe callback.
204
+ */
205
+ onLatency?(listener: (latencyMs: number) => void): () => void;
206
+ /**
207
+ * Manually trigger a reconnect attempt (optional).
208
+ */
209
+ reconnect?(options?: {
210
+ timeout?: number;
211
+ resetBackoff?: boolean;
212
+ }): Promise<void>;
156
213
  /**
157
214
  * Pulls metadata updates and merges them into the supplied Flock replica. If the sync fails, the adapter should throw an error.
158
215
  */
@@ -173,6 +230,12 @@ interface TransportAdapter {
173
230
  * Subscribes to live document updates, returning a cleanup handle.
174
231
  */
175
232
  joinDocRoom(docId: string, loroDoc: LoroDoc, params?: TransportJoinParams): TransportSubscription;
233
+ /**
234
+ * Joins an ephemeral room for non-persistent CRDT state, returning a live store plus a cleanup handle.
235
+ */
236
+ joinEphemeralRoom(roomId: string): TransportSubscription & {
237
+ store: EphemeralStore;
238
+ };
176
239
  }
177
240
  /**
178
241
  * Union describing the kinds of payloads persisted via the storage adapter.
@@ -286,6 +349,32 @@ interface GarbageCollectionOptions {
286
349
  */
287
350
  readonly batchSize?: number;
288
351
  }
352
+ /**
353
+ * Options for marking a document as deleted while leaving it available for a retention window.
354
+ */
355
+ interface DeleteDocOptions {
356
+ /**
357
+ * Timestamp used as the deletion marker. Defaults to Date.now().
358
+ */
359
+ readonly deletedAt?: number;
360
+ /**
361
+ * Force re-marking even if the doc is already tombstoned.
362
+ */
363
+ readonly force?: boolean;
364
+ }
365
+ /**
366
+ * Options for sweeping tombstoned documents whose retention window expired.
367
+ */
368
+ interface GcDeletedDocOptions {
369
+ /**
370
+ * Minimum retention in milliseconds; defaults to repo-level `deletedDocKeepMs`.
371
+ */
372
+ readonly minKeepMs?: number;
373
+ /**
374
+ * Override the timestamp used to evaluate expiration (useful for tests).
375
+ */
376
+ readonly now?: number;
377
+ }
289
378
  /**
290
379
  * Metadata entry returned by `listDoc`.
291
380
  */
@@ -294,11 +383,22 @@ interface RepoDocMeta<Meta extends JsonObject = JsonObject> {
294
383
  * Globally unique identifier for the document (e.g. `note:<uuid>`).
295
384
  */
296
385
  readonly docId: string;
386
+ /**
387
+ * Timestamp (ms) when the doc was soft-deleted, if present.
388
+ */
389
+ readonly deletedAtMs?: number;
297
390
  /**
298
391
  * Merged metadata snapshot respecting the `Meta` contract.
299
392
  */
300
393
  readonly meta: Meta;
301
394
  }
395
+ /**
396
+ * Snapshot returned by `getDocMeta`.
397
+ */
398
+ interface RepoDocSnapshot<Meta extends JsonObject = JsonObject> {
399
+ readonly meta: Meta;
400
+ readonly deletedAtMs?: number;
401
+ }
302
402
  /**
303
403
  * Range-scan parameters for `listDoc`.
304
404
  */
@@ -334,6 +434,11 @@ interface RepoDocHandle {
334
434
  readonly syncOnce: () => Promise<void>;
335
435
  /**
336
436
  * Join the realtime collaboration room.
437
+ *
438
+ * - Idempotent: repeated calls for the same doc reuse the existing join session instead of
439
+ * sending extra join requests.
440
+ * - Reference-counted: each returned subscription increments an internal counter and the room is
441
+ * only left after every caller has invoked `unsubscribe`.
337
442
  */
338
443
  readonly joinRoom: (auth?: Uint8Array) => Promise<TransportSubscription>;
339
444
  }
@@ -345,6 +450,18 @@ type RepoEventBy = "local" | "sync" | "live";
345
450
  * Unified event envelope types emitted by `watch`.
346
451
  */
347
452
  type RepoEventType<Meta extends JsonObject = JsonObject> = {
453
+ "doc-soft-deleted": {
454
+ readonly kind: "doc-soft-deleted";
455
+ readonly docId: string;
456
+ readonly deletedAtMs?: number;
457
+ readonly by: RepoEventBy;
458
+ };
459
+ "doc-purging": {
460
+ readonly kind: "doc-purging";
461
+ readonly docId: string;
462
+ readonly deletedAtMs?: number;
463
+ readonly by: RepoEventBy;
464
+ };
348
465
  "doc-metadata": {
349
466
  readonly kind: "doc-metadata";
350
467
  readonly docId: string;
@@ -415,5 +532,5 @@ interface RepoEventFilter<Meta extends JsonObject = JsonObject> {
415
532
  readonly by?: ReadonlyArray<RepoEventBy>;
416
533
  }
417
534
  //#endregion
418
- export { TransportSyncResult as A, StorageAdapter as C, TransportJoinParams as D, TransportAdapter as E, TransportSubscription as O, RepoWatchHandle as S, SyncScope as T, RepoEventFilter as _, AssetUploadMetadata as a, RepoEventType as b, JsonValue as c, LoroRepoOptions as d, RepoAssetMetadata as f, RepoEventBy as g, RepoEvent as h, AssetTransportAdapter as i, UploadAssetOptions as j, TransportSyncRequest as k, LinkAssetOptions as l, RepoDocMeta as m, AssetDownload as n, GarbageCollectionOptions as o, RepoDocHandle as p, AssetId as r, JsonObject as s, AssetContent as t, ListDocQuery as u, RepoEventKind as v, StorageSavePayload as w, RepoSyncOptions as x, RepoEventListener as y };
535
+ export { TransportConnectionStatus as A, RepoEventType as C, StorageSavePayload as D, StorageAdapter as E, TransportSyncResult as F, UploadAssetOptions as I, TransportRoomStatus as M, TransportSubscription as N, SyncScope as O, TransportSyncRequest as P, RepoEventListener as S, RepoWatchHandle as T, RepoDocSnapshot as _, AssetUploadMetadata as a, RepoEventFilter as b, GcDeletedDocOptions as c, LinkAssetOptions as d, ListDocQuery as f, RepoDocMeta as g, RepoDocHandle as h, AssetTransportAdapter as i, TransportJoinParams as j, TransportAdapter as k, JsonObject as l, RepoAssetMetadata as m, AssetDownload as n, DeleteDocOptions as o, LoroRepoOptions as p, AssetId as r, GarbageCollectionOptions as s, AssetContent as t, JsonValue as u, RepoEvent as v, RepoSyncOptions as w, RepoEventKind as x, RepoEventBy as y };
419
536
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loro-repo",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Draft TypeScript definitions for the LoroRepo orchestrator.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -45,13 +45,13 @@
45
45
  "node": ">=18"
46
46
  },
47
47
  "dependencies": {
48
- "loro-adaptors": "^0.4.1",
49
- "loro-protocol": "^0.2.0",
50
- "loro-websocket": "^0.4.1"
48
+ "loro-adaptors": "^0.6.0",
49
+ "loro-protocol": "^0.3.0",
50
+ "loro-websocket": "^0.6.0"
51
51
  },
52
52
  "peerDependencies": {
53
- "@loro-dev/flock": "^2.1.1",
54
- "loro-crdt": "^1.9.0"
53
+ "@loro-dev/flock": "^4.0.0",
54
+ "loro-crdt": "^1.10.1"
55
55
  },
56
56
  "publishConfig": {
57
57
  "access": "public"
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "license": "MIT",
64
64
  "devDependencies": {
65
- "@loro-dev/flock": "^2.1.1",
65
+ "@loro-dev/flock": "^4.0.0",
66
66
  "@types/node": "^22.18.6",
67
67
  "tsdown": "^0.15.4",
68
68
  "typescript": "^5.9.2",