loro-repo 0.6.0 → 0.8.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":"index.cjs","names":["entry: WatchEntry<Meta>","LoroDoc","promises: Promise<void>[]","by: RepoEventBy","chunks: Uint8Array[]","arr: JsonValue[]","obj: JsonObject","patch: JsonObject","json: JsonObject","scanOptions: ScanOptions","entries: RepoDocMeta<Meta>[]","tsScan: ScanOptions","next: JsonObject","outPatch: JsonObject","patch: JsonObject","patch","docMeta: JsonObject","metadata","metadata: RepoAssetMetadata","storedBytes: Uint8Array | undefined","removed: AssetId[]","removedAssets: Array<[AssetId, AssetRecord]>","docMeta","prefixes: Array<[string, string]>","recordedEvents: FlockEvent[]","record: SubscriptionRecord","entries: ExportBundle[\"entries\"]","Flock"],"sources":["../src/internal/event-bus.ts","../src/internal/logging.ts","../src/internal/doc-manager.ts","../src/utils.ts","../src/internal/metadata-manager.ts","../src/internal/asset-manager.ts","../src/internal/flock-hydrator.ts","../src/internal/sync-runner.ts","../src/internal/repo-state.ts","../src/internal/meta-persister.ts","../src/index.ts"],"sourcesContent":["import type {\n JsonObject,\n RepoEvent,\n RepoEventBy,\n RepoEventFilter,\n RepoEventListener,\n RepoWatchHandle,\n} from \"../types\";\n\ntype WatchEntry<Meta extends JsonObject> = {\n listener: RepoEventListener<Meta>;\n filter: RepoEventFilter<Meta>;\n};\n\nexport class RepoEventBus<Meta extends JsonObject> {\n private readonly watchers = new Set<WatchEntry<Meta>>();\n private readonly eventByStack: RepoEventBy[] = [];\n\n watch(\n listener: RepoEventListener<Meta>,\n filter: RepoEventFilter<Meta> = {},\n ): RepoWatchHandle {\n const entry: WatchEntry<Meta> = { listener, filter };\n this.watchers.add(entry);\n return {\n unsubscribe: () => {\n this.watchers.delete(entry);\n },\n };\n }\n\n emit(event: RepoEvent<Meta>): void {\n for (const entry of this.watchers) {\n if (this.shouldNotify(entry.filter, event)) {\n entry.listener(event);\n }\n }\n }\n\n clear(): void {\n this.watchers.clear();\n this.eventByStack.length = 0;\n }\n\n pushEventBy(by: RepoEventBy): void {\n this.eventByStack.push(by);\n }\n\n popEventBy(): void {\n this.eventByStack.pop();\n }\n\n resolveEventBy(defaultBy: RepoEventBy): RepoEventBy {\n const index = this.eventByStack.length - 1;\n return index >= 0 ? this.eventByStack[index] : defaultBy;\n }\n\n private shouldNotify(\n filter: RepoEventFilter<Meta>,\n event: RepoEvent<Meta>,\n ): boolean {\n if (!filter.docIds && !filter.kinds && !filter.metadataFields && !filter.by)\n return true;\n if (filter.kinds && !filter.kinds.includes(event.kind)) return false;\n if (filter.by && !filter.by.includes(event.by)) return false;\n\n const docId = (() => {\n if (\n event.kind === \"doc-metadata\" ||\n event.kind === \"doc-frontiers\" ||\n event.kind === \"doc-soft-deleted\" ||\n event.kind === \"doc-purging\"\n ) {\n return event.docId;\n }\n if (event.kind === \"asset-link\" || event.kind === \"asset-unlink\") {\n return event.docId;\n }\n return undefined;\n })();\n\n if (filter.docIds && docId && !filter.docIds.includes(docId)) return false;\n if (filter.docIds && !docId) return false;\n\n if (filter.metadataFields && event.kind === \"doc-metadata\") {\n const keys = Object.keys(event.patch);\n if (!keys.some((key) => filter.metadataFields?.includes(key))) {\n return false;\n }\n }\n\n return true;\n }\n}\n","export function logAsyncError(context: string): (error: unknown) => void {\n return (error: unknown) => {\n if (error instanceof Error) {\n console.error(`[loro-repo] ${context} failed: ${error.message}`, error);\n } else {\n console.error(\n `[loro-repo] ${context} failed with non-error reason:`,\n error,\n );\n }\n };\n}\n","import { Flock } from \"@loro-dev/flock\";\nimport { LoroDoc, PeerID, type LoroEventBatch, type VersionVector } from \"loro-crdt\";\n\nimport type {\n JsonObject,\n RepoEventBy,\n StorageAdapter,\n} from \"../types\";\nimport { RepoEventBus } from \"./event-bus\";\nimport { logAsyncError } from \"./logging\";\n\ntype PendingDocFrontierUpdate = {\n timeout: ReturnType<typeof setTimeout>;\n doc: LoroDoc;\n by: RepoEventBy;\n};\n\ninterface DocManagerOptions<Meta extends JsonObject> {\n readonly storage?: StorageAdapter;\n readonly docFrontierDebounceMs: number;\n readonly getMetaFlock: () => Flock;\n readonly eventBus: RepoEventBus<Meta>;\n}\n\nexport class DocManager<Meta extends JsonObject> {\n private readonly storage?: StorageAdapter;\n private readonly docFrontierDebounceMs: number;\n private readonly getMetaFlock: () => Flock;\n private readonly eventBus: RepoEventBus<Meta>;\n\n private readonly docs = new Map<string, LoroDoc>();\n private readonly docSubscriptions = new Map<string, () => void>();\n private readonly docFrontierUpdates = new Map<\n string,\n PendingDocFrontierUpdate\n >();\n private readonly docPersistedVersions = new Map<string, VersionVector>();\n constructor(options: DocManagerOptions<Meta>) {\n this.storage = options.storage;\n this.docFrontierDebounceMs = options.docFrontierDebounceMs;\n this.getMetaFlock = options.getMetaFlock;\n this.eventBus = options.eventBus;\n }\n\n async openPersistedDoc(\n docId: string,\n ): Promise<LoroDoc> {\n const doc = await this.ensureDoc(docId);\n return doc;\n }\n\n async openDetachedDoc(docId: string): Promise<LoroDoc> {\n const doc = await this.materializeDetachedDoc(docId);\n return doc;\n }\n\n async ensureDoc(docId: string): Promise<LoroDoc> {\n const cached = this.docs.get(docId);\n if (cached) {\n this.ensureDocSubscription(docId, cached);\n if (!this.docPersistedVersions.has(docId)) {\n this.docPersistedVersions.set(docId, cached.version());\n }\n return cached;\n }\n\n if (this.storage) {\n const stored = await this.storage.loadDoc(docId);\n if (stored) {\n this.registerDoc(docId, stored);\n return stored;\n }\n }\n\n const created = new LoroDoc();\n this.registerDoc(docId, created);\n return created;\n }\n\n async persistDoc(docId: string, doc: LoroDoc): Promise<void> {\n const previousVersion = this.docPersistedVersions.get(docId);\n const snapshot = doc.export({ mode: \"snapshot\" });\n const nextVersion = doc.version();\n if (!this.storage) {\n this.docPersistedVersions.set(docId, nextVersion);\n return;\n }\n this.docPersistedVersions.set(docId, nextVersion);\n try {\n await this.storage.save({\n type: \"doc-snapshot\",\n docId,\n snapshot,\n });\n } catch (error) {\n if (previousVersion) {\n this.docPersistedVersions.set(docId, previousVersion);\n } else {\n this.docPersistedVersions.delete(docId);\n }\n throw error;\n }\n }\n\n async updateDocFrontiers(\n docId: string,\n doc: LoroDoc,\n defaultBy: RepoEventBy,\n ): Promise<void> {\n const frontiers = doc.oplogFrontiers();\n const vv = doc.version();\n const existingFrontiers = this.readFrontiersFromFlock(docId);\n let mutated = false;\n const metaFlock = this.metaFlock;\n\n for (const f of frontiers) {\n const current = existingFrontiers.get(f.peer);\n if (current !== f.counter) {\n metaFlock.put([\"f\", docId, f.peer], f.counter);\n mutated = true;\n }\n }\n\n if (mutated) {\n for (const [peer, counter] of existingFrontiers) {\n const docCounterEnd = vv.get(peer as PeerID);\n if (docCounterEnd != null && docCounterEnd > counter) {\n metaFlock.delete([\"f\", docId, peer]);\n }\n }\n }\n\n const by = this.eventBus.resolveEventBy(defaultBy);\n this.eventBus.emit({ kind: \"doc-frontiers\", docId, frontiers, by });\n }\n\n async flushScheduledDocFrontierUpdate(docId: string): Promise<boolean> {\n const pending = this.docFrontierUpdates.get(docId);\n if (!pending) return false;\n clearTimeout(pending.timeout);\n this.docFrontierUpdates.delete(docId);\n this.eventBus.pushEventBy(pending.by);\n try {\n await this.updateDocFrontiers(docId, pending.doc, pending.by);\n } finally {\n this.eventBus.popEventBy();\n }\n return true;\n }\n\n async unloadDoc(docId: string): Promise<void> {\n const doc = this.docs.get(docId);\n if (!doc) return;\n\n // 1. Flush any pending frontier updates\n await this.flushScheduledDocFrontierUpdate(docId);\n\n // 2. Persist the final state\n await this.persistDocUpdate(docId, doc);\n\n // 3. Update frontiers one last time if needed (local changes)\n // We assume \"local\" because we are unloading it from the local repo\n await this.updateDocFrontiers(docId, doc, \"local\");\n\n // 4. Cleanup subscriptions and map entries\n const unsubscribe = this.docSubscriptions.get(docId);\n unsubscribe?.();\n this.docSubscriptions.delete(docId);\n this.docs.delete(docId);\n this.docPersistedVersions.delete(docId);\n }\n\n async dropDoc(docId: string): Promise<void> {\n const pending = this.docFrontierUpdates.get(docId);\n if (pending) {\n clearTimeout(pending.timeout);\n this.docFrontierUpdates.delete(docId);\n }\n\n const unsubscribe = this.docSubscriptions.get(docId);\n unsubscribe?.();\n this.docSubscriptions.delete(docId);\n this.docs.delete(docId);\n this.docPersistedVersions.delete(docId);\n\n if (this.storage?.deleteDoc) {\n await this.storage.deleteDoc(docId);\n }\n }\n\n async flush(): Promise<void> {\n const promises: Promise<void>[] = [];\n for (const [docId, doc] of this.docs) {\n promises.push(\n (async () => {\n await this.persistDocUpdate(docId, doc);\n await this.flushScheduledDocFrontierUpdate(docId);\n })()\n );\n }\n await Promise.all(promises);\n }\n\n async close(): Promise<void> {\n // Flush everything before closing\n await this.flush();\n\n for (const unsubscribe of this.docSubscriptions.values()) {\n try {\n unsubscribe();\n } catch {\n // ignore subscriber errors during shutdown\n }\n }\n this.docSubscriptions.clear();\n this.docFrontierUpdates.clear();\n this.docs.clear();\n this.docPersistedVersions.clear();\n }\n\n private get metaFlock(): Flock {\n return this.getMetaFlock();\n }\n\n private readFrontiersFromFlock(docId: string): Map<string, number> {\n const rows = this.metaFlock.scan({ prefix: [\"f\", docId] });\n const frontiers = new Map<string, number>();\n for (const row of rows) {\n if (!Array.isArray(row.key) || row.key.length < 3) continue;\n const peer = row.key[2];\n const counter = row.value;\n if (typeof peer !== \"string\") continue;\n if (typeof counter !== \"number\" || !Number.isFinite(counter)) continue;\n frontiers.set(peer, counter);\n }\n return frontiers;\n }\n\n private registerDoc(docId: string, doc: LoroDoc): void {\n this.docs.set(docId, doc);\n this.docPersistedVersions.set(docId, doc.version());\n this.ensureDocSubscription(docId, doc);\n }\n\n private ensureDocSubscription(docId: string, doc: LoroDoc): void {\n if (this.docSubscriptions.has(docId)) return;\n const unsubscribe = doc.subscribe((batch: LoroEventBatch) => {\n const stackBy = this.eventBus.resolveEventBy(\"local\");\n const by: RepoEventBy =\n stackBy === \"local\" && batch.by === \"import\" ? \"live\" : stackBy;\n this.onDocEvent(docId, doc, batch, by);\n });\n if (typeof unsubscribe === \"function\") {\n this.docSubscriptions.set(docId, unsubscribe as () => void);\n }\n }\n\n private scheduleDocFrontierUpdate(\n docId: string,\n doc: LoroDoc,\n by: RepoEventBy,\n ): void {\n const existing = this.docFrontierUpdates.get(docId);\n const effectiveBy = existing ? this.mergeRepoEventBy(existing.by, by) : by;\n if (existing) {\n clearTimeout(existing.timeout);\n }\n const delay =\n this.docFrontierDebounceMs > 0 ? this.docFrontierDebounceMs : 0;\n const timeout = setTimeout(\n () => this.runScheduledDocFrontierUpdate(docId),\n delay,\n );\n this.docFrontierUpdates.set(docId, { timeout, doc, by: effectiveBy });\n }\n\n private mergeRepoEventBy(\n current: RepoEventBy,\n next: RepoEventBy,\n ): RepoEventBy {\n if (current === next) return current;\n if (current === \"live\" || next === \"live\") return \"live\";\n if (current === \"sync\" || next === \"sync\") return \"sync\";\n return \"local\";\n }\n\n private runScheduledDocFrontierUpdate(docId: string): void {\n const pending = this.docFrontierUpdates.get(docId);\n if (!pending) return;\n this.docFrontierUpdates.delete(docId);\n this.eventBus.pushEventBy(pending.by);\n void (async () => {\n try {\n await this.updateDocFrontiers(docId, pending.doc, pending.by);\n } finally {\n this.eventBus.popEventBy();\n }\n })().catch(logAsyncError(`doc ${docId} frontier debounce`));\n }\n\n private async materializeDetachedDoc(docId: string): Promise<LoroDoc> {\n const snapshot = await this.exportDocSnapshot(docId);\n if (snapshot) {\n return LoroDoc.fromSnapshot(snapshot);\n }\n return new LoroDoc();\n }\n\n private async exportDocSnapshot(\n docId: string,\n ): Promise<Uint8Array | undefined> {\n const cached = this.docs.get(docId);\n if (cached) {\n return cached.export({ mode: \"snapshot\" });\n }\n if (!this.storage) {\n return undefined;\n }\n const stored = await this.storage.loadDoc(docId);\n return stored?.export({ mode: \"snapshot\" });\n }\n\n private async persistDocUpdate(docId: string, doc: LoroDoc): Promise<void> {\n const previousVersion = this.docPersistedVersions.get(docId);\n const nextVersion = doc.oplogVersion();\n if (!this.storage) {\n this.docPersistedVersions.set(docId, nextVersion);\n return;\n }\n\n if (!previousVersion) {\n await this.persistDoc(docId, doc);\n this.docPersistedVersions.set(docId, nextVersion);\n return;\n }\n\n if (previousVersion.compare(nextVersion) === 0) {\n return;\n }\n\n const update = doc.export({ mode: \"update\", from: previousVersion });\n this.docPersistedVersions.set(docId, nextVersion);\n try {\n await this.storage.save({\n type: \"doc-update\",\n docId,\n update,\n });\n } catch (error) {\n this.docPersistedVersions.set(docId, previousVersion);\n throw error;\n }\n }\n\n private onDocEvent(\n docId: string,\n doc: LoroDoc,\n _batch: LoroEventBatch,\n by: RepoEventBy,\n ): void {\n void (async () => {\n const persist = this.persistDocUpdate(docId, doc);\n if (by === \"local\") {\n this.scheduleDocFrontierUpdate(docId, doc, by);\n await persist;\n return;\n }\n\n const flushed = this.flushScheduledDocFrontierUpdate(docId);\n const updated = (async () => {\n this.eventBus.pushEventBy(by);\n try {\n await this.updateDocFrontiers(docId, doc, by);\n } finally {\n this.eventBus.popEventBy();\n }\n })();\n await Promise.all([persist, flushed, updated]);\n })().catch(logAsyncError(`doc ${docId} event processing`));\n }\n}\n","import { Frontiers, LoroDoc, VersionVector } from \"loro-crdt\";\n\nimport type {\n AssetContent,\n JsonObject,\n JsonValue,\n ListDocQuery,\n RepoAssetMetadata,\n} from \"./types\";\n\ntype PossibleCrypto = {\n subtle?: {\n digest: (\n algorithm: string,\n data: ArrayBufferView | ArrayBuffer,\n ) => Promise<ArrayBuffer>;\n };\n};\n\nexport async function streamToUint8Array(\n stream: ReadableStream<Uint8Array>,\n): Promise<Uint8Array> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) {\n chunks.push(value);\n total += value.byteLength;\n }\n }\n const buffer = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n buffer.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return buffer;\n}\n\nexport async function assetContentToUint8Array(\n content: AssetContent,\n): Promise<Uint8Array> {\n if (content instanceof Uint8Array) {\n return content;\n }\n if (ArrayBuffer.isView(content)) {\n return new Uint8Array(\n content.buffer.slice(\n content.byteOffset,\n content.byteOffset + content.byteLength,\n ),\n );\n }\n if (typeof Blob !== \"undefined\" && content instanceof Blob) {\n return new Uint8Array(await content.arrayBuffer());\n }\n if (\n typeof ReadableStream !== \"undefined\" &&\n content instanceof ReadableStream\n ) {\n return streamToUint8Array(content);\n }\n throw new TypeError(\"Unsupported asset content type\");\n}\n\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\n \"\",\n );\n}\n\nexport async function computeSha256(bytes: Uint8Array): Promise<string> {\n const globalCrypto = (globalThis as { crypto?: PossibleCrypto }).crypto;\n if (\n globalCrypto?.subtle &&\n typeof globalCrypto.subtle.digest === \"function\"\n ) {\n const digest = await globalCrypto.subtle.digest(\"SHA-256\", bytes);\n return bytesToHex(new Uint8Array(digest));\n }\n try {\n const { createHash } = await import(\"node:crypto\");\n const hash = createHash(\"sha256\");\n hash.update(bytes);\n return hash.digest(\"hex\");\n } catch {\n throw new Error(\"SHA-256 digest is not available in this environment\");\n }\n}\n\nexport function cloneJsonValue(value: unknown): JsonValue | undefined {\n if (value === null) return null;\n if (typeof value === \"string\" || typeof value === \"boolean\") {\n return value;\n }\n if (typeof value === \"number\") {\n return Number.isFinite(value) ? value : undefined;\n }\n if (Array.isArray(value)) {\n const arr: JsonValue[] = [];\n for (const entry of value) {\n const cloned = cloneJsonValue(entry);\n if (cloned !== undefined) {\n arr.push(cloned);\n }\n }\n return arr;\n }\n if (value && typeof value === \"object\") {\n const input = value as Record<string, unknown>;\n const obj: JsonObject = {};\n for (const [key, entry] of Object.entries(input)) {\n const cloned = cloneJsonValue(entry);\n if (cloned !== undefined) {\n obj[key] = cloned;\n }\n }\n return obj;\n }\n return undefined;\n}\n\nexport function cloneJsonObject(value: JsonObject): JsonObject {\n const cloned = cloneJsonValue(value);\n if (cloned && typeof cloned === \"object\" && !Array.isArray(cloned)) {\n return cloned;\n }\n return {};\n}\n\nexport function asJsonObject(value: unknown): JsonObject | undefined {\n const cloned = cloneJsonValue(value);\n if (cloned && typeof cloned === \"object\" && !Array.isArray(cloned)) {\n return cloned;\n }\n return undefined;\n}\n\nfunction isJsonObjectValue(value: JsonValue): value is JsonObject {\n return Boolean(value && typeof value === \"object\" && !Array.isArray(value));\n}\n\nexport function stableStringify(value: JsonValue): string {\n if (value === null) return \"null\";\n if (typeof value === \"string\") return JSON.stringify(value);\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return JSON.stringify(value);\n }\n if (Array.isArray(value)) {\n return `[${value.map(stableStringify).join(\",\")}]`;\n }\n if (!isJsonObjectValue(value)) {\n return \"null\";\n }\n const entries = Object.keys(value)\n .sort()\n .map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`);\n return `{${entries.join(\",\")}}`;\n}\n\nexport function jsonEquals(a?: JsonValue, b?: JsonValue): boolean {\n if (a === undefined && b === undefined) return true;\n if (a === undefined || b === undefined) return false;\n return stableStringify(a) === stableStringify(b);\n}\n\nexport function diffJsonObjects(\n previous: JsonObject | undefined,\n next: JsonObject,\n): JsonObject {\n const patch: JsonObject = {};\n const keys = new Set<string>();\n if (previous) {\n for (const key of Object.keys(previous)) keys.add(key);\n }\n for (const key of Object.keys(next)) keys.add(key);\n for (const key of keys) {\n const prevValue = previous ? previous[key] : undefined;\n const nextValue = next[key];\n if (!jsonEquals(prevValue, nextValue)) {\n if (nextValue === undefined && previous && key in previous) {\n patch[key] = null;\n continue;\n }\n const cloned = cloneJsonValue(nextValue);\n if (cloned !== undefined) {\n patch[key] = cloned;\n }\n }\n }\n return patch;\n}\n\nexport function assetMetaToJson(meta: RepoAssetMetadata): JsonObject {\n const json: JsonObject = {\n assetId: meta.assetId,\n size: meta.size,\n createdAt: meta.createdAt,\n };\n if (meta.mime !== undefined) json.mime = meta.mime;\n if (meta.policy !== undefined) json.policy = meta.policy;\n if (meta.tag !== undefined) json.tag = meta.tag;\n return json;\n}\n\nexport function assetMetaFromJson(\n value: unknown,\n): RepoAssetMetadata | undefined {\n const obj = asJsonObject(value);\n if (!obj) return undefined;\n const assetId = typeof obj.assetId === \"string\" ? obj.assetId : undefined;\n if (!assetId) return undefined;\n const size = typeof obj.size === \"number\" ? obj.size : undefined;\n const createdAt =\n typeof obj.createdAt === \"number\" ? obj.createdAt : undefined;\n if (size === undefined || createdAt === undefined) return undefined;\n const meta: RepoAssetMetadata = {\n assetId,\n size,\n createdAt,\n ...(typeof obj.mime === \"string\" ? { mime: obj.mime } : {}),\n ...(typeof obj.policy === \"string\" ? { policy: obj.policy } : {}),\n ...(typeof obj.tag === \"string\" ? { tag: obj.tag } : {}),\n };\n return meta;\n}\n\nexport function assetMetadataEqual(\n a: RepoAssetMetadata | undefined,\n b: RepoAssetMetadata | undefined,\n): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n return (\n stableStringify(assetMetaToJson(a)) === stableStringify(assetMetaToJson(b))\n );\n}\n\nexport function cloneRepoAssetMetadata(\n meta: RepoAssetMetadata,\n): RepoAssetMetadata {\n return {\n assetId: meta.assetId,\n size: meta.size,\n createdAt: meta.createdAt,\n ...(meta.mime !== undefined ? { mime: meta.mime } : {}),\n ...(meta.policy !== undefined ? { policy: meta.policy } : {}),\n ...(meta.tag !== undefined ? { tag: meta.tag } : {}),\n };\n}\n\nexport function toReadableStream(\n bytes: Uint8Array,\n): ReadableStream<Uint8Array> {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(bytes);\n controller.close();\n },\n });\n}\n\nexport function computeVersionVector(doc: LoroDoc): VersionVector {\n const candidate = doc as unknown as {\n frontiers?: () => unknown;\n frontiersToVV?: (frontiers: unknown) => VersionVector;\n version?: () => VersionVector;\n };\n if (\n typeof candidate.frontiers === \"function\" &&\n typeof candidate.frontiersToVV === \"function\"\n ) {\n const frontiers = candidate.frontiers();\n return candidate.frontiersToVV(frontiers);\n }\n if (typeof candidate.version === \"function\") {\n return candidate.version();\n }\n return {} as VersionVector;\n}\n\nexport function emptyFrontiers(): Frontiers {\n return [];\n}\n\nexport function versionVectorToJson(vv: VersionVector): JsonObject {\n const map = vv.toJSON();\n const record: JsonObject = {};\n if (map instanceof Map) {\n const entries = Array.from(map.entries()).sort(([a], [b]) =>\n String(a).localeCompare(String(b)),\n );\n for (const [peer, counter] of entries) {\n if (typeof counter !== \"number\" || !Number.isFinite(counter)) continue;\n const key = typeof peer === \"string\" ? peer : JSON.stringify(peer);\n record[key] = counter;\n }\n }\n return record;\n}\n\nexport function canonicalizeVersionVector(vv: VersionVector): {\n json: JsonObject;\n key: string;\n} {\n const json = versionVectorToJson(vv);\n return { json, key: stableStringify(json) };\n}\n\nexport function canonicalizeFrontiers(frontiers: Frontiers): {\n json: JsonValue;\n key: string;\n} {\n const normalized = [...frontiers]\n .map((frontier) => {\n const peer = (frontier as { peer?: unknown }).peer;\n const counter = (frontier as { counter?: unknown }).counter;\n if (\n typeof peer !== \"string\" ||\n typeof counter !== \"number\" ||\n !Number.isFinite(counter)\n ) {\n return undefined;\n }\n return { peer, counter };\n })\n .filter(\n (\n entry,\n ): entry is {\n peer: string;\n counter: number;\n } => Boolean(entry),\n )\n .sort((a, b) => {\n if (a.peer < b.peer) return -1;\n if (a.peer > b.peer) return 1;\n return a.counter - b.counter;\n });\n const json = normalized.map((entry) => ({\n peer: entry.peer,\n counter: entry.counter,\n }));\n return { json, key: stableStringify(json) };\n}\n\nexport function includesFrontiers(\n vv: VersionVector,\n frontiers: Frontiers,\n): boolean {\n for (const frontier of frontiers) {\n const peer = frontier.peer;\n const counter = frontier.counter;\n if (typeof peer !== \"string\" || typeof counter !== \"number\") continue;\n const local = vv.get(peer) ?? 0;\n // Version Vector stores the *next* expected op counter (exclusive).\n // Frontiers store the *last* applied op counter (inclusive).\n // If local <= counter, it means the Version Vector has not yet reached (included) the Frontier op.\n if (local <= counter) return false;\n }\n return true;\n}\n\nexport function matchesQuery(\n docId: string,\n _metadata: JsonObject,\n query?: ListDocQuery,\n): boolean {\n if (!query) return true;\n if (query.prefix && !docId.startsWith(query.prefix)) return false;\n if (query.start && docId < query.start) return false;\n if (query.end && docId > query.end) return false;\n return true;\n}\n","import { Flock } from \"@loro-dev/flock\";\nimport type { ScanOptions } from \"@loro-dev/flock\";\n\nimport type {\n JsonObject,\n JsonValue,\n ListDocQuery,\n RepoDocMeta,\n RepoDocSnapshot,\n RepoEventBy,\n} from \"../types\";\nimport {\n asJsonObject,\n cloneJsonObject,\n cloneJsonValue,\n diffJsonObjects,\n jsonEquals,\n matchesQuery,\n} from \"../utils\";\nimport { RepoEventBus } from \"./event-bus\";\nimport type { RepoState } from \"./repo-state\";\n\ninterface MetadataManagerOptions<Meta extends JsonObject> {\n readonly getMetaFlock: () => Flock;\n readonly eventBus: RepoEventBus<Meta>;\n readonly state: RepoState;\n}\n\nexport class MetadataManager<Meta extends JsonObject> {\n private readonly getMetaFlock: () => Flock;\n private readonly eventBus: RepoEventBus<Meta>;\n private readonly state: RepoState;\n\n constructor(options: MetadataManagerOptions<Meta>) {\n this.getMetaFlock = options.getMetaFlock;\n this.eventBus = options.eventBus;\n this.state = options.state;\n }\n\n getDocIds(): string[] {\n const ids = new Set<string>(this.state.metadata.keys());\n const tombstoneRows = this.metaFlock.scan({ prefix: [\"ts\"] });\n for (const row of tombstoneRows) {\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const docId = row.key[1];\n if (typeof docId === \"string\") {\n ids.add(docId);\n }\n }\n return Array.from(ids);\n }\n\n entries(): IterableIterator<[string, JsonObject]> {\n return this.state.metadata.entries();\n }\n\n get(docId: string): RepoDocSnapshot<Meta> | undefined {\n const metadata = this.state.metadata.get(docId);\n const deletedAtMs = this.readDeletedAtFromFlock(docId);\n if (\n (deletedAtMs === undefined && this.isDocKeyspaceEmpty(docId)) ||\n !metadata ||\n (deletedAtMs === undefined && this.isEmpty(metadata))\n ) {\n if (deletedAtMs === undefined) return undefined;\n return { meta: {} as Meta, deletedAtMs };\n }\n return {\n meta: cloneJsonObject(metadata) as Meta,\n deletedAtMs,\n };\n }\n\n getDeletedAtMs(docId: string): number | undefined {\n return this.readDeletedAtFromFlock(docId);\n }\n\n markDeleted(docId: string, deletedAtMs: number): void {\n this.metaFlock.put([\"ts\", docId], deletedAtMs);\n this.emitSoftDeleted(docId, deletedAtMs, \"local\");\n }\n\n clearDeleted(docId: string): void {\n const existing = this.readDeletedAtFromFlock(docId);\n this.metaFlock.delete([\"ts\", docId]);\n if (existing !== undefined) {\n this.emitSoftDeleted(docId, undefined, \"local\");\n }\n }\n\n // List docs with optional prefix/range/limit filters. We walk two keyspaces:\n // m/* (metadata rows) and ts/* (tombstones) so deleted-but-empty docs still\n // show up with their deletedAtMs. We dedupe via `seen` because a doc may have\n // both metadata and tombstone rows. Empty keyspaces without tombstones are\n // skipped to avoid reviving purged docs.\n listDoc(query?: ListDocQuery): RepoDocMeta<Meta>[] {\n if (query?.limit !== undefined && query.limit <= 0) {\n return [];\n }\n\n const { startKey, endKey } = this.computeDocRangeKeys(query);\n if (startKey && endKey && startKey >= endKey) {\n return [];\n }\n\n const scanOptions: ScanOptions = { prefix: [\"m\"] };\n if (startKey) {\n scanOptions.start = { kind: \"inclusive\", key: [\"m\", startKey] };\n }\n if (endKey) {\n scanOptions.end = { kind: \"exclusive\", key: [\"m\", endKey] };\n }\n\n const seen = new Set<string>();\n const entries: RepoDocMeta<Meta>[] = [];\n\n // Guard that filters duplicates and enforces liveness rules.\n const pushDoc = (docId: string): void => {\n if (seen.has(docId)) return;\n const metadata = this.state.metadata.get(docId);\n const deletedAtMs = this.readDeletedAtFromFlock(docId);\n if (\n (deletedAtMs === undefined && this.isDocKeyspaceEmpty(docId)) ||\n (this.isEmpty(metadata) && deletedAtMs === undefined)\n )\n return;\n if (!matchesQuery(docId, metadata ?? {}, query)) return;\n seen.add(docId);\n entries.push({\n docId,\n deletedAtMs,\n meta: cloneJsonObject(metadata ?? {}) as Meta,\n });\n };\n\n const rows = this.metaFlock.scan(scanOptions);\n // First pass: scan metadata rows (m/*) honoring range/prefix.\n for (const row of rows) {\n if (query?.limit !== undefined && entries.length >= query.limit) {\n break;\n }\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const docId = row.key[1];\n if (typeof docId !== \"string\") continue;\n pushDoc(docId);\n if (query?.limit !== undefined && entries.length >= query.limit) {\n break;\n }\n }\n\n // Second pass: include tombstoned docs that have no metadata rows.\n if (query?.limit === undefined || entries.length < query.limit) {\n const tsScan: ScanOptions = { prefix: [\"ts\"] };\n if (startKey) {\n tsScan.start = { kind: \"inclusive\", key: [\"ts\", startKey] };\n }\n if (endKey) {\n tsScan.end = { kind: \"exclusive\", key: [\"ts\", endKey] };\n }\n const tsRows = this.metaFlock.scan(tsScan);\n for (const row of tsRows) {\n if (query?.limit !== undefined && entries.length >= query.limit) {\n break;\n }\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const docId = row.key[1];\n if (typeof docId !== \"string\") continue;\n pushDoc(docId);\n }\n }\n\n return entries;\n }\n\n async upsert(docId: string, patch: Partial<Meta>): Promise<void> {\n const base = this.state.metadata.get(docId);\n const next: JsonObject = base ? cloneJsonObject(base) : {};\n const outPatch: JsonObject = {};\n let changed = false;\n\n const patchObject = patch as unknown as JsonObject;\n for (const key of Object.keys(patchObject)) {\n const rawValue = patchObject[key];\n if (rawValue === undefined) continue;\n const previousValue = base\n ? (base[key] as JsonValue | undefined)\n : undefined;\n if (jsonEquals(previousValue, rawValue)) {\n continue;\n }\n\n this.metaFlock.put([\"m\", docId, key], rawValue);\n next[key] = rawValue;\n outPatch[key] = rawValue;\n changed = true;\n }\n\n if (!changed) {\n if (!this.state.metadata.has(docId)) {\n this.state.metadata.set(docId, next);\n }\n return;\n }\n\n this.state.metadata.set(docId, next);\n this.eventBus.emit({\n kind: \"doc-metadata\",\n docId,\n patch: outPatch as Partial<Meta>,\n by: \"local\",\n });\n }\n\n refreshFromFlock(docId: string, by: RepoEventBy): void {\n const previousMeta = this.state.metadata.get(docId);\n const nextMeta = this.readDocMetadataFromFlock(docId);\n\n if (!nextMeta) {\n if (previousMeta) {\n this.state.metadata.delete(docId);\n this.eventBus.emit({\n kind: \"doc-metadata\",\n docId,\n patch: {} as Partial<Meta>,\n by,\n });\n }\n return;\n }\n\n this.state.metadata.set(docId, nextMeta);\n const patch = diffJsonObjects(previousMeta, nextMeta);\n if (!previousMeta || Object.keys(patch).length > 0) {\n this.eventBus.emit({\n kind: \"doc-metadata\",\n docId,\n patch: patch as Partial<Meta>,\n by,\n });\n }\n }\n\n replaceAll(nextMetadata: Map<string, JsonObject>, by: RepoEventBy): void {\n const prevMetadata = new Map(this.state.metadata);\n this.state.metadata.clear();\n for (const [docId, meta] of nextMetadata) {\n this.state.metadata.set(docId, meta);\n }\n\n const docIds = new Set<string>([\n ...prevMetadata.keys(),\n ...nextMetadata.keys(),\n ]);\n\n for (const docId of docIds) {\n const previous = prevMetadata.get(docId);\n const current = nextMetadata.get(docId);\n if (!current) {\n if (previous) {\n const patch: JsonObject = {};\n for (const key of Object.keys(previous)) {\n patch[key] = null;\n }\n this.eventBus.emit({\n kind: \"doc-metadata\",\n docId,\n patch: patch as Partial<Meta>,\n by,\n });\n }\n continue;\n }\n const patch = diffJsonObjects(previous, current);\n if (!previous || Object.keys(patch).length > 0) {\n this.eventBus.emit({\n kind: \"doc-metadata\",\n docId,\n patch: patch as Partial<Meta>,\n by,\n });\n }\n }\n }\n\n clear(): void {\n this.state.metadata.clear();\n }\n\n purgeMetadata(docId: string, by: RepoEventBy): void {\n const existed = this.state.metadata.delete(docId);\n if (existed) {\n this.eventBus.emit({\n kind: \"doc-metadata\",\n docId,\n patch: {} as Partial<Meta>,\n by,\n });\n }\n }\n\n emitSoftDeleted(\n docId: string,\n deletedAtMs: number | undefined,\n by: RepoEventBy,\n ): void {\n this.eventBus.emit({\n kind: \"doc-soft-deleted\",\n docId,\n deletedAtMs,\n by,\n });\n }\n\n private computeDocRangeKeys(query?: ListDocQuery): {\n startKey?: string;\n endKey?: string;\n } {\n if (!query) {\n return {};\n }\n\n const prefix =\n query.prefix && query.prefix.length > 0 ? query.prefix : undefined;\n let startKey = query.start;\n if (prefix) {\n startKey = !startKey || prefix > startKey ? prefix : startKey;\n }\n\n let endKey = query.end;\n const prefixEnd = this.nextLexicographicString(prefix);\n if (prefixEnd) {\n endKey = !endKey || prefixEnd < endKey ? prefixEnd : endKey;\n }\n\n return { startKey, endKey };\n }\n\n private nextLexicographicString(value?: string): string | undefined {\n if (!value) return undefined;\n for (let i = value.length - 1; i >= 0; i -= 1) {\n const code = value.charCodeAt(i);\n if (code < 0xffff) {\n return `${value.slice(0, i)}${String.fromCharCode(code + 1)}`;\n }\n }\n return undefined;\n }\n\n private readDocMetadataFromFlock(docId: string): JsonObject | undefined {\n const rows = this.metaFlock.scan({ prefix: [\"m\", docId] });\n if (!rows.length) return undefined;\n const docMeta: JsonObject = {};\n let populated = false;\n let sawRow = false;\n\n for (const row of rows) {\n sawRow = true;\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n if (row.key.length === 2) {\n const obj = asJsonObject(row.value);\n if (!obj) continue;\n for (const [field, value] of Object.entries(obj)) {\n const cloned = cloneJsonValue(value);\n if (cloned !== undefined) {\n docMeta[field] = cloned;\n populated = true;\n }\n }\n continue;\n }\n const fieldKey = row.key[2];\n if (typeof fieldKey !== \"string\") continue;\n const jsonValue = cloneJsonValue(row.value);\n if (jsonValue === undefined) continue;\n docMeta[fieldKey] = jsonValue;\n populated = true;\n }\n if (populated) return docMeta;\n if (sawRow) return {};\n return undefined;\n }\n\n private readDeletedAtFromFlock(docId: string): number | undefined {\n const raw = this.metaFlock.get([\"ts\", docId]);\n if (typeof raw === \"number\" && Number.isFinite(raw)) {\n return raw;\n }\n return undefined;\n }\n\n private isEmpty(metadata?: JsonObject): boolean {\n return !metadata || Object.keys(metadata).length === 0;\n }\n\n private isDocKeyspaceEmpty(docId: string): boolean {\n if (this.state.metadata.has(docId)) return false;\n const hasTruthyKey = (prefix: [string, string]): boolean => {\n const rows = this.metaFlock.scan({ prefix });\n for (const row of rows) {\n const value = row.value;\n const truthy = value !== undefined;\n if (truthy) return true;\n }\n return false;\n };\n\n if (this.metaFlock.get([\"ts\", docId]) !== undefined) return false;\n if (hasTruthyKey([\"m\", docId])) return false;\n if (hasTruthyKey([\"f\", docId])) return false;\n if (hasTruthyKey([\"ld\", docId])) return false;\n return true;\n }\n\n private get metaFlock(): Flock {\n return this.getMetaFlock();\n }\n}\n","import { Flock } from \"@loro-dev/flock\";\n\nimport type {\n AssetDownload,\n AssetId,\n AssetTransportAdapter,\n GarbageCollectionOptions,\n JsonObject,\n LinkAssetOptions,\n RepoAssetMetadata,\n RepoEventBy,\n StorageAdapter,\n UploadAssetOptions,\n} from \"../types\";\nimport {\n assetContentToUint8Array,\n assetMetaFromJson,\n assetMetaToJson,\n assetMetadataEqual,\n cloneRepoAssetMetadata,\n computeSha256,\n streamToUint8Array,\n toReadableStream,\n} from \"../utils\";\nimport { RepoEventBus } from \"./event-bus\";\nimport { logAsyncError } from \"./logging\";\nimport type { RepoState, AssetRecord, OrphanedAssetRecord } from \"./repo-state\";\n\ninterface AssetManagerOptions<Meta extends JsonObject> {\n readonly storage?: StorageAdapter;\n readonly assetTransport?: AssetTransportAdapter;\n readonly getMetaFlock: () => Flock;\n readonly eventBus: RepoEventBus<Meta>;\n readonly state: RepoState;\n}\n\nexport class AssetManager<Meta extends JsonObject> {\n private readonly storage?: StorageAdapter;\n private readonly assetTransport?: AssetTransportAdapter;\n private readonly getMetaFlock: () => Flock;\n private readonly eventBus: RepoEventBus<Meta>;\n private readonly state: RepoState;\n private get docAssets(): Map<string, Map<AssetId, RepoAssetMetadata>> {\n return this.state.docAssets;\n }\n private get assets(): Map<AssetId, AssetRecord> {\n return this.state.assets;\n }\n private get orphanedAssets(): Map<AssetId, OrphanedAssetRecord> {\n return this.state.orphanedAssets;\n }\n private get assetToDocRefs(): Map<AssetId, Set<string>> {\n return this.state.assetToDocRefs;\n }\n\n constructor(options: AssetManagerOptions<Meta>) {\n this.storage = options.storage;\n this.assetTransport = options.assetTransport;\n this.getMetaFlock = options.getMetaFlock;\n this.eventBus = options.eventBus;\n this.state = options.state;\n }\n\n async uploadAsset(params: UploadAssetOptions): Promise<AssetId> {\n const bytes = await assetContentToUint8Array(params.content);\n const assetId = await computeSha256(bytes);\n\n if (params.assetId && params.assetId !== assetId) {\n throw new Error(\"Provided assetId does not match content digest\");\n }\n\n const existing = this.assets.get(assetId);\n if (existing) {\n // Ensure we have the data stored locally if possible\n if (this.storage) {\n const stored = await this.storage.loadAsset(assetId);\n if (!stored) {\n await this.storage.save({\n type: \"asset\",\n assetId,\n data: bytes.slice(),\n });\n }\n }\n\n let metadataMutated = false;\n const metadata = { ...existing.metadata };\n if (params.mime && metadata.mime !== params.mime) {\n metadata.mime = params.mime;\n metadataMutated = true;\n }\n if (params.policy && metadata.policy !== params.policy) {\n metadata.policy = params.policy;\n metadataMutated = true;\n }\n if (params.tag && metadata.tag !== params.tag) {\n metadata.tag = params.tag;\n metadataMutated = true;\n }\n if (\n params.createdAt !== undefined &&\n metadata.createdAt !== params.createdAt\n ) {\n metadata.createdAt = params.createdAt;\n metadataMutated = true;\n }\n if (metadataMutated) {\n existing.metadata = metadata;\n this.metaFlock.put([\"a\", assetId], assetMetaToJson(metadata));\n this.eventBus.emit({\n kind: \"asset-metadata\",\n asset: this.createAssetDownload(assetId, metadata, bytes),\n by: \"local\",\n });\n }\n this.rememberAsset(existing.metadata);\n return assetId;\n }\n\n const metadata: RepoAssetMetadata = {\n assetId,\n size: bytes.byteLength,\n createdAt: params.createdAt ?? Date.now(),\n ...(params.mime ? { mime: params.mime } : {}),\n ...(params.policy ? { policy: params.policy } : {}),\n ...(params.tag ? { tag: params.tag } : {}),\n };\n\n if (this.assetTransport) {\n let shouldUpload = true;\n if (typeof this.assetTransport.ensure === \"function\") {\n shouldUpload = !(await this.assetTransport.ensure(assetId));\n }\n if (shouldUpload) {\n await this.assetTransport.upload(assetId, bytes, {\n mime: params.mime,\n policy: params.policy,\n tag: params.tag,\n });\n }\n }\n\n const storedBytes = bytes.slice();\n\n if (this.storage) {\n await this.storage.save({\n type: \"asset\",\n assetId,\n data: storedBytes.slice(),\n });\n }\n\n this.rememberAsset(metadata);\n this.markAssetAsOrphan(assetId, metadata); // Initially orphan until linked\n\n this.updateDocAssetMetadata(assetId, metadata);\n\n this.metaFlock.put([\"a\", assetId], assetMetaToJson(metadata));\n this.eventBus.emit({\n kind: \"asset-metadata\",\n asset: this.createAssetDownload(assetId, metadata, storedBytes),\n by: \"local\",\n });\n\n return assetId;\n }\n\n async linkAsset(docId: string, params: LinkAssetOptions): Promise<AssetId> {\n const bytes = await assetContentToUint8Array(params.content);\n const assetId = await computeSha256(bytes);\n\n if (params.assetId && params.assetId !== assetId) {\n throw new Error(\"Provided assetId does not match content digest\");\n }\n\n let metadata: RepoAssetMetadata;\n let storedBytes: Uint8Array | undefined;\n let created = false;\n\n const existing = this.assets.get(assetId);\n if (existing) {\n metadata = existing.metadata;\n // Ensure storage\n if (this.storage) {\n const stored = await this.storage.loadAsset(assetId);\n if (!stored) {\n await this.storage.save({\n type: \"asset\",\n assetId,\n data: bytes.slice(),\n });\n }\n }\n\n let nextMetadata = metadata;\n let metadataMutated = false;\n if (params.mime && params.mime !== nextMetadata.mime) {\n nextMetadata = { ...nextMetadata, mime: params.mime };\n metadataMutated = true;\n }\n if (params.policy && params.policy !== nextMetadata.policy) {\n nextMetadata = { ...nextMetadata, policy: params.policy };\n metadataMutated = true;\n }\n if (params.tag && params.tag !== nextMetadata.tag) {\n nextMetadata = { ...nextMetadata, tag: params.tag };\n metadataMutated = true;\n }\n if (\n params.createdAt !== undefined &&\n params.createdAt !== nextMetadata.createdAt\n ) {\n nextMetadata = { ...nextMetadata, createdAt: params.createdAt };\n metadataMutated = true;\n }\n if (metadataMutated) {\n existing.metadata = nextMetadata;\n metadata = nextMetadata;\n this.metaFlock.put([\"a\", assetId], assetMetaToJson(metadata));\n this.eventBus.emit({\n kind: \"asset-metadata\",\n asset: this.createAssetDownload(assetId, metadata, bytes),\n by: \"local\",\n });\n } else {\n metadata = existing.metadata;\n }\n storedBytes = bytes;\n this.rememberAsset(metadata);\n } else {\n metadata = {\n assetId,\n size: bytes.byteLength,\n createdAt: params.createdAt ?? Date.now(),\n ...(params.mime ? { mime: params.mime } : {}),\n ...(params.policy ? { policy: params.policy } : {}),\n ...(params.tag ? { tag: params.tag } : {}),\n };\n\n if (this.assetTransport) {\n let shouldUpload = true;\n if (typeof this.assetTransport.ensure === \"function\") {\n shouldUpload = !(await this.assetTransport.ensure(assetId));\n }\n if (shouldUpload) {\n await this.assetTransport.upload(assetId, bytes, {\n mime: params.mime,\n policy: params.policy,\n tag: params.tag,\n });\n }\n }\n\n storedBytes = bytes.slice();\n\n if (this.storage) {\n await this.storage.save({\n type: \"asset\",\n assetId,\n data: storedBytes.slice(),\n });\n }\n\n this.rememberAsset(metadata);\n\n this.updateDocAssetMetadata(assetId, metadata);\n\n this.metaFlock.put([\"a\", assetId], assetMetaToJson(metadata));\n created = true;\n }\n\n const mapping =\n this.docAssets.get(docId) ?? new Map<AssetId, RepoAssetMetadata>();\n mapping.set(assetId, metadata);\n this.docAssets.set(docId, mapping);\n\n this.addDocReference(assetId, docId);\n\n this.metaFlock.put([\"ld\", docId, assetId], true);\n\n this.eventBus.emit({ kind: \"asset-link\", docId, assetId, by: \"local\" });\n if (created) {\n this.eventBus.emit({\n kind: \"asset-metadata\",\n asset: this.createAssetDownload(\n assetId,\n metadata,\n storedBytes ?? bytes,\n ),\n by: \"local\",\n });\n }\n return assetId;\n }\n\n async unlinkAsset(docId: string, assetId: AssetId): Promise<void> {\n const mapping = this.docAssets.get(docId);\n if (!mapping || !mapping.has(assetId)) return;\n mapping.delete(assetId);\n if (mapping.size === 0) {\n this.docAssets.delete(docId);\n }\n\n this.metaFlock.delete([\"ld\", docId, assetId]);\n\n this.removeDocAssetReference(assetId, docId);\n // Do NOT delete global metadata [\"a\", assetId] automatically.\n // It will be cleaned up if we implement a global GC strategy,\n // or simply ignored. The binary is managed via orphan references.\n\n this.eventBus.emit({ kind: \"asset-unlink\", docId, assetId, by: \"local\" });\n }\n\n purgeDocLinks(docId: string, by: RepoEventBy): void {\n const keys = Array.from(\n this.metaFlock.scan({ prefix: [\"ld\", docId] }),\n (row) => row.key,\n );\n for (const key of keys) {\n this.metaFlock.delete(key);\n }\n this.refreshDocAssetsEntry(docId, by);\n }\n\n async listAssets(docId: string): Promise<RepoAssetMetadata[]> {\n const mapping = this.docAssets.get(docId);\n if (!mapping) return [];\n return Array.from(mapping.values()).map((asset) => ({ ...asset }));\n }\n\n async ensureAsset(assetId: AssetId): Promise<AssetDownload> {\n return this.fetchAsset(assetId);\n }\n\n async fetchAsset(assetId: AssetId): Promise<AssetDownload> {\n const { metadata, bytes } = await this.materializeAsset(assetId);\n return this.createAssetDownload(assetId, metadata, bytes);\n }\n\n async gcAssets(options: GarbageCollectionOptions = {}): Promise<number> {\n const { minKeepMs = 0 } = options;\n const now = Date.now();\n let removed = 0;\n for (const [assetId, orphan] of Array.from(this.orphanedAssets.entries())) {\n if (now - orphan.deletedAt < minKeepMs) {\n continue;\n }\n this.orphanedAssets.delete(assetId);\n if (this.storage?.deleteAsset) {\n try {\n await this.storage.deleteAsset(assetId);\n } catch (error) {\n logAsyncError(`asset ${assetId} delete`)(error);\n }\n }\n removed += 1;\n }\n return removed;\n }\n\n refreshDocAssetsEntry(docId: string, by: RepoEventBy): void {\n const mapping = this.readDocAssetsFromFlock(docId);\n const previous = this.docAssets.get(docId);\n\n if (!mapping.size) {\n if (previous?.size) {\n this.docAssets.delete(docId);\n for (const assetId of previous.keys()) {\n this.removeDocAssetReference(assetId, docId);\n this.eventBus.emit({ kind: \"asset-unlink\", docId, assetId, by });\n }\n }\n return;\n }\n\n this.docAssets.set(docId, mapping);\n\n const removed: AssetId[] = [];\n if (previous) {\n for (const assetId of previous.keys()) {\n if (!mapping.has(assetId)) {\n removed.push(assetId);\n }\n }\n }\n\n for (const assetId of removed) {\n this.removeDocAssetReference(assetId, docId);\n this.eventBus.emit({ kind: \"asset-unlink\", docId, assetId, by });\n }\n\n for (const assetId of mapping.keys()) {\n const isNew = !previous || !previous.has(assetId);\n this.addDocReference(assetId, docId);\n if (isNew) {\n this.eventBus.emit({ kind: \"asset-link\", docId, assetId, by });\n }\n }\n }\n\n refreshAssetMetadataEntry(assetId: AssetId, by: RepoEventBy): void {\n const previous = this.assets.get(assetId);\n const raw = this.metaFlock.get([\"a\", assetId]);\n const metadata = assetMetaFromJson(raw);\n\n if (!metadata) {\n this.handleAssetRemoval(assetId, by);\n return;\n }\n\n this.rememberAsset(metadata);\n\n this.updateDocAssetMetadata(assetId, cloneRepoAssetMetadata(metadata));\n\n if (!previous || !assetMetadataEqual(previous.metadata, metadata)) {\n this.eventBus.emit({\n kind: \"asset-metadata\",\n asset: this.createAssetDownload(assetId, metadata),\n by,\n });\n }\n }\n\n hydrateFromFlock(by: RepoEventBy): void {\n const prevDocAssets = new Map(this.docAssets);\n const prevAssets = new Map(this.assets);\n\n const nextAssets = new Map<AssetId, AssetRecord>();\n const assetRows = this.metaFlock.scan({ prefix: [\"a\"] });\n for (const row of assetRows) {\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const assetId = row.key[1];\n if (typeof assetId !== \"string\") continue;\n const metadata = assetMetaFromJson(row.value);\n if (!metadata) continue;\n // No existing check needed for data preservation\n nextAssets.set(assetId, {\n metadata,\n });\n }\n\n const nextDocAssets = new Map<string, Map<AssetId, RepoAssetMetadata>>();\n const linkRows = this.metaFlock.scan({ prefix: [\"ld\"] });\n for (const row of linkRows) {\n if (!Array.isArray(row.key) || row.key.length < 3) continue;\n const docId = row.key[1];\n const assetId = row.key[2];\n if (typeof docId !== \"string\" || typeof assetId !== \"string\") continue;\n const metadata = nextAssets.get(assetId)?.metadata;\n if (!metadata) continue;\n const mapping =\n nextDocAssets.get(docId) ?? new Map<AssetId, RepoAssetMetadata>();\n mapping.set(assetId, metadata);\n nextDocAssets.set(docId, mapping);\n }\n\n const removedAssets: Array<[AssetId, AssetRecord]> = [];\n for (const [assetId, record] of prevAssets) {\n if (!nextAssets.has(assetId)) {\n removedAssets.push([assetId, record]);\n }\n }\n\n if (removedAssets.length > 0) {\n const now = Date.now();\n for (const [assetId, record] of removedAssets) {\n const existing = this.orphanedAssets.get(assetId);\n const deletedAt = existing?.deletedAt ?? now;\n this.orphanedAssets.set(assetId, {\n metadata: record.metadata,\n deletedAt,\n });\n }\n }\n\n this.docAssets.clear();\n for (const [docId, assets] of nextDocAssets) {\n this.docAssets.set(docId, assets);\n }\n\n this.assetToDocRefs.clear();\n for (const [docId, assets] of nextDocAssets) {\n for (const assetId of assets.keys()) {\n const refs = this.assetToDocRefs.get(assetId) ?? new Set<string>();\n refs.add(docId);\n this.assetToDocRefs.set(assetId, refs);\n }\n }\n\n this.assets.clear();\n for (const record of nextAssets.values()) {\n this.rememberAsset(record.metadata);\n }\n\n // Sync Orphans Logic\n for (const assetId of nextAssets.keys()) {\n const refs = this.assetToDocRefs.get(assetId);\n if (!refs || refs.size === 0) {\n // If refs are 0, it should be an orphan\n if (!this.orphanedAssets.has(assetId)) {\n this.markAssetAsOrphan(assetId, nextAssets.get(assetId)!.metadata);\n }\n } else {\n // If refs > 0, it is NOT an orphan\n this.orphanedAssets.delete(assetId);\n }\n }\n\n for (const [assetId, record] of nextAssets) {\n const previous = prevAssets.get(assetId)?.metadata;\n if (!assetMetadataEqual(previous, record.metadata)) {\n this.eventBus.emit({\n kind: \"asset-metadata\",\n asset: this.createAssetDownload(assetId, record.metadata),\n by,\n });\n }\n }\n\n for (const [docId, assets] of nextDocAssets) {\n const previous = prevDocAssets.get(docId);\n for (const assetId of assets.keys()) {\n if (!previous || !previous.has(assetId)) {\n this.eventBus.emit({ kind: \"asset-link\", docId, assetId, by });\n }\n }\n }\n\n for (const [docId, assets] of prevDocAssets) {\n const current = nextDocAssets.get(docId);\n for (const assetId of assets.keys()) {\n if (!current || !current.has(assetId)) {\n this.eventBus.emit({ kind: \"asset-unlink\", docId, assetId, by });\n }\n }\n }\n }\n\n clear(): void {\n this.docAssets.clear();\n this.assets.clear();\n this.orphanedAssets.clear();\n this.assetToDocRefs.clear();\n }\n\n private readDocAssetsFromFlock(\n docId: string,\n ): Map<AssetId, RepoAssetMetadata> {\n const rows = this.metaFlock.scan({ prefix: [\"ld\", docId] });\n const mapping = new Map<AssetId, RepoAssetMetadata>();\n for (const row of rows) {\n if (!Array.isArray(row.key) || row.key.length < 3) continue;\n const assetId = row.key[2];\n if (typeof assetId !== \"string\") continue;\n const truthy =\n row.value !== undefined && row.value !== null && row.value !== false;\n if (!truthy) continue;\n let metadata = this.assets.get(assetId)?.metadata;\n if (!metadata) {\n metadata = this.readAssetMetadataFromFlock(assetId);\n if (!metadata) continue;\n this.rememberAsset(metadata);\n }\n mapping.set(assetId, cloneRepoAssetMetadata(metadata));\n }\n return mapping;\n }\n\n private readAssetMetadataFromFlock(\n assetId: AssetId,\n ): RepoAssetMetadata | undefined {\n const raw = this.metaFlock.get([\"a\", assetId]);\n return assetMetaFromJson(raw);\n }\n\n private handleAssetRemoval(assetId: AssetId, by: RepoEventBy): void {\n const record = this.assets.get(assetId);\n if (!record) return;\n this.assets.delete(assetId);\n this.markAssetAsOrphan(assetId, record.metadata);\n\n const refs = this.assetToDocRefs.get(assetId);\n if (refs) {\n this.assetToDocRefs.delete(assetId);\n for (const docId of refs) {\n const assets = this.docAssets.get(docId);\n if (assets?.delete(assetId) && assets.size === 0) {\n this.docAssets.delete(docId);\n }\n this.eventBus.emit({ kind: \"asset-unlink\", docId, assetId, by });\n }\n return;\n }\n\n for (const [docId, assets] of this.docAssets) {\n if (assets.delete(assetId)) {\n if (assets.size === 0) {\n this.docAssets.delete(docId);\n }\n this.eventBus.emit({ kind: \"asset-unlink\", docId, assetId, by });\n }\n }\n }\n\n private createAssetDownload(\n assetId: AssetId,\n metadata: RepoAssetMetadata,\n initialBytes?: Uint8Array,\n ): AssetDownload {\n let cached = initialBytes ? initialBytes.slice() : undefined;\n return {\n assetId,\n size: metadata.size,\n createdAt: metadata.createdAt,\n mime: metadata.mime,\n policy: metadata.policy,\n tag: metadata.tag,\n content: async () => {\n if (!cached) {\n const result = await this.materializeAsset(assetId);\n cached = result.bytes.slice();\n }\n return toReadableStream(cached.slice());\n },\n };\n }\n\n private async materializeAsset(\n assetId: AssetId,\n ): Promise<{ metadata: RepoAssetMetadata; bytes: Uint8Array }> {\n const record = this.assets.get(assetId);\n \n // Removed in-memory data check\n \n if (record && this.storage) {\n const stored = await this.storage.loadAsset(assetId);\n if (stored) {\n return { metadata: record.metadata, bytes: stored };\n }\n }\n\n if (!record && this.storage) {\n const stored = await this.storage.loadAsset(assetId);\n if (stored) {\n const metadata = this.getAssetMetadata(assetId);\n if (!metadata) {\n throw new Error(`Missing metadata for asset ${assetId}`);\n }\n // Removed data cache\n this.assets.set(assetId, { metadata });\n this.updateDocAssetMetadata(assetId, metadata);\n return { metadata, bytes: stored };\n }\n }\n\n if (!this.assetTransport) {\n throw new Error(`Asset ${assetId} is not available locally`);\n }\n\n const remote = await this.assetTransport.fetch(assetId);\n if (!remote) {\n throw new Error(`Asset ${assetId} missing from remote store`);\n }\n const remoteStream = await remote.content();\n const remoteBytes = await streamToUint8Array(remoteStream);\n const metadata: RepoAssetMetadata = {\n assetId,\n size: remote.size,\n createdAt: remote.createdAt,\n ...(remote.mime ? { mime: remote.mime } : {}),\n ...(remote.policy ? { policy: remote.policy } : {}),\n ...(remote.tag ? { tag: remote.tag } : {}),\n };\n\n // Removed data cache\n this.assets.set(assetId, { metadata });\n this.updateDocAssetMetadata(assetId, metadata);\n\n this.metaFlock.put([\"a\", assetId], assetMetaToJson(metadata));\n\n if (this.storage) {\n await this.storage.save({\n type: \"asset\",\n assetId,\n data: remoteBytes.slice(),\n });\n }\n\n return { metadata, bytes: remoteBytes };\n }\n\n private updateDocAssetMetadata(\n assetId: AssetId,\n metadata: RepoAssetMetadata,\n ): void {\n const refs = this.assetToDocRefs.get(assetId);\n if (!refs) return;\n for (const docId of refs) {\n const assets = this.docAssets.get(docId);\n if (assets) {\n assets.set(assetId, metadata);\n }\n }\n }\n\n private rememberAsset(metadata: RepoAssetMetadata): void {\n this.assets.set(metadata.assetId, {\n metadata,\n });\n // Removed auto-delete from orphans. Orphans managed by refs.\n }\n\n private addDocReference(assetId: AssetId, docId: string): void {\n const refs = this.assetToDocRefs.get(assetId) ?? new Set<string>();\n refs.add(docId);\n this.assetToDocRefs.set(assetId, refs);\n this.orphanedAssets.delete(assetId); // Ref added -> not orphan\n }\n\n private removeDocAssetReference(assetId: AssetId, docId: string): void {\n const refs = this.assetToDocRefs.get(assetId);\n if (!refs) return;\n refs.delete(docId);\n if (refs.size === 0) {\n this.assetToDocRefs.delete(assetId);\n this.markAssetAsOrphan(assetId); // Ref zero -> orphan\n }\n }\n\n private markAssetAsOrphan(\n assetId: AssetId,\n metadataOverride?: RepoAssetMetadata,\n ): void {\n const metadata = metadataOverride ?? this.assets.get(assetId)?.metadata;\n if (!metadata) return;\n const existing = this.orphanedAssets.get(assetId);\n const deletedAt = existing?.deletedAt ?? Date.now();\n this.orphanedAssets.set(assetId, {\n metadata,\n deletedAt,\n });\n }\n\n private getAssetMetadata(assetId: AssetId): RepoAssetMetadata | undefined {\n const record = this.assets.get(assetId);\n if (record) return record.metadata;\n for (const assets of this.docAssets.values()) {\n const metadata = assets.get(assetId);\n if (metadata) return metadata;\n }\n return undefined;\n }\n\n private get metaFlock(): Flock {\n return this.getMetaFlock();\n }\n}\n","import { Flock, type Event as FlockEvent } from \"@loro-dev/flock\";\n\nimport type { AssetId, JsonObject, RepoEventBy } from \"../types\";\nimport { asJsonObject, cloneJsonValue } from \"../utils\";\nimport { MetadataManager } from \"./metadata-manager\";\nimport { AssetManager } from \"./asset-manager\";\nimport { DocManager } from \"./doc-manager\";\n\ninterface FlockHydratorOptions<Meta extends JsonObject> {\n readonly getMetaFlock: () => Flock;\n readonly metadataManager: MetadataManager<Meta>;\n readonly assetManager: AssetManager<Meta>;\n readonly docManager: DocManager<Meta>;\n}\n\nexport class FlockHydrator<Meta extends JsonObject> {\n private readonly getMetaFlock: () => Flock;\n private readonly metadataManager: MetadataManager<Meta>;\n private readonly assetManager: AssetManager<Meta>;\n private readonly docManager: DocManager<Meta>;\n\n constructor(options: FlockHydratorOptions<Meta>) {\n this.getMetaFlock = options.getMetaFlock;\n this.metadataManager = options.metadataManager;\n this.assetManager = options.assetManager;\n this.docManager = options.docManager;\n }\n\n // Full reload path used on startup or when meta sync produces no delta.\n // We rehydrate metadata/tombstones first, then assets, then sweep any doc\n // whose keyspace is empty (all m/f/ld/ts rows gone) to mirror remote purges.\n hydrateAll(by: RepoEventBy): void {\n const { metadata, tombstones } = this.readAllDocMetadata();\n this.metadataManager.replaceAll(metadata, by);\n for (const [docId, deletedAtMs] of tombstones) {\n this.metadataManager.emitSoftDeleted(docId, deletedAtMs, by);\n }\n this.assetManager.hydrateFromFlock(by);\n\n const docIds = new Set<string>([\n ...metadata.keys(),\n ...tombstones.keys(),\n ...this.collectDocIds([\"f\"]),\n ...this.collectDocIds([\"ld\"]),\n ]);\n for (const docId of docIds) {\n if (this.isDocKeyspaceEmpty(docId)) {\n void (async () => {\n try {\n this.clearDocKeyspace(docId);\n this.metadataManager.purgeMetadata(docId, by);\n await this.docManager.dropDoc(docId);\n } catch (error) {\n console.error(\"Failed to drop purged doc during hydrateAll\", {\n docId,\n error,\n });\n }\n })();\n }\n }\n }\n\n // Incremental path: walk the event batch once to figure out which doc/asset\n // buckets need refreshing, then apply the minimal set of rebuilds. We also\n // detect \"purged\" docs (no m/f/ld/ts rows left) so local caches/storage don't\n // resurrect data that a peer already deleted.\n async applyEvents(events: FlockEvent[], by: RepoEventBy): Promise<void> {\n if (!events.length) return;\n const docMetadataIds = new Set<string>();\n const docTombstoneIds = new Map<string, number | undefined>();\n const docAssetIds = new Set<string>();\n const docFrontierIds = new Set<string>();\n const assetIds = new Set<AssetId>();\n\n for (const event of events) {\n const key = event.key;\n if (!Array.isArray(key) || key.length === 0) continue;\n const root = key[0];\n if (root === \"m\") {\n const docId = key[1];\n if (typeof docId === \"string\") {\n docMetadataIds.add(docId);\n }\n } else if (root === \"f\") {\n const docId = key[1];\n if (typeof docId === \"string\") {\n docFrontierIds.add(docId);\n }\n } else if (root === \"ts\") {\n const docId = key[1];\n if (typeof docId === \"string\") {\n docTombstoneIds.set(docId, this.toDeletedAt(event.value));\n }\n } else if (root === \"a\") {\n const assetId = key[1];\n if (typeof assetId === \"string\") {\n assetIds.add(assetId as AssetId);\n }\n } else if (root === \"ld\") {\n const docId = key[1];\n const assetId = key[2];\n if (typeof docId === \"string\") {\n docAssetIds.add(docId);\n }\n if (typeof assetId === \"string\") {\n assetIds.add(assetId as AssetId);\n }\n }\n }\n\n for (const assetId of assetIds) {\n this.assetManager.refreshAssetMetadataEntry(assetId, by);\n }\n\n for (const docId of docMetadataIds) {\n this.metadataManager.refreshFromFlock(docId, by);\n }\n\n for (const [docId, deletedAtMs] of docTombstoneIds) {\n this.metadataManager.emitSoftDeleted(docId, deletedAtMs, by);\n }\n\n for (const docId of docAssetIds) {\n this.assetManager.refreshDocAssetsEntry(docId, by);\n }\n\n const docIdsToCheck = new Set<string>([\n ...docMetadataIds,\n ...docTombstoneIds.keys(),\n ...docAssetIds,\n ...docFrontierIds,\n ]);\n for (const docId of docIdsToCheck) {\n if (this.isDocKeyspaceEmpty(docId)) {\n try {\n this.clearDocKeyspace(docId);\n this.metadataManager.purgeMetadata(docId, by);\n await this.docManager.dropDoc(docId);\n } catch (error) {\n console.error(\"Failed to drop purged doc during hydration\", {\n docId,\n error,\n });\n }\n }\n }\n\n for (const docId of docIdsToCheck) {\n const snapshot = this.metadataManager.get(docId);\n const tombstone = this.metaFlock.get([\"ts\", docId]);\n if (!snapshot && tombstone === undefined) {\n try {\n this.clearDocKeyspace(docId);\n this.metadataManager.purgeMetadata(docId, by);\n await this.docManager.dropDoc(docId);\n } catch (error) {\n console.error(\"Failed to drop purged doc after refresh\", {\n docId,\n error,\n });\n }\n }\n }\n\n }\n\n // Reconstruct metadata/tombstones from the Flock replica. We ignore\n // undefined values (compacted away) unless we already had a local snapshot,\n // so deletes don't invent empty docs.\n private readAllDocMetadata(): {\n metadata: Map<string, JsonObject>;\n tombstones: Map<string, number>;\n } {\n const nextMetadata = new Map<string, JsonObject>();\n const metadataRows = this.metaFlock.scan({ prefix: [\"m\"] });\n for (const row of metadataRows) {\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const docId = row.key[1];\n if (typeof docId !== \"string\") continue;\n if (row.value === undefined && !this.metadataManager.get(docId)) {\n continue;\n }\n\n if (row.key.length === 2) {\n const obj = asJsonObject(row.value);\n if (!obj) continue;\n let docMeta = nextMetadata.get(docId);\n if (!docMeta) {\n docMeta = {};\n nextMetadata.set(docId, docMeta);\n }\n for (const [field, value] of Object.entries(obj)) {\n const cloned = cloneJsonValue(value);\n if (cloned !== undefined) {\n docMeta[field] = cloned;\n }\n }\n continue;\n }\n\n const fieldKey = row.key[2];\n if (typeof fieldKey !== \"string\") continue;\n const jsonValue = cloneJsonValue(row.value);\n if (jsonValue === undefined) continue;\n let docMeta = nextMetadata.get(docId);\n if (!docMeta) {\n docMeta = {};\n nextMetadata.set(docId, docMeta);\n }\n docMeta[fieldKey] = jsonValue;\n }\n\n const tombstones = new Map<string, number>();\n const tombstoneRows = this.metaFlock.scan({ prefix: [\"ts\"] });\n for (const row of tombstoneRows) {\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const docId = row.key[1];\n if (typeof docId !== \"string\") continue;\n\n const tombstone = cloneJsonValue(row.value);\n if (typeof tombstone === \"number\" && Number.isFinite(tombstone)) {\n tombstones.set(docId, tombstone);\n }\n }\n return { metadata: nextMetadata, tombstones };\n }\n\n private toDeletedAt(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value)\n ? value\n : undefined;\n }\n\n private get metaFlock(): Flock {\n return this.getMetaFlock();\n }\n\n // A doc is \"empty\" when it has no tombstone and no surviving metadata/frontier/link\n // rows. That implies a remote purge or local compaction, so we can drop in-memory\n // caches and storage snapshots safely.\n private isDocKeyspaceEmpty(docId: string): boolean {\n if (this.metaFlock.get([\"ts\", docId]) !== undefined) return false;\n if (this.hasTruthyKey([\"m\", docId])) return false;\n if (this.hasTruthyKey([\"f\", docId])) return false;\n if (this.hasTruthyKey([\"ld\", docId])) return false;\n return true;\n }\n\n private hasTruthyKey(prefix: [string, string]): boolean {\n const rows = this.metaFlock.scan({ prefix });\n for (const row of rows) {\n const value = row.value;\n const present = value !== undefined;\n if (present) return true;\n }\n return false;\n }\n\n private collectDocIds(prefix: [string]): Set<string> {\n const ids = new Set<string>();\n const rows = this.metaFlock.scan({ prefix });\n for (const row of rows) {\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const docId = row.key[1];\n if (typeof docId === \"string\") {\n ids.add(docId);\n }\n }\n return ids;\n }\n\n private clearDocKeyspace(docId: string): void {\n const prefixes: Array<[string, string]> = [\n [\"m\", docId],\n [\"f\", docId],\n [\"ld\", docId],\n ];\n for (const prefix of prefixes) {\n const rows = this.metaFlock.scan({ prefix });\n for (const row of rows) {\n this.metaFlock.delete(row.key);\n }\n }\n this.metaFlock.delete([\"ts\", docId]);\n }\n}\n","import { Flock, type Event as FlockEvent } from \"@loro-dev/flock\";\n\nimport type {\n JsonObject,\n RepoSyncOptions,\n StorageAdapter,\n TransportAdapter,\n TransportJoinParams,\n TransportSubscription,\n} from \"../types\";\nimport { RepoEventBus } from \"./event-bus\";\nimport { DocManager } from \"./doc-manager\";\nimport { MetadataManager } from \"./metadata-manager\";\nimport { AssetManager } from \"./asset-manager\";\nimport { FlockHydrator } from \"./flock-hydrator\";\nimport { logAsyncError } from \"./logging\";\n\ninterface SyncRunnerOptions<Meta extends JsonObject> {\n readonly storage?: StorageAdapter;\n readonly transport?: TransportAdapter;\n readonly eventBus: RepoEventBus<Meta>;\n readonly docManager: DocManager<Meta>;\n readonly metadataManager: MetadataManager<Meta>;\n readonly assetManager: AssetManager<Meta>;\n readonly flockHydrator: FlockHydrator<Meta>;\n readonly getMetaFlock: () => Flock;\n readonly mergeFlock: (snapshot: Flock) => void;\n}\n\ntype SubscriptionRecord = {\n readonly base: TransportSubscription;\n refCount: number;\n};\n\n/**\n * Sync data between storage and transport layer\n */\nexport class SyncRunner<Meta extends JsonObject> {\n private readonly storage?: StorageAdapter;\n private readonly transport?: TransportAdapter;\n private readonly eventBus: RepoEventBus<Meta>;\n private readonly docManager: DocManager<Meta>;\n private readonly metadataManager: MetadataManager<Meta>;\n private readonly assetManager: AssetManager<Meta>;\n private readonly flockHydrator: FlockHydrator<Meta>;\n private readonly getMetaFlock: () => Flock;\n private readonly replaceMetaFlock: (snapshot: Flock) => void;\n\n private readyPromise?: Promise<void>;\n private metaRoomSubscription?: SubscriptionRecord;\n private unsubscribeMetaFlock?: () => void;\n private readonly docSubscriptions = new Map<string, SubscriptionRecord>();\n\n constructor(options: SyncRunnerOptions<Meta>) {\n this.storage = options.storage;\n this.transport = options.transport;\n this.eventBus = options.eventBus;\n this.docManager = options.docManager;\n this.metadataManager = options.metadataManager;\n this.assetManager = options.assetManager;\n this.flockHydrator = options.flockHydrator;\n this.getMetaFlock = options.getMetaFlock;\n this.replaceMetaFlock = options.mergeFlock;\n }\n\n async ready(): Promise<void> {\n if (!this.readyPromise) {\n this.readyPromise = this.initialize();\n }\n await this.readyPromise;\n }\n\n async sync(options: RepoSyncOptions = {}): Promise<void> {\n await this.ready();\n const { scope = \"full\", docIds } = options;\n if (!this.transport) return;\n\n if (!this.transport.isConnected()) {\n await this.transport.connect();\n }\n\n if (scope === \"meta\" || scope === \"full\") {\n this.eventBus.pushEventBy(\"sync\");\n const recordedEvents: FlockEvent[] = [];\n const unsubscribe = this.metaFlock.subscribe((batch) => {\n if (batch.source === \"local\") return;\n recordedEvents.push(...batch.events);\n });\n try {\n const result = await this.transport.syncMeta(this.metaFlock);\n if (!result.ok) {\n throw new Error(\"Metadata sync failed\");\n }\n if (recordedEvents.length > 0) {\n await this.flockHydrator.applyEvents(recordedEvents, \"sync\");\n } else {\n this.flockHydrator.hydrateAll(\"sync\");\n }\n } finally {\n unsubscribe();\n this.eventBus.popEventBy();\n }\n }\n\n if (scope === \"doc\" || scope === \"full\") {\n const targets = docIds ?? this.metadataManager.getDocIds();\n for (const docId of targets) {\n const doc = await this.docManager.ensureDoc(docId);\n this.eventBus.pushEventBy(\"sync\");\n try {\n const result = await this.transport.syncDoc(docId, doc);\n if (!result.ok) {\n throw new Error(`Document sync failed for ${docId}`);\n }\n } finally {\n this.eventBus.popEventBy();\n }\n await this.docManager.persistDoc(docId, doc);\n await this.docManager.updateDocFrontiers(docId, doc, \"sync\");\n }\n }\n }\n\n async joinMetaRoom(\n params?: TransportJoinParams,\n ): Promise<TransportSubscription> {\n await this.ready();\n if (!this.transport) {\n throw new Error(\"Transport adapter not configured\");\n }\n if (!this.transport.isConnected()) {\n await this.transport.connect();\n }\n const existing = this.metaRoomSubscription;\n if (existing) {\n existing.refCount += 1;\n return this.createMetaLease(existing);\n }\n\n this.ensureMetaLiveMonitor();\n\n const base = this.transport.joinMetaRoom(this.metaFlock, params);\n const record: SubscriptionRecord = { base, refCount: 1 };\n this.metaRoomSubscription = record;\n\n void base.firstSyncedWithRemote\n .then(async () => {\n const by = this.eventBus.resolveEventBy(\"live\");\n this.flockHydrator.hydrateAll(by);\n })\n .catch(logAsyncError(\"meta room first sync\"));\n\n return this.createMetaLease(record);\n }\n\n async joinDocRoom(\n docId: string,\n params?: TransportJoinParams,\n ): Promise<TransportSubscription> {\n await this.ready();\n if (!this.transport) {\n throw new Error(\"Transport adapter not configured\");\n }\n if (!this.transport.isConnected()) {\n await this.transport.connect();\n }\n\n const existing = this.docSubscriptions.get(docId);\n if (existing) {\n existing.refCount += 1;\n return this.createDocLease(docId, existing);\n }\n\n const doc = await this.docManager.ensureDoc(docId);\n const base = this.transport.joinDocRoom(docId, doc, params);\n const record: SubscriptionRecord = { base, refCount: 1 };\n this.docSubscriptions.set(docId, record);\n void base.firstSyncedWithRemote.catch(\n logAsyncError(`doc ${docId} first sync`),\n );\n return this.createDocLease(docId, record);\n }\n\n private createMetaLease(record: SubscriptionRecord): TransportSubscription {\n let released = false;\n return {\n unsubscribe: () => {\n if (released) return;\n released = true;\n const current = this.metaRoomSubscription;\n if (!current || current !== record) return;\n current.refCount = Math.max(0, current.refCount - 1);\n if (current.refCount === 0) {\n current.base.unsubscribe();\n this.metaRoomSubscription = undefined;\n if (this.unsubscribeMetaFlock) {\n this.unsubscribeMetaFlock();\n this.unsubscribeMetaFlock = undefined;\n }\n }\n },\n firstSyncedWithRemote: record.base.firstSyncedWithRemote,\n get connected() {\n return record.base.connected;\n },\n get status() {\n return record.base.status;\n },\n onStatusChange: record.base.onStatusChange,\n };\n }\n\n private createDocLease(\n docId: string,\n record: SubscriptionRecord,\n ): TransportSubscription {\n let released = false;\n return {\n unsubscribe: () => {\n if (released) return;\n released = true;\n const current = this.docSubscriptions.get(docId);\n if (!current || current !== record) return;\n current.refCount = Math.max(0, current.refCount - 1);\n if (current.refCount === 0) {\n current.base.unsubscribe();\n if (this.docSubscriptions.get(docId) === current) {\n this.docSubscriptions.delete(docId);\n }\n }\n },\n firstSyncedWithRemote: record.base.firstSyncedWithRemote,\n get connected() {\n return record.base.connected;\n },\n get status() {\n return record.base.status;\n },\n onStatusChange: record.base.onStatusChange,\n };\n }\n\n async destroy(): Promise<void> {\n await this.docManager.close();\n if (this.metaRoomSubscription) {\n this.metaRoomSubscription.base.unsubscribe();\n this.metaRoomSubscription = undefined;\n }\n for (const record of this.docSubscriptions.values()) {\n record.base.unsubscribe();\n }\n this.docSubscriptions.clear();\n this.unsubscribeMetaFlock?.();\n this.unsubscribeMetaFlock = undefined;\n this.eventBus.clear();\n this.metadataManager.clear();\n this.assetManager.clear();\n this.readyPromise = undefined;\n await this.transport?.close();\n }\n\n private async initialize(): Promise<void> {\n if (this.storage) {\n const snapshot = await this.storage.loadMeta();\n if (snapshot) {\n this.replaceMetaFlock(snapshot);\n }\n }\n this.flockHydrator.hydrateAll(\"sync\");\n }\n\n private ensureMetaLiveMonitor(): void {\n if (this.unsubscribeMetaFlock) return;\n this.unsubscribeMetaFlock = this.metaFlock.subscribe((batch) => {\n if (batch.source === \"local\") return;\n const by = this.eventBus.resolveEventBy(\"live\");\n void (async () => {\n await this.flockHydrator.applyEvents(batch.events, by);\n })().catch(logAsyncError(\"meta live monitor sync\"));\n });\n }\n\n private get metaFlock(): Flock {\n return this.getMetaFlock();\n }\n}\n","import type { AssetId, JsonObject, RepoAssetMetadata } from \"../types\";\n\nexport type AssetRecord = {\n metadata: RepoAssetMetadata;\n};\n\nexport type OrphanedAssetRecord = {\n metadata: RepoAssetMetadata;\n deletedAt: number;\n};\n\nexport interface RepoState {\n readonly metadata: Map<string, JsonObject>;\n readonly docAssets: Map<string, Map<AssetId, RepoAssetMetadata>>;\n readonly assets: Map<AssetId, AssetRecord>;\n readonly orphanedAssets: Map<AssetId, OrphanedAssetRecord>;\n readonly assetToDocRefs: Map<AssetId, Set<string>>;\n}\n\nexport function createRepoState(): RepoState {\n return {\n metadata: new Map<string, JsonObject>(),\n docAssets: new Map<string, Map<AssetId, RepoAssetMetadata>>(),\n assets: new Map<AssetId, AssetRecord>(),\n orphanedAssets: new Map<AssetId, OrphanedAssetRecord>(),\n assetToDocRefs: new Map<AssetId, Set<string>>(),\n };\n}\n","import { type ExportBundle, type Flock, type VersionVector } from \"@loro-dev/flock\";\n\nimport type { StorageAdapter } from \"../types\";\n\nconst textEncoder = new TextEncoder();\nconst DEFAULT_META_PERSIST_DEBOUNCE_MS = 5_000;\n\ninterface MetaPersisterOptions {\n readonly getMetaFlock: () => Flock;\n readonly storage?: StorageAdapter;\n readonly debounceMs?: number;\n}\n\nexport class MetaPersister {\n private readonly getMetaFlock: () => Flock;\n private readonly storage?: StorageAdapter;\n private readonly debounceMs: number;\n\n private lastPersistedVersion?: VersionVector;\n private unsubscribe?: () => void;\n private flushPromise: Promise<void> = Promise.resolve();\n private flushTimer?: ReturnType<typeof setTimeout>;\n private forceFullOnNextFlush = false;\n private destroyed = false;\n\n constructor(options: MetaPersisterOptions) {\n this.getMetaFlock = options.getMetaFlock;\n this.storage = options.storage;\n const configuredDebounce = options.debounceMs;\n this.debounceMs =\n typeof configuredDebounce === \"number\" && Number.isFinite(configuredDebounce) && configuredDebounce >= 0\n ? configuredDebounce\n : DEFAULT_META_PERSIST_DEBOUNCE_MS;\n }\n\n start(initialVersion: VersionVector | undefined): void {\n this.lastPersistedVersion = initialVersion;\n if (this.unsubscribe) return;\n this.unsubscribe = this.metaFlock.subscribe(() => {\n this.scheduleFlush();\n });\n }\n\n async destroy(): Promise<void> {\n this.destroyed = true;\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = undefined;\n }\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = undefined;\n }\n await this.flushNow();\n }\n\n async flushNow(forceFull = false): Promise<void> {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = undefined;\n }\n await this.flush(forceFull);\n }\n\n private scheduleFlush(): void {\n if (this.destroyed) return;\n if (this.debounceMs === 0) {\n void this.flush();\n return;\n }\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n }\n this.flushTimer = setTimeout(() => {\n this.flushTimer = undefined;\n void this.flush();\n }, this.debounceMs);\n }\n\n private async flush(forceFull = false): Promise<void> {\n if (forceFull) this.forceFullOnNextFlush = true;\n const run = this.flushPromise.catch(() => {}).then(() => this.flushInternal());\n this.flushPromise = run;\n await run;\n }\n\n private async flushInternal(): Promise<void> {\n const flock = this.metaFlock;\n const currentVersion = flock.version();\n if (this.lastPersistedVersion && this.versionsEqual(currentVersion, this.lastPersistedVersion)) {\n this.forceFullOnNextFlush = false;\n return;\n }\n\n const baseline = this.forceFullOnNextFlush ? undefined : this.lastPersistedVersion;\n const rawBundle = baseline ? flock.exportJson(baseline) : flock.exportJson();\n const bundle = baseline ? this.stripUnchangedEntries(rawBundle, baseline) : rawBundle;\n if (Object.keys(bundle.entries).length === 0) {\n this.forceFullOnNextFlush = false;\n this.lastPersistedVersion = currentVersion;\n return;\n }\n\n const encoded = textEncoder.encode(JSON.stringify(bundle));\n\n if (!this.storage) {\n this.lastPersistedVersion = currentVersion;\n this.forceFullOnNextFlush = false;\n return;\n }\n\n await this.storage.save({ type: \"meta\", update: encoded });\n this.lastPersistedVersion = currentVersion;\n this.forceFullOnNextFlush = false;\n }\n\n private get metaFlock(): Flock {\n return this.getMetaFlock();\n }\n\n private stripUnchangedEntries(bundle: ExportBundle, baseline: VersionVector): ExportBundle {\n const entries: ExportBundle[\"entries\"] = {};\n for (const [key, record] of Object.entries(bundle.entries)) {\n const clock = this.parseClock(record.c);\n if (!clock) {\n entries[key] = record;\n continue;\n }\n const baselineEntry = baseline[clock.peerIdHex];\n if (!baselineEntry) {\n entries[key] = record;\n continue;\n }\n const isNewer =\n clock.physicalTime > baselineEntry.physicalTime ||\n (clock.physicalTime === baselineEntry.physicalTime && clock.logicalCounter > baselineEntry.logicalCounter);\n if (isNewer) {\n entries[key] = record;\n }\n }\n return { version: bundle.version, entries };\n }\n\n private parseClock(raw: unknown): { physicalTime: number; logicalCounter: number; peerIdHex: string } | undefined {\n if (typeof raw !== \"string\") return undefined;\n const [physicalTimeStr, logicalCounterStr, peerIdHex] = raw.split(\",\");\n if (!physicalTimeStr || !logicalCounterStr || !peerIdHex) return undefined;\n const physicalTime = Number(physicalTimeStr);\n const logicalCounter = Number(logicalCounterStr);\n if (!Number.isFinite(physicalTime) || !Number.isFinite(logicalCounter)) return undefined;\n return { physicalTime, logicalCounter, peerIdHex };\n }\n\n private versionsEqual(a?: VersionVector, b?: VersionVector): boolean {\n if (!a || !b) return false;\n const aKeys = Object.keys(a);\n const bKeys = Object.keys(b);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n const aEntry = a[key];\n const bEntry = b[key];\n if (!aEntry || !bEntry) return false;\n if (aEntry.physicalTime !== bEntry.physicalTime || aEntry.logicalCounter !== bEntry.logicalCounter) {\n return false;\n }\n }\n return true;\n }\n}\n","import { Flock } from \"@loro-dev/flock\";\nimport { LoroDoc } from \"loro-crdt\";\nimport type { EphemeralStore } from \"loro-crdt\";\n\nexport * from \"./types\";\n\nimport type {\n AssetDownload,\n AssetId,\n DeleteDocOptions,\n GcDeletedDocOptions,\n GarbageCollectionOptions,\n JsonObject,\n ListDocQuery,\n LoroRepoOptions,\n LinkAssetOptions,\n RepoAssetMetadata,\n RepoDocHandle,\n RepoDocMeta,\n RepoDocSnapshot,\n RepoEvent,\n RepoEventFilter,\n RepoEventListener,\n RepoSyncOptions,\n RepoWatchHandle,\n StorageAdapter,\n TransportAdapter,\n TransportJoinParams,\n TransportSubscription,\n UploadAssetOptions,\n AssetTransportAdapter,\n} from \"./types\";\n\nimport { RepoEventBus } from \"./internal/event-bus\";\nimport { DocManager } from \"./internal/doc-manager\";\nimport { MetadataManager } from \"./internal/metadata-manager\";\nimport { AssetManager } from \"./internal/asset-manager\";\nimport { FlockHydrator } from \"./internal/flock-hydrator\";\nimport { SyncRunner } from \"./internal/sync-runner\";\nimport { createRepoState, type RepoState } from \"./internal/repo-state\";\nimport { MetaPersister } from \"./internal/meta-persister\";\nconst DEFAULT_DOC_FRONTIER_DEBOUNCE_MS = 1_000;\nconst DEFAULT_DELETED_DOC_KEEP_MS = 30 * 24 * 60 * 60 * 1_000;\n\nexport class LoroRepo<Meta extends JsonObject = JsonObject> {\n readonly options: LoroRepoOptions;\n private _destroyed = false;\n private readonly transport?: TransportAdapter;\n private readonly storage?: StorageAdapter;\n private metaFlock: Flock = new Flock();\n private readonly eventBus: RepoEventBus<Meta>;\n private readonly docManager: DocManager<Meta>;\n private readonly metadataManager: MetadataManager<Meta>;\n private readonly assetManager: AssetManager<Meta>;\n private readonly assetTransport?: AssetTransportAdapter;\n private readonly flockHydrator: FlockHydrator<Meta>;\n private readonly state: RepoState;\n private readonly syncRunner: SyncRunner<Meta>;\n private readonly metaPersister: MetaPersister;\n private readonly deletedDocKeepMs: number;\n private readonly purgeWatchHandle: RepoWatchHandle;\n\n private constructor(options: LoroRepoOptions) {\n this.options = options;\n this.transport = options.transportAdapter;\n this.storage = options.storageAdapter;\n this.assetTransport = options.assetTransportAdapter;\n this.eventBus = new RepoEventBus<Meta>();\n this.state = createRepoState();\n const configuredDebounce = options.docFrontierDebounceMs;\n const docFrontierDebounceMs =\n typeof configuredDebounce === \"number\" &&\n Number.isFinite(configuredDebounce) &&\n configuredDebounce >= 0\n ? configuredDebounce\n : DEFAULT_DOC_FRONTIER_DEBOUNCE_MS;\n const configuredDeletedKeepMs = options.deletedDocKeepMs;\n this.deletedDocKeepMs =\n typeof configuredDeletedKeepMs === \"number\" &&\n Number.isFinite(configuredDeletedKeepMs) &&\n configuredDeletedKeepMs >= 0\n ? configuredDeletedKeepMs\n : DEFAULT_DELETED_DOC_KEEP_MS;\n this.docManager = new DocManager<Meta>({\n storage: this.storage,\n docFrontierDebounceMs,\n getMetaFlock: () => this.metaFlock,\n eventBus: this.eventBus,\n });\n this.metadataManager = new MetadataManager<Meta>({\n getMetaFlock: () => this.metaFlock,\n eventBus: this.eventBus,\n state: this.state,\n });\n this.assetManager = new AssetManager<Meta>({\n storage: this.storage,\n assetTransport: this.assetTransport,\n getMetaFlock: () => this.metaFlock,\n eventBus: this.eventBus,\n state: this.state,\n });\n this.metaPersister = new MetaPersister({\n getMetaFlock: () => this.metaFlock,\n storage: this.storage,\n debounceMs: options.metaPersistDebounceMs,\n });\n this.flockHydrator = new FlockHydrator<Meta>({\n getMetaFlock: () => this.metaFlock,\n metadataManager: this.metadataManager,\n assetManager: this.assetManager,\n docManager: this.docManager,\n });\n this.syncRunner = new SyncRunner<Meta>({\n storage: this.storage,\n transport: this.transport,\n eventBus: this.eventBus,\n docManager: this.docManager,\n metadataManager: this.metadataManager,\n assetManager: this.assetManager,\n flockHydrator: this.flockHydrator,\n getMetaFlock: () => this.metaFlock,\n mergeFlock: (snapshot) => {\n this.metaFlock.merge(snapshot)\n },\n });\n\n this.purgeWatchHandle = this.eventBus.watch(\n (event) => this.handlePurgeSignals(event),\n { kinds: [\"doc-soft-deleted\", \"doc-metadata\"], by: [\"sync\", \"live\"] },\n );\n }\n\n static async create<Meta extends JsonObject = JsonObject>(options: LoroRepoOptions): Promise<LoroRepo<Meta>> {\n const repo = new LoroRepo<Meta>(options);\n await repo.storage?.init?.();\n // Transport may not be valid because the client may be offline, and it would hurt DX if we wait for transport layer \n // to initialize here.\n await repo.ready();\n return repo;\n }\n\n /**\n * Load meta from storage.\n * \n * You need to call this before all other operations to make the app functioning correctly.\n * Though we do that implicitly already\n */\n private async ready(): Promise<void> {\n await this.syncRunner.ready();\n this.metaPersister.start(this.metaFlock.version());\n }\n\n private computeDocPurgeAfter(\n docId: string,\n minKeepMs: number,\n ): number | undefined {\n const deletedAt = this.metadataManager.getDeletedAtMs(docId);\n if (deletedAt === undefined) return undefined;\n return deletedAt + minKeepMs;\n }\n\n private purgeDocKeyspace(docId: string): void {\n const metadataKeys = Array.from(\n this.metaFlock.scan({ prefix: [\"m\", docId] }),\n (row) => row.key,\n );\n for (const key of metadataKeys) {\n this.metaFlock.delete(key);\n }\n\n const frontierKeys = Array.from(\n this.metaFlock.scan({ prefix: [\"f\", docId] }),\n (row) => row.key,\n );\n for (const key of frontierKeys) {\n this.metaFlock.delete(key);\n }\n\n this.metaFlock.delete([\"ts\", docId]);\n }\n\n /**\n * Sync selected data via the transport adaptor\n * @param options \n */\n async sync(options: RepoSyncOptions = {}): Promise<void> {\n await this.syncRunner.sync(options);\n }\n\n /**\n * Start syncing the metadata (Flock) room. It will establish a realtime connection to the transport adaptor.\n * All changes on the room will be synced to the Flock, and all changes on the Flock will be synced to the room.\n *\n * - Idempotent: repeated calls reuse the same underlying room session; no extra join request is sent for the same repo.\n * - Reference-counted leave: every call to `joinMetaRoom` returns a subscription that increments an internal counter. The room is\n * actually left only after all returned subscriptions have called `unsubscribe`.\n * @param params \n * @returns \n */\n async joinMetaRoom(\n params?: TransportJoinParams,\n ): Promise<TransportSubscription> {\n const subscription = await this.syncRunner.joinMetaRoom(params);\n return {\n unsubscribe: subscription.unsubscribe,\n get connected() {\n return subscription.connected;\n },\n get status() {\n return subscription.status;\n },\n onStatusChange: subscription.onStatusChange,\n firstSyncedWithRemote: subscription.firstSyncedWithRemote.then(async () => {\n await this.metaPersister.flushNow();\n }),\n };\n }\n\n /**\n * Start syncing the given doc. It will establish a realtime connection to the transport adaptor.\n * All changes on the doc will be synced to the transport, and all changes on the transport will be synced to the doc.\n * \n * All the changes on the room will be reflected on the same doc you get from `repo.openCollaborativeDoc(docId)`\n *\n * - Idempotent: multiple joins for the same `docId` reuse the existing session; no duplicate transport joins are issued.\n * - Reference-counted leave: each returned subscription bumps an internal counter and only the final `unsubscribe()` will\n * actually leave the room. Earlier unsubscribes simply decrement the counter.\n * @param docId \n * @param params \n * @returns \n */\n async joinDocRoom(\n docId: string,\n params?: TransportJoinParams,\n ): Promise<TransportSubscription> {\n const subscription = await this.syncRunner.joinDocRoom(docId, params);\n return {\n ...subscription,\n onStatusChange: subscription.onStatusChange,\n status: subscription.status,\n };\n }\n\n /**\n * Joins an ephemeral CRDT room. This is useful for presence-like state that should not be persisted.\n * The returned store can be used immediately; the first sync promise resolves once the initial handshake completes.\n */\n async joinEphemeralRoom(\n roomId: string,\n ): Promise<TransportSubscription & { store: EphemeralStore }> {\n if (!this.transport) {\n throw new Error(\"Transport adapter not configured\");\n }\n await this.syncRunner.ready();\n if (!this.transport.isConnected()) {\n await this.transport.connect();\n }\n const subscription = this.transport.joinEphemeralRoom(roomId);\n return {\n ...subscription,\n onStatusChange: subscription.onStatusChange,\n status: subscription.status,\n };\n }\n\n /**\n * Opens a document that is automatically persisted to the configured storage adapter.\n * \n * - Edits are saved to storage (debounced).\n * - Frontiers are synced to the metadata (Flock).\n * - Realtime collaboration is NOT enabled by default; use `joinDocRoom` to connect.\n */\n async openPersistedDoc(docId: string): Promise<RepoDocHandle> {\n return {\n doc: await this.docManager.openPersistedDoc(docId),\n syncOnce: () => {\n return this.sync({ scope: \"doc\", docIds: [docId] });\n },\n joinRoom: (auth) => {\n return this.syncRunner.joinDocRoom(docId, { auth })\n }\n }\n }\n\n async upsertDocMeta(\n docId: string,\n patch: Partial<Meta>,\n ): Promise<void> {\n await this.metadataManager.upsert(docId, patch);\n }\n\n async getDocMeta(docId: string): Promise<RepoDocSnapshot<Meta> | undefined> {\n return this.metadataManager.get(docId);\n }\n\n async listDoc(query?: ListDocQuery): Promise<RepoDocMeta<Meta>[]> {\n return this.metadataManager.listDoc(query);\n }\n\n /**\n * Mark a document deleted by writing a `ts/*` tombstone entry (timestamp).\n * The body and metadata remain until purged; callers use the tombstone to\n * render deleted state or trigger retention workflows. For immediate removal,\n * call `purgeDoc` instead.\n */\n async deleteDoc(\n docId: string,\n options: DeleteDocOptions = {},\n ): Promise<void> {\n const existing = this.metadataManager.getDeletedAtMs(docId);\n if (existing !== undefined && !options.force) return; // idempotent without force\n\n const deletedAt = options.deletedAt ?? Date.now();\n this.metadataManager.markDeleted(docId, deletedAt);\n }\n\n /**\n * Undo a soft delete by removing the tombstone entry. Metadata and document\n * state remain untouched.\n */\n async restoreDoc(docId: string): Promise<void> {\n const existing = this.metadataManager.getDeletedAtMs(docId);\n if (existing === undefined) return;\n this.metadataManager.clearDeleted(docId);\n }\n\n /**\n * Hard-delete a document immediately. Removes doc snapshots/updates via the\n * storage adapter (if supported), clears metadata/frontiers/link keys from\n * Flock, and unlinks assets (they become orphaned for asset GC).\n */\n async purgeDoc(docId: string): Promise<void> {\n const deletedAtMs = this.metadataManager.getDeletedAtMs(docId);\n this.eventBus.emit({\n kind: \"doc-purging\",\n docId,\n deletedAtMs,\n by: \"local\",\n });\n await this.docManager.dropDoc(docId);\n\n this.assetManager.purgeDocLinks(docId, \"local\");\n\n this.purgeDocKeyspace(docId);\n this.metadataManager.emitSoftDeleted(docId, undefined, \"local\");\n\n this.metadataManager.refreshFromFlock(docId, \"local\");\n }\n\n /**\n * Sweep tombstoned documents whose retention window expired. Uses\n * `deletedDocKeepMs` by default; pass `minKeepMs`/`now` for overrides.\n */\n async gcDeletedDocs(options: GcDeletedDocOptions = {}): Promise<number> {\n const now = options.now ?? Date.now();\n const minKeepMs = options.minKeepMs ?? this.deletedDocKeepMs;\n const docIds = this.metadataManager.getDocIds();\n let purged = 0;\n for (const docId of docIds) {\n const purgeAfter = this.computeDocPurgeAfter(docId, minKeepMs);\n if (purgeAfter === undefined || now < purgeAfter) continue;\n await this.purgeDoc(docId);\n purged += 1;\n }\n return purged;\n }\n\n getMeta(): Flock {\n return this.metaFlock;\n }\n\n watch(\n listener: RepoEventListener<Meta>,\n filter: RepoEventFilter<Meta> = {},\n ): RepoWatchHandle {\n return this.eventBus.watch(listener, filter);\n }\n\n /**\n * Opens a detached `LoroDoc` snapshot.\n * \n * - **No Persistence**: Edits to this document are NOT saved to storage.\n * - **No Sync**: This document does not participate in realtime updates.\n * - **Use Case**: Ideal for read-only history inspection, temporary drafts, or conflict resolution without affecting the main state.\n */\n async openDetachedDoc(docId: string): Promise<LoroDoc> {\n return this.docManager.openDetachedDoc(docId);\n }\n\n /**\n * Explicitly unloads a document from memory.\n * \n * - **Persists Immediately**: Forces a save of the document's current state to storage.\n * - **Frees Memory**: Removes the document from the internal cache.\n * - **Note**: If the document is currently being synced (via `joinDocRoom`), you should also unsubscribe from the room to fully release resources.\n */\n async unloadDoc(docId: string): Promise<void> {\n await this.docManager.unloadDoc(docId);\n }\n\n async flush(): Promise<void> {\n await this.docManager.flush();\n await this.metaPersister.flushNow();\n }\n\n async uploadAsset(params: UploadAssetOptions): Promise<AssetId> {\n return this.assetManager.uploadAsset(params);\n }\n\n async linkAsset(docId: string, params: LinkAssetOptions): Promise<AssetId> {\n return this.assetManager.linkAsset(docId, params);\n }\n\n async fetchAsset(assetId: AssetId): Promise<AssetDownload> {\n return this.assetManager.fetchAsset(assetId);\n }\n\n async unlinkAsset(docId: string, assetId: AssetId): Promise<void> {\n await this.assetManager.unlinkAsset(docId, assetId);\n }\n\n async listAssets(docId: string): Promise<RepoAssetMetadata[]> {\n return this.assetManager.listAssets(docId);\n }\n\n async ensureAsset(assetId: AssetId): Promise<AssetDownload> {\n return this.assetManager.ensureAsset(assetId);\n }\n\n async gcAssets(options: GarbageCollectionOptions = {}): Promise<number> {\n return this.assetManager.gcAssets(options);\n }\n\n get destroyed(): boolean {\n return this._destroyed;\n }\n\n async destroy(): Promise<void> {\n if (this._destroyed) return;\n this._destroyed = true;\n this.purgeWatchHandle.unsubscribe();\n await this.metaPersister.destroy();\n await this.syncRunner.destroy();\n this.assetTransport?.close?.();\n this.storage?.close?.();\n await this.transport?.close();\n }\n\n private handlePurgeSignals(event: RepoEvent<Meta>): void {\n // This listener is scoped to sync/live events via the watch filter.\n const docId = (() => {\n if (event.kind === \"doc-soft-deleted\") return event.docId;\n if (event.kind === \"doc-metadata\") return event.docId;\n return undefined;\n })();\n\n if (!docId) return;\n\n const metadataCleared =\n event.kind === \"doc-metadata\" &&\n Object.keys(event.patch).length === 0;\n\n const tombstoneCleared =\n event.kind === \"doc-soft-deleted\" && event.deletedAtMs === undefined;\n\n // Only drop local state when we know the remote replica purged the doc:\n // 1) metadataCleared indicates m/* rows were removed.\n // 2) tombstone cleared *and* no remaining metadata snapshot implies purge, not restore.\n const purgeDetected =\n metadataCleared ||\n (tombstoneCleared && this.metadataManager.get(docId) === undefined);\n\n if (!purgeDetected) return;\n\n void this.docManager.dropDoc(docId).catch((error) => {\n console.error(\"Failed to drop purged doc\", { docId, error });\n });\n }\n}\n"],"mappings":";;;;;;;AAcA,IAAa,eAAb,MAAmD;CACjD,AAAiB,2BAAW,IAAI,KAAuB;CACvD,AAAiB,eAA8B,EAAE;CAEjD,MACE,UACA,SAAgC,EAAE,EACjB;EACjB,MAAMA,QAA0B;GAAE;GAAU;GAAQ;AACpD,OAAK,SAAS,IAAI,MAAM;AACxB,SAAO,EACL,mBAAmB;AACjB,QAAK,SAAS,OAAO,MAAM;KAE9B;;CAGH,KAAK,OAA8B;AACjC,OAAK,MAAM,SAAS,KAAK,SACvB,KAAI,KAAK,aAAa,MAAM,QAAQ,MAAM,CACxC,OAAM,SAAS,MAAM;;CAK3B,QAAc;AACZ,OAAK,SAAS,OAAO;AACrB,OAAK,aAAa,SAAS;;CAG7B,YAAY,IAAuB;AACjC,OAAK,aAAa,KAAK,GAAG;;CAG5B,aAAmB;AACjB,OAAK,aAAa,KAAK;;CAGzB,eAAe,WAAqC;EAClD,MAAM,QAAQ,KAAK,aAAa,SAAS;AACzC,SAAO,SAAS,IAAI,KAAK,aAAa,SAAS;;CAGjD,AAAQ,aACN,QACA,OACS;AACT,MAAI,CAAC,OAAO,UAAU,CAAC,OAAO,SAAS,CAAC,OAAO,kBAAkB,CAAC,OAAO,GACvE,QAAO;AACT,MAAI,OAAO,SAAS,CAAC,OAAO,MAAM,SAAS,MAAM,KAAK,CAAE,QAAO;AAC/D,MAAI,OAAO,MAAM,CAAC,OAAO,GAAG,SAAS,MAAM,GAAG,CAAE,QAAO;EAEvD,MAAM,eAAe;AACnB,OACE,MAAM,SAAS,kBACf,MAAM,SAAS,mBACf,MAAM,SAAS,sBACf,MAAM,SAAS,cAEf,QAAO,MAAM;AAEf,OAAI,MAAM,SAAS,gBAAgB,MAAM,SAAS,eAChD,QAAO,MAAM;MAGb;AAEJ,MAAI,OAAO,UAAU,SAAS,CAAC,OAAO,OAAO,SAAS,MAAM,CAAE,QAAO;AACrE,MAAI,OAAO,UAAU,CAAC,MAAO,QAAO;AAEpC,MAAI,OAAO,kBAAkB,MAAM,SAAS,gBAE1C;OAAI,CADS,OAAO,KAAK,MAAM,MAAM,CAC3B,MAAM,QAAQ,OAAO,gBAAgB,SAAS,IAAI,CAAC,CAC3D,QAAO;;AAIX,SAAO;;;;;;AC3FX,SAAgB,cAAc,SAA2C;AACvE,SAAQ,UAAmB;AACzB,MAAI,iBAAiB,MACnB,SAAQ,MAAM,eAAe,QAAQ,WAAW,MAAM,WAAW,MAAM;MAEvE,SAAQ,MACN,eAAe,QAAQ,iCACvB,MACD;;;;;;ACgBP,IAAa,aAAb,MAAiD;CAC/C,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,AAAiB,uBAAO,IAAI,KAAsB;CAClD,AAAiB,mCAAmB,IAAI,KAAyB;CACjE,AAAiB,qCAAqB,IAAI,KAGvC;CACH,AAAiB,uCAAuB,IAAI,KAA4B;CACxE,YAAY,SAAkC;AAC5C,OAAK,UAAU,QAAQ;AACvB,OAAK,wBAAwB,QAAQ;AACrC,OAAK,eAAe,QAAQ;AAC5B,OAAK,WAAW,QAAQ;;CAG1B,MAAM,iBACJ,OACkB;AAElB,SADY,MAAM,KAAK,UAAU,MAAM;;CAIzC,MAAM,gBAAgB,OAAiC;AAErD,SADY,MAAM,KAAK,uBAAuB,MAAM;;CAItD,MAAM,UAAU,OAAiC;EAC/C,MAAM,SAAS,KAAK,KAAK,IAAI,MAAM;AACnC,MAAI,QAAQ;AACV,QAAK,sBAAsB,OAAO,OAAO;AACzC,OAAI,CAAC,KAAK,qBAAqB,IAAI,MAAM,CACvC,MAAK,qBAAqB,IAAI,OAAO,OAAO,SAAS,CAAC;AAExD,UAAO;;AAGT,MAAI,KAAK,SAAS;GAChB,MAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,MAAM;AAChD,OAAI,QAAQ;AACV,SAAK,YAAY,OAAO,OAAO;AAC/B,WAAO;;;EAIX,MAAM,UAAU,IAAIC,mBAAS;AAC7B,OAAK,YAAY,OAAO,QAAQ;AAChC,SAAO;;CAGT,MAAM,WAAW,OAAe,KAA6B;EAC3D,MAAM,kBAAkB,KAAK,qBAAqB,IAAI,MAAM;EAC5D,MAAM,WAAW,IAAI,OAAO,EAAE,MAAM,YAAY,CAAC;EACjD,MAAM,cAAc,IAAI,SAAS;AACjC,MAAI,CAAC,KAAK,SAAS;AACjB,QAAK,qBAAqB,IAAI,OAAO,YAAY;AACjD;;AAEF,OAAK,qBAAqB,IAAI,OAAO,YAAY;AACjD,MAAI;AACF,SAAM,KAAK,QAAQ,KAAK;IACtB,MAAM;IACN;IACA;IACD,CAAC;WACK,OAAO;AACd,OAAI,gBACF,MAAK,qBAAqB,IAAI,OAAO,gBAAgB;OAErD,MAAK,qBAAqB,OAAO,MAAM;AAEzC,SAAM;;;CAIV,MAAM,mBACJ,OACA,KACA,WACe;EACf,MAAM,YAAY,IAAI,gBAAgB;EACtC,MAAM,KAAK,IAAI,SAAS;EACxB,MAAM,oBAAoB,KAAK,uBAAuB,MAAM;EAC5D,IAAI,UAAU;EACd,MAAM,YAAY,KAAK;AAEvB,OAAK,MAAM,KAAK,UAEd,KADgB,kBAAkB,IAAI,EAAE,KAAK,KAC7B,EAAE,SAAS;AACzB,aAAU,IAAI;IAAC;IAAK;IAAO,EAAE;IAAK,EAAE,EAAE,QAAQ;AAC9C,aAAU;;AAId,MAAI,QACF,MAAK,MAAM,CAAC,MAAM,YAAY,mBAAmB;GAC/C,MAAM,gBAAgB,GAAG,IAAI,KAAe;AAC5C,OAAI,iBAAiB,QAAQ,gBAAgB,QAC3C,WAAU,OAAO;IAAC;IAAK;IAAO;IAAK,CAAC;;EAK1C,MAAM,KAAK,KAAK,SAAS,eAAe,UAAU;AAClD,OAAK,SAAS,KAAK;GAAE,MAAM;GAAiB;GAAO;GAAW;GAAI,CAAC;;CAGrE,MAAM,gCAAgC,OAAiC;EACrE,MAAM,UAAU,KAAK,mBAAmB,IAAI,MAAM;AAClD,MAAI,CAAC,QAAS,QAAO;AACrB,eAAa,QAAQ,QAAQ;AAC7B,OAAK,mBAAmB,OAAO,MAAM;AACrC,OAAK,SAAS,YAAY,QAAQ,GAAG;AACrC,MAAI;AACF,SAAM,KAAK,mBAAmB,OAAO,QAAQ,KAAK,QAAQ,GAAG;YACrD;AACR,QAAK,SAAS,YAAY;;AAE5B,SAAO;;CAGT,MAAM,UAAU,OAA8B;EAC5C,MAAM,MAAM,KAAK,KAAK,IAAI,MAAM;AAChC,MAAI,CAAC,IAAK;AAGV,QAAM,KAAK,gCAAgC,MAAM;AAGjD,QAAM,KAAK,iBAAiB,OAAO,IAAI;AAIvC,QAAM,KAAK,mBAAmB,OAAO,KAAK,QAAQ;AAIlD,EADoB,KAAK,iBAAiB,IAAI,MAAM,IACrC;AACf,OAAK,iBAAiB,OAAO,MAAM;AACnC,OAAK,KAAK,OAAO,MAAM;AACvB,OAAK,qBAAqB,OAAO,MAAM;;CAGzC,MAAM,QAAQ,OAA8B;EAC1C,MAAM,UAAU,KAAK,mBAAmB,IAAI,MAAM;AAClD,MAAI,SAAS;AACX,gBAAa,QAAQ,QAAQ;AAC7B,QAAK,mBAAmB,OAAO,MAAM;;AAIvC,EADoB,KAAK,iBAAiB,IAAI,MAAM,IACrC;AACf,OAAK,iBAAiB,OAAO,MAAM;AACnC,OAAK,KAAK,OAAO,MAAM;AACvB,OAAK,qBAAqB,OAAO,MAAM;AAEvC,MAAI,KAAK,SAAS,UAChB,OAAM,KAAK,QAAQ,UAAU,MAAM;;CAIvC,MAAM,QAAuB;EAC3B,MAAMC,WAA4B,EAAE;AACpC,OAAK,MAAM,CAAC,OAAO,QAAQ,KAAK,KAC9B,UAAS,MACN,YAAY;AACX,SAAM,KAAK,iBAAiB,OAAO,IAAI;AACvC,SAAM,KAAK,gCAAgC,MAAM;MAC/C,CACL;AAEH,QAAM,QAAQ,IAAI,SAAS;;CAG7B,MAAM,QAAuB;AAE3B,QAAM,KAAK,OAAO;AAElB,OAAK,MAAM,eAAe,KAAK,iBAAiB,QAAQ,CACtD,KAAI;AACF,gBAAa;UACP;AAIV,OAAK,iBAAiB,OAAO;AAC7B,OAAK,mBAAmB,OAAO;AAC/B,OAAK,KAAK,OAAO;AACjB,OAAK,qBAAqB,OAAO;;CAGnC,IAAY,YAAmB;AAC7B,SAAO,KAAK,cAAc;;CAG5B,AAAQ,uBAAuB,OAAoC;EACjE,MAAM,OAAO,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC,KAAK,MAAM,EAAE,CAAC;EAC1D,MAAM,4BAAY,IAAI,KAAqB;AAC3C,OAAK,MAAM,OAAO,MAAM;AACtB,OAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAG;GACnD,MAAM,OAAO,IAAI,IAAI;GACrB,MAAM,UAAU,IAAI;AACpB,OAAI,OAAO,SAAS,SAAU;AAC9B,OAAI,OAAO,YAAY,YAAY,CAAC,OAAO,SAAS,QAAQ,CAAE;AAC9D,aAAU,IAAI,MAAM,QAAQ;;AAE9B,SAAO;;CAGT,AAAQ,YAAY,OAAe,KAAoB;AACrD,OAAK,KAAK,IAAI,OAAO,IAAI;AACzB,OAAK,qBAAqB,IAAI,OAAO,IAAI,SAAS,CAAC;AACnD,OAAK,sBAAsB,OAAO,IAAI;;CAGxC,AAAQ,sBAAsB,OAAe,KAAoB;AAC/D,MAAI,KAAK,iBAAiB,IAAI,MAAM,CAAE;EACtC,MAAM,cAAc,IAAI,WAAW,UAA0B;GAC3D,MAAM,UAAU,KAAK,SAAS,eAAe,QAAQ;GACrD,MAAMC,KACJ,YAAY,WAAW,MAAM,OAAO,WAAW,SAAS;AAC1D,QAAK,WAAW,OAAO,KAAK,OAAO,GAAG;IACtC;AACF,MAAI,OAAO,gBAAgB,WACzB,MAAK,iBAAiB,IAAI,OAAO,YAA0B;;CAI/D,AAAQ,0BACN,OACA,KACA,IACM;EACN,MAAM,WAAW,KAAK,mBAAmB,IAAI,MAAM;EACnD,MAAM,cAAc,WAAW,KAAK,iBAAiB,SAAS,IAAI,GAAG,GAAG;AACxE,MAAI,SACF,cAAa,SAAS,QAAQ;EAEhC,MAAM,QACJ,KAAK,wBAAwB,IAAI,KAAK,wBAAwB;EAChE,MAAM,UAAU,iBACR,KAAK,8BAA8B,MAAM,EAC/C,MACD;AACD,OAAK,mBAAmB,IAAI,OAAO;GAAE;GAAS;GAAK,IAAI;GAAa,CAAC;;CAGvE,AAAQ,iBACN,SACA,MACa;AACb,MAAI,YAAY,KAAM,QAAO;AAC7B,MAAI,YAAY,UAAU,SAAS,OAAQ,QAAO;AAClD,MAAI,YAAY,UAAU,SAAS,OAAQ,QAAO;AAClD,SAAO;;CAGT,AAAQ,8BAA8B,OAAqB;EACzD,MAAM,UAAU,KAAK,mBAAmB,IAAI,MAAM;AAClD,MAAI,CAAC,QAAS;AACd,OAAK,mBAAmB,OAAO,MAAM;AACrC,OAAK,SAAS,YAAY,QAAQ,GAAG;AACrC,GAAM,YAAY;AAChB,OAAI;AACF,UAAM,KAAK,mBAAmB,OAAO,QAAQ,KAAK,QAAQ,GAAG;aACrD;AACR,SAAK,SAAS,YAAY;;MAE1B,CAAC,MAAM,cAAc,OAAO,MAAM,oBAAoB,CAAC;;CAG7D,MAAc,uBAAuB,OAAiC;EACpE,MAAM,WAAW,MAAM,KAAK,kBAAkB,MAAM;AACpD,MAAI,SACF,QAAOF,kBAAQ,aAAa,SAAS;AAEvC,SAAO,IAAIA,mBAAS;;CAGtB,MAAc,kBACZ,OACiC;EACjC,MAAM,SAAS,KAAK,KAAK,IAAI,MAAM;AACnC,MAAI,OACF,QAAO,OAAO,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAI,CAAC,KAAK,QACR;AAGF,UADe,MAAM,KAAK,QAAQ,QAAQ,MAAM,GACjC,OAAO,EAAE,MAAM,YAAY,CAAC;;CAG7C,MAAc,iBAAiB,OAAe,KAA6B;EACzE,MAAM,kBAAkB,KAAK,qBAAqB,IAAI,MAAM;EAC5D,MAAM,cAAc,IAAI,cAAc;AACtC,MAAI,CAAC,KAAK,SAAS;AACjB,QAAK,qBAAqB,IAAI,OAAO,YAAY;AACjD;;AAGF,MAAI,CAAC,iBAAiB;AACpB,SAAM,KAAK,WAAW,OAAO,IAAI;AACjC,QAAK,qBAAqB,IAAI,OAAO,YAAY;AACjD;;AAGF,MAAI,gBAAgB,QAAQ,YAAY,KAAK,EAC3C;EAGF,MAAM,SAAS,IAAI,OAAO;GAAE,MAAM;GAAU,MAAM;GAAiB,CAAC;AACpE,OAAK,qBAAqB,IAAI,OAAO,YAAY;AACjD,MAAI;AACF,SAAM,KAAK,QAAQ,KAAK;IACtB,MAAM;IACN;IACA;IACD,CAAC;WACK,OAAO;AACd,QAAK,qBAAqB,IAAI,OAAO,gBAAgB;AACrD,SAAM;;;CAIV,AAAQ,WACN,OACA,KACA,QACA,IACM;AACN,GAAM,YAAY;GAChB,MAAM,UAAU,KAAK,iBAAiB,OAAO,IAAI;AACjD,OAAI,OAAO,SAAS;AAClB,SAAK,0BAA0B,OAAO,KAAK,GAAG;AAC9C,UAAM;AACN;;GAGF,MAAM,UAAU,KAAK,gCAAgC,MAAM;GAC3D,MAAM,WAAW,YAAY;AAC3B,SAAK,SAAS,YAAY,GAAG;AAC7B,QAAI;AACF,WAAM,KAAK,mBAAmB,OAAO,KAAK,GAAG;cACrC;AACR,UAAK,SAAS,YAAY;;OAE1B;AACJ,SAAM,QAAQ,IAAI;IAAC;IAAS;IAAS;IAAQ,CAAC;MAC5C,CAAC,MAAM,cAAc,OAAO,MAAM,mBAAmB,CAAC;;;;;;ACvW9D,eAAsB,mBACpB,QACqB;CACrB,MAAM,SAAS,OAAO,WAAW;CACjC,MAAMG,SAAuB,EAAE;CAC/B,IAAI,QAAQ;AACZ,QAAO,MAAM;EACX,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,MAAM;AAC3C,MAAI,KAAM;AACV,MAAI,OAAO;AACT,UAAO,KAAK,MAAM;AAClB,YAAS,MAAM;;;CAGnB,MAAM,SAAS,IAAI,WAAW,MAAM;CACpC,IAAI,SAAS;AACb,MAAK,MAAM,SAAS,QAAQ;AAC1B,SAAO,IAAI,OAAO,OAAO;AACzB,YAAU,MAAM;;AAElB,QAAO;;AAGT,eAAsB,yBACpB,SACqB;AACrB,KAAI,mBAAmB,WACrB,QAAO;AAET,KAAI,YAAY,OAAO,QAAQ,CAC7B,QAAO,IAAI,WACT,QAAQ,OAAO,MACb,QAAQ,YACR,QAAQ,aAAa,QAAQ,WAC9B,CACF;AAEH,KAAI,OAAO,SAAS,eAAe,mBAAmB,KACpD,QAAO,IAAI,WAAW,MAAM,QAAQ,aAAa,CAAC;AAEpD,KACE,OAAO,mBAAmB,eAC1B,mBAAmB,eAEnB,QAAO,mBAAmB,QAAQ;AAEpC,OAAM,IAAI,UAAU,iCAAiC;;AAGvD,SAAgB,WAAW,OAA2B;AACpD,QAAO,MAAM,KAAK,QAAQ,SAAS,KAAK,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,KACrE,GACD;;AAGH,eAAsB,cAAc,OAAoC;CACtE,MAAM,eAAgB,WAA2C;AACjE,KACE,cAAc,UACd,OAAO,aAAa,OAAO,WAAW,YACtC;EACA,MAAM,SAAS,MAAM,aAAa,OAAO,OAAO,WAAW,MAAM;AACjE,SAAO,WAAW,IAAI,WAAW,OAAO,CAAC;;AAE3C,KAAI;EACF,MAAM,EAAE,eAAe,MAAM,OAAO;EACpC,MAAM,OAAO,WAAW,SAAS;AACjC,OAAK,OAAO,MAAM;AAClB,SAAO,KAAK,OAAO,MAAM;SACnB;AACN,QAAM,IAAI,MAAM,sDAAsD;;;AAI1E,SAAgB,eAAe,OAAuC;AACpE,KAAI,UAAU,KAAM,QAAO;AAC3B,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAChD,QAAO;AAET,KAAI,OAAO,UAAU,SACnB,QAAO,OAAO,SAAS,MAAM,GAAG,QAAQ;AAE1C,KAAI,MAAM,QAAQ,MAAM,EAAE;EACxB,MAAMC,MAAmB,EAAE;AAC3B,OAAK,MAAM,SAAS,OAAO;GACzB,MAAM,SAAS,eAAe,MAAM;AACpC,OAAI,WAAW,OACb,KAAI,KAAK,OAAO;;AAGpB,SAAO;;AAET,KAAI,SAAS,OAAO,UAAU,UAAU;EACtC,MAAM,QAAQ;EACd,MAAMC,MAAkB,EAAE;AAC1B,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,EAAE;GAChD,MAAM,SAAS,eAAe,MAAM;AACpC,OAAI,WAAW,OACb,KAAI,OAAO;;AAGf,SAAO;;;AAKX,SAAgB,gBAAgB,OAA+B;CAC7D,MAAM,SAAS,eAAe,MAAM;AACpC,KAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,OAAO,CAChE,QAAO;AAET,QAAO,EAAE;;AAGX,SAAgB,aAAa,OAAwC;CACnE,MAAM,SAAS,eAAe,MAAM;AACpC,KAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,OAAO,CAChE,QAAO;;AAKX,SAAS,kBAAkB,OAAuC;AAChE,QAAO,QAAQ,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,CAAC;;AAG7E,SAAgB,gBAAgB,OAA0B;AACxD,KAAI,UAAU,KAAM,QAAO;AAC3B,KAAI,OAAO,UAAU,SAAU,QAAO,KAAK,UAAU,MAAM;AAC3D,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAChD,QAAO,KAAK,UAAU,MAAM;AAE9B,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,IAAI,MAAM,IAAI,gBAAgB,CAAC,KAAK,IAAI,CAAC;AAElD,KAAI,CAAC,kBAAkB,MAAM,CAC3B,QAAO;AAKT,QAAO,IAHS,OAAO,KAAK,MAAM,CAC/B,MAAM,CACN,KAAK,QAAQ,GAAG,KAAK,UAAU,IAAI,CAAC,GAAG,gBAAgB,MAAM,KAAK,GAAG,CACrD,KAAK,IAAI,CAAC;;AAG/B,SAAgB,WAAW,GAAe,GAAwB;AAChE,KAAI,MAAM,UAAa,MAAM,OAAW,QAAO;AAC/C,KAAI,MAAM,UAAa,MAAM,OAAW,QAAO;AAC/C,QAAO,gBAAgB,EAAE,KAAK,gBAAgB,EAAE;;AAGlD,SAAgB,gBACd,UACA,MACY;CACZ,MAAMC,QAAoB,EAAE;CAC5B,MAAM,uBAAO,IAAI,KAAa;AAC9B,KAAI,SACF,MAAK,MAAM,OAAO,OAAO,KAAK,SAAS,CAAE,MAAK,IAAI,IAAI;AAExD,MAAK,MAAM,OAAO,OAAO,KAAK,KAAK,CAAE,MAAK,IAAI,IAAI;AAClD,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,YAAY,WAAW,SAAS,OAAO;EAC7C,MAAM,YAAY,KAAK;AACvB,MAAI,CAAC,WAAW,WAAW,UAAU,EAAE;AACrC,OAAI,cAAc,UAAa,YAAY,OAAO,UAAU;AAC1D,UAAM,OAAO;AACb;;GAEF,MAAM,SAAS,eAAe,UAAU;AACxC,OAAI,WAAW,OACb,OAAM,OAAO;;;AAInB,QAAO;;AAGT,SAAgB,gBAAgB,MAAqC;CACnE,MAAMC,OAAmB;EACvB,SAAS,KAAK;EACd,MAAM,KAAK;EACX,WAAW,KAAK;EACjB;AACD,KAAI,KAAK,SAAS,OAAW,MAAK,OAAO,KAAK;AAC9C,KAAI,KAAK,WAAW,OAAW,MAAK,SAAS,KAAK;AAClD,KAAI,KAAK,QAAQ,OAAW,MAAK,MAAM,KAAK;AAC5C,QAAO;;AAGT,SAAgB,kBACd,OAC+B;CAC/B,MAAM,MAAM,aAAa,MAAM;AAC/B,KAAI,CAAC,IAAK,QAAO;CACjB,MAAM,UAAU,OAAO,IAAI,YAAY,WAAW,IAAI,UAAU;AAChE,KAAI,CAAC,QAAS,QAAO;CACrB,MAAM,OAAO,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;CACvD,MAAM,YACJ,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY;AACtD,KAAI,SAAS,UAAa,cAAc,OAAW,QAAO;AAS1D,QARgC;EAC9B;EACA;EACA;EACA,GAAI,OAAO,IAAI,SAAS,WAAW,EAAE,MAAM,IAAI,MAAM,GAAG,EAAE;EAC1D,GAAI,OAAO,IAAI,WAAW,WAAW,EAAE,QAAQ,IAAI,QAAQ,GAAG,EAAE;EAChE,GAAI,OAAO,IAAI,QAAQ,WAAW,EAAE,KAAK,IAAI,KAAK,GAAG,EAAE;EACxD;;AAIH,SAAgB,mBACd,GACA,GACS;AACT,KAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,KAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,QACE,gBAAgB,gBAAgB,EAAE,CAAC,KAAK,gBAAgB,gBAAgB,EAAE,CAAC;;AAI/E,SAAgB,uBACd,MACmB;AACnB,QAAO;EACL,SAAS,KAAK;EACd,MAAM,KAAK;EACX,WAAW,KAAK;EAChB,GAAI,KAAK,SAAS,SAAY,EAAE,MAAM,KAAK,MAAM,GAAG,EAAE;EACtD,GAAI,KAAK,WAAW,SAAY,EAAE,QAAQ,KAAK,QAAQ,GAAG,EAAE;EAC5D,GAAI,KAAK,QAAQ,SAAY,EAAE,KAAK,KAAK,KAAK,GAAG,EAAE;EACpD;;AAGH,SAAgB,iBACd,OAC4B;AAC5B,QAAO,IAAI,eAA2B,EACpC,MAAM,YAAY;AAChB,aAAW,QAAQ,MAAM;AACzB,aAAW,OAAO;IAErB,CAAC;;AAwGJ,SAAgB,aACd,OACA,WACA,OACS;AACT,KAAI,CAAC,MAAO,QAAO;AACnB,KAAI,MAAM,UAAU,CAAC,MAAM,WAAW,MAAM,OAAO,CAAE,QAAO;AAC5D,KAAI,MAAM,SAAS,QAAQ,MAAM,MAAO,QAAO;AAC/C,KAAI,MAAM,OAAO,QAAQ,MAAM,IAAK,QAAO;AAC3C,QAAO;;;;;AC3VT,IAAa,kBAAb,MAAsD;CACpD,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,YAAY,SAAuC;AACjD,OAAK,eAAe,QAAQ;AAC5B,OAAK,WAAW,QAAQ;AACxB,OAAK,QAAQ,QAAQ;;CAGvB,YAAsB;EACpB,MAAM,MAAM,IAAI,IAAY,KAAK,MAAM,SAAS,MAAM,CAAC;EACvD,MAAM,gBAAgB,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;AAC7D,OAAK,MAAM,OAAO,eAAe;AAC/B,OAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAG;GACnD,MAAM,QAAQ,IAAI,IAAI;AACtB,OAAI,OAAO,UAAU,SACnB,KAAI,IAAI,MAAM;;AAGlB,SAAO,MAAM,KAAK,IAAI;;CAGxB,UAAkD;AAChD,SAAO,KAAK,MAAM,SAAS,SAAS;;CAGtC,IAAI,OAAkD;EACpD,MAAM,WAAW,KAAK,MAAM,SAAS,IAAI,MAAM;EAC/C,MAAM,cAAc,KAAK,uBAAuB,MAAM;AACtD,MACG,gBAAgB,UAAa,KAAK,mBAAmB,MAAM,IAC5D,CAAC,YACA,gBAAgB,UAAa,KAAK,QAAQ,SAAS,EACpD;AACA,OAAI,gBAAgB,OAAW,QAAO;AACtC,UAAO;IAAE,MAAM,EAAE;IAAU;IAAa;;AAE1C,SAAO;GACL,MAAM,gBAAgB,SAAS;GAC/B;GACD;;CAGH,eAAe,OAAmC;AAChD,SAAO,KAAK,uBAAuB,MAAM;;CAG3C,YAAY,OAAe,aAA2B;AACpD,OAAK,UAAU,IAAI,CAAC,MAAM,MAAM,EAAE,YAAY;AAC9C,OAAK,gBAAgB,OAAO,aAAa,QAAQ;;CAGnD,aAAa,OAAqB;EAChC,MAAM,WAAW,KAAK,uBAAuB,MAAM;AACnD,OAAK,UAAU,OAAO,CAAC,MAAM,MAAM,CAAC;AACpC,MAAI,aAAa,OACf,MAAK,gBAAgB,OAAO,QAAW,QAAQ;;CASnD,QAAQ,OAA2C;AACjD,MAAI,OAAO,UAAU,UAAa,MAAM,SAAS,EAC/C,QAAO,EAAE;EAGX,MAAM,EAAE,UAAU,WAAW,KAAK,oBAAoB,MAAM;AAC5D,MAAI,YAAY,UAAU,YAAY,OACpC,QAAO,EAAE;EAGX,MAAMC,cAA2B,EAAE,QAAQ,CAAC,IAAI,EAAE;AAClD,MAAI,SACF,aAAY,QAAQ;GAAE,MAAM;GAAa,KAAK,CAAC,KAAK,SAAS;GAAE;AAEjE,MAAI,OACF,aAAY,MAAM;GAAE,MAAM;GAAa,KAAK,CAAC,KAAK,OAAO;GAAE;EAG7D,MAAM,uBAAO,IAAI,KAAa;EAC9B,MAAMC,UAA+B,EAAE;EAGvC,MAAM,WAAW,UAAwB;AACvC,OAAI,KAAK,IAAI,MAAM,CAAE;GACrB,MAAM,WAAW,KAAK,MAAM,SAAS,IAAI,MAAM;GAC/C,MAAM,cAAc,KAAK,uBAAuB,MAAM;AACtD,OACG,gBAAgB,UAAa,KAAK,mBAAmB,MAAM,IAC3D,KAAK,QAAQ,SAAS,IAAI,gBAAgB,OAE3C;AACF,OAAI,CAAC,aAAa,OAAO,YAAY,EAAE,EAAE,MAAM,CAAE;AACjD,QAAK,IAAI,MAAM;AACf,WAAQ,KAAK;IACX;IACA;IACA,MAAM,gBAAgB,YAAY,EAAE,CAAC;IACtC,CAAC;;EAGJ,MAAM,OAAO,KAAK,UAAU,KAAK,YAAY;AAE7C,OAAK,MAAM,OAAO,MAAM;AACtB,OAAI,OAAO,UAAU,UAAa,QAAQ,UAAU,MAAM,MACxD;AAEF,OAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAG;GACnD,MAAM,QAAQ,IAAI,IAAI;AACtB,OAAI,OAAO,UAAU,SAAU;AAC/B,WAAQ,MAAM;AACd,OAAI,OAAO,UAAU,UAAa,QAAQ,UAAU,MAAM,MACxD;;AAKJ,MAAI,OAAO,UAAU,UAAa,QAAQ,SAAS,MAAM,OAAO;GAC9D,MAAMC,SAAsB,EAAE,QAAQ,CAAC,KAAK,EAAE;AAC9C,OAAI,SACF,QAAO,QAAQ;IAAE,MAAM;IAAa,KAAK,CAAC,MAAM,SAAS;IAAE;AAE7D,OAAI,OACF,QAAO,MAAM;IAAE,MAAM;IAAa,KAAK,CAAC,MAAM,OAAO;IAAE;GAEzD,MAAM,SAAS,KAAK,UAAU,KAAK,OAAO;AAC1C,QAAK,MAAM,OAAO,QAAQ;AACxB,QAAI,OAAO,UAAU,UAAa,QAAQ,UAAU,MAAM,MACxD;AAEF,QAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAG;IACnD,MAAM,QAAQ,IAAI,IAAI;AACtB,QAAI,OAAO,UAAU,SAAU;AAC/B,YAAQ,MAAM;;;AAIlB,SAAO;;CAGT,MAAM,OAAO,OAAe,OAAqC;EAC/D,MAAM,OAAO,KAAK,MAAM,SAAS,IAAI,MAAM;EAC3C,MAAMC,OAAmB,OAAO,gBAAgB,KAAK,GAAG,EAAE;EAC1D,MAAMC,WAAuB,EAAE;EAC/B,IAAI,UAAU;EAEd,MAAM,cAAc;AACpB,OAAK,MAAM,OAAO,OAAO,KAAK,YAAY,EAAE;GAC1C,MAAM,WAAW,YAAY;AAC7B,OAAI,aAAa,OAAW;AAI5B,OAAI,WAHkB,OACjB,KAAK,OACN,QAC0B,SAAS,CACrC;AAGF,QAAK,UAAU,IAAI;IAAC;IAAK;IAAO;IAAI,EAAE,SAAS;AAC/C,QAAK,OAAO;AACZ,YAAS,OAAO;AAChB,aAAU;;AAGZ,MAAI,CAAC,SAAS;AACZ,OAAI,CAAC,KAAK,MAAM,SAAS,IAAI,MAAM,CACjC,MAAK,MAAM,SAAS,IAAI,OAAO,KAAK;AAEtC;;AAGF,OAAK,MAAM,SAAS,IAAI,OAAO,KAAK;AACpC,OAAK,SAAS,KAAK;GACjB,MAAM;GACN;GACA,OAAO;GACP,IAAI;GACL,CAAC;;CAGJ,iBAAiB,OAAe,IAAuB;EACrD,MAAM,eAAe,KAAK,MAAM,SAAS,IAAI,MAAM;EACnD,MAAM,WAAW,KAAK,yBAAyB,MAAM;AAErD,MAAI,CAAC,UAAU;AACb,OAAI,cAAc;AAChB,SAAK,MAAM,SAAS,OAAO,MAAM;AACjC,SAAK,SAAS,KAAK;KACjB,MAAM;KACN;KACA,OAAO,EAAE;KACT;KACD,CAAC;;AAEJ;;AAGF,OAAK,MAAM,SAAS,IAAI,OAAO,SAAS;EACxC,MAAM,QAAQ,gBAAgB,cAAc,SAAS;AACrD,MAAI,CAAC,gBAAgB,OAAO,KAAK,MAAM,CAAC,SAAS,EAC/C,MAAK,SAAS,KAAK;GACjB,MAAM;GACN;GACO;GACP;GACD,CAAC;;CAIN,WAAW,cAAuC,IAAuB;EACvE,MAAM,eAAe,IAAI,IAAI,KAAK,MAAM,SAAS;AACjD,OAAK,MAAM,SAAS,OAAO;AAC3B,OAAK,MAAM,CAAC,OAAO,SAAS,aAC1B,MAAK,MAAM,SAAS,IAAI,OAAO,KAAK;EAGtC,MAAM,SAAS,IAAI,IAAY,CAC7B,GAAG,aAAa,MAAM,EACtB,GAAG,aAAa,MAAM,CACvB,CAAC;AAEF,OAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,WAAW,aAAa,IAAI,MAAM;GACxC,MAAM,UAAU,aAAa,IAAI,MAAM;AACvC,OAAI,CAAC,SAAS;AACZ,QAAI,UAAU;KACZ,MAAMC,UAAoB,EAAE;AAC5B,UAAK,MAAM,OAAO,OAAO,KAAK,SAAS,CACrC,SAAM,OAAO;AAEf,UAAK,SAAS,KAAK;MACjB,MAAM;MACN;MACA,OAAOC;MACP;MACD,CAAC;;AAEJ;;GAEF,MAAM,QAAQ,gBAAgB,UAAU,QAAQ;AAChD,OAAI,CAAC,YAAY,OAAO,KAAK,MAAM,CAAC,SAAS,EAC3C,MAAK,SAAS,KAAK;IACjB,MAAM;IACN;IACO;IACP;IACD,CAAC;;;CAKR,QAAc;AACZ,OAAK,MAAM,SAAS,OAAO;;CAG7B,cAAc,OAAe,IAAuB;AAElD,MADgB,KAAK,MAAM,SAAS,OAAO,MAAM,CAE/C,MAAK,SAAS,KAAK;GACjB,MAAM;GACN;GACA,OAAO,EAAE;GACT;GACD,CAAC;;CAIN,gBACE,OACA,aACA,IACM;AACN,OAAK,SAAS,KAAK;GACjB,MAAM;GACN;GACA;GACA;GACD,CAAC;;CAGJ,AAAQ,oBAAoB,OAG1B;AACA,MAAI,CAAC,MACH,QAAO,EAAE;EAGX,MAAM,SACJ,MAAM,UAAU,MAAM,OAAO,SAAS,IAAI,MAAM,SAAS;EAC3D,IAAI,WAAW,MAAM;AACrB,MAAI,OACF,YAAW,CAAC,YAAY,SAAS,WAAW,SAAS;EAGvD,IAAI,SAAS,MAAM;EACnB,MAAM,YAAY,KAAK,wBAAwB,OAAO;AACtD,MAAI,UACF,UAAS,CAAC,UAAU,YAAY,SAAS,YAAY;AAGvD,SAAO;GAAE;GAAU;GAAQ;;CAG7B,AAAQ,wBAAwB,OAAoC;AAClE,MAAI,CAAC,MAAO,QAAO;AACnB,OAAK,IAAI,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;GAC7C,MAAM,OAAO,MAAM,WAAW,EAAE;AAChC,OAAI,OAAO,MACT,QAAO,GAAG,MAAM,MAAM,GAAG,EAAE,GAAG,OAAO,aAAa,OAAO,EAAE;;;CAMjE,AAAQ,yBAAyB,OAAuC;EACtE,MAAM,OAAO,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC,KAAK,MAAM,EAAE,CAAC;AAC1D,MAAI,CAAC,KAAK,OAAQ,QAAO;EACzB,MAAMC,UAAsB,EAAE;EAC9B,IAAI,YAAY;EAChB,IAAI,SAAS;AAEb,OAAK,MAAM,OAAO,MAAM;AACtB,YAAS;AACT,OAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAG;AACnD,OAAI,IAAI,IAAI,WAAW,GAAG;IACxB,MAAM,MAAM,aAAa,IAAI,MAAM;AACnC,QAAI,CAAC,IAAK;AACV,SAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,IAAI,EAAE;KAChD,MAAM,SAAS,eAAe,MAAM;AACpC,SAAI,WAAW,QAAW;AACxB,cAAQ,SAAS;AACjB,kBAAY;;;AAGhB;;GAEF,MAAM,WAAW,IAAI,IAAI;AACzB,OAAI,OAAO,aAAa,SAAU;GAClC,MAAM,YAAY,eAAe,IAAI,MAAM;AAC3C,OAAI,cAAc,OAAW;AAC7B,WAAQ,YAAY;AACpB,eAAY;;AAEd,MAAI,UAAW,QAAO;AACtB,MAAI,OAAQ,QAAO,EAAE;;CAIvB,AAAQ,uBAAuB,OAAmC;EAChE,MAAM,MAAM,KAAK,UAAU,IAAI,CAAC,MAAM,MAAM,CAAC;AAC7C,MAAI,OAAO,QAAQ,YAAY,OAAO,SAAS,IAAI,CACjD,QAAO;;CAKX,AAAQ,QAAQ,UAAgC;AAC9C,SAAO,CAAC,YAAY,OAAO,KAAK,SAAS,CAAC,WAAW;;CAGvD,AAAQ,mBAAmB,OAAwB;AACjD,MAAI,KAAK,MAAM,SAAS,IAAI,MAAM,CAAE,QAAO;EAC3C,MAAM,gBAAgB,WAAsC;GAC1D,MAAM,OAAO,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC;AAC5C,QAAK,MAAM,OAAO,KAGhB,KAFc,IAAI,UACO,OACb,QAAO;AAErB,UAAO;;AAGT,MAAI,KAAK,UAAU,IAAI,CAAC,MAAM,MAAM,CAAC,KAAK,OAAW,QAAO;AAC5D,MAAI,aAAa,CAAC,KAAK,MAAM,CAAC,CAAE,QAAO;AACvC,MAAI,aAAa,CAAC,KAAK,MAAM,CAAC,CAAE,QAAO;AACvC,MAAI,aAAa,CAAC,MAAM,MAAM,CAAC,CAAE,QAAO;AACxC,SAAO;;CAGT,IAAY,YAAmB;AAC7B,SAAO,KAAK,cAAc;;;;;;AC1X9B,IAAa,eAAb,MAAmD;CACjD,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,IAAY,YAA0D;AACpE,SAAO,KAAK,MAAM;;CAEpB,IAAY,SAAoC;AAC9C,SAAO,KAAK,MAAM;;CAEpB,IAAY,iBAAoD;AAC9D,SAAO,KAAK,MAAM;;CAEpB,IAAY,iBAA4C;AACtD,SAAO,KAAK,MAAM;;CAGpB,YAAY,SAAoC;AAC9C,OAAK,UAAU,QAAQ;AACvB,OAAK,iBAAiB,QAAQ;AAC9B,OAAK,eAAe,QAAQ;AAC5B,OAAK,WAAW,QAAQ;AACxB,OAAK,QAAQ,QAAQ;;CAGvB,MAAM,YAAY,QAA8C;EAC9D,MAAM,QAAQ,MAAM,yBAAyB,OAAO,QAAQ;EAC5D,MAAM,UAAU,MAAM,cAAc,MAAM;AAE1C,MAAI,OAAO,WAAW,OAAO,YAAY,QACvC,OAAM,IAAI,MAAM,iDAAiD;EAGnE,MAAM,WAAW,KAAK,OAAO,IAAI,QAAQ;AACzC,MAAI,UAAU;AAEZ,OAAI,KAAK,SAEN;QAAI,CADW,MAAM,KAAK,QAAQ,UAAU,QAAQ,CAEjD,OAAM,KAAK,QAAQ,KAAK;KACpB,MAAM;KACN;KACA,MAAM,MAAM,OAAO;KACtB,CAAC;;GAIR,IAAI,kBAAkB;GACtB,MAAMC,aAAW,EAAE,GAAG,SAAS,UAAU;AACzC,OAAI,OAAO,QAAQA,WAAS,SAAS,OAAO,MAAM;AAChD,eAAS,OAAO,OAAO;AACvB,sBAAkB;;AAEpB,OAAI,OAAO,UAAUA,WAAS,WAAW,OAAO,QAAQ;AACtD,eAAS,SAAS,OAAO;AACzB,sBAAkB;;AAEpB,OAAI,OAAO,OAAOA,WAAS,QAAQ,OAAO,KAAK;AAC7C,eAAS,MAAM,OAAO;AACtB,sBAAkB;;AAEpB,OACE,OAAO,cAAc,UACrBA,WAAS,cAAc,OAAO,WAC9B;AACA,eAAS,YAAY,OAAO;AAC5B,sBAAkB;;AAEpB,OAAI,iBAAiB;AACnB,aAAS,WAAWA;AACpB,SAAK,UAAU,IAAI,CAAC,KAAK,QAAQ,EAAE,gBAAgBA,WAAS,CAAC;AAC7D,SAAK,SAAS,KAAK;KACjB,MAAM;KACN,OAAO,KAAK,oBAAoB,SAASA,YAAU,MAAM;KACzD,IAAI;KACL,CAAC;;AAEJ,QAAK,cAAc,SAAS,SAAS;AACrC,UAAO;;EAGT,MAAMC,WAA8B;GAClC;GACA,MAAM,MAAM;GACZ,WAAW,OAAO,aAAa,KAAK,KAAK;GACzC,GAAI,OAAO,OAAO,EAAE,MAAM,OAAO,MAAM,GAAG,EAAE;GAC5C,GAAI,OAAO,SAAS,EAAE,QAAQ,OAAO,QAAQ,GAAG,EAAE;GAClD,GAAI,OAAO,MAAM,EAAE,KAAK,OAAO,KAAK,GAAG,EAAE;GAC1C;AAED,MAAI,KAAK,gBAAgB;GACvB,IAAI,eAAe;AACnB,OAAI,OAAO,KAAK,eAAe,WAAW,WACxC,gBAAe,CAAE,MAAM,KAAK,eAAe,OAAO,QAAQ;AAE5D,OAAI,aACF,OAAM,KAAK,eAAe,OAAO,SAAS,OAAO;IAC/C,MAAM,OAAO;IACb,QAAQ,OAAO;IACf,KAAK,OAAO;IACb,CAAC;;EAIN,MAAM,cAAc,MAAM,OAAO;AAEjC,MAAI,KAAK,QACP,OAAM,KAAK,QAAQ,KAAK;GACtB,MAAM;GACN;GACA,MAAM,YAAY,OAAO;GAC1B,CAAC;AAGJ,OAAK,cAAc,SAAS;AAC5B,OAAK,kBAAkB,SAAS,SAAS;AAEzC,OAAK,uBAAuB,SAAS,SAAS;AAE9C,OAAK,UAAU,IAAI,CAAC,KAAK,QAAQ,EAAE,gBAAgB,SAAS,CAAC;AAC7D,OAAK,SAAS,KAAK;GACjB,MAAM;GACN,OAAO,KAAK,oBAAoB,SAAS,UAAU,YAAY;GAC/D,IAAI;GACL,CAAC;AAEF,SAAO;;CAGT,MAAM,UAAU,OAAe,QAA4C;EACzE,MAAM,QAAQ,MAAM,yBAAyB,OAAO,QAAQ;EAC5D,MAAM,UAAU,MAAM,cAAc,MAAM;AAE1C,MAAI,OAAO,WAAW,OAAO,YAAY,QACvC,OAAM,IAAI,MAAM,iDAAiD;EAGnE,IAAIA;EACJ,IAAIC;EACJ,IAAI,UAAU;EAEd,MAAM,WAAW,KAAK,OAAO,IAAI,QAAQ;AACzC,MAAI,UAAU;AACZ,cAAW,SAAS;AAEpB,OAAI,KAAK,SAEN;QAAI,CADW,MAAM,KAAK,QAAQ,UAAU,QAAQ,CAEjD,OAAM,KAAK,QAAQ,KAAK;KACpB,MAAM;KACN;KACA,MAAM,MAAM,OAAO;KACtB,CAAC;;GAIR,IAAI,eAAe;GACnB,IAAI,kBAAkB;AACtB,OAAI,OAAO,QAAQ,OAAO,SAAS,aAAa,MAAM;AACpD,mBAAe;KAAE,GAAG;KAAc,MAAM,OAAO;KAAM;AACrD,sBAAkB;;AAEpB,OAAI,OAAO,UAAU,OAAO,WAAW,aAAa,QAAQ;AAC1D,mBAAe;KAAE,GAAG;KAAc,QAAQ,OAAO;KAAQ;AACzD,sBAAkB;;AAEpB,OAAI,OAAO,OAAO,OAAO,QAAQ,aAAa,KAAK;AACjD,mBAAe;KAAE,GAAG;KAAc,KAAK,OAAO;KAAK;AACnD,sBAAkB;;AAEpB,OACE,OAAO,cAAc,UACrB,OAAO,cAAc,aAAa,WAClC;AACA,mBAAe;KAAE,GAAG;KAAc,WAAW,OAAO;KAAW;AAC/D,sBAAkB;;AAEpB,OAAI,iBAAiB;AACnB,aAAS,WAAW;AACpB,eAAW;AACX,SAAK,UAAU,IAAI,CAAC,KAAK,QAAQ,EAAE,gBAAgB,SAAS,CAAC;AAC7D,SAAK,SAAS,KAAK;KACjB,MAAM;KACN,OAAO,KAAK,oBAAoB,SAAS,UAAU,MAAM;KACzD,IAAI;KACL,CAAC;SAEF,YAAW,SAAS;AAEtB,iBAAc;AACd,QAAK,cAAc,SAAS;SACvB;AACL,cAAW;IACT;IACA,MAAM,MAAM;IACZ,WAAW,OAAO,aAAa,KAAK,KAAK;IACzC,GAAI,OAAO,OAAO,EAAE,MAAM,OAAO,MAAM,GAAG,EAAE;IAC5C,GAAI,OAAO,SAAS,EAAE,QAAQ,OAAO,QAAQ,GAAG,EAAE;IAClD,GAAI,OAAO,MAAM,EAAE,KAAK,OAAO,KAAK,GAAG,EAAE;IAC1C;AAED,OAAI,KAAK,gBAAgB;IACvB,IAAI,eAAe;AACnB,QAAI,OAAO,KAAK,eAAe,WAAW,WACxC,gBAAe,CAAE,MAAM,KAAK,eAAe,OAAO,QAAQ;AAE5D,QAAI,aACF,OAAM,KAAK,eAAe,OAAO,SAAS,OAAO;KAC/C,MAAM,OAAO;KACb,QAAQ,OAAO;KACf,KAAK,OAAO;KACb,CAAC;;AAIN,iBAAc,MAAM,OAAO;AAE3B,OAAI,KAAK,QACP,OAAM,KAAK,QAAQ,KAAK;IACtB,MAAM;IACN;IACA,MAAM,YAAY,OAAO;IAC1B,CAAC;AAGJ,QAAK,cAAc,SAAS;AAE5B,QAAK,uBAAuB,SAAS,SAAS;AAE9C,QAAK,UAAU,IAAI,CAAC,KAAK,QAAQ,EAAE,gBAAgB,SAAS,CAAC;AAC7D,aAAU;;EAGZ,MAAM,UACJ,KAAK,UAAU,IAAI,MAAM,oBAAI,IAAI,KAAiC;AACpE,UAAQ,IAAI,SAAS,SAAS;AAC9B,OAAK,UAAU,IAAI,OAAO,QAAQ;AAElC,OAAK,gBAAgB,SAAS,MAAM;AAEpC,OAAK,UAAU,IAAI;GAAC;GAAM;GAAO;GAAQ,EAAE,KAAK;AAEhD,OAAK,SAAS,KAAK;GAAE,MAAM;GAAc;GAAO;GAAS,IAAI;GAAS,CAAC;AACvE,MAAI,QACF,MAAK,SAAS,KAAK;GACjB,MAAM;GACN,OAAO,KAAK,oBACV,SACA,UACA,eAAe,MAChB;GACD,IAAI;GACL,CAAC;AAEJ,SAAO;;CAGT,MAAM,YAAY,OAAe,SAAiC;EAChE,MAAM,UAAU,KAAK,UAAU,IAAI,MAAM;AACzC,MAAI,CAAC,WAAW,CAAC,QAAQ,IAAI,QAAQ,CAAE;AACvC,UAAQ,OAAO,QAAQ;AACvB,MAAI,QAAQ,SAAS,EACnB,MAAK,UAAU,OAAO,MAAM;AAG9B,OAAK,UAAU,OAAO;GAAC;GAAM;GAAO;GAAQ,CAAC;AAE7C,OAAK,wBAAwB,SAAS,MAAM;AAK5C,OAAK,SAAS,KAAK;GAAE,MAAM;GAAgB;GAAO;GAAS,IAAI;GAAS,CAAC;;CAG3E,cAAc,OAAe,IAAuB;EAClD,MAAM,OAAO,MAAM,KACjB,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC,MAAM,MAAM,EAAE,CAAC,GAC7C,QAAQ,IAAI,IACd;AACD,OAAK,MAAM,OAAO,KAChB,MAAK,UAAU,OAAO,IAAI;AAE5B,OAAK,sBAAsB,OAAO,GAAG;;CAGvC,MAAM,WAAW,OAA6C;EAC5D,MAAM,UAAU,KAAK,UAAU,IAAI,MAAM;AACzC,MAAI,CAAC,QAAS,QAAO,EAAE;AACvB,SAAO,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC,KAAK,WAAW,EAAE,GAAG,OAAO,EAAE;;CAGpE,MAAM,YAAY,SAA0C;AAC1D,SAAO,KAAK,WAAW,QAAQ;;CAGjC,MAAM,WAAW,SAA0C;EACzD,MAAM,EAAE,UAAU,UAAU,MAAM,KAAK,iBAAiB,QAAQ;AAChE,SAAO,KAAK,oBAAoB,SAAS,UAAU,MAAM;;CAG3D,MAAM,SAAS,UAAoC,EAAE,EAAmB;EACtE,MAAM,EAAE,YAAY,MAAM;EAC1B,MAAM,MAAM,KAAK,KAAK;EACtB,IAAI,UAAU;AACd,OAAK,MAAM,CAAC,SAAS,WAAW,MAAM,KAAK,KAAK,eAAe,SAAS,CAAC,EAAE;AACzE,OAAI,MAAM,OAAO,YAAY,UAC3B;AAEF,QAAK,eAAe,OAAO,QAAQ;AACnC,OAAI,KAAK,SAAS,YAChB,KAAI;AACF,UAAM,KAAK,QAAQ,YAAY,QAAQ;YAChC,OAAO;AACd,kBAAc,SAAS,QAAQ,SAAS,CAAC,MAAM;;AAGnD,cAAW;;AAEb,SAAO;;CAGT,sBAAsB,OAAe,IAAuB;EAC1D,MAAM,UAAU,KAAK,uBAAuB,MAAM;EAClD,MAAM,WAAW,KAAK,UAAU,IAAI,MAAM;AAE1C,MAAI,CAAC,QAAQ,MAAM;AACjB,OAAI,UAAU,MAAM;AAClB,SAAK,UAAU,OAAO,MAAM;AAC5B,SAAK,MAAM,WAAW,SAAS,MAAM,EAAE;AACrC,UAAK,wBAAwB,SAAS,MAAM;AAC5C,UAAK,SAAS,KAAK;MAAE,MAAM;MAAgB;MAAO;MAAS;MAAI,CAAC;;;AAGpE;;AAGF,OAAK,UAAU,IAAI,OAAO,QAAQ;EAElC,MAAMC,UAAqB,EAAE;AAC7B,MAAI,UACF;QAAK,MAAM,WAAW,SAAS,MAAM,CACnC,KAAI,CAAC,QAAQ,IAAI,QAAQ,CACvB,SAAQ,KAAK,QAAQ;;AAK3B,OAAK,MAAM,WAAW,SAAS;AAC7B,QAAK,wBAAwB,SAAS,MAAM;AAC5C,QAAK,SAAS,KAAK;IAAE,MAAM;IAAgB;IAAO;IAAS;IAAI,CAAC;;AAGlE,OAAK,MAAM,WAAW,QAAQ,MAAM,EAAE;GACpC,MAAM,QAAQ,CAAC,YAAY,CAAC,SAAS,IAAI,QAAQ;AACjD,QAAK,gBAAgB,SAAS,MAAM;AACpC,OAAI,MACF,MAAK,SAAS,KAAK;IAAE,MAAM;IAAc;IAAO;IAAS;IAAI,CAAC;;;CAKpE,0BAA0B,SAAkB,IAAuB;EACjE,MAAM,WAAW,KAAK,OAAO,IAAI,QAAQ;EAEzC,MAAM,WAAW,kBADL,KAAK,UAAU,IAAI,CAAC,KAAK,QAAQ,CAAC,CACP;AAEvC,MAAI,CAAC,UAAU;AACb,QAAK,mBAAmB,SAAS,GAAG;AACpC;;AAGF,OAAK,cAAc,SAAS;AAE5B,OAAK,uBAAuB,SAAS,uBAAuB,SAAS,CAAC;AAEtE,MAAI,CAAC,YAAY,CAAC,mBAAmB,SAAS,UAAU,SAAS,CAC/D,MAAK,SAAS,KAAK;GACjB,MAAM;GACN,OAAO,KAAK,oBAAoB,SAAS,SAAS;GAClD;GACD,CAAC;;CAIN,iBAAiB,IAAuB;EACtC,MAAM,gBAAgB,IAAI,IAAI,KAAK,UAAU;EAC7C,MAAM,aAAa,IAAI,IAAI,KAAK,OAAO;EAEvC,MAAM,6BAAa,IAAI,KAA2B;EAClD,MAAM,YAAY,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;AACxD,OAAK,MAAM,OAAO,WAAW;AAC3B,OAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAG;GACnD,MAAM,UAAU,IAAI,IAAI;AACxB,OAAI,OAAO,YAAY,SAAU;GACjC,MAAM,WAAW,kBAAkB,IAAI,MAAM;AAC7C,OAAI,CAAC,SAAU;AAEf,cAAW,IAAI,SAAS,EACtB,UACD,CAAC;;EAGJ,MAAM,gCAAgB,IAAI,KAA8C;EACxE,MAAM,WAAW,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;AACxD,OAAK,MAAM,OAAO,UAAU;AAC1B,OAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAG;GACnD,MAAM,QAAQ,IAAI,IAAI;GACtB,MAAM,UAAU,IAAI,IAAI;AACxB,OAAI,OAAO,UAAU,YAAY,OAAO,YAAY,SAAU;GAC9D,MAAM,WAAW,WAAW,IAAI,QAAQ,EAAE;AAC1C,OAAI,CAAC,SAAU;GACf,MAAM,UACJ,cAAc,IAAI,MAAM,oBAAI,IAAI,KAAiC;AACnE,WAAQ,IAAI,SAAS,SAAS;AAC9B,iBAAc,IAAI,OAAO,QAAQ;;EAGnC,MAAMC,gBAA+C,EAAE;AACvD,OAAK,MAAM,CAAC,SAAS,WAAW,WAC9B,KAAI,CAAC,WAAW,IAAI,QAAQ,CAC1B,eAAc,KAAK,CAAC,SAAS,OAAO,CAAC;AAIzC,MAAI,cAAc,SAAS,GAAG;GAC5B,MAAM,MAAM,KAAK,KAAK;AACtB,QAAK,MAAM,CAAC,SAAS,WAAW,eAAe;IAE7C,MAAM,YADW,KAAK,eAAe,IAAI,QAAQ,EACrB,aAAa;AACzC,SAAK,eAAe,IAAI,SAAS;KAC/B,UAAU,OAAO;KACjB;KACD,CAAC;;;AAIN,OAAK,UAAU,OAAO;AACtB,OAAK,MAAM,CAAC,OAAO,WAAW,cAC5B,MAAK,UAAU,IAAI,OAAO,OAAO;AAGnC,OAAK,eAAe,OAAO;AAC3B,OAAK,MAAM,CAAC,OAAO,WAAW,cAC5B,MAAK,MAAM,WAAW,OAAO,MAAM,EAAE;GACnC,MAAM,OAAO,KAAK,eAAe,IAAI,QAAQ,oBAAI,IAAI,KAAa;AAClE,QAAK,IAAI,MAAM;AACf,QAAK,eAAe,IAAI,SAAS,KAAK;;AAI1C,OAAK,OAAO,OAAO;AACnB,OAAK,MAAM,UAAU,WAAW,QAAQ,CACtC,MAAK,cAAc,OAAO,SAAS;AAIrC,OAAK,MAAM,WAAW,WAAW,MAAM,EAAE;GACrC,MAAM,OAAO,KAAK,eAAe,IAAI,QAAQ;AAC7C,OAAI,CAAC,QAAQ,KAAK,SAAS,GAExB;QAAI,CAAC,KAAK,eAAe,IAAI,QAAQ,CACjC,MAAK,kBAAkB,SAAS,WAAW,IAAI,QAAQ,CAAE,SAAS;SAIrE,MAAK,eAAe,OAAO,QAAQ;;AAI3C,OAAK,MAAM,CAAC,SAAS,WAAW,YAAY;GAC1C,MAAM,WAAW,WAAW,IAAI,QAAQ,EAAE;AAC1C,OAAI,CAAC,mBAAmB,UAAU,OAAO,SAAS,CAChD,MAAK,SAAS,KAAK;IACjB,MAAM;IACN,OAAO,KAAK,oBAAoB,SAAS,OAAO,SAAS;IACzD;IACD,CAAC;;AAIN,OAAK,MAAM,CAAC,OAAO,WAAW,eAAe;GAC3C,MAAM,WAAW,cAAc,IAAI,MAAM;AACzC,QAAK,MAAM,WAAW,OAAO,MAAM,CACjC,KAAI,CAAC,YAAY,CAAC,SAAS,IAAI,QAAQ,CACrC,MAAK,SAAS,KAAK;IAAE,MAAM;IAAc;IAAO;IAAS;IAAI,CAAC;;AAKpE,OAAK,MAAM,CAAC,OAAO,WAAW,eAAe;GAC3C,MAAM,UAAU,cAAc,IAAI,MAAM;AACxC,QAAK,MAAM,WAAW,OAAO,MAAM,CACjC,KAAI,CAAC,WAAW,CAAC,QAAQ,IAAI,QAAQ,CACnC,MAAK,SAAS,KAAK;IAAE,MAAM;IAAgB;IAAO;IAAS;IAAI,CAAC;;;CAMxE,QAAc;AACZ,OAAK,UAAU,OAAO;AACtB,OAAK,OAAO,OAAO;AACnB,OAAK,eAAe,OAAO;AAC3B,OAAK,eAAe,OAAO;;CAG7B,AAAQ,uBACN,OACiC;EACjC,MAAM,OAAO,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC,MAAM,MAAM,EAAE,CAAC;EAC3D,MAAM,0BAAU,IAAI,KAAiC;AACrD,OAAK,MAAM,OAAO,MAAM;AACtB,OAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAG;GACnD,MAAM,UAAU,IAAI,IAAI;AACxB,OAAI,OAAO,YAAY,SAAU;AAGjC,OAAI,EADF,IAAI,UAAU,UAAa,IAAI,UAAU,QAAQ,IAAI,UAAU,OACpD;GACb,IAAI,WAAW,KAAK,OAAO,IAAI,QAAQ,EAAE;AACzC,OAAI,CAAC,UAAU;AACb,eAAW,KAAK,2BAA2B,QAAQ;AACnD,QAAI,CAAC,SAAU;AACf,SAAK,cAAc,SAAS;;AAE9B,WAAQ,IAAI,SAAS,uBAAuB,SAAS,CAAC;;AAExD,SAAO;;CAGT,AAAQ,2BACN,SAC+B;AAE/B,SAAO,kBADK,KAAK,UAAU,IAAI,CAAC,KAAK,QAAQ,CAAC,CACjB;;CAG/B,AAAQ,mBAAmB,SAAkB,IAAuB;EAClE,MAAM,SAAS,KAAK,OAAO,IAAI,QAAQ;AACvC,MAAI,CAAC,OAAQ;AACb,OAAK,OAAO,OAAO,QAAQ;AAC3B,OAAK,kBAAkB,SAAS,OAAO,SAAS;EAEhD,MAAM,OAAO,KAAK,eAAe,IAAI,QAAQ;AAC7C,MAAI,MAAM;AACR,QAAK,eAAe,OAAO,QAAQ;AACnC,QAAK,MAAM,SAAS,MAAM;IACxB,MAAM,SAAS,KAAK,UAAU,IAAI,MAAM;AACxC,QAAI,QAAQ,OAAO,QAAQ,IAAI,OAAO,SAAS,EAC7C,MAAK,UAAU,OAAO,MAAM;AAE9B,SAAK,SAAS,KAAK;KAAE,MAAM;KAAgB;KAAO;KAAS;KAAI,CAAC;;AAElE;;AAGF,OAAK,MAAM,CAAC,OAAO,WAAW,KAAK,UACjC,KAAI,OAAO,OAAO,QAAQ,EAAE;AAC1B,OAAI,OAAO,SAAS,EAClB,MAAK,UAAU,OAAO,MAAM;AAE9B,QAAK,SAAS,KAAK;IAAE,MAAM;IAAgB;IAAO;IAAS;IAAI,CAAC;;;CAKtE,AAAQ,oBACN,SACA,UACA,cACe;EACf,IAAI,SAAS,eAAe,aAAa,OAAO,GAAG;AACnD,SAAO;GACL;GACA,MAAM,SAAS;GACf,WAAW,SAAS;GACpB,MAAM,SAAS;GACf,QAAQ,SAAS;GACjB,KAAK,SAAS;GACd,SAAS,YAAY;AACnB,QAAI,CAAC,OAEH,WADe,MAAM,KAAK,iBAAiB,QAAQ,EACnC,MAAM,OAAO;AAE/B,WAAO,iBAAiB,OAAO,OAAO,CAAC;;GAE1C;;CAGH,MAAc,iBACZ,SAC6D;EAC7D,MAAM,SAAS,KAAK,OAAO,IAAI,QAAQ;AAIvC,MAAI,UAAU,KAAK,SAAS;GAC1B,MAAM,SAAS,MAAM,KAAK,QAAQ,UAAU,QAAQ;AACpD,OAAI,OACF,QAAO;IAAE,UAAU,OAAO;IAAU,OAAO;IAAQ;;AAIvD,MAAI,CAAC,UAAU,KAAK,SAAS;GAC3B,MAAM,SAAS,MAAM,KAAK,QAAQ,UAAU,QAAQ;AACpD,OAAI,QAAQ;IACV,MAAMJ,aAAW,KAAK,iBAAiB,QAAQ;AAC/C,QAAI,CAACA,WACH,OAAM,IAAI,MAAM,8BAA8B,UAAU;AAG1D,SAAK,OAAO,IAAI,SAAS,EAAE,sBAAU,CAAC;AACtC,SAAK,uBAAuB,SAASA,WAAS;AAC9C,WAAO;KAAE;KAAU,OAAO;KAAQ;;;AAItC,MAAI,CAAC,KAAK,eACR,OAAM,IAAI,MAAM,SAAS,QAAQ,2BAA2B;EAG9D,MAAM,SAAS,MAAM,KAAK,eAAe,MAAM,QAAQ;AACvD,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,SAAS,QAAQ,4BAA4B;EAG/D,MAAM,cAAc,MAAM,mBADL,MAAM,OAAO,SAAS,CACe;EAC1D,MAAMC,WAA8B;GAClC;GACA,MAAM,OAAO;GACb,WAAW,OAAO;GAClB,GAAI,OAAO,OAAO,EAAE,MAAM,OAAO,MAAM,GAAG,EAAE;GAC5C,GAAI,OAAO,SAAS,EAAE,QAAQ,OAAO,QAAQ,GAAG,EAAE;GAClD,GAAI,OAAO,MAAM,EAAE,KAAK,OAAO,KAAK,GAAG,EAAE;GAC1C;AAGD,OAAK,OAAO,IAAI,SAAS,EAAE,UAAU,CAAC;AACtC,OAAK,uBAAuB,SAAS,SAAS;AAE9C,OAAK,UAAU,IAAI,CAAC,KAAK,QAAQ,EAAE,gBAAgB,SAAS,CAAC;AAE7D,MAAI,KAAK,QACP,OAAM,KAAK,QAAQ,KAAK;GACtB,MAAM;GACN;GACA,MAAM,YAAY,OAAO;GAC1B,CAAC;AAGJ,SAAO;GAAE;GAAU,OAAO;GAAa;;CAGzC,AAAQ,uBACN,SACA,UACM;EACN,MAAM,OAAO,KAAK,eAAe,IAAI,QAAQ;AAC7C,MAAI,CAAC,KAAM;AACX,OAAK,MAAM,SAAS,MAAM;GACxB,MAAM,SAAS,KAAK,UAAU,IAAI,MAAM;AACxC,OAAI,OACF,QAAO,IAAI,SAAS,SAAS;;;CAKnC,AAAQ,cAAc,UAAmC;AACvD,OAAK,OAAO,IAAI,SAAS,SAAS,EAChC,UACD,CAAC;;CAIJ,AAAQ,gBAAgB,SAAkB,OAAqB;EAC7D,MAAM,OAAO,KAAK,eAAe,IAAI,QAAQ,oBAAI,IAAI,KAAa;AAClE,OAAK,IAAI,MAAM;AACf,OAAK,eAAe,IAAI,SAAS,KAAK;AACtC,OAAK,eAAe,OAAO,QAAQ;;CAGrC,AAAQ,wBAAwB,SAAkB,OAAqB;EACrE,MAAM,OAAO,KAAK,eAAe,IAAI,QAAQ;AAC7C,MAAI,CAAC,KAAM;AACX,OAAK,OAAO,MAAM;AAClB,MAAI,KAAK,SAAS,GAAG;AACnB,QAAK,eAAe,OAAO,QAAQ;AACnC,QAAK,kBAAkB,QAAQ;;;CAInC,AAAQ,kBACN,SACA,kBACM;EACN,MAAM,WAAW,oBAAoB,KAAK,OAAO,IAAI,QAAQ,EAAE;AAC/D,MAAI,CAAC,SAAU;EAEf,MAAM,YADW,KAAK,eAAe,IAAI,QAAQ,EACrB,aAAa,KAAK,KAAK;AACnD,OAAK,eAAe,IAAI,SAAS;GAC/B;GACA;GACD,CAAC;;CAGJ,AAAQ,iBAAiB,SAAiD;EACxE,MAAM,SAAS,KAAK,OAAO,IAAI,QAAQ;AACvC,MAAI,OAAQ,QAAO,OAAO;AAC1B,OAAK,MAAM,UAAU,KAAK,UAAU,QAAQ,EAAE;GAC5C,MAAM,WAAW,OAAO,IAAI,QAAQ;AACpC,OAAI,SAAU,QAAO;;;CAKzB,IAAY,YAAmB;AAC7B,SAAO,KAAK,cAAc;;;;;;ACnuB9B,IAAa,gBAAb,MAAoD;CAClD,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,YAAY,SAAqC;AAC/C,OAAK,eAAe,QAAQ;AAC5B,OAAK,kBAAkB,QAAQ;AAC/B,OAAK,eAAe,QAAQ;AAC5B,OAAK,aAAa,QAAQ;;CAM5B,WAAW,IAAuB;EAChC,MAAM,EAAE,UAAU,eAAe,KAAK,oBAAoB;AAC1D,OAAK,gBAAgB,WAAW,UAAU,GAAG;AAC7C,OAAK,MAAM,CAAC,OAAO,gBAAgB,WACjC,MAAK,gBAAgB,gBAAgB,OAAO,aAAa,GAAG;AAE9D,OAAK,aAAa,iBAAiB,GAAG;EAEtC,MAAM,SAAS,IAAI,IAAY;GAC7B,GAAG,SAAS,MAAM;GAClB,GAAG,WAAW,MAAM;GACpB,GAAG,KAAK,cAAc,CAAC,IAAI,CAAC;GAC5B,GAAG,KAAK,cAAc,CAAC,KAAK,CAAC;GAC9B,CAAC;AACF,OAAK,MAAM,SAAS,OAClB,KAAI,KAAK,mBAAmB,MAAM,CAChC,EAAM,YAAY;AAChB,OAAI;AACF,SAAK,iBAAiB,MAAM;AAC5B,SAAK,gBAAgB,cAAc,OAAO,GAAG;AAC7C,UAAM,KAAK,WAAW,QAAQ,MAAM;YAC7B,OAAO;AACd,YAAQ,MAAM,+CAA+C;KAC3D;KACA;KACD,CAAC;;MAEF;;CASV,MAAM,YAAY,QAAsB,IAAgC;AACtE,MAAI,CAAC,OAAO,OAAQ;EACpB,MAAM,iCAAiB,IAAI,KAAa;EACxC,MAAM,kCAAkB,IAAI,KAAiC;EAC7D,MAAM,8BAAc,IAAI,KAAa;EACrC,MAAM,iCAAiB,IAAI,KAAa;EACxC,MAAM,2BAAW,IAAI,KAAc;AAEnC,OAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,MAAM,MAAM;AAClB,OAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,IAAI,WAAW,EAAG;GAC7C,MAAM,OAAO,IAAI;AACjB,OAAI,SAAS,KAAK;IAChB,MAAM,QAAQ,IAAI;AAClB,QAAI,OAAO,UAAU,SACnB,gBAAe,IAAI,MAAM;cAElB,SAAS,KAAK;IACvB,MAAM,QAAQ,IAAI;AAClB,QAAI,OAAO,UAAU,SACnB,gBAAe,IAAI,MAAM;cAElB,SAAS,MAAM;IACxB,MAAM,QAAQ,IAAI;AAClB,QAAI,OAAO,UAAU,SACnB,iBAAgB,IAAI,OAAO,KAAK,YAAY,MAAM,MAAM,CAAC;cAElD,SAAS,KAAK;IACvB,MAAM,UAAU,IAAI;AACpB,QAAI,OAAO,YAAY,SACrB,UAAS,IAAI,QAAmB;cAEzB,SAAS,MAAM;IACxB,MAAM,QAAQ,IAAI;IAClB,MAAM,UAAU,IAAI;AACpB,QAAI,OAAO,UAAU,SACnB,aAAY,IAAI,MAAM;AAExB,QAAI,OAAO,YAAY,SACrB,UAAS,IAAI,QAAmB;;;AAKtC,OAAK,MAAM,WAAW,SACpB,MAAK,aAAa,0BAA0B,SAAS,GAAG;AAG1D,OAAK,MAAM,SAAS,eAClB,MAAK,gBAAgB,iBAAiB,OAAO,GAAG;AAGlD,OAAK,MAAM,CAAC,OAAO,gBAAgB,gBACjC,MAAK,gBAAgB,gBAAgB,OAAO,aAAa,GAAG;AAG9D,OAAK,MAAM,SAAS,YAClB,MAAK,aAAa,sBAAsB,OAAO,GAAG;EAGpD,MAAM,gBAAgB,IAAI,IAAY;GACpC,GAAG;GACH,GAAG,gBAAgB,MAAM;GACzB,GAAG;GACH,GAAG;GACJ,CAAC;AACF,OAAK,MAAM,SAAS,cAClB,KAAI,KAAK,mBAAmB,MAAM,CAChC,KAAI;AACF,QAAK,iBAAiB,MAAM;AAC5B,QAAK,gBAAgB,cAAc,OAAO,GAAG;AAC7C,SAAM,KAAK,WAAW,QAAQ,MAAM;WAC7B,OAAO;AACd,WAAQ,MAAM,8CAA8C;IAC1D;IACA;IACD,CAAC;;AAKR,OAAK,MAAM,SAAS,eAAe;GACjC,MAAM,WAAW,KAAK,gBAAgB,IAAI,MAAM;GAChD,MAAM,YAAY,KAAK,UAAU,IAAI,CAAC,MAAM,MAAM,CAAC;AACnD,OAAI,CAAC,YAAY,cAAc,OAC7B,KAAI;AACF,SAAK,iBAAiB,MAAM;AAC5B,SAAK,gBAAgB,cAAc,OAAO,GAAG;AAC7C,UAAM,KAAK,WAAW,QAAQ,MAAM;YAC7B,OAAO;AACd,YAAQ,MAAM,2CAA2C;KACvD;KACA;KACD,CAAC;;;;CAUV,AAAQ,qBAGN;EACA,MAAM,+BAAe,IAAI,KAAyB;EAClD,MAAM,eAAe,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;AAC3D,OAAK,MAAM,OAAO,cAAc;AAC9B,OAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAG;GACnD,MAAM,QAAQ,IAAI,IAAI;AACtB,OAAI,OAAO,UAAU,SAAU;AAC/B,OAAI,IAAI,UAAU,UAAa,CAAC,KAAK,gBAAgB,IAAI,MAAM,CAC7D;AAGF,OAAI,IAAI,IAAI,WAAW,GAAG;IACxB,MAAM,MAAM,aAAa,IAAI,MAAM;AACnC,QAAI,CAAC,IAAK;IACV,IAAII,YAAU,aAAa,IAAI,MAAM;AACrC,QAAI,CAACA,WAAS;AACZ,iBAAU,EAAE;AACZ,kBAAa,IAAI,OAAOA,UAAQ;;AAElC,SAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,IAAI,EAAE;KAChD,MAAM,SAAS,eAAe,MAAM;AACpC,SAAI,WAAW,OACb,WAAQ,SAAS;;AAGrB;;GAGF,MAAM,WAAW,IAAI,IAAI;AACzB,OAAI,OAAO,aAAa,SAAU;GAClC,MAAM,YAAY,eAAe,IAAI,MAAM;AAC3C,OAAI,cAAc,OAAW;GAC7B,IAAI,UAAU,aAAa,IAAI,MAAM;AACrC,OAAI,CAAC,SAAS;AACZ,cAAU,EAAE;AACZ,iBAAa,IAAI,OAAO,QAAQ;;AAElC,WAAQ,YAAY;;EAGtB,MAAM,6BAAa,IAAI,KAAqB;EAC5C,MAAM,gBAAgB,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;AAC7D,OAAK,MAAM,OAAO,eAAe;AAC/B,OAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAG;GACnD,MAAM,QAAQ,IAAI,IAAI;AACtB,OAAI,OAAO,UAAU,SAAU;GAE/B,MAAM,YAAY,eAAe,IAAI,MAAM;AAC3C,OAAI,OAAO,cAAc,YAAY,OAAO,SAAS,UAAU,CAC7D,YAAW,IAAI,OAAO,UAAU;;AAGpC,SAAO;GAAE,UAAU;GAAc;GAAY;;CAG/C,AAAQ,YAAY,OAAoC;AACtD,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,MAAM,GACtD,QACA;;CAGN,IAAY,YAAmB;AAC7B,SAAO,KAAK,cAAc;;CAM5B,AAAQ,mBAAmB,OAAwB;AACjD,MAAI,KAAK,UAAU,IAAI,CAAC,MAAM,MAAM,CAAC,KAAK,OAAW,QAAO;AAC5D,MAAI,KAAK,aAAa,CAAC,KAAK,MAAM,CAAC,CAAE,QAAO;AAC5C,MAAI,KAAK,aAAa,CAAC,KAAK,MAAM,CAAC,CAAE,QAAO;AAC5C,MAAI,KAAK,aAAa,CAAC,MAAM,MAAM,CAAC,CAAE,QAAO;AAC7C,SAAO;;CAGT,AAAQ,aAAa,QAAmC;EACtD,MAAM,OAAO,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC;AAC5C,OAAK,MAAM,OAAO,KAGhB,KAFc,IAAI,UACQ,OACb,QAAO;AAEtB,SAAO;;CAGT,AAAQ,cAAc,QAA+B;EACnD,MAAM,sBAAM,IAAI,KAAa;EAC7B,MAAM,OAAO,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC;AAC5C,OAAK,MAAM,OAAO,MAAM;AACtB,OAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAG;GACnD,MAAM,QAAQ,IAAI,IAAI;AACtB,OAAI,OAAO,UAAU,SACnB,KAAI,IAAI,MAAM;;AAGlB,SAAO;;CAGT,AAAQ,iBAAiB,OAAqB;EAC5C,MAAMC,WAAoC;GACxC,CAAC,KAAK,MAAM;GACZ,CAAC,KAAK,MAAM;GACZ,CAAC,MAAM,MAAM;GACd;AACD,OAAK,MAAM,UAAU,UAAU;GAC7B,MAAM,OAAO,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC;AAC5C,QAAK,MAAM,OAAO,KAChB,MAAK,UAAU,OAAO,IAAI,IAAI;;AAGlC,OAAK,UAAU,OAAO,CAAC,MAAM,MAAM,CAAC;;;;;;;;;ACvPxC,IAAa,aAAb,MAAiD;CAC/C,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAiB,mCAAmB,IAAI,KAAiC;CAEzE,YAAY,SAAkC;AAC5C,OAAK,UAAU,QAAQ;AACvB,OAAK,YAAY,QAAQ;AACzB,OAAK,WAAW,QAAQ;AACxB,OAAK,aAAa,QAAQ;AAC1B,OAAK,kBAAkB,QAAQ;AAC/B,OAAK,eAAe,QAAQ;AAC5B,OAAK,gBAAgB,QAAQ;AAC7B,OAAK,eAAe,QAAQ;AAC5B,OAAK,mBAAmB,QAAQ;;CAGlC,MAAM,QAAuB;AAC3B,MAAI,CAAC,KAAK,aACR,MAAK,eAAe,KAAK,YAAY;AAEvC,QAAM,KAAK;;CAGb,MAAM,KAAK,UAA2B,EAAE,EAAiB;AACvD,QAAM,KAAK,OAAO;EAClB,MAAM,EAAE,QAAQ,QAAQ,WAAW;AACnC,MAAI,CAAC,KAAK,UAAW;AAErB,MAAI,CAAC,KAAK,UAAU,aAAa,CAC/B,OAAM,KAAK,UAAU,SAAS;AAGhC,MAAI,UAAU,UAAU,UAAU,QAAQ;AACxC,QAAK,SAAS,YAAY,OAAO;GACjC,MAAMC,iBAA+B,EAAE;GACvC,MAAM,cAAc,KAAK,UAAU,WAAW,UAAU;AACtD,QAAI,MAAM,WAAW,QAAS;AAC9B,mBAAe,KAAK,GAAG,MAAM,OAAO;KACpC;AACF,OAAI;AAEF,QAAI,EADW,MAAM,KAAK,UAAU,SAAS,KAAK,UAAU,EAChD,GACV,OAAM,IAAI,MAAM,uBAAuB;AAEzC,QAAI,eAAe,SAAS,EAC1B,OAAM,KAAK,cAAc,YAAY,gBAAgB,OAAO;QAE5D,MAAK,cAAc,WAAW,OAAO;aAE/B;AACR,iBAAa;AACb,SAAK,SAAS,YAAY;;;AAI9B,MAAI,UAAU,SAAS,UAAU,QAAQ;GACvC,MAAM,UAAU,UAAU,KAAK,gBAAgB,WAAW;AAC1D,QAAK,MAAM,SAAS,SAAS;IAC3B,MAAM,MAAM,MAAM,KAAK,WAAW,UAAU,MAAM;AAClD,SAAK,SAAS,YAAY,OAAO;AACjC,QAAI;AAEF,SAAI,EADW,MAAM,KAAK,UAAU,QAAQ,OAAO,IAAI,EAC3C,GACV,OAAM,IAAI,MAAM,4BAA4B,QAAQ;cAE9C;AACR,UAAK,SAAS,YAAY;;AAE5B,UAAM,KAAK,WAAW,WAAW,OAAO,IAAI;AAC5C,UAAM,KAAK,WAAW,mBAAmB,OAAO,KAAK,OAAO;;;;CAKlE,MAAM,aACJ,QACgC;AAChC,QAAM,KAAK,OAAO;AAClB,MAAI,CAAC,KAAK,UACR,OAAM,IAAI,MAAM,mCAAmC;AAErD,MAAI,CAAC,KAAK,UAAU,aAAa,CAC/B,OAAM,KAAK,UAAU,SAAS;EAEhC,MAAM,WAAW,KAAK;AACtB,MAAI,UAAU;AACZ,YAAS,YAAY;AACrB,UAAO,KAAK,gBAAgB,SAAS;;AAGvC,OAAK,uBAAuB;EAE5B,MAAM,OAAO,KAAK,UAAU,aAAa,KAAK,WAAW,OAAO;EAChE,MAAMC,SAA6B;GAAE;GAAM,UAAU;GAAG;AACxD,OAAK,uBAAuB;AAE5B,EAAK,KAAK,sBACP,KAAK,YAAY;GAChB,MAAM,KAAK,KAAK,SAAS,eAAe,OAAO;AAC/C,QAAK,cAAc,WAAW,GAAG;IACjC,CACD,MAAM,cAAc,uBAAuB,CAAC;AAE/C,SAAO,KAAK,gBAAgB,OAAO;;CAGrC,MAAM,YACJ,OACA,QACgC;AAChC,QAAM,KAAK,OAAO;AAClB,MAAI,CAAC,KAAK,UACR,OAAM,IAAI,MAAM,mCAAmC;AAErD,MAAI,CAAC,KAAK,UAAU,aAAa,CAC/B,OAAM,KAAK,UAAU,SAAS;EAGhC,MAAM,WAAW,KAAK,iBAAiB,IAAI,MAAM;AACjD,MAAI,UAAU;AACZ,YAAS,YAAY;AACrB,UAAO,KAAK,eAAe,OAAO,SAAS;;EAG7C,MAAM,MAAM,MAAM,KAAK,WAAW,UAAU,MAAM;EAClD,MAAM,OAAO,KAAK,UAAU,YAAY,OAAO,KAAK,OAAO;EAC3D,MAAMA,SAA6B;GAAE;GAAM,UAAU;GAAG;AACxD,OAAK,iBAAiB,IAAI,OAAO,OAAO;AACxC,EAAK,KAAK,sBAAsB,MAC9B,cAAc,OAAO,MAAM,aAAa,CACzC;AACD,SAAO,KAAK,eAAe,OAAO,OAAO;;CAG3C,AAAQ,gBAAgB,QAAmD;EACzE,IAAI,WAAW;AACf,SAAO;GACL,mBAAmB;AACjB,QAAI,SAAU;AACd,eAAW;IACX,MAAM,UAAU,KAAK;AACrB,QAAI,CAAC,WAAW,YAAY,OAAQ;AACpC,YAAQ,WAAW,KAAK,IAAI,GAAG,QAAQ,WAAW,EAAE;AACpD,QAAI,QAAQ,aAAa,GAAG;AAC1B,aAAQ,KAAK,aAAa;AAC1B,UAAK,uBAAuB;AAC5B,SAAI,KAAK,sBAAsB;AAC7B,WAAK,sBAAsB;AAC3B,WAAK,uBAAuB;;;;GAIlC,uBAAuB,OAAO,KAAK;GACnC,IAAI,YAAY;AACd,WAAO,OAAO,KAAK;;GAErB,IAAI,SAAS;AACX,WAAO,OAAO,KAAK;;GAErB,gBAAgB,OAAO,KAAK;GAC7B;;CAGH,AAAQ,eACN,OACA,QACuB;EACvB,IAAI,WAAW;AACf,SAAO;GACL,mBAAmB;AACjB,QAAI,SAAU;AACd,eAAW;IACX,MAAM,UAAU,KAAK,iBAAiB,IAAI,MAAM;AAChD,QAAI,CAAC,WAAW,YAAY,OAAQ;AACpC,YAAQ,WAAW,KAAK,IAAI,GAAG,QAAQ,WAAW,EAAE;AACpD,QAAI,QAAQ,aAAa,GAAG;AAC1B,aAAQ,KAAK,aAAa;AAC1B,SAAI,KAAK,iBAAiB,IAAI,MAAM,KAAK,QACvC,MAAK,iBAAiB,OAAO,MAAM;;;GAIzC,uBAAuB,OAAO,KAAK;GACnC,IAAI,YAAY;AACd,WAAO,OAAO,KAAK;;GAErB,IAAI,SAAS;AACX,WAAO,OAAO,KAAK;;GAErB,gBAAgB,OAAO,KAAK;GAC7B;;CAGH,MAAM,UAAyB;AAC7B,QAAM,KAAK,WAAW,OAAO;AAC7B,MAAI,KAAK,sBAAsB;AAC7B,QAAK,qBAAqB,KAAK,aAAa;AAC5C,QAAK,uBAAuB;;AAE9B,OAAK,MAAM,UAAU,KAAK,iBAAiB,QAAQ,CACjD,QAAO,KAAK,aAAa;AAE3B,OAAK,iBAAiB,OAAO;AAC7B,OAAK,wBAAwB;AAC7B,OAAK,uBAAuB;AAC5B,OAAK,SAAS,OAAO;AACrB,OAAK,gBAAgB,OAAO;AAC5B,OAAK,aAAa,OAAO;AACzB,OAAK,eAAe;AACpB,QAAM,KAAK,WAAW,OAAO;;CAG/B,MAAc,aAA4B;AACxC,MAAI,KAAK,SAAS;GAChB,MAAM,WAAW,MAAM,KAAK,QAAQ,UAAU;AAC9C,OAAI,SACF,MAAK,iBAAiB,SAAS;;AAGnC,OAAK,cAAc,WAAW,OAAO;;CAGvC,AAAQ,wBAA8B;AACpC,MAAI,KAAK,qBAAsB;AAC/B,OAAK,uBAAuB,KAAK,UAAU,WAAW,UAAU;AAC9D,OAAI,MAAM,WAAW,QAAS;GAC9B,MAAM,KAAK,KAAK,SAAS,eAAe,OAAO;AAC/C,IAAM,YAAY;AAChB,UAAM,KAAK,cAAc,YAAY,MAAM,QAAQ,GAAG;OACpD,CAAC,MAAM,cAAc,yBAAyB,CAAC;IACnD;;CAGJ,IAAY,YAAmB;AAC7B,SAAO,KAAK,cAAc;;;;;;ACxQ9B,SAAgB,kBAA6B;AAC3C,QAAO;EACL,0BAAU,IAAI,KAAyB;EACvC,2BAAW,IAAI,KAA8C;EAC7D,wBAAQ,IAAI,KAA2B;EACvC,gCAAgB,IAAI,KAAmC;EACvD,gCAAgB,IAAI,KAA2B;EAChD;;;;;ACtBH,MAAM,cAAc,IAAI,aAAa;AACrC,MAAM,mCAAmC;AAQzC,IAAa,gBAAb,MAA2B;CACzB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,AAAQ;CACR,AAAQ;CACR,AAAQ,eAA8B,QAAQ,SAAS;CACvD,AAAQ;CACR,AAAQ,uBAAuB;CAC/B,AAAQ,YAAY;CAEpB,YAAY,SAA+B;AACzC,OAAK,eAAe,QAAQ;AAC5B,OAAK,UAAU,QAAQ;EACvB,MAAM,qBAAqB,QAAQ;AACnC,OAAK,aACH,OAAO,uBAAuB,YAAY,OAAO,SAAS,mBAAmB,IAAI,sBAAsB,IACnG,qBACA;;CAGR,MAAM,gBAAiD;AACrD,OAAK,uBAAuB;AAC5B,MAAI,KAAK,YAAa;AACtB,OAAK,cAAc,KAAK,UAAU,gBAAgB;AAChD,QAAK,eAAe;IACpB;;CAGJ,MAAM,UAAyB;AAC7B,OAAK,YAAY;AACjB,MAAI,KAAK,YAAY;AACnB,gBAAa,KAAK,WAAW;AAC7B,QAAK,aAAa;;AAEpB,MAAI,KAAK,aAAa;AACpB,QAAK,aAAa;AAClB,QAAK,cAAc;;AAErB,QAAM,KAAK,UAAU;;CAGvB,MAAM,SAAS,YAAY,OAAsB;AAC/C,MAAI,KAAK,YAAY;AACnB,gBAAa,KAAK,WAAW;AAC7B,QAAK,aAAa;;AAEpB,QAAM,KAAK,MAAM,UAAU;;CAG7B,AAAQ,gBAAsB;AAC5B,MAAI,KAAK,UAAW;AACpB,MAAI,KAAK,eAAe,GAAG;AACzB,GAAK,KAAK,OAAO;AACjB;;AAEF,MAAI,KAAK,WACP,cAAa,KAAK,WAAW;AAE/B,OAAK,aAAa,iBAAiB;AACjC,QAAK,aAAa;AAClB,GAAK,KAAK,OAAO;KAChB,KAAK,WAAW;;CAGrB,MAAc,MAAM,YAAY,OAAsB;AACpD,MAAI,UAAW,MAAK,uBAAuB;EAC3C,MAAM,MAAM,KAAK,aAAa,YAAY,GAAG,CAAC,WAAW,KAAK,eAAe,CAAC;AAC9E,OAAK,eAAe;AACpB,QAAM;;CAGR,MAAc,gBAA+B;EAC3C,MAAM,QAAQ,KAAK;EACnB,MAAM,iBAAiB,MAAM,SAAS;AACtC,MAAI,KAAK,wBAAwB,KAAK,cAAc,gBAAgB,KAAK,qBAAqB,EAAE;AAC9F,QAAK,uBAAuB;AAC5B;;EAGF,MAAM,WAAW,KAAK,uBAAuB,SAAY,KAAK;EAC9D,MAAM,YAAY,WAAW,MAAM,WAAW,SAAS,GAAG,MAAM,YAAY;EAC5E,MAAM,SAAS,WAAW,KAAK,sBAAsB,WAAW,SAAS,GAAG;AAC5E,MAAI,OAAO,KAAK,OAAO,QAAQ,CAAC,WAAW,GAAG;AAC5C,QAAK,uBAAuB;AAC5B,QAAK,uBAAuB;AAC5B;;EAGF,MAAM,UAAU,YAAY,OAAO,KAAK,UAAU,OAAO,CAAC;AAE1D,MAAI,CAAC,KAAK,SAAS;AACjB,QAAK,uBAAuB;AAC5B,QAAK,uBAAuB;AAC5B;;AAGF,QAAM,KAAK,QAAQ,KAAK;GAAE,MAAM;GAAQ,QAAQ;GAAS,CAAC;AAC1D,OAAK,uBAAuB;AAC5B,OAAK,uBAAuB;;CAG9B,IAAY,YAAmB;AAC7B,SAAO,KAAK,cAAc;;CAG5B,AAAQ,sBAAsB,QAAsB,UAAuC;EACzF,MAAMC,UAAmC,EAAE;AAC3C,OAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,OAAO,QAAQ,EAAE;GAC1D,MAAM,QAAQ,KAAK,WAAW,OAAO,EAAE;AACvC,OAAI,CAAC,OAAO;AACV,YAAQ,OAAO;AACf;;GAEF,MAAM,gBAAgB,SAAS,MAAM;AACrC,OAAI,CAAC,eAAe;AAClB,YAAQ,OAAO;AACf;;AAKF,OAFE,MAAM,eAAe,cAAc,gBAClC,MAAM,iBAAiB,cAAc,gBAAgB,MAAM,iBAAiB,cAAc,eAE3F,SAAQ,OAAO;;AAGnB,SAAO;GAAE,SAAS,OAAO;GAAS;GAAS;;CAG7C,AAAQ,WAAW,KAA+F;AAChH,MAAI,OAAO,QAAQ,SAAU,QAAO;EACpC,MAAM,CAAC,iBAAiB,mBAAmB,aAAa,IAAI,MAAM,IAAI;AACtE,MAAI,CAAC,mBAAmB,CAAC,qBAAqB,CAAC,UAAW,QAAO;EACjE,MAAM,eAAe,OAAO,gBAAgB;EAC5C,MAAM,iBAAiB,OAAO,kBAAkB;AAChD,MAAI,CAAC,OAAO,SAAS,aAAa,IAAI,CAAC,OAAO,SAAS,eAAe,CAAE,QAAO;AAC/E,SAAO;GAAE;GAAc;GAAgB;GAAW;;CAGpD,AAAQ,cAAc,GAAmB,GAA4B;AACnE,MAAI,CAAC,KAAK,CAAC,EAAG,QAAO;EACrB,MAAM,QAAQ,OAAO,KAAK,EAAE;EAC5B,MAAM,QAAQ,OAAO,KAAK,EAAE;AAC5B,MAAI,MAAM,WAAW,MAAM,OAAQ,QAAO;AAC1C,OAAK,MAAM,OAAO,OAAO;GACvB,MAAM,SAAS,EAAE;GACjB,MAAM,SAAS,EAAE;AACjB,OAAI,CAAC,UAAU,CAAC,OAAQ,QAAO;AAC/B,OAAI,OAAO,iBAAiB,OAAO,gBAAgB,OAAO,mBAAmB,OAAO,eAClF,QAAO;;AAGX,SAAO;;;;;;AC7HX,MAAM,mCAAmC;AACzC,MAAM,8BAA8B,MAAU,KAAK,KAAK;AAExD,IAAa,WAAb,MAAa,SAA+C;CAC1D,AAAS;CACT,AAAQ,aAAa;CACrB,AAAiB;CACjB,AAAiB;CACjB,AAAQ,YAAmB,IAAIC,wBAAO;CACtC,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,AAAQ,YAAY,SAA0B;AAC5C,OAAK,UAAU;AACf,OAAK,YAAY,QAAQ;AACzB,OAAK,UAAU,QAAQ;AACvB,OAAK,iBAAiB,QAAQ;AAC9B,OAAK,WAAW,IAAI,cAAoB;AACxC,OAAK,QAAQ,iBAAiB;EAC9B,MAAM,qBAAqB,QAAQ;EACnC,MAAM,wBACJ,OAAO,uBAAuB,YAC5B,OAAO,SAAS,mBAAmB,IACnC,sBAAsB,IACpB,qBACA;EACN,MAAM,0BAA0B,QAAQ;AACxC,OAAK,mBACH,OAAO,4BAA4B,YACjC,OAAO,SAAS,wBAAwB,IACxC,2BAA2B,IACzB,0BACA;AACN,OAAK,aAAa,IAAI,WAAiB;GACrC,SAAS,KAAK;GACd;GACA,oBAAoB,KAAK;GACzB,UAAU,KAAK;GAChB,CAAC;AACF,OAAK,kBAAkB,IAAI,gBAAsB;GAC/C,oBAAoB,KAAK;GACzB,UAAU,KAAK;GACf,OAAO,KAAK;GACb,CAAC;AACF,OAAK,eAAe,IAAI,aAAmB;GACzC,SAAS,KAAK;GACd,gBAAgB,KAAK;GACrB,oBAAoB,KAAK;GACzB,UAAU,KAAK;GACf,OAAO,KAAK;GACb,CAAC;AACF,OAAK,gBAAgB,IAAI,cAAc;GACrC,oBAAoB,KAAK;GACzB,SAAS,KAAK;GACd,YAAY,QAAQ;GACrB,CAAC;AACF,OAAK,gBAAgB,IAAI,cAAoB;GAC3C,oBAAoB,KAAK;GACzB,iBAAiB,KAAK;GACtB,cAAc,KAAK;GACnB,YAAY,KAAK;GAClB,CAAC;AACF,OAAK,aAAa,IAAI,WAAiB;GACrC,SAAS,KAAK;GACd,WAAW,KAAK;GAChB,UAAU,KAAK;GACf,YAAY,KAAK;GACjB,iBAAiB,KAAK;GACtB,cAAc,KAAK;GACnB,eAAe,KAAK;GACpB,oBAAoB,KAAK;GACzB,aAAa,aAAa;AACxB,SAAK,UAAU,MAAM,SAAS;;GAEjC,CAAC;AAEF,OAAK,mBAAmB,KAAK,SAAS,OACnC,UAAU,KAAK,mBAAmB,MAAM,EACzC;GAAE,OAAO,CAAC,oBAAoB,eAAe;GAAE,IAAI,CAAC,QAAQ,OAAO;GAAE,CACtE;;CAGH,aAAa,OAA6C,SAAmD;EAC3G,MAAM,OAAO,IAAI,SAAe,QAAQ;AACxC,QAAM,KAAK,SAAS,QAAQ;AAG5B,QAAM,KAAK,OAAO;AAClB,SAAO;;;;;;;;CAST,MAAc,QAAuB;AACnC,QAAM,KAAK,WAAW,OAAO;AAC7B,OAAK,cAAc,MAAM,KAAK,UAAU,SAAS,CAAC;;CAGpD,AAAQ,qBACN,OACA,WACoB;EACpB,MAAM,YAAY,KAAK,gBAAgB,eAAe,MAAM;AAC5D,MAAI,cAAc,OAAW,QAAO;AACpC,SAAO,YAAY;;CAGrB,AAAQ,iBAAiB,OAAqB;EAC5C,MAAM,eAAe,MAAM,KACzB,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC,KAAK,MAAM,EAAE,CAAC,GAC5C,QAAQ,IAAI,IACd;AACD,OAAK,MAAM,OAAO,aAChB,MAAK,UAAU,OAAO,IAAI;EAG5B,MAAM,eAAe,MAAM,KACzB,KAAK,UAAU,KAAK,EAAE,QAAQ,CAAC,KAAK,MAAM,EAAE,CAAC,GAC5C,QAAQ,IAAI,IACd;AACD,OAAK,MAAM,OAAO,aAChB,MAAK,UAAU,OAAO,IAAI;AAG5B,OAAK,UAAU,OAAO,CAAC,MAAM,MAAM,CAAC;;;;;;CAOtC,MAAM,KAAK,UAA2B,EAAE,EAAiB;AACvD,QAAM,KAAK,WAAW,KAAK,QAAQ;;;;;;;;;;;;CAarC,MAAM,aACJ,QACgC;EAChC,MAAM,eAAe,MAAM,KAAK,WAAW,aAAa,OAAO;AAC/D,SAAO;GACL,aAAa,aAAa;GAC1B,IAAI,YAAY;AACd,WAAO,aAAa;;GAEtB,IAAI,SAAS;AACX,WAAO,aAAa;;GAEtB,gBAAgB,aAAa;GAC7B,uBAAuB,aAAa,sBAAsB,KAAK,YAAY;AACzE,UAAM,KAAK,cAAc,UAAU;KACnC;GACH;;;;;;;;;;;;;;;CAgBH,MAAM,YACJ,OACA,QACgC;EAChC,MAAM,eAAe,MAAM,KAAK,WAAW,YAAY,OAAO,OAAO;AACrE,SAAO;GACL,GAAG;GACH,gBAAgB,aAAa;GAC7B,QAAQ,aAAa;GACtB;;;;;;CAOH,MAAM,kBACJ,QAC4D;AAC5D,MAAI,CAAC,KAAK,UACR,OAAM,IAAI,MAAM,mCAAmC;AAErD,QAAM,KAAK,WAAW,OAAO;AAC7B,MAAI,CAAC,KAAK,UAAU,aAAa,CAC/B,OAAM,KAAK,UAAU,SAAS;EAEhC,MAAM,eAAe,KAAK,UAAU,kBAAkB,OAAO;AAC7D,SAAO;GACL,GAAG;GACH,gBAAgB,aAAa;GAC7B,QAAQ,aAAa;GACtB;;;;;;;;;CAUH,MAAM,iBAAiB,OAAuC;AAC5D,SAAO;GACL,KAAK,MAAM,KAAK,WAAW,iBAAiB,MAAM;GAClD,gBAAgB;AACd,WAAO,KAAK,KAAK;KAAE,OAAO;KAAO,QAAQ,CAAC,MAAM;KAAE,CAAC;;GAErD,WAAW,SAAS;AAClB,WAAO,KAAK,WAAW,YAAY,OAAO,EAAE,MAAM,CAAC;;GAEtD;;CAGH,MAAM,cACJ,OACA,OACe;AACf,QAAM,KAAK,gBAAgB,OAAO,OAAO,MAAM;;CAGjD,MAAM,WAAW,OAA2D;AAC1E,SAAO,KAAK,gBAAgB,IAAI,MAAM;;CAGxC,MAAM,QAAQ,OAAoD;AAChE,SAAO,KAAK,gBAAgB,QAAQ,MAAM;;;;;;;;CAS5C,MAAM,UACJ,OACA,UAA4B,EAAE,EACf;AAEf,MADiB,KAAK,gBAAgB,eAAe,MAAM,KAC1C,UAAa,CAAC,QAAQ,MAAO;EAE9C,MAAM,YAAY,QAAQ,aAAa,KAAK,KAAK;AACjD,OAAK,gBAAgB,YAAY,OAAO,UAAU;;;;;;CAOpD,MAAM,WAAW,OAA8B;AAE7C,MADiB,KAAK,gBAAgB,eAAe,MAAM,KAC1C,OAAW;AAC5B,OAAK,gBAAgB,aAAa,MAAM;;;;;;;CAQ1C,MAAM,SAAS,OAA8B;EAC3C,MAAM,cAAc,KAAK,gBAAgB,eAAe,MAAM;AAC9D,OAAK,SAAS,KAAK;GACjB,MAAM;GACN;GACA;GACA,IAAI;GACL,CAAC;AACF,QAAM,KAAK,WAAW,QAAQ,MAAM;AAEpC,OAAK,aAAa,cAAc,OAAO,QAAQ;AAE/C,OAAK,iBAAiB,MAAM;AAC5B,OAAK,gBAAgB,gBAAgB,OAAO,QAAW,QAAQ;AAE/D,OAAK,gBAAgB,iBAAiB,OAAO,QAAQ;;;;;;CAOvD,MAAM,cAAc,UAA+B,EAAE,EAAmB;EACtE,MAAM,MAAM,QAAQ,OAAO,KAAK,KAAK;EACrC,MAAM,YAAY,QAAQ,aAAa,KAAK;EAC5C,MAAM,SAAS,KAAK,gBAAgB,WAAW;EAC/C,IAAI,SAAS;AACb,OAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,aAAa,KAAK,qBAAqB,OAAO,UAAU;AAC9D,OAAI,eAAe,UAAa,MAAM,WAAY;AAClD,SAAM,KAAK,SAAS,MAAM;AAC1B,aAAU;;AAEZ,SAAO;;CAGT,UAAiB;AACf,SAAO,KAAK;;CAGd,MACE,UACA,SAAgC,EAAE,EACjB;AACjB,SAAO,KAAK,SAAS,MAAM,UAAU,OAAO;;;;;;;;;CAU9C,MAAM,gBAAgB,OAAiC;AACrD,SAAO,KAAK,WAAW,gBAAgB,MAAM;;;;;;;;;CAU/C,MAAM,UAAU,OAA8B;AAC5C,QAAM,KAAK,WAAW,UAAU,MAAM;;CAGxC,MAAM,QAAuB;AAC3B,QAAM,KAAK,WAAW,OAAO;AAC7B,QAAM,KAAK,cAAc,UAAU;;CAGrC,MAAM,YAAY,QAA8C;AAC9D,SAAO,KAAK,aAAa,YAAY,OAAO;;CAG9C,MAAM,UAAU,OAAe,QAA4C;AACzE,SAAO,KAAK,aAAa,UAAU,OAAO,OAAO;;CAGnD,MAAM,WAAW,SAA0C;AACzD,SAAO,KAAK,aAAa,WAAW,QAAQ;;CAG9C,MAAM,YAAY,OAAe,SAAiC;AAChE,QAAM,KAAK,aAAa,YAAY,OAAO,QAAQ;;CAGrD,MAAM,WAAW,OAA6C;AAC5D,SAAO,KAAK,aAAa,WAAW,MAAM;;CAG5C,MAAM,YAAY,SAA0C;AAC1D,SAAO,KAAK,aAAa,YAAY,QAAQ;;CAG/C,MAAM,SAAS,UAAoC,EAAE,EAAmB;AACtE,SAAO,KAAK,aAAa,SAAS,QAAQ;;CAG5C,IAAI,YAAqB;AACvB,SAAO,KAAK;;CAGd,MAAM,UAAyB;AAC7B,MAAI,KAAK,WAAY;AACrB,OAAK,aAAa;AAClB,OAAK,iBAAiB,aAAa;AACnC,QAAM,KAAK,cAAc,SAAS;AAClC,QAAM,KAAK,WAAW,SAAS;AAC/B,OAAK,gBAAgB,SAAS;AAC9B,OAAK,SAAS,SAAS;AACvB,QAAM,KAAK,WAAW,OAAO;;CAG/B,AAAQ,mBAAmB,OAA8B;EAEvD,MAAM,eAAe;AACnB,OAAI,MAAM,SAAS,mBAAoB,QAAO,MAAM;AACpD,OAAI,MAAM,SAAS,eAAgB,QAAO,MAAM;MAE9C;AAEJ,MAAI,CAAC,MAAO;EAEZ,MAAM,kBACJ,MAAM,SAAS,kBACf,OAAO,KAAK,MAAM,MAAM,CAAC,WAAW;EAEtC,MAAM,mBACJ,MAAM,SAAS,sBAAsB,MAAM,gBAAgB;AAS7D,MAAI,EAHF,mBACC,oBAAoB,KAAK,gBAAgB,IAAI,MAAM,KAAK,QAEvC;AAEpB,EAAK,KAAK,WAAW,QAAQ,MAAM,CAAC,OAAO,UAAU;AACnD,WAAQ,MAAM,6BAA6B;IAAE;IAAO;IAAO,CAAC;IAC5D"}
1
+ {"version":3,"file":"index.cjs","names":["entry: WatchEntry<Meta>","LoroDoc","promises: Promise<void>[]","by: RepoEventBy","chunks: Uint8Array[]","arr: JsonValue[]","obj: JsonObject","patch: JsonObject","json: JsonObject","scanOptions: ScanOptions","entries: RepoDocMeta<Meta>[]","tsScan: ScanOptions","next: JsonObject","outPatch: JsonObject","patch: JsonObject","patch","docMeta: JsonObject","metadata","metadata: RepoAssetMetadata","storedBytes: Uint8Array | undefined","removed: AssetId[]","removedAssets: Array<[AssetId, AssetRecord]>","docMeta","prefixes: Array<[string, string]>","recordedEvents: FlockEvent[]","record: SubscriptionRecord","entries: ExportBundle[\"entries\"]","Flock"],"sources":["../src/internal/event-bus.ts","../src/internal/logging.ts","../src/internal/doc-manager.ts","../src/utils.ts","../src/internal/metadata-manager.ts","../src/internal/asset-manager.ts","../src/internal/flock-hydrator.ts","../src/internal/sync-runner.ts","../src/internal/repo-state.ts","../src/internal/meta-persister.ts","../src/index.ts"],"sourcesContent":["import type {\n JsonObject,\n RepoEvent,\n RepoEventBy,\n RepoEventFilter,\n RepoEventListener,\n RepoWatchHandle,\n} from \"../types\";\n\ntype WatchEntry<Meta extends JsonObject> = {\n listener: RepoEventListener<Meta>;\n filter: RepoEventFilter<Meta>;\n};\n\nexport class RepoEventBus<Meta extends JsonObject> {\n private readonly watchers = new Set<WatchEntry<Meta>>();\n private readonly eventByStack: RepoEventBy[] = [];\n\n watch(\n listener: RepoEventListener<Meta>,\n filter: RepoEventFilter<Meta> = {},\n ): RepoWatchHandle {\n const entry: WatchEntry<Meta> = { listener, filter };\n this.watchers.add(entry);\n return {\n unsubscribe: () => {\n this.watchers.delete(entry);\n },\n };\n }\n\n emit(event: RepoEvent<Meta>): void {\n for (const entry of this.watchers) {\n if (this.shouldNotify(entry.filter, event)) {\n entry.listener(event);\n }\n }\n }\n\n clear(): void {\n this.watchers.clear();\n this.eventByStack.length = 0;\n }\n\n pushEventBy(by: RepoEventBy): void {\n this.eventByStack.push(by);\n }\n\n popEventBy(): void {\n this.eventByStack.pop();\n }\n\n resolveEventBy(defaultBy: RepoEventBy): RepoEventBy {\n const index = this.eventByStack.length - 1;\n return index >= 0 ? this.eventByStack[index] : defaultBy;\n }\n\n private shouldNotify(\n filter: RepoEventFilter<Meta>,\n event: RepoEvent<Meta>,\n ): boolean {\n if (!filter.docIds && !filter.kinds && !filter.metadataFields && !filter.by)\n return true;\n if (filter.kinds && !filter.kinds.includes(event.kind)) return false;\n if (filter.by && !filter.by.includes(event.by)) return false;\n\n const docId = (() => {\n if (\n event.kind === \"doc-metadata\" ||\n event.kind === \"doc-frontiers\" ||\n event.kind === \"doc-soft-deleted\" ||\n event.kind === \"doc-purging\"\n ) {\n return event.docId;\n }\n if (event.kind === \"asset-link\" || event.kind === \"asset-unlink\") {\n return event.docId;\n }\n return undefined;\n })();\n\n if (filter.docIds && docId && !filter.docIds.includes(docId)) return false;\n if (filter.docIds && !docId) return false;\n\n if (filter.metadataFields && event.kind === \"doc-metadata\") {\n const keys = Object.keys(event.patch);\n if (!keys.some((key) => filter.metadataFields?.includes(key))) {\n return false;\n }\n }\n\n return true;\n }\n}\n","export function logAsyncError(context: string): (error: unknown) => void {\n return (error: unknown) => {\n if (error instanceof Error) {\n console.error(`[loro-repo] ${context} failed: ${error.message}`, error);\n } else {\n console.error(\n `[loro-repo] ${context} failed with non-error reason:`,\n error,\n );\n }\n };\n}\n","import { Flock } from \"@loro-dev/flock\";\nimport { LoroDoc, PeerID, type LoroEventBatch, type VersionVector } from \"loro-crdt\";\n\nimport type {\n JsonObject,\n RepoEventBy,\n StorageAdapter,\n} from \"../types\";\nimport { RepoEventBus } from \"./event-bus\";\nimport { logAsyncError } from \"./logging\";\n\ntype PendingDocFrontierUpdate = {\n timeout: ReturnType<typeof setTimeout>;\n doc: LoroDoc;\n by: RepoEventBy;\n};\n\ninterface DocManagerOptions<Meta extends JsonObject> {\n readonly storage?: StorageAdapter;\n readonly docFrontierDebounceMs: number;\n readonly getMetaFlock: () => Flock;\n readonly eventBus: RepoEventBus<Meta>;\n}\n\nexport class DocManager<Meta extends JsonObject> {\n private readonly storage?: StorageAdapter;\n private readonly docFrontierDebounceMs: number;\n private readonly getMetaFlock: () => Flock;\n private readonly eventBus: RepoEventBus<Meta>;\n\n private readonly docs = new Map<string, LoroDoc>();\n private readonly docSubscriptions = new Map<string, () => void>();\n private readonly docFrontierUpdates = new Map<\n string,\n PendingDocFrontierUpdate\n >();\n private readonly docPersistedVersions = new Map<string, VersionVector>();\n constructor(options: DocManagerOptions<Meta>) {\n this.storage = options.storage;\n this.docFrontierDebounceMs = options.docFrontierDebounceMs;\n this.getMetaFlock = options.getMetaFlock;\n this.eventBus = options.eventBus;\n }\n\n async openPersistedDoc(\n docId: string,\n ): Promise<LoroDoc> {\n const doc = await this.ensureDoc(docId);\n return doc;\n }\n\n async openDetachedDoc(docId: string): Promise<LoroDoc> {\n const doc = await this.materializeDetachedDoc(docId);\n return doc;\n }\n\n async ensureDoc(docId: string): Promise<LoroDoc> {\n const cached = this.docs.get(docId);\n if (cached) {\n this.ensureDocSubscription(docId, cached);\n if (!this.docPersistedVersions.has(docId)) {\n this.docPersistedVersions.set(docId, cached.version());\n }\n return cached;\n }\n\n if (this.storage) {\n const stored = await this.storage.loadDoc(docId);\n if (stored) {\n this.registerDoc(docId, stored);\n return stored;\n }\n }\n\n const created = new LoroDoc();\n this.registerDoc(docId, created);\n return created;\n }\n\n async persistDoc(docId: string, doc: LoroDoc): Promise<void> {\n const previousVersion = this.docPersistedVersions.get(docId);\n const snapshot = doc.export({ mode: \"snapshot\" });\n const nextVersion = doc.version();\n if (!this.storage) {\n this.docPersistedVersions.set(docId, nextVersion);\n return;\n }\n this.docPersistedVersions.set(docId, nextVersion);\n try {\n await this.storage.save({\n type: \"doc-snapshot\",\n docId,\n snapshot,\n });\n } catch (error) {\n if (previousVersion) {\n this.docPersistedVersions.set(docId, previousVersion);\n } else {\n this.docPersistedVersions.delete(docId);\n }\n throw error;\n }\n }\n\n async updateDocFrontiers(\n docId: string,\n doc: LoroDoc,\n defaultBy: RepoEventBy,\n ): Promise<void> {\n const frontiers = doc.oplogFrontiers();\n const vv = doc.version();\n const existingFrontiers = this.readFrontiersFromFlock(docId);\n let mutated = false;\n const metaFlock = this.metaFlock;\n\n for (const f of frontiers) {\n const current = existingFrontiers.get(f.peer);\n if (current !== f.counter) {\n metaFlock.put([\"f\", docId, f.peer], f.counter);\n mutated = true;\n }\n }\n\n if (mutated) {\n for (const [peer, counter] of existingFrontiers) {\n const docCounterEnd = vv.get(peer as PeerID);\n if (docCounterEnd != null && docCounterEnd > counter) {\n metaFlock.delete([\"f\", docId, peer]);\n }\n }\n }\n\n const by = this.eventBus.resolveEventBy(defaultBy);\n this.eventBus.emit({ kind: \"doc-frontiers\", docId, frontiers, by });\n }\n\n async flushScheduledDocFrontierUpdate(docId: string): Promise<boolean> {\n const pending = this.docFrontierUpdates.get(docId);\n if (!pending) return false;\n clearTimeout(pending.timeout);\n this.docFrontierUpdates.delete(docId);\n this.eventBus.pushEventBy(pending.by);\n try {\n await this.updateDocFrontiers(docId, pending.doc, pending.by);\n } finally {\n this.eventBus.popEventBy();\n }\n return true;\n }\n\n async unloadDoc(docId: string): Promise<void> {\n const doc = this.docs.get(docId);\n if (!doc) return;\n\n // 1. Flush any pending frontier updates\n await this.flushScheduledDocFrontierUpdate(docId);\n\n // 2. Persist the final state\n await this.persistDocUpdate(docId, doc);\n\n // 3. Update frontiers one last time if needed (local changes)\n // We assume \"local\" because we are unloading it from the local repo\n await this.updateDocFrontiers(docId, doc, \"local\");\n\n // 4. Cleanup subscriptions and map entries\n const unsubscribe = this.docSubscriptions.get(docId);\n unsubscribe?.();\n this.docSubscriptions.delete(docId);\n this.docs.delete(docId);\n this.docPersistedVersions.delete(docId);\n }\n\n async dropDoc(docId: string): Promise<void> {\n const pending = this.docFrontierUpdates.get(docId);\n if (pending) {\n clearTimeout(pending.timeout);\n this.docFrontierUpdates.delete(docId);\n }\n\n const unsubscribe = this.docSubscriptions.get(docId);\n unsubscribe?.();\n this.docSubscriptions.delete(docId);\n this.docs.delete(docId);\n this.docPersistedVersions.delete(docId);\n\n if (this.storage?.deleteDoc) {\n await this.storage.deleteDoc(docId);\n }\n }\n\n async flush(): Promise<void> {\n const promises: Promise<void>[] = [];\n for (const [docId, doc] of this.docs) {\n promises.push(\n (async () => {\n await this.persistDocUpdate(docId, doc);\n await this.flushScheduledDocFrontierUpdate(docId);\n })()\n );\n }\n await Promise.all(promises);\n }\n\n async close(): Promise<void> {\n // Flush everything before closing\n await this.flush();\n\n for (const unsubscribe of this.docSubscriptions.values()) {\n try {\n unsubscribe();\n } catch {\n // ignore subscriber errors during shutdown\n }\n }\n this.docSubscriptions.clear();\n this.docFrontierUpdates.clear();\n this.docs.clear();\n this.docPersistedVersions.clear();\n }\n\n private get metaFlock(): Flock {\n return this.getMetaFlock();\n }\n\n private readFrontiersFromFlock(docId: string): Map<string, number> {\n const rows = this.metaFlock.scan({ prefix: [\"f\", docId] });\n const frontiers = new Map<string, number>();\n for (const row of rows) {\n if (!Array.isArray(row.key) || row.key.length < 3) continue;\n const peer = row.key[2];\n const counter = row.value;\n if (typeof peer !== \"string\") continue;\n if (typeof counter !== \"number\" || !Number.isFinite(counter)) continue;\n frontiers.set(peer, counter);\n }\n return frontiers;\n }\n\n private registerDoc(docId: string, doc: LoroDoc): void {\n this.docs.set(docId, doc);\n this.docPersistedVersions.set(docId, doc.version());\n this.ensureDocSubscription(docId, doc);\n }\n\n private ensureDocSubscription(docId: string, doc: LoroDoc): void {\n if (this.docSubscriptions.has(docId)) return;\n const unsubscribe = doc.subscribe((batch: LoroEventBatch) => {\n const stackBy = this.eventBus.resolveEventBy(\"local\");\n const by: RepoEventBy =\n stackBy === \"local\" && batch.by === \"import\" ? \"live\" : stackBy;\n this.onDocEvent(docId, doc, batch, by);\n });\n if (typeof unsubscribe === \"function\") {\n this.docSubscriptions.set(docId, unsubscribe as () => void);\n }\n }\n\n private scheduleDocFrontierUpdate(\n docId: string,\n doc: LoroDoc,\n by: RepoEventBy,\n ): void {\n const existing = this.docFrontierUpdates.get(docId);\n const effectiveBy = existing ? this.mergeRepoEventBy(existing.by, by) : by;\n if (existing) {\n clearTimeout(existing.timeout);\n }\n const delay =\n this.docFrontierDebounceMs > 0 ? this.docFrontierDebounceMs : 0;\n const timeout = setTimeout(\n () => this.runScheduledDocFrontierUpdate(docId),\n delay,\n );\n this.docFrontierUpdates.set(docId, { timeout, doc, by: effectiveBy });\n }\n\n private mergeRepoEventBy(\n current: RepoEventBy,\n next: RepoEventBy,\n ): RepoEventBy {\n if (current === next) return current;\n if (current === \"live\" || next === \"live\") return \"live\";\n if (current === \"sync\" || next === \"sync\") return \"sync\";\n return \"local\";\n }\n\n private runScheduledDocFrontierUpdate(docId: string): void {\n const pending = this.docFrontierUpdates.get(docId);\n if (!pending) return;\n this.docFrontierUpdates.delete(docId);\n this.eventBus.pushEventBy(pending.by);\n void (async () => {\n try {\n await this.updateDocFrontiers(docId, pending.doc, pending.by);\n } finally {\n this.eventBus.popEventBy();\n }\n })().catch(logAsyncError(`doc ${docId} frontier debounce`));\n }\n\n private async materializeDetachedDoc(docId: string): Promise<LoroDoc> {\n const snapshot = await this.exportDocSnapshot(docId);\n if (snapshot) {\n return LoroDoc.fromSnapshot(snapshot);\n }\n return new LoroDoc();\n }\n\n private async exportDocSnapshot(\n docId: string,\n ): Promise<Uint8Array | undefined> {\n const cached = this.docs.get(docId);\n if (cached) {\n return cached.export({ mode: \"snapshot\" });\n }\n if (!this.storage) {\n return undefined;\n }\n const stored = await this.storage.loadDoc(docId);\n return stored?.export({ mode: \"snapshot\" });\n }\n\n private async persistDocUpdate(docId: string, doc: LoroDoc): Promise<void> {\n const previousVersion = this.docPersistedVersions.get(docId);\n const nextVersion = doc.oplogVersion();\n if (!this.storage) {\n this.docPersistedVersions.set(docId, nextVersion);\n return;\n }\n\n if (!previousVersion) {\n await this.persistDoc(docId, doc);\n this.docPersistedVersions.set(docId, nextVersion);\n return;\n }\n\n if (previousVersion.compare(nextVersion) === 0) {\n return;\n }\n\n const update = doc.export({ mode: \"update\", from: previousVersion });\n this.docPersistedVersions.set(docId, nextVersion);\n try {\n await this.storage.save({\n type: \"doc-update\",\n docId,\n update,\n });\n } catch (error) {\n this.docPersistedVersions.set(docId, previousVersion);\n throw error;\n }\n }\n\n private onDocEvent(\n docId: string,\n doc: LoroDoc,\n _batch: LoroEventBatch,\n by: RepoEventBy,\n ): void {\n void (async () => {\n const persist = this.persistDocUpdate(docId, doc);\n if (by === \"local\") {\n this.scheduleDocFrontierUpdate(docId, doc, by);\n await persist;\n return;\n }\n\n const flushed = this.flushScheduledDocFrontierUpdate(docId);\n const updated = (async () => {\n this.eventBus.pushEventBy(by);\n try {\n await this.updateDocFrontiers(docId, doc, by);\n } finally {\n this.eventBus.popEventBy();\n }\n })();\n await Promise.all([persist, flushed, updated]);\n })().catch(logAsyncError(`doc ${docId} event processing`));\n }\n}\n","import { Frontiers, LoroDoc, VersionVector } from \"loro-crdt\";\n\nimport type {\n AssetContent,\n JsonObject,\n JsonValue,\n ListDocQuery,\n RepoAssetMetadata,\n} from \"./types\";\n\ntype PossibleCrypto = {\n subtle?: {\n digest: (\n algorithm: string,\n data: ArrayBufferView | ArrayBuffer,\n ) => Promise<ArrayBuffer>;\n };\n};\n\nexport async function streamToUint8Array(\n stream: ReadableStream<Uint8Array>,\n): Promise<Uint8Array> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) {\n chunks.push(value);\n total += value.byteLength;\n }\n }\n const buffer = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n buffer.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return buffer;\n}\n\nexport async function assetContentToUint8Array(\n content: AssetContent,\n): Promise<Uint8Array> {\n if (content instanceof Uint8Array) {\n return content;\n }\n if (ArrayBuffer.isView(content)) {\n return new Uint8Array(\n content.buffer.slice(\n content.byteOffset,\n content.byteOffset + content.byteLength,\n ),\n );\n }\n if (typeof Blob !== \"undefined\" && content instanceof Blob) {\n return new Uint8Array(await content.arrayBuffer());\n }\n if (\n typeof ReadableStream !== \"undefined\" &&\n content instanceof ReadableStream\n ) {\n return streamToUint8Array(content);\n }\n throw new TypeError(\"Unsupported asset content type\");\n}\n\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\n \"\",\n );\n}\n\nexport async function computeSha256(bytes: Uint8Array): Promise<string> {\n const globalCrypto = (globalThis as { crypto?: PossibleCrypto }).crypto;\n if (\n globalCrypto?.subtle &&\n typeof globalCrypto.subtle.digest === \"function\"\n ) {\n const digest = await globalCrypto.subtle.digest(\"SHA-256\", bytes);\n return bytesToHex(new Uint8Array(digest));\n }\n try {\n const { createHash } = await import(\"node:crypto\");\n const hash = createHash(\"sha256\");\n hash.update(bytes);\n return hash.digest(\"hex\");\n } catch {\n throw new Error(\"SHA-256 digest is not available in this environment\");\n }\n}\n\nexport function cloneJsonValue(value: unknown): JsonValue | undefined {\n if (value === null) return null;\n if (typeof value === \"string\" || typeof value === \"boolean\") {\n return value;\n }\n if (typeof value === \"number\") {\n return Number.isFinite(value) ? value : undefined;\n }\n if (Array.isArray(value)) {\n const arr: JsonValue[] = [];\n for (const entry of value) {\n const cloned = cloneJsonValue(entry);\n if (cloned !== undefined) {\n arr.push(cloned);\n }\n }\n return arr;\n }\n if (value && typeof value === \"object\") {\n const input = value as Record<string, unknown>;\n const obj: JsonObject = {};\n for (const [key, entry] of Object.entries(input)) {\n const cloned = cloneJsonValue(entry);\n if (cloned !== undefined) {\n obj[key] = cloned;\n }\n }\n return obj;\n }\n return undefined;\n}\n\nexport function cloneJsonObject(value: JsonObject): JsonObject {\n const cloned = cloneJsonValue(value);\n if (cloned && typeof cloned === \"object\" && !Array.isArray(cloned)) {\n return cloned;\n }\n return {};\n}\n\nexport function asJsonObject(value: unknown): JsonObject | undefined {\n const cloned = cloneJsonValue(value);\n if (cloned && typeof cloned === \"object\" && !Array.isArray(cloned)) {\n return cloned;\n }\n return undefined;\n}\n\nfunction isJsonObjectValue(value: JsonValue): value is JsonObject {\n return Boolean(value && typeof value === \"object\" && !Array.isArray(value));\n}\n\nexport function stableStringify(value: JsonValue): string {\n if (value === null) return \"null\";\n if (typeof value === \"string\") return JSON.stringify(value);\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return JSON.stringify(value);\n }\n if (Array.isArray(value)) {\n return `[${value.map(stableStringify).join(\",\")}]`;\n }\n if (!isJsonObjectValue(value)) {\n return \"null\";\n }\n const entries = Object.keys(value)\n .sort()\n .map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`);\n return `{${entries.join(\",\")}}`;\n}\n\nexport function jsonEquals(a?: JsonValue, b?: JsonValue): boolean {\n if (a === undefined && b === undefined) return true;\n if (a === undefined || b === undefined) return false;\n return stableStringify(a) === stableStringify(b);\n}\n\nexport function diffJsonObjects(\n previous: JsonObject | undefined,\n next: JsonObject,\n): JsonObject {\n const patch: JsonObject = {};\n const keys = new Set<string>();\n if (previous) {\n for (const key of Object.keys(previous)) keys.add(key);\n }\n for (const key of Object.keys(next)) keys.add(key);\n for (const key of keys) {\n const prevValue = previous ? previous[key] : undefined;\n const nextValue = next[key];\n if (!jsonEquals(prevValue, nextValue)) {\n if (nextValue === undefined && previous && key in previous) {\n patch[key] = null;\n continue;\n }\n const cloned = cloneJsonValue(nextValue);\n if (cloned !== undefined) {\n patch[key] = cloned;\n }\n }\n }\n return patch;\n}\n\nexport function assetMetaToJson(meta: RepoAssetMetadata): JsonObject {\n const json: JsonObject = {\n assetId: meta.assetId,\n size: meta.size,\n createdAt: meta.createdAt,\n };\n if (meta.mime !== undefined) json.mime = meta.mime;\n if (meta.policy !== undefined) json.policy = meta.policy;\n if (meta.tag !== undefined) json.tag = meta.tag;\n return json;\n}\n\nexport function assetMetaFromJson(\n value: unknown,\n): RepoAssetMetadata | undefined {\n const obj = asJsonObject(value);\n if (!obj) return undefined;\n const assetId = typeof obj.assetId === \"string\" ? obj.assetId : undefined;\n if (!assetId) return undefined;\n const size = typeof obj.size === \"number\" ? obj.size : undefined;\n const createdAt =\n typeof obj.createdAt === \"number\" ? obj.createdAt : undefined;\n if (size === undefined || createdAt === undefined) return undefined;\n const meta: RepoAssetMetadata = {\n assetId,\n size,\n createdAt,\n ...(typeof obj.mime === \"string\" ? { mime: obj.mime } : {}),\n ...(typeof obj.policy === \"string\" ? { policy: obj.policy } : {}),\n ...(typeof obj.tag === \"string\" ? { tag: obj.tag } : {}),\n };\n return meta;\n}\n\nexport function assetMetadataEqual(\n a: RepoAssetMetadata | undefined,\n b: RepoAssetMetadata | undefined,\n): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n return (\n stableStringify(assetMetaToJson(a)) === stableStringify(assetMetaToJson(b))\n );\n}\n\nexport function cloneRepoAssetMetadata(\n meta: RepoAssetMetadata,\n): RepoAssetMetadata {\n return {\n assetId: meta.assetId,\n size: meta.size,\n createdAt: meta.createdAt,\n ...(meta.mime !== undefined ? { mime: meta.mime } : {}),\n ...(meta.policy !== undefined ? { policy: meta.policy } : {}),\n ...(meta.tag !== undefined ? { tag: meta.tag } : {}),\n };\n}\n\nexport function toReadableStream(\n bytes: Uint8Array,\n): ReadableStream<Uint8Array> {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(bytes);\n controller.close();\n },\n });\n}\n\nexport function computeVersionVector(doc: LoroDoc): VersionVector {\n const candidate = doc as unknown as {\n frontiers?: () => unknown;\n frontiersToVV?: (frontiers: unknown) => VersionVector;\n version?: () => VersionVector;\n };\n if (\n typeof candidate.frontiers === \"function\" &&\n typeof candidate.frontiersToVV === \"function\"\n ) {\n const frontiers = candidate.frontiers();\n return candidate.frontiersToVV(frontiers);\n }\n if (typeof candidate.version === \"function\") {\n return candidate.version();\n }\n return {} as VersionVector;\n}\n\nexport function emptyFrontiers(): Frontiers {\n return [];\n}\n\nexport function versionVectorToJson(vv: VersionVector): JsonObject {\n const map = vv.toJSON();\n const record: JsonObject = {};\n if (map instanceof Map) {\n const entries = Array.from(map.entries()).sort(([a], [b]) =>\n String(a).localeCompare(String(b)),\n );\n for (const [peer, counter] of entries) {\n if (typeof counter !== \"number\" || !Number.isFinite(counter)) continue;\n const key = typeof peer === \"string\" ? peer : JSON.stringify(peer);\n record[key] = counter;\n }\n }\n return record;\n}\n\nexport function canonicalizeVersionVector(vv: VersionVector): {\n json: JsonObject;\n key: string;\n} {\n const json = versionVectorToJson(vv);\n return { json, key: stableStringify(json) };\n}\n\nexport function canonicalizeFrontiers(frontiers: Frontiers): {\n json: JsonValue;\n key: string;\n} {\n const normalized = [...frontiers]\n .map((frontier) => {\n const peer = (frontier as { peer?: unknown }).peer;\n const counter = (frontier as { counter?: unknown }).counter;\n if (\n typeof peer !== \"string\" ||\n typeof counter !== \"number\" ||\n !Number.isFinite(counter)\n ) {\n return undefined;\n }\n return { peer, counter };\n })\n .filter(\n (\n entry,\n ): entry is {\n peer: string;\n counter: number;\n } => Boolean(entry),\n )\n .sort((a, b) => {\n if (a.peer < b.peer) return -1;\n if (a.peer > b.peer) return 1;\n return a.counter - b.counter;\n });\n const json = normalized.map((entry) => ({\n peer: entry.peer,\n counter: entry.counter,\n }));\n return { json, key: stableStringify(json) };\n}\n\nexport function includesFrontiers(\n vv: VersionVector,\n frontiers: Frontiers,\n): boolean {\n for (const frontier of frontiers) {\n const peer = frontier.peer;\n const counter = frontier.counter;\n if (typeof peer !== \"string\" || typeof counter !== \"number\") continue;\n const local = vv.get(peer) ?? 0;\n // Version Vector stores the *next* expected op counter (exclusive).\n // Frontiers store the *last* applied op counter (inclusive).\n // If local <= counter, it means the Version Vector has not yet reached (included) the Frontier op.\n if (local <= counter) return false;\n }\n return true;\n}\n\nexport function matchesQuery(\n docId: string,\n _metadata: JsonObject,\n query?: ListDocQuery,\n): boolean {\n if (!query) return true;\n if (query.prefix && !docId.startsWith(query.prefix)) return false;\n if (query.start && docId < query.start) return false;\n if (query.end && docId > query.end) return false;\n return true;\n}\n","import { Flock } from \"@loro-dev/flock\";\nimport type { ScanOptions } from \"@loro-dev/flock\";\n\nimport type {\n JsonObject,\n JsonValue,\n ListDocQuery,\n RepoDocMeta,\n RepoDocSnapshot,\n RepoEventBy,\n} from \"../types\";\nimport {\n asJsonObject,\n cloneJsonObject,\n cloneJsonValue,\n diffJsonObjects,\n jsonEquals,\n matchesQuery,\n} from \"../utils\";\nimport { RepoEventBus } from \"./event-bus\";\nimport type { RepoState } from \"./repo-state\";\n\ninterface MetadataManagerOptions<Meta extends JsonObject> {\n readonly getMetaFlock: () => Flock;\n readonly eventBus: RepoEventBus<Meta>;\n readonly state: RepoState;\n}\n\nexport class MetadataManager<Meta extends JsonObject> {\n private readonly getMetaFlock: () => Flock;\n private readonly eventBus: RepoEventBus<Meta>;\n private readonly state: RepoState;\n\n constructor(options: MetadataManagerOptions<Meta>) {\n this.getMetaFlock = options.getMetaFlock;\n this.eventBus = options.eventBus;\n this.state = options.state;\n }\n\n getDocIds(): string[] {\n const ids = new Set<string>(this.state.metadata.keys());\n const tombstoneRows = this.metaFlock.scan({ prefix: [\"ts\"] });\n for (const row of tombstoneRows) {\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const docId = row.key[1];\n if (typeof docId === \"string\") {\n ids.add(docId);\n }\n }\n return Array.from(ids);\n }\n\n entries(): IterableIterator<[string, JsonObject]> {\n return this.state.metadata.entries();\n }\n\n get(docId: string): RepoDocSnapshot<Meta> | undefined {\n const metadata = this.state.metadata.get(docId);\n const deletedAtMs = this.readDeletedAtFromFlock(docId);\n if (\n (deletedAtMs === undefined && this.isDocKeyspaceEmpty(docId)) ||\n !metadata ||\n (deletedAtMs === undefined && this.isEmpty(metadata))\n ) {\n if (deletedAtMs === undefined) return undefined;\n return { meta: {} as Meta, deletedAtMs };\n }\n return {\n meta: cloneJsonObject(metadata) as Meta,\n deletedAtMs,\n };\n }\n\n getDeletedAtMs(docId: string): number | undefined {\n return this.readDeletedAtFromFlock(docId);\n }\n\n markDeleted(docId: string, deletedAtMs: number): void {\n this.metaFlock.put([\"ts\", docId], deletedAtMs);\n this.emitSoftDeleted(docId, deletedAtMs, \"local\");\n }\n\n clearDeleted(docId: string): void {\n const existing = this.readDeletedAtFromFlock(docId);\n this.metaFlock.delete([\"ts\", docId]);\n if (existing !== undefined) {\n this.emitSoftDeleted(docId, undefined, \"local\");\n }\n }\n\n // List docs with optional prefix/range/limit filters. We walk two keyspaces:\n // m/* (metadata rows) and ts/* (tombstones) so deleted-but-empty docs still\n // show up with their deletedAtMs. We dedupe via `seen` because a doc may have\n // both metadata and tombstone rows. Empty keyspaces without tombstones are\n // skipped to avoid reviving purged docs.\n listDoc(query?: ListDocQuery): RepoDocMeta<Meta>[] {\n if (query?.limit !== undefined && query.limit <= 0) {\n return [];\n }\n\n const { startKey, endKey } = this.computeDocRangeKeys(query);\n if (startKey && endKey && startKey >= endKey) {\n return [];\n }\n\n const scanOptions: ScanOptions = { prefix: [\"m\"] };\n if (startKey) {\n scanOptions.start = { kind: \"inclusive\", key: [\"m\", startKey] };\n }\n if (endKey) {\n scanOptions.end = { kind: \"exclusive\", key: [\"m\", endKey] };\n }\n\n const seen = new Set<string>();\n const entries: RepoDocMeta<Meta>[] = [];\n\n // Guard that filters duplicates and enforces liveness rules.\n const pushDoc = (docId: string): void => {\n if (seen.has(docId)) return;\n const metadata = this.state.metadata.get(docId);\n const deletedAtMs = this.readDeletedAtFromFlock(docId);\n if (\n (deletedAtMs === undefined && this.isDocKeyspaceEmpty(docId)) ||\n (this.isEmpty(metadata) && deletedAtMs === undefined)\n )\n return;\n if (!matchesQuery(docId, metadata ?? {}, query)) return;\n seen.add(docId);\n entries.push({\n docId,\n deletedAtMs,\n meta: cloneJsonObject(metadata ?? {}) as Meta,\n });\n };\n\n const rows = this.metaFlock.scan(scanOptions);\n // First pass: scan metadata rows (m/*) honoring range/prefix.\n for (const row of rows) {\n if (query?.limit !== undefined && entries.length >= query.limit) {\n break;\n }\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const docId = row.key[1];\n if (typeof docId !== \"string\") continue;\n pushDoc(docId);\n if (query?.limit !== undefined && entries.length >= query.limit) {\n break;\n }\n }\n\n // Second pass: include tombstoned docs that have no metadata rows.\n if (query?.limit === undefined || entries.length < query.limit) {\n const tsScan: ScanOptions = { prefix: [\"ts\"] };\n if (startKey) {\n tsScan.start = { kind: \"inclusive\", key: [\"ts\", startKey] };\n }\n if (endKey) {\n tsScan.end = { kind: \"exclusive\", key: [\"ts\", endKey] };\n }\n const tsRows = this.metaFlock.scan(tsScan);\n for (const row of tsRows) {\n if (query?.limit !== undefined && entries.length >= query.limit) {\n break;\n }\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const docId = row.key[1];\n if (typeof docId !== \"string\") continue;\n pushDoc(docId);\n }\n }\n\n return entries;\n }\n\n async upsert(docId: string, patch: Partial<Meta>): Promise<void> {\n const base = this.state.metadata.get(docId);\n const next: JsonObject = base ? cloneJsonObject(base) : {};\n const outPatch: JsonObject = {};\n let changed = false;\n\n const patchObject = patch as unknown as JsonObject;\n for (const key of Object.keys(patchObject)) {\n const rawValue = patchObject[key];\n if (rawValue === undefined) continue;\n const previousValue = base\n ? (base[key] as JsonValue | undefined)\n : undefined;\n if (jsonEquals(previousValue, rawValue)) {\n continue;\n }\n\n this.metaFlock.put([\"m\", docId, key], rawValue);\n next[key] = rawValue;\n outPatch[key] = rawValue;\n changed = true;\n }\n\n if (!changed) {\n if (!this.state.metadata.has(docId)) {\n this.state.metadata.set(docId, next);\n }\n return;\n }\n\n this.state.metadata.set(docId, next);\n this.eventBus.emit({\n kind: \"doc-metadata\",\n docId,\n patch: outPatch as Partial<Meta>,\n by: \"local\",\n });\n }\n\n refreshFromFlock(docId: string, by: RepoEventBy): void {\n const previousMeta = this.state.metadata.get(docId);\n const nextMeta = this.readDocMetadataFromFlock(docId);\n\n if (!nextMeta) {\n if (previousMeta) {\n this.state.metadata.delete(docId);\n this.eventBus.emit({\n kind: \"doc-metadata\",\n docId,\n patch: {} as Partial<Meta>,\n by,\n });\n }\n return;\n }\n\n this.state.metadata.set(docId, nextMeta);\n const patch = diffJsonObjects(previousMeta, nextMeta);\n if (!previousMeta || Object.keys(patch).length > 0) {\n this.eventBus.emit({\n kind: \"doc-metadata\",\n docId,\n patch: patch as Partial<Meta>,\n by,\n });\n }\n }\n\n replaceAll(nextMetadata: Map<string, JsonObject>, by: RepoEventBy): void {\n const prevMetadata = new Map(this.state.metadata);\n this.state.metadata.clear();\n for (const [docId, meta] of nextMetadata) {\n this.state.metadata.set(docId, meta);\n }\n\n const docIds = new Set<string>([\n ...prevMetadata.keys(),\n ...nextMetadata.keys(),\n ]);\n\n for (const docId of docIds) {\n const previous = prevMetadata.get(docId);\n const current = nextMetadata.get(docId);\n if (!current) {\n if (previous) {\n const patch: JsonObject = {};\n for (const key of Object.keys(previous)) {\n patch[key] = null;\n }\n this.eventBus.emit({\n kind: \"doc-metadata\",\n docId,\n patch: patch as Partial<Meta>,\n by,\n });\n }\n continue;\n }\n const patch = diffJsonObjects(previous, current);\n if (!previous || Object.keys(patch).length > 0) {\n this.eventBus.emit({\n kind: \"doc-metadata\",\n docId,\n patch: patch as Partial<Meta>,\n by,\n });\n }\n }\n }\n\n clear(): void {\n this.state.metadata.clear();\n }\n\n purgeMetadata(docId: string, by: RepoEventBy): void {\n const existed = this.state.metadata.delete(docId);\n if (existed) {\n this.eventBus.emit({\n kind: \"doc-metadata\",\n docId,\n patch: {} as Partial<Meta>,\n by,\n });\n }\n }\n\n emitSoftDeleted(\n docId: string,\n deletedAtMs: number | undefined,\n by: RepoEventBy,\n ): void {\n this.eventBus.emit({\n kind: \"doc-soft-deleted\",\n docId,\n deletedAtMs,\n by,\n });\n }\n\n private computeDocRangeKeys(query?: ListDocQuery): {\n startKey?: string;\n endKey?: string;\n } {\n if (!query) {\n return {};\n }\n\n const prefix =\n query.prefix && query.prefix.length > 0 ? query.prefix : undefined;\n let startKey = query.start;\n if (prefix) {\n startKey = !startKey || prefix > startKey ? prefix : startKey;\n }\n\n let endKey = query.end;\n const prefixEnd = this.nextLexicographicString(prefix);\n if (prefixEnd) {\n endKey = !endKey || prefixEnd < endKey ? prefixEnd : endKey;\n }\n\n return { startKey, endKey };\n }\n\n private nextLexicographicString(value?: string): string | undefined {\n if (!value) return undefined;\n for (let i = value.length - 1; i >= 0; i -= 1) {\n const code = value.charCodeAt(i);\n if (code < 0xffff) {\n return `${value.slice(0, i)}${String.fromCharCode(code + 1)}`;\n }\n }\n return undefined;\n }\n\n private readDocMetadataFromFlock(docId: string): JsonObject | undefined {\n const rows = this.metaFlock.scan({ prefix: [\"m\", docId] });\n if (!rows.length) return undefined;\n const docMeta: JsonObject = {};\n let populated = false;\n let sawRow = false;\n\n for (const row of rows) {\n sawRow = true;\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n if (row.key.length === 2) {\n const obj = asJsonObject(row.value);\n if (!obj) continue;\n for (const [field, value] of Object.entries(obj)) {\n const cloned = cloneJsonValue(value);\n if (cloned !== undefined) {\n docMeta[field] = cloned;\n populated = true;\n }\n }\n continue;\n }\n const fieldKey = row.key[2];\n if (typeof fieldKey !== \"string\") continue;\n const jsonValue = cloneJsonValue(row.value);\n if (jsonValue === undefined) continue;\n docMeta[fieldKey] = jsonValue;\n populated = true;\n }\n if (populated) return docMeta;\n if (sawRow) return {};\n return undefined;\n }\n\n private readDeletedAtFromFlock(docId: string): number | undefined {\n const raw = this.metaFlock.get([\"ts\", docId]);\n if (typeof raw === \"number\" && Number.isFinite(raw)) {\n return raw;\n }\n return undefined;\n }\n\n private isEmpty(metadata?: JsonObject): boolean {\n return !metadata || Object.keys(metadata).length === 0;\n }\n\n private isDocKeyspaceEmpty(docId: string): boolean {\n if (this.state.metadata.has(docId)) return false;\n const hasTruthyKey = (prefix: [string, string]): boolean => {\n const rows = this.metaFlock.scan({ prefix });\n for (const row of rows) {\n const value = row.value;\n const truthy = value !== undefined;\n if (truthy) return true;\n }\n return false;\n };\n\n if (this.metaFlock.get([\"ts\", docId]) !== undefined) return false;\n if (hasTruthyKey([\"m\", docId])) return false;\n if (hasTruthyKey([\"f\", docId])) return false;\n if (hasTruthyKey([\"ld\", docId])) return false;\n return true;\n }\n\n private get metaFlock(): Flock {\n return this.getMetaFlock();\n }\n}\n","import { Flock } from \"@loro-dev/flock\";\n\nimport type {\n AssetDownload,\n AssetId,\n AssetTransportAdapter,\n GarbageCollectionOptions,\n JsonObject,\n LinkAssetOptions,\n RepoAssetMetadata,\n RepoEventBy,\n StorageAdapter,\n UploadAssetOptions,\n} from \"../types\";\nimport {\n assetContentToUint8Array,\n assetMetaFromJson,\n assetMetaToJson,\n assetMetadataEqual,\n cloneRepoAssetMetadata,\n computeSha256,\n streamToUint8Array,\n toReadableStream,\n} from \"../utils\";\nimport { RepoEventBus } from \"./event-bus\";\nimport { logAsyncError } from \"./logging\";\nimport type { RepoState, AssetRecord, OrphanedAssetRecord } from \"./repo-state\";\n\ninterface AssetManagerOptions<Meta extends JsonObject> {\n readonly storage?: StorageAdapter;\n readonly assetTransport?: AssetTransportAdapter;\n readonly getMetaFlock: () => Flock;\n readonly eventBus: RepoEventBus<Meta>;\n readonly state: RepoState;\n}\n\nexport class AssetManager<Meta extends JsonObject> {\n private readonly storage?: StorageAdapter;\n private readonly assetTransport?: AssetTransportAdapter;\n private readonly getMetaFlock: () => Flock;\n private readonly eventBus: RepoEventBus<Meta>;\n private readonly state: RepoState;\n private get docAssets(): Map<string, Map<AssetId, RepoAssetMetadata>> {\n return this.state.docAssets;\n }\n private get assets(): Map<AssetId, AssetRecord> {\n return this.state.assets;\n }\n private get orphanedAssets(): Map<AssetId, OrphanedAssetRecord> {\n return this.state.orphanedAssets;\n }\n private get assetToDocRefs(): Map<AssetId, Set<string>> {\n return this.state.assetToDocRefs;\n }\n\n constructor(options: AssetManagerOptions<Meta>) {\n this.storage = options.storage;\n this.assetTransport = options.assetTransport;\n this.getMetaFlock = options.getMetaFlock;\n this.eventBus = options.eventBus;\n this.state = options.state;\n }\n\n async uploadAsset(params: UploadAssetOptions): Promise<AssetId> {\n const bytes = await assetContentToUint8Array(params.content);\n const assetId = await computeSha256(bytes);\n\n if (params.assetId && params.assetId !== assetId) {\n throw new Error(\"Provided assetId does not match content digest\");\n }\n\n const existing = this.assets.get(assetId);\n if (existing) {\n // Ensure we have the data stored locally if possible\n if (this.storage) {\n const stored = await this.storage.loadAsset(assetId);\n if (!stored) {\n await this.storage.save({\n type: \"asset\",\n assetId,\n data: bytes.slice(),\n });\n }\n }\n\n let metadataMutated = false;\n const metadata = { ...existing.metadata };\n if (params.mime && metadata.mime !== params.mime) {\n metadata.mime = params.mime;\n metadataMutated = true;\n }\n if (params.policy && metadata.policy !== params.policy) {\n metadata.policy = params.policy;\n metadataMutated = true;\n }\n if (params.tag && metadata.tag !== params.tag) {\n metadata.tag = params.tag;\n metadataMutated = true;\n }\n if (\n params.createdAt !== undefined &&\n metadata.createdAt !== params.createdAt\n ) {\n metadata.createdAt = params.createdAt;\n metadataMutated = true;\n }\n if (metadataMutated) {\n existing.metadata = metadata;\n this.metaFlock.put([\"a\", assetId], assetMetaToJson(metadata));\n this.eventBus.emit({\n kind: \"asset-metadata\",\n asset: this.createAssetDownload(assetId, metadata, bytes),\n by: \"local\",\n });\n }\n this.rememberAsset(existing.metadata);\n return assetId;\n }\n\n const metadata: RepoAssetMetadata = {\n assetId,\n size: bytes.byteLength,\n createdAt: params.createdAt ?? Date.now(),\n ...(params.mime ? { mime: params.mime } : {}),\n ...(params.policy ? { policy: params.policy } : {}),\n ...(params.tag ? { tag: params.tag } : {}),\n };\n\n if (this.assetTransport) {\n let shouldUpload = true;\n if (typeof this.assetTransport.ensure === \"function\") {\n shouldUpload = !(await this.assetTransport.ensure(assetId));\n }\n if (shouldUpload) {\n await this.assetTransport.upload(assetId, bytes, {\n mime: params.mime,\n policy: params.policy,\n tag: params.tag,\n });\n }\n }\n\n const storedBytes = bytes.slice();\n\n if (this.storage) {\n await this.storage.save({\n type: \"asset\",\n assetId,\n data: storedBytes.slice(),\n });\n }\n\n this.rememberAsset(metadata);\n this.markAssetAsOrphan(assetId, metadata); // Initially orphan until linked\n\n this.updateDocAssetMetadata(assetId, metadata);\n\n this.metaFlock.put([\"a\", assetId], assetMetaToJson(metadata));\n this.eventBus.emit({\n kind: \"asset-metadata\",\n asset: this.createAssetDownload(assetId, metadata, storedBytes),\n by: \"local\",\n });\n\n return assetId;\n }\n\n async linkAsset(docId: string, params: LinkAssetOptions): Promise<AssetId> {\n const bytes = await assetContentToUint8Array(params.content);\n const assetId = await computeSha256(bytes);\n\n if (params.assetId && params.assetId !== assetId) {\n throw new Error(\"Provided assetId does not match content digest\");\n }\n\n let metadata: RepoAssetMetadata;\n let storedBytes: Uint8Array | undefined;\n let created = false;\n\n const existing = this.assets.get(assetId);\n if (existing) {\n metadata = existing.metadata;\n // Ensure storage\n if (this.storage) {\n const stored = await this.storage.loadAsset(assetId);\n if (!stored) {\n await this.storage.save({\n type: \"asset\",\n assetId,\n data: bytes.slice(),\n });\n }\n }\n\n let nextMetadata = metadata;\n let metadataMutated = false;\n if (params.mime && params.mime !== nextMetadata.mime) {\n nextMetadata = { ...nextMetadata, mime: params.mime };\n metadataMutated = true;\n }\n if (params.policy && params.policy !== nextMetadata.policy) {\n nextMetadata = { ...nextMetadata, policy: params.policy };\n metadataMutated = true;\n }\n if (params.tag && params.tag !== nextMetadata.tag) {\n nextMetadata = { ...nextMetadata, tag: params.tag };\n metadataMutated = true;\n }\n if (\n params.createdAt !== undefined &&\n params.createdAt !== nextMetadata.createdAt\n ) {\n nextMetadata = { ...nextMetadata, createdAt: params.createdAt };\n metadataMutated = true;\n }\n if (metadataMutated) {\n existing.metadata = nextMetadata;\n metadata = nextMetadata;\n this.metaFlock.put([\"a\", assetId], assetMetaToJson(metadata));\n this.eventBus.emit({\n kind: \"asset-metadata\",\n asset: this.createAssetDownload(assetId, metadata, bytes),\n by: \"local\",\n });\n } else {\n metadata = existing.metadata;\n }\n storedBytes = bytes;\n this.rememberAsset(metadata);\n } else {\n metadata = {\n assetId,\n size: bytes.byteLength,\n createdAt: params.createdAt ?? Date.now(),\n ...(params.mime ? { mime: params.mime } : {}),\n ...(params.policy ? { policy: params.policy } : {}),\n ...(params.tag ? { tag: params.tag } : {}),\n };\n\n if (this.assetTransport) {\n let shouldUpload = true;\n if (typeof this.assetTransport.ensure === \"function\") {\n shouldUpload = !(await this.assetTransport.ensure(assetId));\n }\n if (shouldUpload) {\n await this.assetTransport.upload(assetId, bytes, {\n mime: params.mime,\n policy: params.policy,\n tag: params.tag,\n });\n }\n }\n\n storedBytes = bytes.slice();\n\n if (this.storage) {\n await this.storage.save({\n type: \"asset\",\n assetId,\n data: storedBytes.slice(),\n });\n }\n\n this.rememberAsset(metadata);\n\n this.updateDocAssetMetadata(assetId, metadata);\n\n this.metaFlock.put([\"a\", assetId], assetMetaToJson(metadata));\n created = true;\n }\n\n const mapping =\n this.docAssets.get(docId) ?? new Map<AssetId, RepoAssetMetadata>();\n mapping.set(assetId, metadata);\n this.docAssets.set(docId, mapping);\n\n this.addDocReference(assetId, docId);\n\n this.metaFlock.put([\"ld\", docId, assetId], true);\n\n this.eventBus.emit({ kind: \"asset-link\", docId, assetId, by: \"local\" });\n if (created) {\n this.eventBus.emit({\n kind: \"asset-metadata\",\n asset: this.createAssetDownload(\n assetId,\n metadata,\n storedBytes ?? bytes,\n ),\n by: \"local\",\n });\n }\n return assetId;\n }\n\n async unlinkAsset(docId: string, assetId: AssetId): Promise<void> {\n const mapping = this.docAssets.get(docId);\n if (!mapping || !mapping.has(assetId)) return;\n mapping.delete(assetId);\n if (mapping.size === 0) {\n this.docAssets.delete(docId);\n }\n\n this.metaFlock.delete([\"ld\", docId, assetId]);\n\n this.removeDocAssetReference(assetId, docId);\n // Do NOT delete global metadata [\"a\", assetId] automatically.\n // It will be cleaned up if we implement a global GC strategy,\n // or simply ignored. The binary is managed via orphan references.\n\n this.eventBus.emit({ kind: \"asset-unlink\", docId, assetId, by: \"local\" });\n }\n\n purgeDocLinks(docId: string, by: RepoEventBy): void {\n const keys = Array.from(\n this.metaFlock.scan({ prefix: [\"ld\", docId] }),\n (row) => row.key,\n );\n for (const key of keys) {\n this.metaFlock.delete(key);\n }\n this.refreshDocAssetsEntry(docId, by);\n }\n\n async listAssets(docId: string): Promise<RepoAssetMetadata[]> {\n const mapping = this.docAssets.get(docId);\n if (!mapping) return [];\n return Array.from(mapping.values()).map((asset) => ({ ...asset }));\n }\n\n async ensureAsset(assetId: AssetId): Promise<AssetDownload> {\n return this.fetchAsset(assetId);\n }\n\n async fetchAsset(assetId: AssetId): Promise<AssetDownload> {\n const { metadata, bytes } = await this.materializeAsset(assetId);\n return this.createAssetDownload(assetId, metadata, bytes);\n }\n\n async gcAssets(options: GarbageCollectionOptions = {}): Promise<number> {\n const { minKeepMs = 0 } = options;\n const now = Date.now();\n let removed = 0;\n for (const [assetId, orphan] of Array.from(this.orphanedAssets.entries())) {\n if (now - orphan.deletedAt < minKeepMs) {\n continue;\n }\n this.orphanedAssets.delete(assetId);\n if (this.storage?.deleteAsset) {\n try {\n await this.storage.deleteAsset(assetId);\n } catch (error) {\n logAsyncError(`asset ${assetId} delete`)(error);\n }\n }\n removed += 1;\n }\n return removed;\n }\n\n refreshDocAssetsEntry(docId: string, by: RepoEventBy): void {\n const mapping = this.readDocAssetsFromFlock(docId);\n const previous = this.docAssets.get(docId);\n\n if (!mapping.size) {\n if (previous?.size) {\n this.docAssets.delete(docId);\n for (const assetId of previous.keys()) {\n this.removeDocAssetReference(assetId, docId);\n this.eventBus.emit({ kind: \"asset-unlink\", docId, assetId, by });\n }\n }\n return;\n }\n\n this.docAssets.set(docId, mapping);\n\n const removed: AssetId[] = [];\n if (previous) {\n for (const assetId of previous.keys()) {\n if (!mapping.has(assetId)) {\n removed.push(assetId);\n }\n }\n }\n\n for (const assetId of removed) {\n this.removeDocAssetReference(assetId, docId);\n this.eventBus.emit({ kind: \"asset-unlink\", docId, assetId, by });\n }\n\n for (const assetId of mapping.keys()) {\n const isNew = !previous || !previous.has(assetId);\n this.addDocReference(assetId, docId);\n if (isNew) {\n this.eventBus.emit({ kind: \"asset-link\", docId, assetId, by });\n }\n }\n }\n\n refreshAssetMetadataEntry(assetId: AssetId, by: RepoEventBy): void {\n const previous = this.assets.get(assetId);\n const raw = this.metaFlock.get([\"a\", assetId]);\n const metadata = assetMetaFromJson(raw);\n\n if (!metadata) {\n this.handleAssetRemoval(assetId, by);\n return;\n }\n\n this.rememberAsset(metadata);\n\n this.updateDocAssetMetadata(assetId, cloneRepoAssetMetadata(metadata));\n\n if (!previous || !assetMetadataEqual(previous.metadata, metadata)) {\n this.eventBus.emit({\n kind: \"asset-metadata\",\n asset: this.createAssetDownload(assetId, metadata),\n by,\n });\n }\n }\n\n hydrateFromFlock(by: RepoEventBy): void {\n const prevDocAssets = new Map(this.docAssets);\n const prevAssets = new Map(this.assets);\n\n const nextAssets = new Map<AssetId, AssetRecord>();\n const assetRows = this.metaFlock.scan({ prefix: [\"a\"] });\n for (const row of assetRows) {\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const assetId = row.key[1];\n if (typeof assetId !== \"string\") continue;\n const metadata = assetMetaFromJson(row.value);\n if (!metadata) continue;\n // No existing check needed for data preservation\n nextAssets.set(assetId, {\n metadata,\n });\n }\n\n const nextDocAssets = new Map<string, Map<AssetId, RepoAssetMetadata>>();\n const linkRows = this.metaFlock.scan({ prefix: [\"ld\"] });\n for (const row of linkRows) {\n if (!Array.isArray(row.key) || row.key.length < 3) continue;\n const docId = row.key[1];\n const assetId = row.key[2];\n if (typeof docId !== \"string\" || typeof assetId !== \"string\") continue;\n const metadata = nextAssets.get(assetId)?.metadata;\n if (!metadata) continue;\n const mapping =\n nextDocAssets.get(docId) ?? new Map<AssetId, RepoAssetMetadata>();\n mapping.set(assetId, metadata);\n nextDocAssets.set(docId, mapping);\n }\n\n const removedAssets: Array<[AssetId, AssetRecord]> = [];\n for (const [assetId, record] of prevAssets) {\n if (!nextAssets.has(assetId)) {\n removedAssets.push([assetId, record]);\n }\n }\n\n if (removedAssets.length > 0) {\n const now = Date.now();\n for (const [assetId, record] of removedAssets) {\n const existing = this.orphanedAssets.get(assetId);\n const deletedAt = existing?.deletedAt ?? now;\n this.orphanedAssets.set(assetId, {\n metadata: record.metadata,\n deletedAt,\n });\n }\n }\n\n this.docAssets.clear();\n for (const [docId, assets] of nextDocAssets) {\n this.docAssets.set(docId, assets);\n }\n\n this.assetToDocRefs.clear();\n for (const [docId, assets] of nextDocAssets) {\n for (const assetId of assets.keys()) {\n const refs = this.assetToDocRefs.get(assetId) ?? new Set<string>();\n refs.add(docId);\n this.assetToDocRefs.set(assetId, refs);\n }\n }\n\n this.assets.clear();\n for (const record of nextAssets.values()) {\n this.rememberAsset(record.metadata);\n }\n\n // Sync Orphans Logic\n for (const assetId of nextAssets.keys()) {\n const refs = this.assetToDocRefs.get(assetId);\n if (!refs || refs.size === 0) {\n // If refs are 0, it should be an orphan\n if (!this.orphanedAssets.has(assetId)) {\n this.markAssetAsOrphan(assetId, nextAssets.get(assetId)!.metadata);\n }\n } else {\n // If refs > 0, it is NOT an orphan\n this.orphanedAssets.delete(assetId);\n }\n }\n\n for (const [assetId, record] of nextAssets) {\n const previous = prevAssets.get(assetId)?.metadata;\n if (!assetMetadataEqual(previous, record.metadata)) {\n this.eventBus.emit({\n kind: \"asset-metadata\",\n asset: this.createAssetDownload(assetId, record.metadata),\n by,\n });\n }\n }\n\n for (const [docId, assets] of nextDocAssets) {\n const previous = prevDocAssets.get(docId);\n for (const assetId of assets.keys()) {\n if (!previous || !previous.has(assetId)) {\n this.eventBus.emit({ kind: \"asset-link\", docId, assetId, by });\n }\n }\n }\n\n for (const [docId, assets] of prevDocAssets) {\n const current = nextDocAssets.get(docId);\n for (const assetId of assets.keys()) {\n if (!current || !current.has(assetId)) {\n this.eventBus.emit({ kind: \"asset-unlink\", docId, assetId, by });\n }\n }\n }\n }\n\n clear(): void {\n this.docAssets.clear();\n this.assets.clear();\n this.orphanedAssets.clear();\n this.assetToDocRefs.clear();\n }\n\n private readDocAssetsFromFlock(\n docId: string,\n ): Map<AssetId, RepoAssetMetadata> {\n const rows = this.metaFlock.scan({ prefix: [\"ld\", docId] });\n const mapping = new Map<AssetId, RepoAssetMetadata>();\n for (const row of rows) {\n if (!Array.isArray(row.key) || row.key.length < 3) continue;\n const assetId = row.key[2];\n if (typeof assetId !== \"string\") continue;\n const truthy =\n row.value !== undefined && row.value !== null && row.value !== false;\n if (!truthy) continue;\n let metadata = this.assets.get(assetId)?.metadata;\n if (!metadata) {\n metadata = this.readAssetMetadataFromFlock(assetId);\n if (!metadata) continue;\n this.rememberAsset(metadata);\n }\n mapping.set(assetId, cloneRepoAssetMetadata(metadata));\n }\n return mapping;\n }\n\n private readAssetMetadataFromFlock(\n assetId: AssetId,\n ): RepoAssetMetadata | undefined {\n const raw = this.metaFlock.get([\"a\", assetId]);\n return assetMetaFromJson(raw);\n }\n\n private handleAssetRemoval(assetId: AssetId, by: RepoEventBy): void {\n const record = this.assets.get(assetId);\n if (!record) return;\n this.assets.delete(assetId);\n this.markAssetAsOrphan(assetId, record.metadata);\n\n const refs = this.assetToDocRefs.get(assetId);\n if (refs) {\n this.assetToDocRefs.delete(assetId);\n for (const docId of refs) {\n const assets = this.docAssets.get(docId);\n if (assets?.delete(assetId) && assets.size === 0) {\n this.docAssets.delete(docId);\n }\n this.eventBus.emit({ kind: \"asset-unlink\", docId, assetId, by });\n }\n return;\n }\n\n for (const [docId, assets] of this.docAssets) {\n if (assets.delete(assetId)) {\n if (assets.size === 0) {\n this.docAssets.delete(docId);\n }\n this.eventBus.emit({ kind: \"asset-unlink\", docId, assetId, by });\n }\n }\n }\n\n private createAssetDownload(\n assetId: AssetId,\n metadata: RepoAssetMetadata,\n initialBytes?: Uint8Array,\n ): AssetDownload {\n let cached = initialBytes ? initialBytes.slice() : undefined;\n return {\n assetId,\n size: metadata.size,\n createdAt: metadata.createdAt,\n mime: metadata.mime,\n policy: metadata.policy,\n tag: metadata.tag,\n content: async () => {\n if (!cached) {\n const result = await this.materializeAsset(assetId);\n cached = result.bytes.slice();\n }\n return toReadableStream(cached.slice());\n },\n };\n }\n\n private async materializeAsset(\n assetId: AssetId,\n ): Promise<{ metadata: RepoAssetMetadata; bytes: Uint8Array }> {\n const record = this.assets.get(assetId);\n \n // Removed in-memory data check\n \n if (record && this.storage) {\n const stored = await this.storage.loadAsset(assetId);\n if (stored) {\n return { metadata: record.metadata, bytes: stored };\n }\n }\n\n if (!record && this.storage) {\n const stored = await this.storage.loadAsset(assetId);\n if (stored) {\n const metadata = this.getAssetMetadata(assetId);\n if (!metadata) {\n throw new Error(`Missing metadata for asset ${assetId}`);\n }\n // Removed data cache\n this.assets.set(assetId, { metadata });\n this.updateDocAssetMetadata(assetId, metadata);\n return { metadata, bytes: stored };\n }\n }\n\n if (!this.assetTransport) {\n throw new Error(`Asset ${assetId} is not available locally`);\n }\n\n const remote = await this.assetTransport.fetch(assetId);\n if (!remote) {\n throw new Error(`Asset ${assetId} missing from remote store`);\n }\n const remoteStream = await remote.content();\n const remoteBytes = await streamToUint8Array(remoteStream);\n const metadata: RepoAssetMetadata = {\n assetId,\n size: remote.size,\n createdAt: remote.createdAt,\n ...(remote.mime ? { mime: remote.mime } : {}),\n ...(remote.policy ? { policy: remote.policy } : {}),\n ...(remote.tag ? { tag: remote.tag } : {}),\n };\n\n // Removed data cache\n this.assets.set(assetId, { metadata });\n this.updateDocAssetMetadata(assetId, metadata);\n\n this.metaFlock.put([\"a\", assetId], assetMetaToJson(metadata));\n\n if (this.storage) {\n await this.storage.save({\n type: \"asset\",\n assetId,\n data: remoteBytes.slice(),\n });\n }\n\n return { metadata, bytes: remoteBytes };\n }\n\n private updateDocAssetMetadata(\n assetId: AssetId,\n metadata: RepoAssetMetadata,\n ): void {\n const refs = this.assetToDocRefs.get(assetId);\n if (!refs) return;\n for (const docId of refs) {\n const assets = this.docAssets.get(docId);\n if (assets) {\n assets.set(assetId, metadata);\n }\n }\n }\n\n private rememberAsset(metadata: RepoAssetMetadata): void {\n this.assets.set(metadata.assetId, {\n metadata,\n });\n // Removed auto-delete from orphans. Orphans managed by refs.\n }\n\n private addDocReference(assetId: AssetId, docId: string): void {\n const refs = this.assetToDocRefs.get(assetId) ?? new Set<string>();\n refs.add(docId);\n this.assetToDocRefs.set(assetId, refs);\n this.orphanedAssets.delete(assetId); // Ref added -> not orphan\n }\n\n private removeDocAssetReference(assetId: AssetId, docId: string): void {\n const refs = this.assetToDocRefs.get(assetId);\n if (!refs) return;\n refs.delete(docId);\n if (refs.size === 0) {\n this.assetToDocRefs.delete(assetId);\n this.markAssetAsOrphan(assetId); // Ref zero -> orphan\n }\n }\n\n private markAssetAsOrphan(\n assetId: AssetId,\n metadataOverride?: RepoAssetMetadata,\n ): void {\n const metadata = metadataOverride ?? this.assets.get(assetId)?.metadata;\n if (!metadata) return;\n const existing = this.orphanedAssets.get(assetId);\n const deletedAt = existing?.deletedAt ?? Date.now();\n this.orphanedAssets.set(assetId, {\n metadata,\n deletedAt,\n });\n }\n\n private getAssetMetadata(assetId: AssetId): RepoAssetMetadata | undefined {\n const record = this.assets.get(assetId);\n if (record) return record.metadata;\n for (const assets of this.docAssets.values()) {\n const metadata = assets.get(assetId);\n if (metadata) return metadata;\n }\n return undefined;\n }\n\n private get metaFlock(): Flock {\n return this.getMetaFlock();\n }\n}\n","import { Flock, type Event as FlockEvent } from \"@loro-dev/flock\";\n\nimport type { AssetId, JsonObject, RepoEventBy } from \"../types\";\nimport { asJsonObject, cloneJsonValue } from \"../utils\";\nimport { MetadataManager } from \"./metadata-manager\";\nimport { AssetManager } from \"./asset-manager\";\nimport { DocManager } from \"./doc-manager\";\n\ninterface FlockHydratorOptions<Meta extends JsonObject> {\n readonly getMetaFlock: () => Flock;\n readonly metadataManager: MetadataManager<Meta>;\n readonly assetManager: AssetManager<Meta>;\n readonly docManager: DocManager<Meta>;\n}\n\nexport class FlockHydrator<Meta extends JsonObject> {\n private readonly getMetaFlock: () => Flock;\n private readonly metadataManager: MetadataManager<Meta>;\n private readonly assetManager: AssetManager<Meta>;\n private readonly docManager: DocManager<Meta>;\n\n constructor(options: FlockHydratorOptions<Meta>) {\n this.getMetaFlock = options.getMetaFlock;\n this.metadataManager = options.metadataManager;\n this.assetManager = options.assetManager;\n this.docManager = options.docManager;\n }\n\n // Full reload path used on startup or when meta sync produces no delta.\n // We rehydrate metadata/tombstones first, then assets, then sweep any doc\n // whose keyspace is empty (all m/f/ld/ts rows gone) to mirror remote purges.\n hydrateAll(by: RepoEventBy): void {\n const { metadata, tombstones } = this.readAllDocMetadata();\n this.metadataManager.replaceAll(metadata, by);\n for (const [docId, deletedAtMs] of tombstones) {\n this.metadataManager.emitSoftDeleted(docId, deletedAtMs, by);\n }\n this.assetManager.hydrateFromFlock(by);\n\n const docIds = new Set<string>([\n ...metadata.keys(),\n ...tombstones.keys(),\n ...this.collectDocIds([\"f\"]),\n ...this.collectDocIds([\"ld\"]),\n ]);\n for (const docId of docIds) {\n if (this.isDocKeyspaceEmpty(docId)) {\n void (async () => {\n try {\n this.clearDocKeyspace(docId);\n this.metadataManager.purgeMetadata(docId, by);\n await this.docManager.dropDoc(docId);\n } catch (error) {\n console.error(\"Failed to drop purged doc during hydrateAll\", {\n docId,\n error,\n });\n }\n })();\n }\n }\n }\n\n // Incremental path: walk the event batch once to figure out which doc/asset\n // buckets need refreshing, then apply the minimal set of rebuilds. We also\n // detect \"purged\" docs (no m/f/ld/ts rows left) so local caches/storage don't\n // resurrect data that a peer already deleted.\n async applyEvents(events: FlockEvent[], by: RepoEventBy): Promise<void> {\n if (!events.length) return;\n const docMetadataIds = new Set<string>();\n const docTombstoneIds = new Map<string, number | undefined>();\n const docAssetIds = new Set<string>();\n const docFrontierIds = new Set<string>();\n const assetIds = new Set<AssetId>();\n\n for (const event of events) {\n const key = event.key;\n if (!Array.isArray(key) || key.length === 0) continue;\n const root = key[0];\n if (root === \"m\") {\n const docId = key[1];\n if (typeof docId === \"string\") {\n docMetadataIds.add(docId);\n }\n } else if (root === \"f\") {\n const docId = key[1];\n if (typeof docId === \"string\") {\n docFrontierIds.add(docId);\n }\n } else if (root === \"ts\") {\n const docId = key[1];\n if (typeof docId === \"string\") {\n docTombstoneIds.set(docId, this.toDeletedAt(event.value));\n }\n } else if (root === \"a\") {\n const assetId = key[1];\n if (typeof assetId === \"string\") {\n assetIds.add(assetId as AssetId);\n }\n } else if (root === \"ld\") {\n const docId = key[1];\n const assetId = key[2];\n if (typeof docId === \"string\") {\n docAssetIds.add(docId);\n }\n if (typeof assetId === \"string\") {\n assetIds.add(assetId as AssetId);\n }\n }\n }\n\n for (const assetId of assetIds) {\n this.assetManager.refreshAssetMetadataEntry(assetId, by);\n }\n\n for (const docId of docMetadataIds) {\n this.metadataManager.refreshFromFlock(docId, by);\n }\n\n for (const [docId, deletedAtMs] of docTombstoneIds) {\n this.metadataManager.emitSoftDeleted(docId, deletedAtMs, by);\n }\n\n for (const docId of docAssetIds) {\n this.assetManager.refreshDocAssetsEntry(docId, by);\n }\n\n const docIdsToCheck = new Set<string>([\n ...docMetadataIds,\n ...docTombstoneIds.keys(),\n ...docAssetIds,\n ...docFrontierIds,\n ]);\n for (const docId of docIdsToCheck) {\n if (this.isDocKeyspaceEmpty(docId)) {\n try {\n this.clearDocKeyspace(docId);\n this.metadataManager.purgeMetadata(docId, by);\n await this.docManager.dropDoc(docId);\n } catch (error) {\n console.error(\"Failed to drop purged doc during hydration\", {\n docId,\n error,\n });\n }\n }\n }\n\n for (const docId of docIdsToCheck) {\n const snapshot = this.metadataManager.get(docId);\n const tombstone = this.metaFlock.get([\"ts\", docId]);\n if (!snapshot && tombstone === undefined) {\n try {\n this.clearDocKeyspace(docId);\n this.metadataManager.purgeMetadata(docId, by);\n await this.docManager.dropDoc(docId);\n } catch (error) {\n console.error(\"Failed to drop purged doc after refresh\", {\n docId,\n error,\n });\n }\n }\n }\n\n }\n\n // Reconstruct metadata/tombstones from the Flock replica. We ignore\n // undefined values (compacted away) unless we already had a local snapshot,\n // so deletes don't invent empty docs.\n private readAllDocMetadata(): {\n metadata: Map<string, JsonObject>;\n tombstones: Map<string, number>;\n } {\n const nextMetadata = new Map<string, JsonObject>();\n const metadataRows = this.metaFlock.scan({ prefix: [\"m\"] });\n for (const row of metadataRows) {\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const docId = row.key[1];\n if (typeof docId !== \"string\") continue;\n if (row.value === undefined && !this.metadataManager.get(docId)) {\n continue;\n }\n\n if (row.key.length === 2) {\n const obj = asJsonObject(row.value);\n if (!obj) continue;\n let docMeta = nextMetadata.get(docId);\n if (!docMeta) {\n docMeta = {};\n nextMetadata.set(docId, docMeta);\n }\n for (const [field, value] of Object.entries(obj)) {\n const cloned = cloneJsonValue(value);\n if (cloned !== undefined) {\n docMeta[field] = cloned;\n }\n }\n continue;\n }\n\n const fieldKey = row.key[2];\n if (typeof fieldKey !== \"string\") continue;\n const jsonValue = cloneJsonValue(row.value);\n if (jsonValue === undefined) continue;\n let docMeta = nextMetadata.get(docId);\n if (!docMeta) {\n docMeta = {};\n nextMetadata.set(docId, docMeta);\n }\n docMeta[fieldKey] = jsonValue;\n }\n\n const tombstones = new Map<string, number>();\n const tombstoneRows = this.metaFlock.scan({ prefix: [\"ts\"] });\n for (const row of tombstoneRows) {\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const docId = row.key[1];\n if (typeof docId !== \"string\") continue;\n\n const tombstone = cloneJsonValue(row.value);\n if (typeof tombstone === \"number\" && Number.isFinite(tombstone)) {\n tombstones.set(docId, tombstone);\n }\n }\n return { metadata: nextMetadata, tombstones };\n }\n\n private toDeletedAt(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value)\n ? value\n : undefined;\n }\n\n private get metaFlock(): Flock {\n return this.getMetaFlock();\n }\n\n // A doc is \"empty\" when it has no tombstone and no surviving metadata/frontier/link\n // rows. That implies a remote purge or local compaction, so we can drop in-memory\n // caches and storage snapshots safely.\n private isDocKeyspaceEmpty(docId: string): boolean {\n if (this.metaFlock.get([\"ts\", docId]) !== undefined) return false;\n if (this.hasTruthyKey([\"m\", docId])) return false;\n if (this.hasTruthyKey([\"f\", docId])) return false;\n if (this.hasTruthyKey([\"ld\", docId])) return false;\n return true;\n }\n\n private hasTruthyKey(prefix: [string, string]): boolean {\n const rows = this.metaFlock.scan({ prefix });\n for (const row of rows) {\n const value = row.value;\n const present = value !== undefined;\n if (present) return true;\n }\n return false;\n }\n\n private collectDocIds(prefix: [string]): Set<string> {\n const ids = new Set<string>();\n const rows = this.metaFlock.scan({ prefix });\n for (const row of rows) {\n if (!Array.isArray(row.key) || row.key.length < 2) continue;\n const docId = row.key[1];\n if (typeof docId === \"string\") {\n ids.add(docId);\n }\n }\n return ids;\n }\n\n private clearDocKeyspace(docId: string): void {\n const prefixes: Array<[string, string]> = [\n [\"m\", docId],\n [\"f\", docId],\n [\"ld\", docId],\n ];\n for (const prefix of prefixes) {\n const rows = this.metaFlock.scan({ prefix });\n for (const row of rows) {\n this.metaFlock.delete(row.key);\n }\n }\n this.metaFlock.delete([\"ts\", docId]);\n }\n}\n","import { Flock, type Event as FlockEvent } from \"@loro-dev/flock\";\n\nimport type {\n JsonObject,\n RepoSyncOptions,\n StorageAdapter,\n TransportAdapter,\n TransportJoinParams,\n TransportSubscription,\n} from \"../types\";\nimport { RepoEventBus } from \"./event-bus\";\nimport { DocManager } from \"./doc-manager\";\nimport { MetadataManager } from \"./metadata-manager\";\nimport { AssetManager } from \"./asset-manager\";\nimport { FlockHydrator } from \"./flock-hydrator\";\nimport { logAsyncError } from \"./logging\";\n\ninterface SyncRunnerOptions<Meta extends JsonObject> {\n readonly storage?: StorageAdapter;\n readonly transport?: TransportAdapter;\n readonly eventBus: RepoEventBus<Meta>;\n readonly docManager: DocManager<Meta>;\n readonly metadataManager: MetadataManager<Meta>;\n readonly assetManager: AssetManager<Meta>;\n readonly flockHydrator: FlockHydrator<Meta>;\n readonly getMetaFlock: () => Flock;\n readonly mergeFlock: (snapshot: Flock) => void;\n}\n\ntype SubscriptionRecord = {\n readonly base: TransportSubscription;\n refCount: number;\n};\n\n/**\n * Sync data between storage and transport layer\n */\nexport class SyncRunner<Meta extends JsonObject> {\n private readonly storage?: StorageAdapter;\n private transport?: TransportAdapter;\n private readonly eventBus: RepoEventBus<Meta>;\n private readonly docManager: DocManager<Meta>;\n private readonly metadataManager: MetadataManager<Meta>;\n private readonly assetManager: AssetManager<Meta>;\n private readonly flockHydrator: FlockHydrator<Meta>;\n private readonly getMetaFlock: () => Flock;\n private readonly replaceMetaFlock: (snapshot: Flock) => void;\n\n private readyPromise?: Promise<void>;\n private metaRoomSubscription?: SubscriptionRecord;\n private unsubscribeMetaFlock?: () => void;\n private readonly docSubscriptions = new Map<string, SubscriptionRecord>();\n\n constructor(options: SyncRunnerOptions<Meta>) {\n this.storage = options.storage;\n this.transport = options.transport;\n this.eventBus = options.eventBus;\n this.docManager = options.docManager;\n this.metadataManager = options.metadataManager;\n this.assetManager = options.assetManager;\n this.flockHydrator = options.flockHydrator;\n this.getMetaFlock = options.getMetaFlock;\n this.replaceMetaFlock = options.mergeFlock;\n }\n\n setTransport(transport?: TransportAdapter): void {\n if (this.transport === transport) return;\n this.leaveRooms();\n this.transport = transport;\n }\n\n async ready(): Promise<void> {\n if (!this.readyPromise) {\n this.readyPromise = this.initialize();\n }\n await this.readyPromise;\n }\n\n async sync(options: RepoSyncOptions = {}): Promise<void> {\n await this.ready();\n const { scope = \"full\", docIds } = options;\n if (!this.transport) return;\n\n if (!this.transport.isConnected()) {\n await this.transport.connect();\n }\n\n if (scope === \"meta\" || scope === \"full\") {\n this.eventBus.pushEventBy(\"sync\");\n const recordedEvents: FlockEvent[] = [];\n const unsubscribe = this.metaFlock.subscribe((batch) => {\n if (batch.source === \"local\") return;\n recordedEvents.push(...batch.events);\n });\n try {\n const result = await this.transport.syncMeta(this.metaFlock);\n if (!result.ok) {\n throw new Error(\"Metadata sync failed\");\n }\n if (recordedEvents.length > 0) {\n await this.flockHydrator.applyEvents(recordedEvents, \"sync\");\n } else {\n this.flockHydrator.hydrateAll(\"sync\");\n }\n } finally {\n unsubscribe();\n this.eventBus.popEventBy();\n }\n }\n\n if (scope === \"doc\" || scope === \"full\") {\n const targets = docIds ?? this.metadataManager.getDocIds();\n for (const docId of targets) {\n const doc = await this.docManager.ensureDoc(docId);\n this.eventBus.pushEventBy(\"sync\");\n try {\n const result = await this.transport.syncDoc(docId, doc);\n if (!result.ok) {\n throw new Error(`Document sync failed for ${docId}`);\n }\n } finally {\n this.eventBus.popEventBy();\n }\n await this.docManager.persistDoc(docId, doc);\n await this.docManager.updateDocFrontiers(docId, doc, \"sync\");\n }\n }\n }\n\n async joinMetaRoom(\n params?: TransportJoinParams,\n ): Promise<TransportSubscription> {\n await this.ready();\n if (!this.transport) {\n throw new Error(\"Transport adapter not configured\");\n }\n if (!this.transport.isConnected()) {\n await this.transport.connect();\n }\n const existing = this.metaRoomSubscription;\n if (existing) {\n existing.refCount += 1;\n return this.createMetaLease(existing);\n }\n\n this.ensureMetaLiveMonitor();\n\n const base = this.transport.joinMetaRoom(this.metaFlock, params);\n const record: SubscriptionRecord = { base, refCount: 1 };\n this.metaRoomSubscription = record;\n\n void base.firstSyncedWithRemote\n .then(async () => {\n const by = this.eventBus.resolveEventBy(\"live\");\n this.flockHydrator.hydrateAll(by);\n })\n .catch(logAsyncError(\"meta room first sync\"));\n\n return this.createMetaLease(record);\n }\n\n async joinDocRoom(\n docId: string,\n params?: TransportJoinParams,\n ): Promise<TransportSubscription> {\n await this.ready();\n if (!this.transport) {\n throw new Error(\"Transport adapter not configured\");\n }\n if (!this.transport.isConnected()) {\n await this.transport.connect();\n }\n\n const existing = this.docSubscriptions.get(docId);\n if (existing) {\n existing.refCount += 1;\n return this.createDocLease(docId, existing);\n }\n\n const doc = await this.docManager.ensureDoc(docId);\n const base = this.transport.joinDocRoom(docId, doc, params);\n const record: SubscriptionRecord = { base, refCount: 1 };\n this.docSubscriptions.set(docId, record);\n void base.firstSyncedWithRemote.catch(\n logAsyncError(`doc ${docId} first sync`),\n );\n return this.createDocLease(docId, record);\n }\n\n private createMetaLease(record: SubscriptionRecord): TransportSubscription {\n let released = false;\n return {\n unsubscribe: () => {\n if (released) return;\n released = true;\n const current = this.metaRoomSubscription;\n if (!current || current !== record) return;\n current.refCount = Math.max(0, current.refCount - 1);\n if (current.refCount === 0) {\n current.base.unsubscribe();\n this.metaRoomSubscription = undefined;\n if (this.unsubscribeMetaFlock) {\n this.unsubscribeMetaFlock();\n this.unsubscribeMetaFlock = undefined;\n }\n }\n },\n firstSyncedWithRemote: record.base.firstSyncedWithRemote,\n get connected() {\n return record.base.connected;\n },\n get status() {\n return record.base.status;\n },\n onStatusChange: record.base.onStatusChange,\n };\n }\n\n private createDocLease(\n docId: string,\n record: SubscriptionRecord,\n ): TransportSubscription {\n let released = false;\n return {\n unsubscribe: () => {\n if (released) return;\n released = true;\n const current = this.docSubscriptions.get(docId);\n if (!current || current !== record) return;\n current.refCount = Math.max(0, current.refCount - 1);\n if (current.refCount === 0) {\n current.base.unsubscribe();\n if (this.docSubscriptions.get(docId) === current) {\n this.docSubscriptions.delete(docId);\n }\n }\n },\n firstSyncedWithRemote: record.base.firstSyncedWithRemote,\n get connected() {\n return record.base.connected;\n },\n get status() {\n return record.base.status;\n },\n onStatusChange: record.base.onStatusChange,\n };\n }\n\n async destroy(): Promise<void> {\n await this.docManager.close();\n if (this.metaRoomSubscription) {\n this.metaRoomSubscription.base.unsubscribe();\n this.metaRoomSubscription = undefined;\n }\n for (const record of this.docSubscriptions.values()) {\n record.base.unsubscribe();\n }\n this.docSubscriptions.clear();\n this.unsubscribeMetaFlock?.();\n this.unsubscribeMetaFlock = undefined;\n this.eventBus.clear();\n this.metadataManager.clear();\n this.assetManager.clear();\n this.readyPromise = undefined;\n await this.transport?.close();\n }\n\n private async initialize(): Promise<void> {\n if (this.storage) {\n const snapshot = await this.storage.loadMeta();\n if (snapshot) {\n this.replaceMetaFlock(snapshot);\n }\n }\n this.flockHydrator.hydrateAll(\"sync\");\n }\n\n private ensureMetaLiveMonitor(): void {\n if (this.unsubscribeMetaFlock) return;\n this.unsubscribeMetaFlock = this.metaFlock.subscribe((batch) => {\n if (batch.source === \"local\") return;\n const by = this.eventBus.resolveEventBy(\"live\");\n void (async () => {\n await this.flockHydrator.applyEvents(batch.events, by);\n })().catch(logAsyncError(\"meta live monitor sync\"));\n });\n }\n\n private get metaFlock(): Flock {\n return this.getMetaFlock();\n }\n\n private leaveRooms(): void {\n if (this.metaRoomSubscription) {\n this.metaRoomSubscription.base.unsubscribe();\n this.metaRoomSubscription = undefined;\n }\n\n for (const record of this.docSubscriptions.values()) {\n record.base.unsubscribe();\n }\n this.docSubscriptions.clear();\n\n this.unsubscribeMetaFlock?.();\n this.unsubscribeMetaFlock = undefined;\n }\n}\n","import type { AssetId, JsonObject, RepoAssetMetadata } from \"../types\";\n\nexport type AssetRecord = {\n metadata: RepoAssetMetadata;\n};\n\nexport type OrphanedAssetRecord = {\n metadata: RepoAssetMetadata;\n deletedAt: number;\n};\n\nexport interface RepoState {\n readonly metadata: Map<string, JsonObject>;\n readonly docAssets: Map<string, Map<AssetId, RepoAssetMetadata>>;\n readonly assets: Map<AssetId, AssetRecord>;\n readonly orphanedAssets: Map<AssetId, OrphanedAssetRecord>;\n readonly assetToDocRefs: Map<AssetId, Set<string>>;\n}\n\nexport function createRepoState(): RepoState {\n return {\n metadata: new Map<string, JsonObject>(),\n docAssets: new Map<string, Map<AssetId, RepoAssetMetadata>>(),\n assets: new Map<AssetId, AssetRecord>(),\n orphanedAssets: new Map<AssetId, OrphanedAssetRecord>(),\n assetToDocRefs: new Map<AssetId, Set<string>>(),\n };\n}\n","import { type ExportBundle, type Flock, type VersionVector } from \"@loro-dev/flock\";\n\nimport type { StorageAdapter } from \"../types\";\n\nconst textEncoder = new TextEncoder();\nconst DEFAULT_META_PERSIST_DEBOUNCE_MS = 5_000;\n\ninterface MetaPersisterOptions {\n readonly getMetaFlock: () => Flock;\n readonly storage?: StorageAdapter;\n readonly debounceMs?: number;\n}\n\nexport class MetaPersister {\n private readonly getMetaFlock: () => Flock;\n private readonly storage?: StorageAdapter;\n private readonly debounceMs: number;\n\n private lastPersistedVersion?: VersionVector;\n private unsubscribe?: () => void;\n private flushPromise: Promise<void> = Promise.resolve();\n private flushTimer?: ReturnType<typeof setTimeout>;\n private forceFullOnNextFlush = false;\n private destroyed = false;\n\n constructor(options: MetaPersisterOptions) {\n this.getMetaFlock = options.getMetaFlock;\n this.storage = options.storage;\n const configuredDebounce = options.debounceMs;\n this.debounceMs =\n typeof configuredDebounce === \"number\" && Number.isFinite(configuredDebounce) && configuredDebounce >= 0\n ? configuredDebounce\n : DEFAULT_META_PERSIST_DEBOUNCE_MS;\n }\n\n start(initialVersion: VersionVector | undefined): void {\n this.lastPersistedVersion = initialVersion;\n if (this.unsubscribe) return;\n this.unsubscribe = this.metaFlock.subscribe(() => {\n this.scheduleFlush();\n });\n }\n\n async destroy(): Promise<void> {\n this.destroyed = true;\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = undefined;\n }\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = undefined;\n }\n await this.flushNow();\n }\n\n async flushNow(forceFull = false): Promise<void> {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = undefined;\n }\n await this.flush(forceFull);\n }\n\n private scheduleFlush(): void {\n if (this.destroyed) return;\n if (this.debounceMs === 0) {\n void this.flush();\n return;\n }\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n }\n this.flushTimer = setTimeout(() => {\n this.flushTimer = undefined;\n void this.flush();\n }, this.debounceMs);\n }\n\n private async flush(forceFull = false): Promise<void> {\n if (forceFull) this.forceFullOnNextFlush = true;\n const run = this.flushPromise.catch(() => {}).then(() => this.flushInternal());\n this.flushPromise = run;\n await run;\n }\n\n private async flushInternal(): Promise<void> {\n const flock = this.metaFlock;\n const currentVersion = flock.version();\n if (this.lastPersistedVersion && this.versionsEqual(currentVersion, this.lastPersistedVersion)) {\n this.forceFullOnNextFlush = false;\n return;\n }\n\n const baseline = this.forceFullOnNextFlush ? undefined : this.lastPersistedVersion;\n const rawBundle = baseline ? flock.exportJson(baseline) : flock.exportJson();\n const bundle = baseline ? this.stripUnchangedEntries(rawBundle, baseline) : rawBundle;\n if (Object.keys(bundle.entries).length === 0) {\n this.forceFullOnNextFlush = false;\n this.lastPersistedVersion = currentVersion;\n return;\n }\n\n const encoded = textEncoder.encode(JSON.stringify(bundle));\n\n if (!this.storage) {\n this.lastPersistedVersion = currentVersion;\n this.forceFullOnNextFlush = false;\n return;\n }\n\n await this.storage.save({ type: \"meta\", update: encoded });\n this.lastPersistedVersion = currentVersion;\n this.forceFullOnNextFlush = false;\n }\n\n private get metaFlock(): Flock {\n return this.getMetaFlock();\n }\n\n private stripUnchangedEntries(bundle: ExportBundle, baseline: VersionVector): ExportBundle {\n const entries: ExportBundle[\"entries\"] = {};\n for (const [key, record] of Object.entries(bundle.entries)) {\n const clock = this.parseClock(record.c);\n if (!clock) {\n entries[key] = record;\n continue;\n }\n const baselineEntry = baseline[clock.peerIdHex];\n if (!baselineEntry) {\n entries[key] = record;\n continue;\n }\n const isNewer =\n clock.physicalTime > baselineEntry.physicalTime ||\n (clock.physicalTime === baselineEntry.physicalTime && clock.logicalCounter > baselineEntry.logicalCounter);\n if (isNewer) {\n entries[key] = record;\n }\n }\n return { version: bundle.version, entries };\n }\n\n private parseClock(raw: unknown): { physicalTime: number; logicalCounter: number; peerIdHex: string } | undefined {\n if (typeof raw !== \"string\") return undefined;\n const [physicalTimeStr, logicalCounterStr, peerIdHex] = raw.split(\",\");\n if (!physicalTimeStr || !logicalCounterStr || !peerIdHex) return undefined;\n const physicalTime = Number(physicalTimeStr);\n const logicalCounter = Number(logicalCounterStr);\n if (!Number.isFinite(physicalTime) || !Number.isFinite(logicalCounter)) return undefined;\n return { physicalTime, logicalCounter, peerIdHex };\n }\n\n private versionsEqual(a?: VersionVector, b?: VersionVector): boolean {\n if (!a || !b) return false;\n const aKeys = Object.keys(a);\n const bKeys = Object.keys(b);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n const aEntry = a[key];\n const bEntry = b[key];\n if (!aEntry || !bEntry) return false;\n if (aEntry.physicalTime !== bEntry.physicalTime || aEntry.logicalCounter !== bEntry.logicalCounter) {\n return false;\n }\n }\n return true;\n }\n}\n","import { Flock } from \"@loro-dev/flock\";\nimport { LoroDoc } from \"loro-crdt\";\nimport type { EphemeralStore } from \"loro-crdt\";\n\nexport * from \"./types\";\n\nimport type {\n AssetDownload,\n AssetId,\n DeleteDocOptions,\n GcDeletedDocOptions,\n GarbageCollectionOptions,\n JsonObject,\n ListDocQuery,\n LoroRepoOptions,\n LinkAssetOptions,\n RepoAssetMetadata,\n RepoDocHandle,\n RepoDocMeta,\n RepoDocSnapshot,\n RepoEvent,\n RepoEventFilter,\n RepoEventListener,\n RepoSyncOptions,\n RepoWatchHandle,\n StorageAdapter,\n TransportAdapter,\n TransportJoinParams,\n TransportSubscription,\n UploadAssetOptions,\n AssetTransportAdapter,\n} from \"./types\";\n\nimport { RepoEventBus } from \"./internal/event-bus\";\nimport { DocManager } from \"./internal/doc-manager\";\nimport { MetadataManager } from \"./internal/metadata-manager\";\nimport { AssetManager } from \"./internal/asset-manager\";\nimport { FlockHydrator } from \"./internal/flock-hydrator\";\nimport { SyncRunner } from \"./internal/sync-runner\";\nimport { createRepoState, type RepoState } from \"./internal/repo-state\";\nimport { MetaPersister } from \"./internal/meta-persister\";\nconst DEFAULT_DOC_FRONTIER_DEBOUNCE_MS = 1_000;\nconst DEFAULT_DELETED_DOC_KEEP_MS = 30 * 24 * 60 * 60 * 1_000;\n\nexport class LoroRepo<Meta extends JsonObject = JsonObject> {\n private _destroyed = false;\n private transport?: TransportAdapter;\n private readonly storage?: StorageAdapter;\n private metaFlock: Flock = new Flock();\n private readonly eventBus: RepoEventBus<Meta>;\n private readonly docManager: DocManager<Meta>;\n private readonly metadataManager: MetadataManager<Meta>;\n private readonly assetManager: AssetManager<Meta>;\n private readonly assetTransport?: AssetTransportAdapter;\n private readonly flockHydrator: FlockHydrator<Meta>;\n private readonly state: RepoState;\n private readonly syncRunner: SyncRunner<Meta>;\n private readonly metaPersister: MetaPersister;\n private readonly deletedDocKeepMs: number;\n private readonly purgeWatchHandle: RepoWatchHandle;\n\n private constructor(options: LoroRepoOptions) {\n this.transport = options.transportAdapter;\n this.storage = options.storageAdapter;\n this.assetTransport = options.assetTransportAdapter;\n this.eventBus = new RepoEventBus<Meta>();\n this.state = createRepoState();\n const configuredDebounce = options.docFrontierDebounceMs;\n const docFrontierDebounceMs =\n typeof configuredDebounce === \"number\" &&\n Number.isFinite(configuredDebounce) &&\n configuredDebounce >= 0\n ? configuredDebounce\n : DEFAULT_DOC_FRONTIER_DEBOUNCE_MS;\n const configuredDeletedKeepMs = options.deletedDocKeepMs;\n this.deletedDocKeepMs =\n typeof configuredDeletedKeepMs === \"number\" &&\n Number.isFinite(configuredDeletedKeepMs) &&\n configuredDeletedKeepMs >= 0\n ? configuredDeletedKeepMs\n : DEFAULT_DELETED_DOC_KEEP_MS;\n this.docManager = new DocManager<Meta>({\n storage: this.storage,\n docFrontierDebounceMs,\n getMetaFlock: () => this.metaFlock,\n eventBus: this.eventBus,\n });\n this.metadataManager = new MetadataManager<Meta>({\n getMetaFlock: () => this.metaFlock,\n eventBus: this.eventBus,\n state: this.state,\n });\n this.assetManager = new AssetManager<Meta>({\n storage: this.storage,\n assetTransport: this.assetTransport,\n getMetaFlock: () => this.metaFlock,\n eventBus: this.eventBus,\n state: this.state,\n });\n this.metaPersister = new MetaPersister({\n getMetaFlock: () => this.metaFlock,\n storage: this.storage,\n debounceMs: options.metaPersistDebounceMs,\n });\n this.flockHydrator = new FlockHydrator<Meta>({\n getMetaFlock: () => this.metaFlock,\n metadataManager: this.metadataManager,\n assetManager: this.assetManager,\n docManager: this.docManager,\n });\n this.syncRunner = new SyncRunner<Meta>({\n storage: this.storage,\n transport: this.transport,\n eventBus: this.eventBus,\n docManager: this.docManager,\n metadataManager: this.metadataManager,\n assetManager: this.assetManager,\n flockHydrator: this.flockHydrator,\n getMetaFlock: () => this.metaFlock,\n mergeFlock: (snapshot) => {\n this.metaFlock.merge(snapshot)\n },\n });\n\n this.purgeWatchHandle = this.eventBus.watch(\n (event) => this.handlePurgeSignals(event),\n { kinds: [\"doc-soft-deleted\", \"doc-metadata\"], by: [\"sync\", \"live\"] },\n );\n }\n\n static async create<Meta extends JsonObject = JsonObject>(options: LoroRepoOptions): Promise<LoroRepo<Meta>> {\n const repo = new LoroRepo<Meta>(options);\n await repo.storage?.init?.();\n // Transport may not be valid because the client may be offline, and it would hurt DX if we wait for transport layer \n // to initialize here.\n await repo.ready();\n return repo;\n }\n\n /**\n * Load meta from storage.\n * \n * You need to call this before all other operations to make the app functioning correctly.\n * Though we do that implicitly already\n */\n private async ready(): Promise<void> {\n await this.syncRunner.ready();\n this.metaPersister.start(this.metaFlock.version());\n }\n\n private computeDocPurgeAfter(\n docId: string,\n minKeepMs: number,\n ): number | undefined {\n const deletedAt = this.metadataManager.getDeletedAtMs(docId);\n if (deletedAt === undefined) return undefined;\n return deletedAt + minKeepMs;\n }\n\n private purgeDocKeyspace(docId: string): void {\n const metadataKeys = Array.from(\n this.metaFlock.scan({ prefix: [\"m\", docId] }),\n (row) => row.key,\n );\n for (const key of metadataKeys) {\n this.metaFlock.delete(key);\n }\n\n const frontierKeys = Array.from(\n this.metaFlock.scan({ prefix: [\"f\", docId] }),\n (row) => row.key,\n );\n for (const key of frontierKeys) {\n this.metaFlock.delete(key);\n }\n\n this.metaFlock.delete([\"ts\", docId]);\n }\n\n /**\n * Sync selected data via the transport adaptor\n * @param options \n */\n async sync(options: RepoSyncOptions = {}): Promise<void> {\n await this.syncRunner.sync(options);\n }\n\n /**\n * Sets (or replaces) the transport adapter used for syncing and realtime rooms.\n *\n * Swapping transports will leave any joined meta/doc rooms managed by the repo.\n */\n async setTransportAdapter(transport?: TransportAdapter): Promise<void> {\n if (this._destroyed) {\n throw new Error(\"Repo has been destroyed\");\n }\n if (this.transport === transport) return;\n\n const previous = this.transport;\n this.transport = transport;\n this.syncRunner.setTransport(transport);\n await previous?.close();\n }\n\n hasTransport(): boolean {\n return Boolean(this.transport);\n }\n\n hasStorage(): boolean {\n return Boolean(this.storage);\n }\n\n /**\n * Start syncing the metadata (Flock) room. It will establish a realtime connection to the transport adaptor.\n * All changes on the room will be synced to the Flock, and all changes on the Flock will be synced to the room.\n *\n * - Idempotent: repeated calls reuse the same underlying room session; no extra join request is sent for the same repo.\n * - Reference-counted leave: every call to `joinMetaRoom` returns a subscription that increments an internal counter. The room is\n * actually left only after all returned subscriptions have called `unsubscribe`.\n * @param params \n * @returns \n */\n async joinMetaRoom(\n params?: TransportJoinParams,\n ): Promise<TransportSubscription> {\n const subscription = await this.syncRunner.joinMetaRoom(params);\n return {\n unsubscribe: subscription.unsubscribe,\n get connected() {\n return subscription.connected;\n },\n get status() {\n return subscription.status;\n },\n onStatusChange: subscription.onStatusChange,\n firstSyncedWithRemote: subscription.firstSyncedWithRemote.then(async () => {\n await this.metaPersister.flushNow();\n }),\n };\n }\n\n /**\n * Start syncing the given doc. It will establish a realtime connection to the transport adaptor.\n * All changes on the doc will be synced to the transport, and all changes on the transport will be synced to the doc.\n * \n * All the changes on the room will be reflected on the same doc you get from `repo.openCollaborativeDoc(docId)`\n *\n * - Idempotent: multiple joins for the same `docId` reuse the existing session; no duplicate transport joins are issued.\n * - Reference-counted leave: each returned subscription bumps an internal counter and only the final `unsubscribe()` will\n * actually leave the room. Earlier unsubscribes simply decrement the counter.\n * @param docId \n * @param params \n * @returns \n */\n async joinDocRoom(\n docId: string,\n params?: TransportJoinParams,\n ): Promise<TransportSubscription> {\n const subscription = await this.syncRunner.joinDocRoom(docId, params);\n return {\n ...subscription,\n onStatusChange: subscription.onStatusChange,\n status: subscription.status,\n };\n }\n\n /**\n * Joins an ephemeral CRDT room. This is useful for presence-like state that should not be persisted.\n * The returned store can be used immediately; the first sync promise resolves once the initial handshake completes.\n */\n async joinEphemeralRoom(\n roomId: string,\n ): Promise<TransportSubscription & { store: EphemeralStore }> {\n if (!this.transport) {\n throw new Error(\"Transport adapter not configured\");\n }\n await this.syncRunner.ready();\n if (!this.transport.isConnected()) {\n await this.transport.connect();\n }\n const subscription = this.transport.joinEphemeralRoom(roomId);\n return {\n ...subscription,\n onStatusChange: subscription.onStatusChange,\n status: subscription.status,\n };\n }\n\n /**\n * Opens a document that is automatically persisted to the configured storage adapter.\n * \n * - Edits are saved to storage (debounced).\n * - Frontiers are synced to the metadata (Flock).\n * - Realtime collaboration is NOT enabled by default; use `joinDocRoom` to connect.\n */\n async openPersistedDoc(docId: string): Promise<RepoDocHandle> {\n return {\n doc: await this.docManager.openPersistedDoc(docId),\n syncOnce: () => {\n return this.sync({ scope: \"doc\", docIds: [docId] });\n },\n joinRoom: (auth) => {\n return this.syncRunner.joinDocRoom(docId, { auth })\n }\n }\n }\n\n async upsertDocMeta(\n docId: string,\n patch: Partial<Meta>,\n ): Promise<void> {\n await this.metadataManager.upsert(docId, patch);\n }\n\n async getDocMeta(docId: string): Promise<RepoDocSnapshot<Meta> | undefined> {\n return this.metadataManager.get(docId);\n }\n\n async listDoc(query?: ListDocQuery): Promise<RepoDocMeta<Meta>[]> {\n return this.metadataManager.listDoc(query);\n }\n\n /**\n * Mark a document deleted by writing a `ts/*` tombstone entry (timestamp).\n * The body and metadata remain until purged; callers use the tombstone to\n * render deleted state or trigger retention workflows. For immediate removal,\n * call `purgeDoc` instead.\n */\n async deleteDoc(\n docId: string,\n options: DeleteDocOptions = {},\n ): Promise<void> {\n const existing = this.metadataManager.getDeletedAtMs(docId);\n if (existing !== undefined && !options.force) return; // idempotent without force\n\n const deletedAt = options.deletedAt ?? Date.now();\n this.metadataManager.markDeleted(docId, deletedAt);\n }\n\n /**\n * Undo a soft delete by removing the tombstone entry. Metadata and document\n * state remain untouched.\n */\n async restoreDoc(docId: string): Promise<void> {\n const existing = this.metadataManager.getDeletedAtMs(docId);\n if (existing === undefined) return;\n this.metadataManager.clearDeleted(docId);\n }\n\n /**\n * Hard-delete a document immediately. Removes doc snapshots/updates via the\n * storage adapter (if supported), clears metadata/frontiers/link keys from\n * Flock, and unlinks assets (they become orphaned for asset GC).\n */\n async purgeDoc(docId: string): Promise<void> {\n const deletedAtMs = this.metadataManager.getDeletedAtMs(docId);\n this.eventBus.emit({\n kind: \"doc-purging\",\n docId,\n deletedAtMs,\n by: \"local\",\n });\n await this.docManager.dropDoc(docId);\n\n this.assetManager.purgeDocLinks(docId, \"local\");\n\n this.purgeDocKeyspace(docId);\n this.metadataManager.emitSoftDeleted(docId, undefined, \"local\");\n\n this.metadataManager.refreshFromFlock(docId, \"local\");\n }\n\n /**\n * Sweep tombstoned documents whose retention window expired. Uses\n * `deletedDocKeepMs` by default; pass `minKeepMs`/`now` for overrides.\n */\n async gcDeletedDocs(options: GcDeletedDocOptions = {}): Promise<number> {\n const now = options.now ?? Date.now();\n const minKeepMs = options.minKeepMs ?? this.deletedDocKeepMs;\n const docIds = this.metadataManager.getDocIds();\n let purged = 0;\n for (const docId of docIds) {\n const purgeAfter = this.computeDocPurgeAfter(docId, minKeepMs);\n if (purgeAfter === undefined || now < purgeAfter) continue;\n await this.purgeDoc(docId);\n purged += 1;\n }\n return purged;\n }\n\n getMeta(): Flock {\n return this.metaFlock;\n }\n\n watch(\n listener: RepoEventListener<Meta>,\n filter: RepoEventFilter<Meta> = {},\n ): RepoWatchHandle {\n return this.eventBus.watch(listener, filter);\n }\n\n /**\n * Opens a detached `LoroDoc` snapshot.\n * \n * - **No Persistence**: Edits to this document are NOT saved to storage.\n * - **No Sync**: This document does not participate in realtime updates.\n * - **Use Case**: Ideal for read-only history inspection, temporary drafts, or conflict resolution without affecting the main state.\n */\n async openDetachedDoc(docId: string): Promise<LoroDoc> {\n return this.docManager.openDetachedDoc(docId);\n }\n\n /**\n * Explicitly unloads a document from memory.\n * \n * - **Persists Immediately**: Forces a save of the document's current state to storage.\n * - **Frees Memory**: Removes the document from the internal cache.\n * - **Note**: If the document is currently being synced (via `joinDocRoom`), you should also unsubscribe from the room to fully release resources.\n */\n async unloadDoc(docId: string): Promise<void> {\n await this.docManager.unloadDoc(docId);\n }\n\n async flush(): Promise<void> {\n await this.docManager.flush();\n await this.metaPersister.flushNow();\n }\n\n async uploadAsset(params: UploadAssetOptions): Promise<AssetId> {\n return this.assetManager.uploadAsset(params);\n }\n\n async linkAsset(docId: string, params: LinkAssetOptions): Promise<AssetId> {\n return this.assetManager.linkAsset(docId, params);\n }\n\n async fetchAsset(assetId: AssetId): Promise<AssetDownload> {\n return this.assetManager.fetchAsset(assetId);\n }\n\n async unlinkAsset(docId: string, assetId: AssetId): Promise<void> {\n await this.assetManager.unlinkAsset(docId, assetId);\n }\n\n async listAssets(docId: string): Promise<RepoAssetMetadata[]> {\n return this.assetManager.listAssets(docId);\n }\n\n async ensureAsset(assetId: AssetId): Promise<AssetDownload> {\n return this.assetManager.ensureAsset(assetId);\n }\n\n async gcAssets(options: GarbageCollectionOptions = {}): Promise<number> {\n return this.assetManager.gcAssets(options);\n }\n\n get destroyed(): boolean {\n return this._destroyed;\n }\n\n async destroy(): Promise<void> {\n if (this._destroyed) return;\n this._destroyed = true;\n this.purgeWatchHandle.unsubscribe();\n await this.metaPersister.destroy();\n await this.syncRunner.destroy();\n this.assetTransport?.close?.();\n this.storage?.close?.();\n await this.transport?.close();\n }\n\n private handlePurgeSignals(event: RepoEvent<Meta>): void {\n // This listener is scoped to sync/live events via the watch filter.\n const docId = (() => {\n if (event.kind === \"doc-soft-deleted\") return event.docId;\n if (event.kind === \"doc-metadata\") return event.docId;\n return undefined;\n })();\n\n if (!docId) return;\n\n const metadataCleared =\n event.kind === \"doc-metadata\" &&\n Object.keys(event.patch).length === 0;\n\n const tombstoneCleared =\n event.kind === \"doc-soft-deleted\" && event.deletedAtMs === undefined;\n\n // Only drop local state when we know the remote replica purged the doc:\n // 1) metadataCleared indicates m/* rows were removed.\n // 2) tombstone cleared *and* no remaining metadata snapshot implies purge, not restore.\n const purgeDetected =\n metadataCleared ||\n (tombstoneCleared && this.metadataManager.get(docId) === undefined);\n\n if (!purgeDetected) return;\n\n void this.docManager.dropDoc(docId).catch((error) => {\n console.error(\"Failed to drop purged doc\", { docId, error });\n });\n }\n}\n"],"mappings":"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"}