doxum 0.1.27 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -3
- package/dist/advanced.cjs +40 -14
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +27 -2
- package/dist/advanced.d.ts +27 -2
- package/dist/advanced.js +27 -1
- package/dist/advanced.js.map +1 -1
- package/dist/{definition-xgk3YNBp.d.ts → dependency-B3E65M1E.d.cts} +27 -3
- package/dist/{definition-CDOuvb5C.d.cts → dependency-BYbdDHNa.d.ts} +27 -3
- package/dist/driver-B5vdPohL.js.map +1 -1
- package/dist/driver-BL91gn7_.cjs.map +1 -1
- package/dist/index.cjs +663 -344
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -32
- package/dist/index.d.ts +23 -32
- package/dist/index.js +549 -230
- package/dist/index.js.map +1 -1
- package/dist/local-sync.cjs.map +1 -1
- package/dist/local-sync.js.map +1 -1
- package/dist/{scheduler-vzf3OcjT.js → transform-B4T3AcJr.js} +356 -11
- package/dist/transform-B4T3AcJr.js.map +1 -0
- package/dist/{scheduler-k1ZGR1PG.cjs → transform-DUleAsG0.cjs} +393 -12
- package/dist/transform-DUleAsG0.cjs.map +1 -0
- package/package.json +1 -1
- package/skills/doxum-runtime/SKILL.md +11 -5
- package/skills/doxum-runtime/references/api.en.md +40 -21
- package/skills/doxum-runtime/references/api.zh-CN.md +21 -2
- package/skills/doxum-runtime/references/guide.en.md +2 -1
- package/skills/doxum-runtime/references/guide.zh-CN.md +2 -1
- package/skills/doxum-runtime/references/invariants.en.md +2 -2
- package/skills/doxum-runtime/references/invariants.zh-CN.md +2 -2
- package/skills/doxum-runtime/references/patterns.en.md +1 -1
- package/skills/doxum-runtime/references/patterns.zh-CN.md +1 -1
- package/skills/doxum-runtime/references/projections.en.md +47 -3
- package/skills/doxum-runtime/references/projections.zh-CN.md +45 -3
- package/dist/scheduler-k1ZGR1PG.cjs.map +0 -1
- package/dist/scheduler-vzf3OcjT.js.map +0 -1
package/dist/local-sync.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-sync.cjs","names":["isPlainObject","positiveInteger","isRecord","installRuntimeWriteDriver"],"sources":["../core/src/local-sync/error.ts","../core/src/local-sync/json.ts","../core/src/local-sync/timeline.ts","../core/src/local-sync/session.ts"],"sourcesContent":["export type LocalSyncErrorCode =\n | 'unavailable'\n | 'schema-mismatch'\n | 'consistency'\n | 'read-only'\n | 'unsupported-operation'\n | 'disposed'\n | 'invalid-data';\n\nexport class LocalSyncError extends Error {\n readonly code: LocalSyncErrorCode;\n\n constructor(code: LocalSyncErrorCode, message: string, options?: { readonly cause?: unknown }) {\n super(message, options);\n this.name = 'LocalSyncError';\n this.code = code;\n }\n}\n\nexport const normalizeLocalSyncError = (\n error: unknown,\n code: LocalSyncErrorCode,\n message: string\n): LocalSyncError =>\n error instanceof LocalSyncError ? error : new LocalSyncError(code, message, { cause: error });\n","import { isPlainObject } from '../value/record';\nimport * as changeSet from '../mutation/changes';\nimport type { ChangeSet } from '../changes';\nimport { LocalSyncError } from './error';\n\nexport type JsonPrimitive = null | boolean | number | string;\nexport type JsonValue =\n JsonPrimitive | readonly JsonValue[] | { readonly [key: string]: JsonValue };\n\nexport type JsonChangeLimits = {\n readonly maxChanges?: number;\n readonly maxBytes?: number;\n readonly maxDepth?: number;\n readonly maxStringLength?: number;\n};\n\ntype ResolvedJsonLimits = {\n readonly maxChanges: number;\n readonly maxBytes: number;\n readonly maxDepth: number;\n readonly maxStringLength: number;\n};\n\nexport const defaultJsonChangeLimits: Readonly<ResolvedJsonLimits> = Object.freeze({\n maxChanges: 1_000,\n maxBytes: 1_000_000,\n maxDepth: 64,\n maxStringLength: 256_000,\n});\n\nconst describePath = (path: readonly string[]): string =>\n path.length === 0 ? 'value' : path.join('.');\n\nconst limit = (value: number | undefined, fallback: number, label: string): number => {\n if (value === undefined) return fallback;\n if (Number.isSafeInteger(value) && value > 0) return value;\n throw new TypeError(`${label} must be a positive safe integer.`);\n};\n\nconst resolveLimits = (input: JsonChangeLimits | undefined): ResolvedJsonLimits => ({\n maxChanges: limit(input?.maxChanges, defaultJsonChangeLimits.maxChanges, 'maxChanges'),\n maxBytes: limit(input?.maxBytes, defaultJsonChangeLimits.maxBytes, 'maxBytes'),\n maxDepth: limit(input?.maxDepth, defaultJsonChangeLimits.maxDepth, 'maxDepth'),\n maxStringLength: limit(\n input?.maxStringLength,\n defaultJsonChangeLimits.maxStringLength,\n 'maxStringLength'\n ),\n});\n\nconst validate = (\n value: unknown,\n path: string[],\n limits: ResolvedJsonLimits | undefined,\n depth: number,\n ancestors: WeakSet<object>\n): JsonValue => {\n if (limits && depth > limits.maxDepth)\n throw new LocalSyncError(\n 'invalid-data',\n `${describePath(path)} exceeds the maximum JSON depth.`\n );\n if (value === null || typeof value === 'boolean') return value;\n if (typeof value === 'string') {\n if (!limits || value.length <= limits.maxStringLength) return value;\n throw new LocalSyncError(\n 'invalid-data',\n `${describePath(path)} exceeds the maximum string length.`\n );\n }\n if (typeof value === 'number') {\n if (Number.isFinite(value)) return value;\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a non-finite number.`);\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value))\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (let index = 0; index < value.length; index += 1) {\n path.push(String(index));\n validate(value[index], path, limits, depth + 1, ancestors);\n path.pop();\n }\n } finally {\n ancestors.delete(value);\n }\n return value as readonly JsonValue[];\n }\n if (isPlainObject(value)) {\n if (ancestors.has(value))\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (const key of Object.keys(value)) {\n path.push(key);\n validate(value[key], path, limits, depth + 1, ancestors);\n path.pop();\n }\n } finally {\n ancestors.delete(value);\n }\n return value as { readonly [key: string]: JsonValue };\n }\n throw new LocalSyncError('invalid-data', `${describePath(path)} must be JSON data.`);\n};\n\nexport const json = (value: unknown, label: string): JsonValue =>\n validate(value, [label], undefined, 0, new WeakSet());\n\nexport const jsonChanges = (value: unknown, label: string, input?: JsonChangeLimits): ChangeSet => {\n const limits = input === undefined ? undefined : resolveLimits(input);\n let changes: ChangeSet;\n try {\n changes = changeSet.decodeChanges(value);\n } catch {\n throw new LocalSyncError('invalid-data', `${label} contains an invalid ChangeSet.`);\n }\n if (limits && changeSet.changeCount(changes) > limits.maxChanges)\n throw new LocalSyncError('invalid-data', `${label} exceeds the maximum change count.`);\n validate(changes.changes, [label], limits, 0, new WeakSet());\n if (limits) {\n const serialized = JSON.stringify(changes.changes);\n if (new TextEncoder().encode(serialized).byteLength > limits.maxBytes)\n throw new LocalSyncError('invalid-data', `${label} exceeds the maximum ChangeSet size.`);\n }\n return changes;\n};\n","import { LocalSyncError, normalizeLocalSyncError } from './error';\nimport { json, jsonChanges, type JsonValue } from './json';\nimport type { ChangeSet } from '../changes';\nimport { isRecord } from '../value/record';\n\nconst DATABASE_VERSION = 5;\nconst FORMAT_VERSION = 3;\nconst DOCUMENTS = 'documents';\nconst COMMITS = 'commits';\n\nexport type StoredDocument = {\n readonly formatVersion: number;\n readonly documentId: string;\n readonly schemaVersion: number;\n readonly checkpointSeq: number;\n readonly headSeq: number;\n readonly checkpoint: JsonValue;\n};\n\nexport type StoredCommit = {\n readonly formatVersion: number;\n readonly documentId: string;\n readonly seq: number;\n readonly changes: ChangeSet;\n readonly createdAt: number;\n};\n\nconst request = <T>(value: IDBRequest<T>): Promise<T> =>\n new Promise((resolve, reject) => {\n value.onsuccess = () => resolve(value.result);\n value.onerror = () =>\n reject(normalizeLocalSyncError(value.error, 'unavailable', 'IndexedDB request failed.'));\n });\n\nconst complete = (transaction: IDBTransaction): Promise<void> =>\n new Promise((resolve, reject) => {\n transaction.oncomplete = () => resolve();\n transaction.onabort = () =>\n reject(\n normalizeLocalSyncError(\n transaction.error,\n 'unavailable',\n 'IndexedDB transaction was aborted.'\n )\n );\n transaction.onerror = () =>\n reject(\n normalizeLocalSyncError(transaction.error, 'unavailable', 'IndexedDB transaction failed.')\n );\n });\n\nconst factory = (): IDBFactory => {\n if (!globalThis.indexedDB)\n throw new LocalSyncError(\n 'unavailable',\n 'IndexedDB is required by doxum/local-sync in this environment.'\n );\n return globalThis.indexedDB;\n};\n\nconst keyRange = (): typeof IDBKeyRange => {\n if (!globalThis.IDBKeyRange)\n throw new LocalSyncError(\n 'unavailable',\n 'IndexedDB is required by doxum/local-sync in this environment.'\n );\n return globalThis.IDBKeyRange;\n};\n\nconst string = (value: unknown, label: string): string => {\n if (typeof value === 'string') return value;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst nonNegativeInteger = (value: unknown, label: string): number => {\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) return value;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst positiveInteger = (value: unknown, label: string): number => {\n const parsed = nonNegativeInteger(value, label);\n if (parsed > 0) return parsed;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst documentRecord = (value: unknown): StoredDocument => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncError('consistency', 'Document record is malformed in IndexedDB.');\n const checkpointSeq = nonNegativeInteger(value.checkpointSeq, 'document.checkpointSeq');\n const headSeq = nonNegativeInteger(value.headSeq, 'document.headSeq');\n if (checkpointSeq > headSeq)\n throw new LocalSyncError('consistency', 'Document checkpoint exceeds its head sequence.');\n return Object.freeze({\n formatVersion: FORMAT_VERSION,\n documentId: string(value.documentId, 'document.documentId'),\n schemaVersion: positiveInteger(value.schemaVersion, 'document.schemaVersion'),\n checkpointSeq,\n headSeq,\n checkpoint: json(value.checkpoint, 'document.checkpoint'),\n });\n};\n\nconst commitRecord = (value: unknown): StoredCommit => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncError('consistency', 'Commit record is malformed in IndexedDB.');\n return Object.freeze({\n formatVersion: FORMAT_VERSION,\n documentId: string(value.documentId, 'commit.documentId'),\n seq: positiveInteger(value.seq, 'commit.seq'),\n changes: jsonChanges({ changes: value.changes }, 'commit.changes'),\n createdAt: nonNegativeInteger(value.createdAt, 'commit.createdAt'),\n });\n};\n\nexport type IndexedDbTimeline = {\n readonly initialize: (\n documentId: string,\n schemaVersion: number,\n checkpoint: JsonValue\n ) => Promise<StoredDocument>;\n readonly read: (documentId: string) => Promise<StoredDocument>;\n readonly tail: (documentId: string, afterSeq: number) => Promise<readonly StoredCommit[]>;\n readonly append: (input: {\n readonly documentId: string;\n readonly expectedHeadSeq: number;\n readonly changes: ChangeSet;\n }) => Promise<StoredCommit>;\n readonly close: () => void;\n};\n\nexport const openIndexedDbTimeline = async (databaseName: string): Promise<IndexedDbTimeline> => {\n const database = await new Promise<IDBDatabase>((resolve, reject) => {\n let incompatible = false;\n const open = factory().open(databaseName, DATABASE_VERSION);\n open.onerror = () =>\n reject(\n incompatible\n ? new LocalSyncError(\n 'consistency',\n 'Unsupported Doxum storage format. Existing data was left unchanged.'\n )\n : (open.error ?? new Error('Unable to open IndexedDB.'))\n );\n open.onblocked = () => reject(new Error(`IndexedDB database '${databaseName}' is blocked.`));\n open.onupgradeneeded = event => {\n if (event.oldVersion !== 0) {\n incompatible = true;\n open.transaction?.abort();\n return;\n }\n const db = open.result;\n if (!db.objectStoreNames.contains(DOCUMENTS))\n db.createObjectStore(DOCUMENTS, { keyPath: 'documentId' });\n if (!db.objectStoreNames.contains(COMMITS))\n db.createObjectStore(COMMITS, { keyPath: ['documentId', 'seq'] });\n };\n open.onsuccess = () => resolve(open.result);\n });\n\n const readDocument = async (\n transaction: IDBTransaction,\n documentId: string\n ): Promise<StoredDocument> => {\n const value = await request(transaction.objectStore(DOCUMENTS).get(documentId));\n if (value === undefined)\n throw new LocalSyncError('consistency', `Local document '${documentId}' does not exist.`);\n return documentRecord(value);\n };\n\n return {\n initialize: async (documentId, schemaVersion, checkpoint) => {\n const transaction = database.transaction(DOCUMENTS, 'readwrite');\n const store = transaction.objectStore(DOCUMENTS);\n const existing = await request(store.get(documentId));\n if (existing !== undefined) {\n const record = documentRecord(existing);\n if (record.schemaVersion !== schemaVersion)\n throw new LocalSyncError(\n 'schema-mismatch',\n `Local document '${documentId}' uses schema version ${record.schemaVersion}, but version ${schemaVersion} was requested.`\n );\n await complete(transaction);\n return record;\n }\n const record: StoredDocument = {\n formatVersion: FORMAT_VERSION,\n documentId,\n schemaVersion,\n checkpointSeq: 0,\n headSeq: 0,\n checkpoint,\n };\n store.put(record);\n await complete(transaction);\n return Object.freeze(record);\n },\n read: async documentId => {\n const transaction = database.transaction(DOCUMENTS, 'readonly');\n const record = await readDocument(transaction, documentId);\n await complete(transaction);\n return record;\n },\n tail: async (documentId, afterSeq) => {\n const transaction = database.transaction(COMMITS, 'readonly');\n const range = keyRange().bound(\n [documentId, afterSeq + 1],\n [documentId, Number.MAX_SAFE_INTEGER]\n );\n const values = await request(transaction.objectStore(COMMITS).getAll(range));\n await complete(transaction);\n return Object.freeze(values.map(commitRecord).sort((left, right) => left.seq - right.seq));\n },\n append: async input => {\n const transaction = database.transaction([DOCUMENTS, COMMITS], 'readwrite');\n const documents = transaction.objectStore(DOCUMENTS);\n const current = await readDocument(transaction, input.documentId);\n if (current.headSeq !== input.expectedHeadSeq)\n throw new LocalSyncError(\n 'consistency',\n 'Local document advanced before the leader could append its commit.'\n );\n const commit: StoredCommit = {\n formatVersion: FORMAT_VERSION,\n documentId: input.documentId,\n seq: current.headSeq + 1,\n changes: input.changes,\n createdAt: Date.now(),\n };\n documents.put({ ...current, headSeq: commit.seq });\n transaction.objectStore(COMMITS).put({ ...commit, changes: commit.changes.changes });\n await complete(transaction);\n return Object.freeze(commit);\n },\n close: () => database.close(),\n };\n};\n","import {\n installRuntimeWriteDriver,\n type RuntimeWriteDriverLease,\n type RuntimeWriteIntent,\n} from '../runtime/driver';\nimport type { DocumentCommit } from '../runtime/contract';\nimport type { Infer, ObjectSchema } from '../schema/model';\nimport { type AttachLocalSyncOptions, type LocalSync, type LocalSyncState } from './contract';\nimport { LocalSyncError, normalizeLocalSyncError } from './error';\nimport { json, jsonChanges } from './json';\nimport type { ChangeSet } from '../changes';\nimport { openIndexedDbTimeline, type StoredCommit } from './timeline';\n\ntype LockOptions = {\n readonly mode: 'exclusive';\n readonly ifAvailable?: boolean;\n};\n\ntype LockManager = {\n readonly request: <TResult>(\n name: string,\n options: LockOptions,\n callback: (lock: unknown | null) => TResult | PromiseLike<TResult>\n ) => Promise<TResult>;\n};\n\ntype Channel = {\n onmessage: ((event: MessageEvent<unknown>) => void) | null;\n postMessage(message: unknown): void;\n close(): void;\n};\n\ntype ChannelConstructor = new (name: string) => Channel;\n\ntype CommitNotification = {\n readonly kind: 'commit';\n readonly documentId: string;\n readonly headSeq: number;\n};\n\nconst locks = (): LockManager => {\n const value: unknown = globalThis.navigator?.locks;\n if (\n typeof value !== 'object' ||\n value === null ||\n !('request' in value) ||\n typeof value.request !== 'function'\n )\n throw new LocalSyncError(\n 'unavailable',\n 'Web Locks is required by doxum/local-sync in this environment.'\n );\n return value as LockManager;\n};\n\nconst channelConstructor = (): ChannelConstructor => {\n const value: unknown = globalThis.BroadcastChannel;\n if (typeof value !== 'function')\n throw new LocalSyncError(\n 'unavailable',\n 'BroadcastChannel is required by doxum/local-sync in this environment.'\n );\n return value as ChannelConstructor;\n};\n\nconst requiredString = (value: string, label: string): string => {\n if (value.length > 0) return value;\n throw new TypeError(`${label} must not be empty.`);\n};\n\nconst positiveInteger = (value: number, label: string): number => {\n if (Number.isSafeInteger(value) && value > 0) return value;\n throw new TypeError(`${label} must be a positive safe integer.`);\n};\n\nconst notification = (value: unknown): CommitNotification | undefined => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;\n const record = value as Record<string, unknown>;\n if (\n record.kind !== 'commit' ||\n typeof record.documentId !== 'string' ||\n typeof record.headSeq !== 'number' ||\n !Number.isSafeInteger(record.headSeq) ||\n record.headSeq < 0\n )\n return undefined;\n return record as CommitNotification;\n};\n\nexport const attachLocalSync = async <TSchema extends ObjectSchema<object>>(\n input: AttachLocalSyncOptions<TSchema>\n): Promise<LocalSync> => {\n const runtime = input.runtime;\n const databaseName = requiredString(input.database, 'database');\n const documentId = requiredString(input.documentId, 'documentId');\n const schemaVersion = positiveInteger(input.schemaVersion ?? 1, 'schemaVersion');\n const lockManager = locks();\n const BroadcastChannel = channelConstructor();\n const timeline = await openIndexedDbTimeline(databaseName).catch(error => {\n throw normalizeLocalSyncError(error, 'unavailable', 'Unable to open local sync storage.');\n });\n\n let channel: Channel | undefined;\n let unsubscribe: (() => void) | undefined;\n let driver: RuntimeWriteDriverLease | undefined;\n try {\n const stored = await timeline.initialize(\n documentId,\n schemaVersion,\n json(runtime.snapshot(), 'runtime snapshot')\n );\n let headSeq = stored.checkpointSeq;\n let checkpointSeq = stored.checkpointSeq;\n let role: 'leader' | 'follower' = 'follower';\n let fault: LocalSyncError | undefined;\n let closing = false;\n let disposed = false;\n let releaseLeadership: (() => void) | undefined;\n let leadershipLease: Promise<void> | undefined;\n let queued: Promise<void> = Promise.resolve();\n let snapshot: LocalSyncState = Object.freeze({ status: role, headSeq, checkpointSeq });\n let stateRevision = 0;\n const stateListeners = new Set<() => void>();\n const publishState = (): void => {\n const next: LocalSyncState = disposed\n ? { status: 'disposed' }\n : fault\n ? { status: 'error', headSeq, checkpointSeq, error: fault }\n : { status: role, headSeq, checkpointSeq };\n if (\n snapshot.status === next.status &&\n (snapshot.status === 'disposed' ||\n (next.status !== 'disposed' &&\n snapshot.headSeq === next.headSeq &&\n snapshot.checkpointSeq === next.checkpointSeq &&\n (snapshot.status !== 'error' ||\n (next.status === 'error' && snapshot.error === next.error))))\n )\n return;\n snapshot = Object.freeze(next);\n stateRevision++;\n for (const listener of [...stateListeners]) {\n if (!stateListeners.has(listener)) continue;\n try {\n listener();\n } catch {\n // Consumer state listeners are isolated from synchronization state.\n }\n }\n };\n\n const fail = (error: unknown): LocalSyncError => {\n const failure = normalizeLocalSyncError(\n error,\n 'unavailable',\n 'Local synchronization failed.'\n );\n if (fault || disposed) return fault ?? failure;\n fault = failure;\n publishState();\n try {\n input.onError?.(failure);\n } catch {\n // Error reporting cannot repair or replace the original synchronization failure.\n }\n return failure;\n };\n\n const assertOpen = (): void => {\n if (closing || disposed)\n throw new LocalSyncError('disposed', 'Local sync has been disposed.');\n if (fault) throw fault;\n };\n\n const assertLeader = (intent: RuntimeWriteIntent): void => {\n assertOpen();\n if (role !== 'leader')\n throw new LocalSyncError(\n 'read-only',\n 'This tab is following the local document and cannot write until it becomes the leader.'\n );\n if (intent.kind === 'replace' || (intent.kind === 'apply' && intent.source === 'remote'))\n throw new LocalSyncError(\n 'unsupported-operation',\n 'Local sync appends committed changes. runtime.replace() and externally supplied remote changes are unavailable while it is attached.'\n );\n };\n\n const runRuntime = <TResult>(run: () => TResult): TResult => driver?.run(run) ?? run();\n const replayRuntime = <TResult>(run: () => TResult, message: string): TResult => {\n try {\n return runRuntime(run);\n } catch (error) {\n throw normalizeLocalSyncError(error, 'consistency', message);\n }\n };\n\n const applyStored = (commit: StoredCommit): void => {\n if (commit.seq !== headSeq + 1)\n throw new LocalSyncError('consistency', 'Local commit log contains a sequence gap.');\n const result = replayRuntime(\n () =>\n runtime.apply(commit.changes, {\n expectedRevision: runtime.revision(),\n source: 'remote',\n }),\n 'A stored local ChangeSet could not be replayed.'\n );\n if (result.status !== 'committed')\n throw new LocalSyncError('consistency', 'A stored local ChangeSet could not be applied.');\n headSeq = commit.seq;\n };\n\n const restore = async (reset = false): Promise<void> => {\n const current = await timeline.read(documentId);\n if (current.schemaVersion !== schemaVersion)\n throw new LocalSyncError(\n 'consistency',\n 'Local document schema changed while this attachment was open.'\n );\n if (headSeq > current.headSeq)\n throw new LocalSyncError('consistency', 'The local timeline moved behind this attachment.');\n if (reset || headSeq < current.checkpointSeq) {\n const result = replayRuntime(\n () => runtime.replace(current.checkpoint as Infer<TSchema>, { source: 'remote' }),\n 'The local checkpoint could not be restored.'\n );\n if (result.status === 'rejected')\n throw new LocalSyncError('consistency', 'The local checkpoint could not be restored.');\n headSeq = current.checkpointSeq;\n }\n const tail = await timeline.tail(documentId, headSeq);\n for (const commit of tail) applyStored(commit);\n if (headSeq !== current.headSeq)\n throw new LocalSyncError(\n 'consistency',\n 'Local commit log does not reach its recorded head sequence.'\n );\n checkpointSeq = current.checkpointSeq;\n publishState();\n };\n\n const enqueue = (run: () => Promise<void>): Promise<void> => {\n const next = queued.then(run, run);\n queued = next.then(\n () => undefined,\n error => {\n fail(error);\n }\n );\n return next;\n };\n\n const persist = async (changes: ChangeSet): Promise<void> => {\n const storedCommit = await timeline.append({\n documentId,\n expectedHeadSeq: headSeq,\n changes,\n });\n if (storedCommit.seq !== headSeq + 1)\n throw new LocalSyncError(\n 'consistency',\n 'A local commit was assigned an unexpected sequence.'\n );\n headSeq = storedCommit.seq;\n publishState();\n channel?.postMessage({\n kind: 'commit',\n documentId,\n headSeq,\n } satisfies CommitNotification);\n };\n\n const record = (commit: DocumentCommit<TSchema>): void => {\n try {\n const changes = jsonChanges(\n commit.changes,\n 'local commit changes',\n input.changeLimits ?? {}\n );\n void enqueue(() => persist(changes)).catch(() => undefined);\n } catch (error) {\n fail(error);\n }\n };\n\n const holdLeadership = (): Promise<void> =>\n new Promise(resolve => {\n releaseLeadership = resolve;\n });\n\n const lead = async (activated?: () => void): Promise<void> => {\n if (closing || disposed || fault) return;\n await restore();\n if (closing || disposed || fault) return;\n role = 'leader';\n publishState();\n activated?.();\n try {\n await holdLeadership();\n } finally {\n releaseLeadership = undefined;\n if (!closing && !disposed && !fault) {\n role = 'follower';\n publishState();\n }\n }\n };\n\n const watchLeadership = (): void => {\n leadershipLease = lockManager\n .request(`doxum:${documentId}`, { mode: 'exclusive' }, async () => {\n try {\n await lead();\n } catch (error) {\n fail(error);\n }\n })\n .catch(error => {\n if (!closing && !disposed) fail(error);\n });\n };\n\n const claimInitialLeadership = async (): Promise<void> => {\n let resolve!: (value: boolean) => void;\n const claimed = new Promise<boolean>(done => {\n resolve = done;\n });\n leadershipLease = lockManager\n .request(`doxum:${documentId}`, { mode: 'exclusive', ifAvailable: true }, async lock => {\n if (lock === null) {\n resolve(false);\n return;\n }\n try {\n await lead(() => resolve(true));\n } catch (error) {\n fail(error);\n resolve(false);\n }\n })\n .catch(error => {\n if (!closing && !disposed) fail(error);\n resolve(false);\n });\n if (!(await claimed) && !fault && !closing && !disposed) watchLeadership();\n };\n\n await restore(true);\n // The timeline, not a runtime constructed before attachment, is the baseline\n // for local undo. Even an identical restored snapshot must discard prior history.\n runtime.history.clear();\n driver = installRuntimeWriteDriver(runtime, { assertWritable: assertLeader });\n channel = new BroadcastChannel(`doxum:${databaseName}:${documentId}`);\n unsubscribe = runtime.subscribe(commit => {\n if (\n (commit.source === 'local' || commit.source === 'system' || commit.source === 'history') &&\n !closing &&\n !disposed\n )\n record(commit);\n });\n channel.onmessage = event => {\n const message = notification(event.data);\n if (\n !message ||\n message.documentId !== documentId ||\n message.headSeq <= headSeq ||\n closing ||\n disposed ||\n fault\n )\n return;\n void enqueue(restore).catch(() => undefined);\n };\n await claimInitialLeadership();\n if (fault) throw fault;\n\n const localSync: LocalSync = {\n state: Object.freeze({\n current: () => snapshot,\n revision: () => stateRevision,\n subscribe: (listener: () => void) => {\n if (disposed) throw new LocalSyncError('disposed', 'Local sync has been disposed.');\n stateListeners.add(listener);\n return () => {\n stateListeners.delete(listener);\n };\n },\n }),\n flush: async (): Promise<void> => {\n assertOpen();\n try {\n await enqueue(restore);\n } catch (error) {\n throw fail(error);\n }\n assertOpen();\n },\n dispose: async (): Promise<void> => {\n if (disposed) return;\n if (closing) {\n await queued;\n return;\n }\n closing = true;\n unsubscribe?.();\n await queued;\n releaseLeadership?.();\n if (role === 'leader') await leadershipLease;\n disposed = true;\n driver?.dispose();\n channel?.close();\n timeline.close();\n publishState();\n stateListeners.clear();\n },\n };\n return Object.freeze(localSync);\n } catch (error) {\n unsubscribe?.();\n driver?.dispose();\n channel?.close();\n timeline.close();\n throw normalizeLocalSyncError(error, 'unavailable', 'Unable to attach local sync.');\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"local-sync.cjs","names":["isPlainObject","positiveInteger","isRecord","installRuntimeWriteDriver"],"sources":["../core/src/local-sync/error.ts","../core/src/local-sync/json.ts","../core/src/local-sync/timeline.ts","../core/src/local-sync/session.ts"],"sourcesContent":["export type LocalSyncErrorCode =\n | 'unavailable'\n | 'schema-mismatch'\n | 'consistency'\n | 'read-only'\n | 'unsupported-operation'\n | 'disposed'\n | 'invalid-data';\n\nexport class LocalSyncError extends Error {\n readonly code: LocalSyncErrorCode;\n\n constructor(code: LocalSyncErrorCode, message: string, options?: { readonly cause?: unknown }) {\n super(message, options);\n this.name = 'LocalSyncError';\n this.code = code;\n }\n}\n\nexport const normalizeLocalSyncError = (\n error: unknown,\n code: LocalSyncErrorCode,\n message: string\n): LocalSyncError =>\n error instanceof LocalSyncError ? error : new LocalSyncError(code, message, { cause: error });\n","import { isPlainObject } from '@/value/record';\nimport * as changeSet from '@/mutation/changes';\nimport type { ChangeSet } from '@/changes';\nimport { LocalSyncError } from './error';\n\nexport type JsonPrimitive = null | boolean | number | string;\nexport type JsonValue =\n JsonPrimitive | readonly JsonValue[] | { readonly [key: string]: JsonValue };\n\nexport type JsonChangeLimits = {\n readonly maxChanges?: number;\n readonly maxBytes?: number;\n readonly maxDepth?: number;\n readonly maxStringLength?: number;\n};\n\ntype ResolvedJsonLimits = {\n readonly maxChanges: number;\n readonly maxBytes: number;\n readonly maxDepth: number;\n readonly maxStringLength: number;\n};\n\nexport const defaultJsonChangeLimits: Readonly<ResolvedJsonLimits> = Object.freeze({\n maxChanges: 1_000,\n maxBytes: 1_000_000,\n maxDepth: 64,\n maxStringLength: 256_000,\n});\n\nconst describePath = (path: readonly string[]): string =>\n path.length === 0 ? 'value' : path.join('.');\n\nconst limit = (value: number | undefined, fallback: number, label: string): number => {\n if (value === undefined) return fallback;\n if (Number.isSafeInteger(value) && value > 0) return value;\n throw new TypeError(`${label} must be a positive safe integer.`);\n};\n\nconst resolveLimits = (input: JsonChangeLimits | undefined): ResolvedJsonLimits => ({\n maxChanges: limit(input?.maxChanges, defaultJsonChangeLimits.maxChanges, 'maxChanges'),\n maxBytes: limit(input?.maxBytes, defaultJsonChangeLimits.maxBytes, 'maxBytes'),\n maxDepth: limit(input?.maxDepth, defaultJsonChangeLimits.maxDepth, 'maxDepth'),\n maxStringLength: limit(\n input?.maxStringLength,\n defaultJsonChangeLimits.maxStringLength,\n 'maxStringLength'\n ),\n});\n\nconst validate = (\n value: unknown,\n path: string[],\n limits: ResolvedJsonLimits | undefined,\n depth: number,\n ancestors: WeakSet<object>\n): JsonValue => {\n if (limits && depth > limits.maxDepth)\n throw new LocalSyncError(\n 'invalid-data',\n `${describePath(path)} exceeds the maximum JSON depth.`\n );\n if (value === null || typeof value === 'boolean') return value;\n if (typeof value === 'string') {\n if (!limits || value.length <= limits.maxStringLength) return value;\n throw new LocalSyncError(\n 'invalid-data',\n `${describePath(path)} exceeds the maximum string length.`\n );\n }\n if (typeof value === 'number') {\n if (Number.isFinite(value)) return value;\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a non-finite number.`);\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value))\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (let index = 0; index < value.length; index += 1) {\n path.push(String(index));\n validate(value[index], path, limits, depth + 1, ancestors);\n path.pop();\n }\n } finally {\n ancestors.delete(value);\n }\n return value as readonly JsonValue[];\n }\n if (isPlainObject(value)) {\n if (ancestors.has(value))\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (const key of Object.keys(value)) {\n path.push(key);\n validate(value[key], path, limits, depth + 1, ancestors);\n path.pop();\n }\n } finally {\n ancestors.delete(value);\n }\n return value as { readonly [key: string]: JsonValue };\n }\n throw new LocalSyncError('invalid-data', `${describePath(path)} must be JSON data.`);\n};\n\nexport const json = (value: unknown, label: string): JsonValue =>\n validate(value, [label], undefined, 0, new WeakSet());\n\nexport const jsonChanges = (value: unknown, label: string, input?: JsonChangeLimits): ChangeSet => {\n const limits = input === undefined ? undefined : resolveLimits(input);\n let changes: ChangeSet;\n try {\n changes = changeSet.decodeChanges(value);\n } catch {\n throw new LocalSyncError('invalid-data', `${label} contains an invalid ChangeSet.`);\n }\n if (limits && changeSet.changeCount(changes) > limits.maxChanges)\n throw new LocalSyncError('invalid-data', `${label} exceeds the maximum change count.`);\n validate(changes.changes, [label], limits, 0, new WeakSet());\n if (limits) {\n const serialized = JSON.stringify(changes.changes);\n if (new TextEncoder().encode(serialized).byteLength > limits.maxBytes)\n throw new LocalSyncError('invalid-data', `${label} exceeds the maximum ChangeSet size.`);\n }\n return changes;\n};\n","import { LocalSyncError, normalizeLocalSyncError } from './error';\nimport { json, jsonChanges, type JsonValue } from './json';\nimport type { ChangeSet } from '@/changes';\nimport { isRecord } from '@/value/record';\n\nconst DATABASE_VERSION = 5;\nconst FORMAT_VERSION = 3;\nconst DOCUMENTS = 'documents';\nconst COMMITS = 'commits';\n\nexport type StoredDocument = {\n readonly formatVersion: number;\n readonly documentId: string;\n readonly schemaVersion: number;\n readonly checkpointSeq: number;\n readonly headSeq: number;\n readonly checkpoint: JsonValue;\n};\n\nexport type StoredCommit = {\n readonly formatVersion: number;\n readonly documentId: string;\n readonly seq: number;\n readonly changes: ChangeSet;\n readonly createdAt: number;\n};\n\nconst request = <T>(value: IDBRequest<T>): Promise<T> =>\n new Promise((resolve, reject) => {\n value.onsuccess = () => resolve(value.result);\n value.onerror = () =>\n reject(normalizeLocalSyncError(value.error, 'unavailable', 'IndexedDB request failed.'));\n });\n\nconst complete = (transaction: IDBTransaction): Promise<void> =>\n new Promise((resolve, reject) => {\n transaction.oncomplete = () => resolve();\n transaction.onabort = () =>\n reject(\n normalizeLocalSyncError(\n transaction.error,\n 'unavailable',\n 'IndexedDB transaction was aborted.'\n )\n );\n transaction.onerror = () =>\n reject(\n normalizeLocalSyncError(transaction.error, 'unavailable', 'IndexedDB transaction failed.')\n );\n });\n\nconst factory = (): IDBFactory => {\n if (!globalThis.indexedDB)\n throw new LocalSyncError(\n 'unavailable',\n 'IndexedDB is required by doxum/local-sync in this environment.'\n );\n return globalThis.indexedDB;\n};\n\nconst keyRange = (): typeof IDBKeyRange => {\n if (!globalThis.IDBKeyRange)\n throw new LocalSyncError(\n 'unavailable',\n 'IndexedDB is required by doxum/local-sync in this environment.'\n );\n return globalThis.IDBKeyRange;\n};\n\nconst string = (value: unknown, label: string): string => {\n if (typeof value === 'string') return value;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst nonNegativeInteger = (value: unknown, label: string): number => {\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) return value;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst positiveInteger = (value: unknown, label: string): number => {\n const parsed = nonNegativeInteger(value, label);\n if (parsed > 0) return parsed;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst documentRecord = (value: unknown): StoredDocument => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncError('consistency', 'Document record is malformed in IndexedDB.');\n const checkpointSeq = nonNegativeInteger(value.checkpointSeq, 'document.checkpointSeq');\n const headSeq = nonNegativeInteger(value.headSeq, 'document.headSeq');\n if (checkpointSeq > headSeq)\n throw new LocalSyncError('consistency', 'Document checkpoint exceeds its head sequence.');\n return Object.freeze({\n formatVersion: FORMAT_VERSION,\n documentId: string(value.documentId, 'document.documentId'),\n schemaVersion: positiveInteger(value.schemaVersion, 'document.schemaVersion'),\n checkpointSeq,\n headSeq,\n checkpoint: json(value.checkpoint, 'document.checkpoint'),\n });\n};\n\nconst commitRecord = (value: unknown): StoredCommit => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncError('consistency', 'Commit record is malformed in IndexedDB.');\n return Object.freeze({\n formatVersion: FORMAT_VERSION,\n documentId: string(value.documentId, 'commit.documentId'),\n seq: positiveInteger(value.seq, 'commit.seq'),\n changes: jsonChanges({ changes: value.changes }, 'commit.changes'),\n createdAt: nonNegativeInteger(value.createdAt, 'commit.createdAt'),\n });\n};\n\nexport type IndexedDbTimeline = {\n readonly initialize: (\n documentId: string,\n schemaVersion: number,\n checkpoint: JsonValue\n ) => Promise<StoredDocument>;\n readonly read: (documentId: string) => Promise<StoredDocument>;\n readonly tail: (documentId: string, afterSeq: number) => Promise<readonly StoredCommit[]>;\n readonly append: (input: {\n readonly documentId: string;\n readonly expectedHeadSeq: number;\n readonly changes: ChangeSet;\n }) => Promise<StoredCommit>;\n readonly close: () => void;\n};\n\nexport const openIndexedDbTimeline = async (databaseName: string): Promise<IndexedDbTimeline> => {\n const database = await new Promise<IDBDatabase>((resolve, reject) => {\n let incompatible = false;\n const open = factory().open(databaseName, DATABASE_VERSION);\n open.onerror = () =>\n reject(\n incompatible\n ? new LocalSyncError(\n 'consistency',\n 'Unsupported Doxum storage format. Existing data was left unchanged.'\n )\n : (open.error ?? new Error('Unable to open IndexedDB.'))\n );\n open.onblocked = () => reject(new Error(`IndexedDB database '${databaseName}' is blocked.`));\n open.onupgradeneeded = event => {\n if (event.oldVersion !== 0) {\n incompatible = true;\n open.transaction?.abort();\n return;\n }\n const db = open.result;\n if (!db.objectStoreNames.contains(DOCUMENTS))\n db.createObjectStore(DOCUMENTS, { keyPath: 'documentId' });\n if (!db.objectStoreNames.contains(COMMITS))\n db.createObjectStore(COMMITS, { keyPath: ['documentId', 'seq'] });\n };\n open.onsuccess = () => resolve(open.result);\n });\n\n const readDocument = async (\n transaction: IDBTransaction,\n documentId: string\n ): Promise<StoredDocument> => {\n const value = await request(transaction.objectStore(DOCUMENTS).get(documentId));\n if (value === undefined)\n throw new LocalSyncError('consistency', `Local document '${documentId}' does not exist.`);\n return documentRecord(value);\n };\n\n return {\n initialize: async (documentId, schemaVersion, checkpoint) => {\n const transaction = database.transaction(DOCUMENTS, 'readwrite');\n const store = transaction.objectStore(DOCUMENTS);\n const existing = await request(store.get(documentId));\n if (existing !== undefined) {\n const record = documentRecord(existing);\n if (record.schemaVersion !== schemaVersion)\n throw new LocalSyncError(\n 'schema-mismatch',\n `Local document '${documentId}' uses schema version ${record.schemaVersion}, but version ${schemaVersion} was requested.`\n );\n await complete(transaction);\n return record;\n }\n const record: StoredDocument = {\n formatVersion: FORMAT_VERSION,\n documentId,\n schemaVersion,\n checkpointSeq: 0,\n headSeq: 0,\n checkpoint,\n };\n store.put(record);\n await complete(transaction);\n return Object.freeze(record);\n },\n read: async documentId => {\n const transaction = database.transaction(DOCUMENTS, 'readonly');\n const record = await readDocument(transaction, documentId);\n await complete(transaction);\n return record;\n },\n tail: async (documentId, afterSeq) => {\n const transaction = database.transaction(COMMITS, 'readonly');\n const range = keyRange().bound(\n [documentId, afterSeq + 1],\n [documentId, Number.MAX_SAFE_INTEGER]\n );\n const values = await request(transaction.objectStore(COMMITS).getAll(range));\n await complete(transaction);\n return Object.freeze(values.map(commitRecord).sort((left, right) => left.seq - right.seq));\n },\n append: async input => {\n const transaction = database.transaction([DOCUMENTS, COMMITS], 'readwrite');\n const documents = transaction.objectStore(DOCUMENTS);\n const current = await readDocument(transaction, input.documentId);\n if (current.headSeq !== input.expectedHeadSeq)\n throw new LocalSyncError(\n 'consistency',\n 'Local document advanced before the leader could append its commit.'\n );\n const commit: StoredCommit = {\n formatVersion: FORMAT_VERSION,\n documentId: input.documentId,\n seq: current.headSeq + 1,\n changes: input.changes,\n createdAt: Date.now(),\n };\n documents.put({ ...current, headSeq: commit.seq });\n transaction.objectStore(COMMITS).put({ ...commit, changes: commit.changes.changes });\n await complete(transaction);\n return Object.freeze(commit);\n },\n close: () => database.close(),\n };\n};\n","import {\n installRuntimeWriteDriver,\n type RuntimeWriteDriverLease,\n type RuntimeWriteIntent,\n} from '@/runtime/driver';\nimport type { DocumentCommit } from '@/runtime/contract';\nimport type { Infer, ObjectSchema } from '@/schema/model';\nimport { type AttachLocalSyncOptions, type LocalSync, type LocalSyncState } from './contract';\nimport { LocalSyncError, normalizeLocalSyncError } from './error';\nimport { json, jsonChanges } from './json';\nimport type { ChangeSet } from '@/changes';\nimport { openIndexedDbTimeline, type StoredCommit } from './timeline';\n\ntype LockOptions = {\n readonly mode: 'exclusive';\n readonly ifAvailable?: boolean;\n};\n\ntype LockManager = {\n readonly request: <TResult>(\n name: string,\n options: LockOptions,\n callback: (lock: unknown | null) => TResult | PromiseLike<TResult>\n ) => Promise<TResult>;\n};\n\ntype Channel = {\n onmessage: ((event: MessageEvent<unknown>) => void) | null;\n postMessage(message: unknown): void;\n close(): void;\n};\n\ntype ChannelConstructor = new (name: string) => Channel;\n\ntype CommitNotification = {\n readonly kind: 'commit';\n readonly documentId: string;\n readonly headSeq: number;\n};\n\nconst locks = (): LockManager => {\n const value: unknown = globalThis.navigator?.locks;\n if (\n typeof value !== 'object' ||\n value === null ||\n !('request' in value) ||\n typeof value.request !== 'function'\n )\n throw new LocalSyncError(\n 'unavailable',\n 'Web Locks is required by doxum/local-sync in this environment.'\n );\n return value as LockManager;\n};\n\nconst channelConstructor = (): ChannelConstructor => {\n const value: unknown = globalThis.BroadcastChannel;\n if (typeof value !== 'function')\n throw new LocalSyncError(\n 'unavailable',\n 'BroadcastChannel is required by doxum/local-sync in this environment.'\n );\n return value as ChannelConstructor;\n};\n\nconst requiredString = (value: string, label: string): string => {\n if (value.length > 0) return value;\n throw new TypeError(`${label} must not be empty.`);\n};\n\nconst positiveInteger = (value: number, label: string): number => {\n if (Number.isSafeInteger(value) && value > 0) return value;\n throw new TypeError(`${label} must be a positive safe integer.`);\n};\n\nconst notification = (value: unknown): CommitNotification | undefined => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;\n const record = value as Record<string, unknown>;\n if (\n record.kind !== 'commit' ||\n typeof record.documentId !== 'string' ||\n typeof record.headSeq !== 'number' ||\n !Number.isSafeInteger(record.headSeq) ||\n record.headSeq < 0\n )\n return undefined;\n return record as CommitNotification;\n};\n\nexport const attachLocalSync = async <TSchema extends ObjectSchema<object>>(\n input: AttachLocalSyncOptions<TSchema>\n): Promise<LocalSync> => {\n const runtime = input.runtime;\n const databaseName = requiredString(input.database, 'database');\n const documentId = requiredString(input.documentId, 'documentId');\n const schemaVersion = positiveInteger(input.schemaVersion ?? 1, 'schemaVersion');\n const lockManager = locks();\n const BroadcastChannel = channelConstructor();\n const timeline = await openIndexedDbTimeline(databaseName).catch(error => {\n throw normalizeLocalSyncError(error, 'unavailable', 'Unable to open local sync storage.');\n });\n\n let channel: Channel | undefined;\n let unsubscribe: (() => void) | undefined;\n let driver: RuntimeWriteDriverLease | undefined;\n try {\n const stored = await timeline.initialize(\n documentId,\n schemaVersion,\n json(runtime.snapshot(), 'runtime snapshot')\n );\n let headSeq = stored.checkpointSeq;\n let checkpointSeq = stored.checkpointSeq;\n let role: 'leader' | 'follower' = 'follower';\n let fault: LocalSyncError | undefined;\n let closing = false;\n let disposed = false;\n let releaseLeadership: (() => void) | undefined;\n let leadershipLease: Promise<void> | undefined;\n let queued: Promise<void> = Promise.resolve();\n let snapshot: LocalSyncState = Object.freeze({ status: role, headSeq, checkpointSeq });\n let stateRevision = 0;\n const stateListeners = new Set<() => void>();\n const publishState = (): void => {\n const next: LocalSyncState = disposed\n ? { status: 'disposed' }\n : fault\n ? { status: 'error', headSeq, checkpointSeq, error: fault }\n : { status: role, headSeq, checkpointSeq };\n if (\n snapshot.status === next.status &&\n (snapshot.status === 'disposed' ||\n (next.status !== 'disposed' &&\n snapshot.headSeq === next.headSeq &&\n snapshot.checkpointSeq === next.checkpointSeq &&\n (snapshot.status !== 'error' ||\n (next.status === 'error' && snapshot.error === next.error))))\n )\n return;\n snapshot = Object.freeze(next);\n stateRevision++;\n for (const listener of [...stateListeners]) {\n if (!stateListeners.has(listener)) continue;\n try {\n listener();\n } catch {\n // Consumer state listeners are isolated from synchronization state.\n }\n }\n };\n\n const fail = (error: unknown): LocalSyncError => {\n const failure = normalizeLocalSyncError(\n error,\n 'unavailable',\n 'Local synchronization failed.'\n );\n if (fault || disposed) return fault ?? failure;\n fault = failure;\n publishState();\n try {\n input.onError?.(failure);\n } catch {\n // Error reporting cannot repair or replace the original synchronization failure.\n }\n return failure;\n };\n\n const assertOpen = (): void => {\n if (closing || disposed)\n throw new LocalSyncError('disposed', 'Local sync has been disposed.');\n if (fault) throw fault;\n };\n\n const assertLeader = (intent: RuntimeWriteIntent): void => {\n assertOpen();\n if (role !== 'leader')\n throw new LocalSyncError(\n 'read-only',\n 'This tab is following the local document and cannot write until it becomes the leader.'\n );\n if (intent.kind === 'replace' || (intent.kind === 'apply' && intent.source === 'remote'))\n throw new LocalSyncError(\n 'unsupported-operation',\n 'Local sync appends committed changes. runtime.replace() and externally supplied remote changes are unavailable while it is attached.'\n );\n };\n\n const runRuntime = <TResult>(run: () => TResult): TResult => driver?.run(run) ?? run();\n const replayRuntime = <TResult>(run: () => TResult, message: string): TResult => {\n try {\n return runRuntime(run);\n } catch (error) {\n throw normalizeLocalSyncError(error, 'consistency', message);\n }\n };\n\n const applyStored = (commit: StoredCommit): void => {\n if (commit.seq !== headSeq + 1)\n throw new LocalSyncError('consistency', 'Local commit log contains a sequence gap.');\n const result = replayRuntime(\n () =>\n runtime.apply(commit.changes, {\n expectedRevision: runtime.revision(),\n source: 'remote',\n }),\n 'A stored local ChangeSet could not be replayed.'\n );\n if (result.status !== 'committed')\n throw new LocalSyncError('consistency', 'A stored local ChangeSet could not be applied.');\n headSeq = commit.seq;\n };\n\n const restore = async (reset = false): Promise<void> => {\n const current = await timeline.read(documentId);\n if (current.schemaVersion !== schemaVersion)\n throw new LocalSyncError(\n 'consistency',\n 'Local document schema changed while this attachment was open.'\n );\n if (headSeq > current.headSeq)\n throw new LocalSyncError('consistency', 'The local timeline moved behind this attachment.');\n if (reset || headSeq < current.checkpointSeq) {\n const result = replayRuntime(\n () => runtime.replace(current.checkpoint as Infer<TSchema>, { source: 'remote' }),\n 'The local checkpoint could not be restored.'\n );\n if (result.status === 'rejected')\n throw new LocalSyncError('consistency', 'The local checkpoint could not be restored.');\n headSeq = current.checkpointSeq;\n }\n const tail = await timeline.tail(documentId, headSeq);\n for (const commit of tail) applyStored(commit);\n if (headSeq !== current.headSeq)\n throw new LocalSyncError(\n 'consistency',\n 'Local commit log does not reach its recorded head sequence.'\n );\n checkpointSeq = current.checkpointSeq;\n publishState();\n };\n\n const enqueue = (run: () => Promise<void>): Promise<void> => {\n const next = queued.then(run, run);\n queued = next.then(\n () => undefined,\n error => {\n fail(error);\n }\n );\n return next;\n };\n\n const persist = async (changes: ChangeSet): Promise<void> => {\n const storedCommit = await timeline.append({\n documentId,\n expectedHeadSeq: headSeq,\n changes,\n });\n if (storedCommit.seq !== headSeq + 1)\n throw new LocalSyncError(\n 'consistency',\n 'A local commit was assigned an unexpected sequence.'\n );\n headSeq = storedCommit.seq;\n publishState();\n channel?.postMessage({\n kind: 'commit',\n documentId,\n headSeq,\n } satisfies CommitNotification);\n };\n\n const record = (commit: DocumentCommit<TSchema>): void => {\n try {\n const changes = jsonChanges(\n commit.changes,\n 'local commit changes',\n input.changeLimits ?? {}\n );\n void enqueue(() => persist(changes)).catch(() => undefined);\n } catch (error) {\n fail(error);\n }\n };\n\n const holdLeadership = (): Promise<void> =>\n new Promise(resolve => {\n releaseLeadership = resolve;\n });\n\n const lead = async (activated?: () => void): Promise<void> => {\n if (closing || disposed || fault) return;\n await restore();\n if (closing || disposed || fault) return;\n role = 'leader';\n publishState();\n activated?.();\n try {\n await holdLeadership();\n } finally {\n releaseLeadership = undefined;\n if (!closing && !disposed && !fault) {\n role = 'follower';\n publishState();\n }\n }\n };\n\n const watchLeadership = (): void => {\n leadershipLease = lockManager\n .request(`doxum:${documentId}`, { mode: 'exclusive' }, async () => {\n try {\n await lead();\n } catch (error) {\n fail(error);\n }\n })\n .catch(error => {\n if (!closing && !disposed) fail(error);\n });\n };\n\n const claimInitialLeadership = async (): Promise<void> => {\n let resolve!: (value: boolean) => void;\n const claimed = new Promise<boolean>(done => {\n resolve = done;\n });\n leadershipLease = lockManager\n .request(`doxum:${documentId}`, { mode: 'exclusive', ifAvailable: true }, async lock => {\n if (lock === null) {\n resolve(false);\n return;\n }\n try {\n await lead(() => resolve(true));\n } catch (error) {\n fail(error);\n resolve(false);\n }\n })\n .catch(error => {\n if (!closing && !disposed) fail(error);\n resolve(false);\n });\n if (!(await claimed) && !fault && !closing && !disposed) watchLeadership();\n };\n\n await restore(true);\n // The timeline, not a runtime constructed before attachment, is the baseline\n // for local undo. Even an identical restored snapshot must discard prior history.\n runtime.history.clear();\n driver = installRuntimeWriteDriver(runtime, { assertWritable: assertLeader });\n channel = new BroadcastChannel(`doxum:${databaseName}:${documentId}`);\n unsubscribe = runtime.subscribe(commit => {\n if (\n (commit.source === 'local' || commit.source === 'system' || commit.source === 'history') &&\n !closing &&\n !disposed\n )\n record(commit);\n });\n channel.onmessage = event => {\n const message = notification(event.data);\n if (\n !message ||\n message.documentId !== documentId ||\n message.headSeq <= headSeq ||\n closing ||\n disposed ||\n fault\n )\n return;\n void enqueue(restore).catch(() => undefined);\n };\n await claimInitialLeadership();\n if (fault) throw fault;\n\n const localSync: LocalSync = {\n state: Object.freeze({\n current: () => snapshot,\n revision: () => stateRevision,\n subscribe: (listener: () => void) => {\n if (disposed) throw new LocalSyncError('disposed', 'Local sync has been disposed.');\n stateListeners.add(listener);\n return () => {\n stateListeners.delete(listener);\n };\n },\n }),\n flush: async (): Promise<void> => {\n assertOpen();\n try {\n await enqueue(restore);\n } catch (error) {\n throw fail(error);\n }\n assertOpen();\n },\n dispose: async (): Promise<void> => {\n if (disposed) return;\n if (closing) {\n await queued;\n return;\n }\n closing = true;\n unsubscribe?.();\n await queued;\n releaseLeadership?.();\n if (role === 'leader') await leadershipLease;\n disposed = true;\n driver?.dispose();\n channel?.close();\n timeline.close();\n publishState();\n stateListeners.clear();\n },\n };\n return Object.freeze(localSync);\n } catch (error) {\n unsubscribe?.();\n driver?.dispose();\n channel?.close();\n timeline.close();\n throw normalizeLocalSyncError(error, 'unavailable', 'Unable to attach local sync.');\n }\n};\n"],"mappings":"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"}
|
package/dist/local-sync.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-sync.js","names":["changeSet.decodeChanges","changeSet.changeCount","positiveInteger"],"sources":["../core/src/local-sync/error.ts","../core/src/local-sync/json.ts","../core/src/local-sync/timeline.ts","../core/src/local-sync/session.ts"],"sourcesContent":["export type LocalSyncErrorCode =\n | 'unavailable'\n | 'schema-mismatch'\n | 'consistency'\n | 'read-only'\n | 'unsupported-operation'\n | 'disposed'\n | 'invalid-data';\n\nexport class LocalSyncError extends Error {\n readonly code: LocalSyncErrorCode;\n\n constructor(code: LocalSyncErrorCode, message: string, options?: { readonly cause?: unknown }) {\n super(message, options);\n this.name = 'LocalSyncError';\n this.code = code;\n }\n}\n\nexport const normalizeLocalSyncError = (\n error: unknown,\n code: LocalSyncErrorCode,\n message: string\n): LocalSyncError =>\n error instanceof LocalSyncError ? error : new LocalSyncError(code, message, { cause: error });\n","import { isPlainObject } from '../value/record';\nimport * as changeSet from '../mutation/changes';\nimport type { ChangeSet } from '../changes';\nimport { LocalSyncError } from './error';\n\nexport type JsonPrimitive = null | boolean | number | string;\nexport type JsonValue =\n JsonPrimitive | readonly JsonValue[] | { readonly [key: string]: JsonValue };\n\nexport type JsonChangeLimits = {\n readonly maxChanges?: number;\n readonly maxBytes?: number;\n readonly maxDepth?: number;\n readonly maxStringLength?: number;\n};\n\ntype ResolvedJsonLimits = {\n readonly maxChanges: number;\n readonly maxBytes: number;\n readonly maxDepth: number;\n readonly maxStringLength: number;\n};\n\nexport const defaultJsonChangeLimits: Readonly<ResolvedJsonLimits> = Object.freeze({\n maxChanges: 1_000,\n maxBytes: 1_000_000,\n maxDepth: 64,\n maxStringLength: 256_000,\n});\n\nconst describePath = (path: readonly string[]): string =>\n path.length === 0 ? 'value' : path.join('.');\n\nconst limit = (value: number | undefined, fallback: number, label: string): number => {\n if (value === undefined) return fallback;\n if (Number.isSafeInteger(value) && value > 0) return value;\n throw new TypeError(`${label} must be a positive safe integer.`);\n};\n\nconst resolveLimits = (input: JsonChangeLimits | undefined): ResolvedJsonLimits => ({\n maxChanges: limit(input?.maxChanges, defaultJsonChangeLimits.maxChanges, 'maxChanges'),\n maxBytes: limit(input?.maxBytes, defaultJsonChangeLimits.maxBytes, 'maxBytes'),\n maxDepth: limit(input?.maxDepth, defaultJsonChangeLimits.maxDepth, 'maxDepth'),\n maxStringLength: limit(\n input?.maxStringLength,\n defaultJsonChangeLimits.maxStringLength,\n 'maxStringLength'\n ),\n});\n\nconst validate = (\n value: unknown,\n path: string[],\n limits: ResolvedJsonLimits | undefined,\n depth: number,\n ancestors: WeakSet<object>\n): JsonValue => {\n if (limits && depth > limits.maxDepth)\n throw new LocalSyncError(\n 'invalid-data',\n `${describePath(path)} exceeds the maximum JSON depth.`\n );\n if (value === null || typeof value === 'boolean') return value;\n if (typeof value === 'string') {\n if (!limits || value.length <= limits.maxStringLength) return value;\n throw new LocalSyncError(\n 'invalid-data',\n `${describePath(path)} exceeds the maximum string length.`\n );\n }\n if (typeof value === 'number') {\n if (Number.isFinite(value)) return value;\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a non-finite number.`);\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value))\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (let index = 0; index < value.length; index += 1) {\n path.push(String(index));\n validate(value[index], path, limits, depth + 1, ancestors);\n path.pop();\n }\n } finally {\n ancestors.delete(value);\n }\n return value as readonly JsonValue[];\n }\n if (isPlainObject(value)) {\n if (ancestors.has(value))\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (const key of Object.keys(value)) {\n path.push(key);\n validate(value[key], path, limits, depth + 1, ancestors);\n path.pop();\n }\n } finally {\n ancestors.delete(value);\n }\n return value as { readonly [key: string]: JsonValue };\n }\n throw new LocalSyncError('invalid-data', `${describePath(path)} must be JSON data.`);\n};\n\nexport const json = (value: unknown, label: string): JsonValue =>\n validate(value, [label], undefined, 0, new WeakSet());\n\nexport const jsonChanges = (value: unknown, label: string, input?: JsonChangeLimits): ChangeSet => {\n const limits = input === undefined ? undefined : resolveLimits(input);\n let changes: ChangeSet;\n try {\n changes = changeSet.decodeChanges(value);\n } catch {\n throw new LocalSyncError('invalid-data', `${label} contains an invalid ChangeSet.`);\n }\n if (limits && changeSet.changeCount(changes) > limits.maxChanges)\n throw new LocalSyncError('invalid-data', `${label} exceeds the maximum change count.`);\n validate(changes.changes, [label], limits, 0, new WeakSet());\n if (limits) {\n const serialized = JSON.stringify(changes.changes);\n if (new TextEncoder().encode(serialized).byteLength > limits.maxBytes)\n throw new LocalSyncError('invalid-data', `${label} exceeds the maximum ChangeSet size.`);\n }\n return changes;\n};\n","import { LocalSyncError, normalizeLocalSyncError } from './error';\nimport { json, jsonChanges, type JsonValue } from './json';\nimport type { ChangeSet } from '../changes';\nimport { isRecord } from '../value/record';\n\nconst DATABASE_VERSION = 5;\nconst FORMAT_VERSION = 3;\nconst DOCUMENTS = 'documents';\nconst COMMITS = 'commits';\n\nexport type StoredDocument = {\n readonly formatVersion: number;\n readonly documentId: string;\n readonly schemaVersion: number;\n readonly checkpointSeq: number;\n readonly headSeq: number;\n readonly checkpoint: JsonValue;\n};\n\nexport type StoredCommit = {\n readonly formatVersion: number;\n readonly documentId: string;\n readonly seq: number;\n readonly changes: ChangeSet;\n readonly createdAt: number;\n};\n\nconst request = <T>(value: IDBRequest<T>): Promise<T> =>\n new Promise((resolve, reject) => {\n value.onsuccess = () => resolve(value.result);\n value.onerror = () =>\n reject(normalizeLocalSyncError(value.error, 'unavailable', 'IndexedDB request failed.'));\n });\n\nconst complete = (transaction: IDBTransaction): Promise<void> =>\n new Promise((resolve, reject) => {\n transaction.oncomplete = () => resolve();\n transaction.onabort = () =>\n reject(\n normalizeLocalSyncError(\n transaction.error,\n 'unavailable',\n 'IndexedDB transaction was aborted.'\n )\n );\n transaction.onerror = () =>\n reject(\n normalizeLocalSyncError(transaction.error, 'unavailable', 'IndexedDB transaction failed.')\n );\n });\n\nconst factory = (): IDBFactory => {\n if (!globalThis.indexedDB)\n throw new LocalSyncError(\n 'unavailable',\n 'IndexedDB is required by doxum/local-sync in this environment.'\n );\n return globalThis.indexedDB;\n};\n\nconst keyRange = (): typeof IDBKeyRange => {\n if (!globalThis.IDBKeyRange)\n throw new LocalSyncError(\n 'unavailable',\n 'IndexedDB is required by doxum/local-sync in this environment.'\n );\n return globalThis.IDBKeyRange;\n};\n\nconst string = (value: unknown, label: string): string => {\n if (typeof value === 'string') return value;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst nonNegativeInteger = (value: unknown, label: string): number => {\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) return value;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst positiveInteger = (value: unknown, label: string): number => {\n const parsed = nonNegativeInteger(value, label);\n if (parsed > 0) return parsed;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst documentRecord = (value: unknown): StoredDocument => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncError('consistency', 'Document record is malformed in IndexedDB.');\n const checkpointSeq = nonNegativeInteger(value.checkpointSeq, 'document.checkpointSeq');\n const headSeq = nonNegativeInteger(value.headSeq, 'document.headSeq');\n if (checkpointSeq > headSeq)\n throw new LocalSyncError('consistency', 'Document checkpoint exceeds its head sequence.');\n return Object.freeze({\n formatVersion: FORMAT_VERSION,\n documentId: string(value.documentId, 'document.documentId'),\n schemaVersion: positiveInteger(value.schemaVersion, 'document.schemaVersion'),\n checkpointSeq,\n headSeq,\n checkpoint: json(value.checkpoint, 'document.checkpoint'),\n });\n};\n\nconst commitRecord = (value: unknown): StoredCommit => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncError('consistency', 'Commit record is malformed in IndexedDB.');\n return Object.freeze({\n formatVersion: FORMAT_VERSION,\n documentId: string(value.documentId, 'commit.documentId'),\n seq: positiveInteger(value.seq, 'commit.seq'),\n changes: jsonChanges({ changes: value.changes }, 'commit.changes'),\n createdAt: nonNegativeInteger(value.createdAt, 'commit.createdAt'),\n });\n};\n\nexport type IndexedDbTimeline = {\n readonly initialize: (\n documentId: string,\n schemaVersion: number,\n checkpoint: JsonValue\n ) => Promise<StoredDocument>;\n readonly read: (documentId: string) => Promise<StoredDocument>;\n readonly tail: (documentId: string, afterSeq: number) => Promise<readonly StoredCommit[]>;\n readonly append: (input: {\n readonly documentId: string;\n readonly expectedHeadSeq: number;\n readonly changes: ChangeSet;\n }) => Promise<StoredCommit>;\n readonly close: () => void;\n};\n\nexport const openIndexedDbTimeline = async (databaseName: string): Promise<IndexedDbTimeline> => {\n const database = await new Promise<IDBDatabase>((resolve, reject) => {\n let incompatible = false;\n const open = factory().open(databaseName, DATABASE_VERSION);\n open.onerror = () =>\n reject(\n incompatible\n ? new LocalSyncError(\n 'consistency',\n 'Unsupported Doxum storage format. Existing data was left unchanged.'\n )\n : (open.error ?? new Error('Unable to open IndexedDB.'))\n );\n open.onblocked = () => reject(new Error(`IndexedDB database '${databaseName}' is blocked.`));\n open.onupgradeneeded = event => {\n if (event.oldVersion !== 0) {\n incompatible = true;\n open.transaction?.abort();\n return;\n }\n const db = open.result;\n if (!db.objectStoreNames.contains(DOCUMENTS))\n db.createObjectStore(DOCUMENTS, { keyPath: 'documentId' });\n if (!db.objectStoreNames.contains(COMMITS))\n db.createObjectStore(COMMITS, { keyPath: ['documentId', 'seq'] });\n };\n open.onsuccess = () => resolve(open.result);\n });\n\n const readDocument = async (\n transaction: IDBTransaction,\n documentId: string\n ): Promise<StoredDocument> => {\n const value = await request(transaction.objectStore(DOCUMENTS).get(documentId));\n if (value === undefined)\n throw new LocalSyncError('consistency', `Local document '${documentId}' does not exist.`);\n return documentRecord(value);\n };\n\n return {\n initialize: async (documentId, schemaVersion, checkpoint) => {\n const transaction = database.transaction(DOCUMENTS, 'readwrite');\n const store = transaction.objectStore(DOCUMENTS);\n const existing = await request(store.get(documentId));\n if (existing !== undefined) {\n const record = documentRecord(existing);\n if (record.schemaVersion !== schemaVersion)\n throw new LocalSyncError(\n 'schema-mismatch',\n `Local document '${documentId}' uses schema version ${record.schemaVersion}, but version ${schemaVersion} was requested.`\n );\n await complete(transaction);\n return record;\n }\n const record: StoredDocument = {\n formatVersion: FORMAT_VERSION,\n documentId,\n schemaVersion,\n checkpointSeq: 0,\n headSeq: 0,\n checkpoint,\n };\n store.put(record);\n await complete(transaction);\n return Object.freeze(record);\n },\n read: async documentId => {\n const transaction = database.transaction(DOCUMENTS, 'readonly');\n const record = await readDocument(transaction, documentId);\n await complete(transaction);\n return record;\n },\n tail: async (documentId, afterSeq) => {\n const transaction = database.transaction(COMMITS, 'readonly');\n const range = keyRange().bound(\n [documentId, afterSeq + 1],\n [documentId, Number.MAX_SAFE_INTEGER]\n );\n const values = await request(transaction.objectStore(COMMITS).getAll(range));\n await complete(transaction);\n return Object.freeze(values.map(commitRecord).sort((left, right) => left.seq - right.seq));\n },\n append: async input => {\n const transaction = database.transaction([DOCUMENTS, COMMITS], 'readwrite');\n const documents = transaction.objectStore(DOCUMENTS);\n const current = await readDocument(transaction, input.documentId);\n if (current.headSeq !== input.expectedHeadSeq)\n throw new LocalSyncError(\n 'consistency',\n 'Local document advanced before the leader could append its commit.'\n );\n const commit: StoredCommit = {\n formatVersion: FORMAT_VERSION,\n documentId: input.documentId,\n seq: current.headSeq + 1,\n changes: input.changes,\n createdAt: Date.now(),\n };\n documents.put({ ...current, headSeq: commit.seq });\n transaction.objectStore(COMMITS).put({ ...commit, changes: commit.changes.changes });\n await complete(transaction);\n return Object.freeze(commit);\n },\n close: () => database.close(),\n };\n};\n","import {\n installRuntimeWriteDriver,\n type RuntimeWriteDriverLease,\n type RuntimeWriteIntent,\n} from '../runtime/driver';\nimport type { DocumentCommit } from '../runtime/contract';\nimport type { Infer, ObjectSchema } from '../schema/model';\nimport { type AttachLocalSyncOptions, type LocalSync, type LocalSyncState } from './contract';\nimport { LocalSyncError, normalizeLocalSyncError } from './error';\nimport { json, jsonChanges } from './json';\nimport type { ChangeSet } from '../changes';\nimport { openIndexedDbTimeline, type StoredCommit } from './timeline';\n\ntype LockOptions = {\n readonly mode: 'exclusive';\n readonly ifAvailable?: boolean;\n};\n\ntype LockManager = {\n readonly request: <TResult>(\n name: string,\n options: LockOptions,\n callback: (lock: unknown | null) => TResult | PromiseLike<TResult>\n ) => Promise<TResult>;\n};\n\ntype Channel = {\n onmessage: ((event: MessageEvent<unknown>) => void) | null;\n postMessage(message: unknown): void;\n close(): void;\n};\n\ntype ChannelConstructor = new (name: string) => Channel;\n\ntype CommitNotification = {\n readonly kind: 'commit';\n readonly documentId: string;\n readonly headSeq: number;\n};\n\nconst locks = (): LockManager => {\n const value: unknown = globalThis.navigator?.locks;\n if (\n typeof value !== 'object' ||\n value === null ||\n !('request' in value) ||\n typeof value.request !== 'function'\n )\n throw new LocalSyncError(\n 'unavailable',\n 'Web Locks is required by doxum/local-sync in this environment.'\n );\n return value as LockManager;\n};\n\nconst channelConstructor = (): ChannelConstructor => {\n const value: unknown = globalThis.BroadcastChannel;\n if (typeof value !== 'function')\n throw new LocalSyncError(\n 'unavailable',\n 'BroadcastChannel is required by doxum/local-sync in this environment.'\n );\n return value as ChannelConstructor;\n};\n\nconst requiredString = (value: string, label: string): string => {\n if (value.length > 0) return value;\n throw new TypeError(`${label} must not be empty.`);\n};\n\nconst positiveInteger = (value: number, label: string): number => {\n if (Number.isSafeInteger(value) && value > 0) return value;\n throw new TypeError(`${label} must be a positive safe integer.`);\n};\n\nconst notification = (value: unknown): CommitNotification | undefined => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;\n const record = value as Record<string, unknown>;\n if (\n record.kind !== 'commit' ||\n typeof record.documentId !== 'string' ||\n typeof record.headSeq !== 'number' ||\n !Number.isSafeInteger(record.headSeq) ||\n record.headSeq < 0\n )\n return undefined;\n return record as CommitNotification;\n};\n\nexport const attachLocalSync = async <TSchema extends ObjectSchema<object>>(\n input: AttachLocalSyncOptions<TSchema>\n): Promise<LocalSync> => {\n const runtime = input.runtime;\n const databaseName = requiredString(input.database, 'database');\n const documentId = requiredString(input.documentId, 'documentId');\n const schemaVersion = positiveInteger(input.schemaVersion ?? 1, 'schemaVersion');\n const lockManager = locks();\n const BroadcastChannel = channelConstructor();\n const timeline = await openIndexedDbTimeline(databaseName).catch(error => {\n throw normalizeLocalSyncError(error, 'unavailable', 'Unable to open local sync storage.');\n });\n\n let channel: Channel | undefined;\n let unsubscribe: (() => void) | undefined;\n let driver: RuntimeWriteDriverLease | undefined;\n try {\n const stored = await timeline.initialize(\n documentId,\n schemaVersion,\n json(runtime.snapshot(), 'runtime snapshot')\n );\n let headSeq = stored.checkpointSeq;\n let checkpointSeq = stored.checkpointSeq;\n let role: 'leader' | 'follower' = 'follower';\n let fault: LocalSyncError | undefined;\n let closing = false;\n let disposed = false;\n let releaseLeadership: (() => void) | undefined;\n let leadershipLease: Promise<void> | undefined;\n let queued: Promise<void> = Promise.resolve();\n let snapshot: LocalSyncState = Object.freeze({ status: role, headSeq, checkpointSeq });\n let stateRevision = 0;\n const stateListeners = new Set<() => void>();\n const publishState = (): void => {\n const next: LocalSyncState = disposed\n ? { status: 'disposed' }\n : fault\n ? { status: 'error', headSeq, checkpointSeq, error: fault }\n : { status: role, headSeq, checkpointSeq };\n if (\n snapshot.status === next.status &&\n (snapshot.status === 'disposed' ||\n (next.status !== 'disposed' &&\n snapshot.headSeq === next.headSeq &&\n snapshot.checkpointSeq === next.checkpointSeq &&\n (snapshot.status !== 'error' ||\n (next.status === 'error' && snapshot.error === next.error))))\n )\n return;\n snapshot = Object.freeze(next);\n stateRevision++;\n for (const listener of [...stateListeners]) {\n if (!stateListeners.has(listener)) continue;\n try {\n listener();\n } catch {\n // Consumer state listeners are isolated from synchronization state.\n }\n }\n };\n\n const fail = (error: unknown): LocalSyncError => {\n const failure = normalizeLocalSyncError(\n error,\n 'unavailable',\n 'Local synchronization failed.'\n );\n if (fault || disposed) return fault ?? failure;\n fault = failure;\n publishState();\n try {\n input.onError?.(failure);\n } catch {\n // Error reporting cannot repair or replace the original synchronization failure.\n }\n return failure;\n };\n\n const assertOpen = (): void => {\n if (closing || disposed)\n throw new LocalSyncError('disposed', 'Local sync has been disposed.');\n if (fault) throw fault;\n };\n\n const assertLeader = (intent: RuntimeWriteIntent): void => {\n assertOpen();\n if (role !== 'leader')\n throw new LocalSyncError(\n 'read-only',\n 'This tab is following the local document and cannot write until it becomes the leader.'\n );\n if (intent.kind === 'replace' || (intent.kind === 'apply' && intent.source === 'remote'))\n throw new LocalSyncError(\n 'unsupported-operation',\n 'Local sync appends committed changes. runtime.replace() and externally supplied remote changes are unavailable while it is attached.'\n );\n };\n\n const runRuntime = <TResult>(run: () => TResult): TResult => driver?.run(run) ?? run();\n const replayRuntime = <TResult>(run: () => TResult, message: string): TResult => {\n try {\n return runRuntime(run);\n } catch (error) {\n throw normalizeLocalSyncError(error, 'consistency', message);\n }\n };\n\n const applyStored = (commit: StoredCommit): void => {\n if (commit.seq !== headSeq + 1)\n throw new LocalSyncError('consistency', 'Local commit log contains a sequence gap.');\n const result = replayRuntime(\n () =>\n runtime.apply(commit.changes, {\n expectedRevision: runtime.revision(),\n source: 'remote',\n }),\n 'A stored local ChangeSet could not be replayed.'\n );\n if (result.status !== 'committed')\n throw new LocalSyncError('consistency', 'A stored local ChangeSet could not be applied.');\n headSeq = commit.seq;\n };\n\n const restore = async (reset = false): Promise<void> => {\n const current = await timeline.read(documentId);\n if (current.schemaVersion !== schemaVersion)\n throw new LocalSyncError(\n 'consistency',\n 'Local document schema changed while this attachment was open.'\n );\n if (headSeq > current.headSeq)\n throw new LocalSyncError('consistency', 'The local timeline moved behind this attachment.');\n if (reset || headSeq < current.checkpointSeq) {\n const result = replayRuntime(\n () => runtime.replace(current.checkpoint as Infer<TSchema>, { source: 'remote' }),\n 'The local checkpoint could not be restored.'\n );\n if (result.status === 'rejected')\n throw new LocalSyncError('consistency', 'The local checkpoint could not be restored.');\n headSeq = current.checkpointSeq;\n }\n const tail = await timeline.tail(documentId, headSeq);\n for (const commit of tail) applyStored(commit);\n if (headSeq !== current.headSeq)\n throw new LocalSyncError(\n 'consistency',\n 'Local commit log does not reach its recorded head sequence.'\n );\n checkpointSeq = current.checkpointSeq;\n publishState();\n };\n\n const enqueue = (run: () => Promise<void>): Promise<void> => {\n const next = queued.then(run, run);\n queued = next.then(\n () => undefined,\n error => {\n fail(error);\n }\n );\n return next;\n };\n\n const persist = async (changes: ChangeSet): Promise<void> => {\n const storedCommit = await timeline.append({\n documentId,\n expectedHeadSeq: headSeq,\n changes,\n });\n if (storedCommit.seq !== headSeq + 1)\n throw new LocalSyncError(\n 'consistency',\n 'A local commit was assigned an unexpected sequence.'\n );\n headSeq = storedCommit.seq;\n publishState();\n channel?.postMessage({\n kind: 'commit',\n documentId,\n headSeq,\n } satisfies CommitNotification);\n };\n\n const record = (commit: DocumentCommit<TSchema>): void => {\n try {\n const changes = jsonChanges(\n commit.changes,\n 'local commit changes',\n input.changeLimits ?? {}\n );\n void enqueue(() => persist(changes)).catch(() => undefined);\n } catch (error) {\n fail(error);\n }\n };\n\n const holdLeadership = (): Promise<void> =>\n new Promise(resolve => {\n releaseLeadership = resolve;\n });\n\n const lead = async (activated?: () => void): Promise<void> => {\n if (closing || disposed || fault) return;\n await restore();\n if (closing || disposed || fault) return;\n role = 'leader';\n publishState();\n activated?.();\n try {\n await holdLeadership();\n } finally {\n releaseLeadership = undefined;\n if (!closing && !disposed && !fault) {\n role = 'follower';\n publishState();\n }\n }\n };\n\n const watchLeadership = (): void => {\n leadershipLease = lockManager\n .request(`doxum:${documentId}`, { mode: 'exclusive' }, async () => {\n try {\n await lead();\n } catch (error) {\n fail(error);\n }\n })\n .catch(error => {\n if (!closing && !disposed) fail(error);\n });\n };\n\n const claimInitialLeadership = async (): Promise<void> => {\n let resolve!: (value: boolean) => void;\n const claimed = new Promise<boolean>(done => {\n resolve = done;\n });\n leadershipLease = lockManager\n .request(`doxum:${documentId}`, { mode: 'exclusive', ifAvailable: true }, async lock => {\n if (lock === null) {\n resolve(false);\n return;\n }\n try {\n await lead(() => resolve(true));\n } catch (error) {\n fail(error);\n resolve(false);\n }\n })\n .catch(error => {\n if (!closing && !disposed) fail(error);\n resolve(false);\n });\n if (!(await claimed) && !fault && !closing && !disposed) watchLeadership();\n };\n\n await restore(true);\n // The timeline, not a runtime constructed before attachment, is the baseline\n // for local undo. Even an identical restored snapshot must discard prior history.\n runtime.history.clear();\n driver = installRuntimeWriteDriver(runtime, { assertWritable: assertLeader });\n channel = new BroadcastChannel(`doxum:${databaseName}:${documentId}`);\n unsubscribe = runtime.subscribe(commit => {\n if (\n (commit.source === 'local' || commit.source === 'system' || commit.source === 'history') &&\n !closing &&\n !disposed\n )\n record(commit);\n });\n channel.onmessage = event => {\n const message = notification(event.data);\n if (\n !message ||\n message.documentId !== documentId ||\n message.headSeq <= headSeq ||\n closing ||\n disposed ||\n fault\n )\n return;\n void enqueue(restore).catch(() => undefined);\n };\n await claimInitialLeadership();\n if (fault) throw fault;\n\n const localSync: LocalSync = {\n state: Object.freeze({\n current: () => snapshot,\n revision: () => stateRevision,\n subscribe: (listener: () => void) => {\n if (disposed) throw new LocalSyncError('disposed', 'Local sync has been disposed.');\n stateListeners.add(listener);\n return () => {\n stateListeners.delete(listener);\n };\n },\n }),\n flush: async (): Promise<void> => {\n assertOpen();\n try {\n await enqueue(restore);\n } catch (error) {\n throw fail(error);\n }\n assertOpen();\n },\n dispose: async (): Promise<void> => {\n if (disposed) return;\n if (closing) {\n await queued;\n return;\n }\n closing = true;\n unsubscribe?.();\n await queued;\n releaseLeadership?.();\n if (role === 'leader') await leadershipLease;\n disposed = true;\n driver?.dispose();\n channel?.close();\n timeline.close();\n publishState();\n stateListeners.clear();\n },\n };\n return Object.freeze(localSync);\n } catch (error) {\n unsubscribe?.();\n driver?.dispose();\n channel?.close();\n timeline.close();\n throw normalizeLocalSyncError(error, 'unavailable', 'Unable to attach local sync.');\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"local-sync.js","names":["changeSet.decodeChanges","changeSet.changeCount","positiveInteger"],"sources":["../core/src/local-sync/error.ts","../core/src/local-sync/json.ts","../core/src/local-sync/timeline.ts","../core/src/local-sync/session.ts"],"sourcesContent":["export type LocalSyncErrorCode =\n | 'unavailable'\n | 'schema-mismatch'\n | 'consistency'\n | 'read-only'\n | 'unsupported-operation'\n | 'disposed'\n | 'invalid-data';\n\nexport class LocalSyncError extends Error {\n readonly code: LocalSyncErrorCode;\n\n constructor(code: LocalSyncErrorCode, message: string, options?: { readonly cause?: unknown }) {\n super(message, options);\n this.name = 'LocalSyncError';\n this.code = code;\n }\n}\n\nexport const normalizeLocalSyncError = (\n error: unknown,\n code: LocalSyncErrorCode,\n message: string\n): LocalSyncError =>\n error instanceof LocalSyncError ? error : new LocalSyncError(code, message, { cause: error });\n","import { isPlainObject } from '@/value/record';\nimport * as changeSet from '@/mutation/changes';\nimport type { ChangeSet } from '@/changes';\nimport { LocalSyncError } from './error';\n\nexport type JsonPrimitive = null | boolean | number | string;\nexport type JsonValue =\n JsonPrimitive | readonly JsonValue[] | { readonly [key: string]: JsonValue };\n\nexport type JsonChangeLimits = {\n readonly maxChanges?: number;\n readonly maxBytes?: number;\n readonly maxDepth?: number;\n readonly maxStringLength?: number;\n};\n\ntype ResolvedJsonLimits = {\n readonly maxChanges: number;\n readonly maxBytes: number;\n readonly maxDepth: number;\n readonly maxStringLength: number;\n};\n\nexport const defaultJsonChangeLimits: Readonly<ResolvedJsonLimits> = Object.freeze({\n maxChanges: 1_000,\n maxBytes: 1_000_000,\n maxDepth: 64,\n maxStringLength: 256_000,\n});\n\nconst describePath = (path: readonly string[]): string =>\n path.length === 0 ? 'value' : path.join('.');\n\nconst limit = (value: number | undefined, fallback: number, label: string): number => {\n if (value === undefined) return fallback;\n if (Number.isSafeInteger(value) && value > 0) return value;\n throw new TypeError(`${label} must be a positive safe integer.`);\n};\n\nconst resolveLimits = (input: JsonChangeLimits | undefined): ResolvedJsonLimits => ({\n maxChanges: limit(input?.maxChanges, defaultJsonChangeLimits.maxChanges, 'maxChanges'),\n maxBytes: limit(input?.maxBytes, defaultJsonChangeLimits.maxBytes, 'maxBytes'),\n maxDepth: limit(input?.maxDepth, defaultJsonChangeLimits.maxDepth, 'maxDepth'),\n maxStringLength: limit(\n input?.maxStringLength,\n defaultJsonChangeLimits.maxStringLength,\n 'maxStringLength'\n ),\n});\n\nconst validate = (\n value: unknown,\n path: string[],\n limits: ResolvedJsonLimits | undefined,\n depth: number,\n ancestors: WeakSet<object>\n): JsonValue => {\n if (limits && depth > limits.maxDepth)\n throw new LocalSyncError(\n 'invalid-data',\n `${describePath(path)} exceeds the maximum JSON depth.`\n );\n if (value === null || typeof value === 'boolean') return value;\n if (typeof value === 'string') {\n if (!limits || value.length <= limits.maxStringLength) return value;\n throw new LocalSyncError(\n 'invalid-data',\n `${describePath(path)} exceeds the maximum string length.`\n );\n }\n if (typeof value === 'number') {\n if (Number.isFinite(value)) return value;\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a non-finite number.`);\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value))\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (let index = 0; index < value.length; index += 1) {\n path.push(String(index));\n validate(value[index], path, limits, depth + 1, ancestors);\n path.pop();\n }\n } finally {\n ancestors.delete(value);\n }\n return value as readonly JsonValue[];\n }\n if (isPlainObject(value)) {\n if (ancestors.has(value))\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (const key of Object.keys(value)) {\n path.push(key);\n validate(value[key], path, limits, depth + 1, ancestors);\n path.pop();\n }\n } finally {\n ancestors.delete(value);\n }\n return value as { readonly [key: string]: JsonValue };\n }\n throw new LocalSyncError('invalid-data', `${describePath(path)} must be JSON data.`);\n};\n\nexport const json = (value: unknown, label: string): JsonValue =>\n validate(value, [label], undefined, 0, new WeakSet());\n\nexport const jsonChanges = (value: unknown, label: string, input?: JsonChangeLimits): ChangeSet => {\n const limits = input === undefined ? undefined : resolveLimits(input);\n let changes: ChangeSet;\n try {\n changes = changeSet.decodeChanges(value);\n } catch {\n throw new LocalSyncError('invalid-data', `${label} contains an invalid ChangeSet.`);\n }\n if (limits && changeSet.changeCount(changes) > limits.maxChanges)\n throw new LocalSyncError('invalid-data', `${label} exceeds the maximum change count.`);\n validate(changes.changes, [label], limits, 0, new WeakSet());\n if (limits) {\n const serialized = JSON.stringify(changes.changes);\n if (new TextEncoder().encode(serialized).byteLength > limits.maxBytes)\n throw new LocalSyncError('invalid-data', `${label} exceeds the maximum ChangeSet size.`);\n }\n return changes;\n};\n","import { LocalSyncError, normalizeLocalSyncError } from './error';\nimport { json, jsonChanges, type JsonValue } from './json';\nimport type { ChangeSet } from '@/changes';\nimport { isRecord } from '@/value/record';\n\nconst DATABASE_VERSION = 5;\nconst FORMAT_VERSION = 3;\nconst DOCUMENTS = 'documents';\nconst COMMITS = 'commits';\n\nexport type StoredDocument = {\n readonly formatVersion: number;\n readonly documentId: string;\n readonly schemaVersion: number;\n readonly checkpointSeq: number;\n readonly headSeq: number;\n readonly checkpoint: JsonValue;\n};\n\nexport type StoredCommit = {\n readonly formatVersion: number;\n readonly documentId: string;\n readonly seq: number;\n readonly changes: ChangeSet;\n readonly createdAt: number;\n};\n\nconst request = <T>(value: IDBRequest<T>): Promise<T> =>\n new Promise((resolve, reject) => {\n value.onsuccess = () => resolve(value.result);\n value.onerror = () =>\n reject(normalizeLocalSyncError(value.error, 'unavailable', 'IndexedDB request failed.'));\n });\n\nconst complete = (transaction: IDBTransaction): Promise<void> =>\n new Promise((resolve, reject) => {\n transaction.oncomplete = () => resolve();\n transaction.onabort = () =>\n reject(\n normalizeLocalSyncError(\n transaction.error,\n 'unavailable',\n 'IndexedDB transaction was aborted.'\n )\n );\n transaction.onerror = () =>\n reject(\n normalizeLocalSyncError(transaction.error, 'unavailable', 'IndexedDB transaction failed.')\n );\n });\n\nconst factory = (): IDBFactory => {\n if (!globalThis.indexedDB)\n throw new LocalSyncError(\n 'unavailable',\n 'IndexedDB is required by doxum/local-sync in this environment.'\n );\n return globalThis.indexedDB;\n};\n\nconst keyRange = (): typeof IDBKeyRange => {\n if (!globalThis.IDBKeyRange)\n throw new LocalSyncError(\n 'unavailable',\n 'IndexedDB is required by doxum/local-sync in this environment.'\n );\n return globalThis.IDBKeyRange;\n};\n\nconst string = (value: unknown, label: string): string => {\n if (typeof value === 'string') return value;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst nonNegativeInteger = (value: unknown, label: string): number => {\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) return value;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst positiveInteger = (value: unknown, label: string): number => {\n const parsed = nonNegativeInteger(value, label);\n if (parsed > 0) return parsed;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst documentRecord = (value: unknown): StoredDocument => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncError('consistency', 'Document record is malformed in IndexedDB.');\n const checkpointSeq = nonNegativeInteger(value.checkpointSeq, 'document.checkpointSeq');\n const headSeq = nonNegativeInteger(value.headSeq, 'document.headSeq');\n if (checkpointSeq > headSeq)\n throw new LocalSyncError('consistency', 'Document checkpoint exceeds its head sequence.');\n return Object.freeze({\n formatVersion: FORMAT_VERSION,\n documentId: string(value.documentId, 'document.documentId'),\n schemaVersion: positiveInteger(value.schemaVersion, 'document.schemaVersion'),\n checkpointSeq,\n headSeq,\n checkpoint: json(value.checkpoint, 'document.checkpoint'),\n });\n};\n\nconst commitRecord = (value: unknown): StoredCommit => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncError('consistency', 'Commit record is malformed in IndexedDB.');\n return Object.freeze({\n formatVersion: FORMAT_VERSION,\n documentId: string(value.documentId, 'commit.documentId'),\n seq: positiveInteger(value.seq, 'commit.seq'),\n changes: jsonChanges({ changes: value.changes }, 'commit.changes'),\n createdAt: nonNegativeInteger(value.createdAt, 'commit.createdAt'),\n });\n};\n\nexport type IndexedDbTimeline = {\n readonly initialize: (\n documentId: string,\n schemaVersion: number,\n checkpoint: JsonValue\n ) => Promise<StoredDocument>;\n readonly read: (documentId: string) => Promise<StoredDocument>;\n readonly tail: (documentId: string, afterSeq: number) => Promise<readonly StoredCommit[]>;\n readonly append: (input: {\n readonly documentId: string;\n readonly expectedHeadSeq: number;\n readonly changes: ChangeSet;\n }) => Promise<StoredCommit>;\n readonly close: () => void;\n};\n\nexport const openIndexedDbTimeline = async (databaseName: string): Promise<IndexedDbTimeline> => {\n const database = await new Promise<IDBDatabase>((resolve, reject) => {\n let incompatible = false;\n const open = factory().open(databaseName, DATABASE_VERSION);\n open.onerror = () =>\n reject(\n incompatible\n ? new LocalSyncError(\n 'consistency',\n 'Unsupported Doxum storage format. Existing data was left unchanged.'\n )\n : (open.error ?? new Error('Unable to open IndexedDB.'))\n );\n open.onblocked = () => reject(new Error(`IndexedDB database '${databaseName}' is blocked.`));\n open.onupgradeneeded = event => {\n if (event.oldVersion !== 0) {\n incompatible = true;\n open.transaction?.abort();\n return;\n }\n const db = open.result;\n if (!db.objectStoreNames.contains(DOCUMENTS))\n db.createObjectStore(DOCUMENTS, { keyPath: 'documentId' });\n if (!db.objectStoreNames.contains(COMMITS))\n db.createObjectStore(COMMITS, { keyPath: ['documentId', 'seq'] });\n };\n open.onsuccess = () => resolve(open.result);\n });\n\n const readDocument = async (\n transaction: IDBTransaction,\n documentId: string\n ): Promise<StoredDocument> => {\n const value = await request(transaction.objectStore(DOCUMENTS).get(documentId));\n if (value === undefined)\n throw new LocalSyncError('consistency', `Local document '${documentId}' does not exist.`);\n return documentRecord(value);\n };\n\n return {\n initialize: async (documentId, schemaVersion, checkpoint) => {\n const transaction = database.transaction(DOCUMENTS, 'readwrite');\n const store = transaction.objectStore(DOCUMENTS);\n const existing = await request(store.get(documentId));\n if (existing !== undefined) {\n const record = documentRecord(existing);\n if (record.schemaVersion !== schemaVersion)\n throw new LocalSyncError(\n 'schema-mismatch',\n `Local document '${documentId}' uses schema version ${record.schemaVersion}, but version ${schemaVersion} was requested.`\n );\n await complete(transaction);\n return record;\n }\n const record: StoredDocument = {\n formatVersion: FORMAT_VERSION,\n documentId,\n schemaVersion,\n checkpointSeq: 0,\n headSeq: 0,\n checkpoint,\n };\n store.put(record);\n await complete(transaction);\n return Object.freeze(record);\n },\n read: async documentId => {\n const transaction = database.transaction(DOCUMENTS, 'readonly');\n const record = await readDocument(transaction, documentId);\n await complete(transaction);\n return record;\n },\n tail: async (documentId, afterSeq) => {\n const transaction = database.transaction(COMMITS, 'readonly');\n const range = keyRange().bound(\n [documentId, afterSeq + 1],\n [documentId, Number.MAX_SAFE_INTEGER]\n );\n const values = await request(transaction.objectStore(COMMITS).getAll(range));\n await complete(transaction);\n return Object.freeze(values.map(commitRecord).sort((left, right) => left.seq - right.seq));\n },\n append: async input => {\n const transaction = database.transaction([DOCUMENTS, COMMITS], 'readwrite');\n const documents = transaction.objectStore(DOCUMENTS);\n const current = await readDocument(transaction, input.documentId);\n if (current.headSeq !== input.expectedHeadSeq)\n throw new LocalSyncError(\n 'consistency',\n 'Local document advanced before the leader could append its commit.'\n );\n const commit: StoredCommit = {\n formatVersion: FORMAT_VERSION,\n documentId: input.documentId,\n seq: current.headSeq + 1,\n changes: input.changes,\n createdAt: Date.now(),\n };\n documents.put({ ...current, headSeq: commit.seq });\n transaction.objectStore(COMMITS).put({ ...commit, changes: commit.changes.changes });\n await complete(transaction);\n return Object.freeze(commit);\n },\n close: () => database.close(),\n };\n};\n","import {\n installRuntimeWriteDriver,\n type RuntimeWriteDriverLease,\n type RuntimeWriteIntent,\n} from '@/runtime/driver';\nimport type { DocumentCommit } from '@/runtime/contract';\nimport type { Infer, ObjectSchema } from '@/schema/model';\nimport { type AttachLocalSyncOptions, type LocalSync, type LocalSyncState } from './contract';\nimport { LocalSyncError, normalizeLocalSyncError } from './error';\nimport { json, jsonChanges } from './json';\nimport type { ChangeSet } from '@/changes';\nimport { openIndexedDbTimeline, type StoredCommit } from './timeline';\n\ntype LockOptions = {\n readonly mode: 'exclusive';\n readonly ifAvailable?: boolean;\n};\n\ntype LockManager = {\n readonly request: <TResult>(\n name: string,\n options: LockOptions,\n callback: (lock: unknown | null) => TResult | PromiseLike<TResult>\n ) => Promise<TResult>;\n};\n\ntype Channel = {\n onmessage: ((event: MessageEvent<unknown>) => void) | null;\n postMessage(message: unknown): void;\n close(): void;\n};\n\ntype ChannelConstructor = new (name: string) => Channel;\n\ntype CommitNotification = {\n readonly kind: 'commit';\n readonly documentId: string;\n readonly headSeq: number;\n};\n\nconst locks = (): LockManager => {\n const value: unknown = globalThis.navigator?.locks;\n if (\n typeof value !== 'object' ||\n value === null ||\n !('request' in value) ||\n typeof value.request !== 'function'\n )\n throw new LocalSyncError(\n 'unavailable',\n 'Web Locks is required by doxum/local-sync in this environment.'\n );\n return value as LockManager;\n};\n\nconst channelConstructor = (): ChannelConstructor => {\n const value: unknown = globalThis.BroadcastChannel;\n if (typeof value !== 'function')\n throw new LocalSyncError(\n 'unavailable',\n 'BroadcastChannel is required by doxum/local-sync in this environment.'\n );\n return value as ChannelConstructor;\n};\n\nconst requiredString = (value: string, label: string): string => {\n if (value.length > 0) return value;\n throw new TypeError(`${label} must not be empty.`);\n};\n\nconst positiveInteger = (value: number, label: string): number => {\n if (Number.isSafeInteger(value) && value > 0) return value;\n throw new TypeError(`${label} must be a positive safe integer.`);\n};\n\nconst notification = (value: unknown): CommitNotification | undefined => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;\n const record = value as Record<string, unknown>;\n if (\n record.kind !== 'commit' ||\n typeof record.documentId !== 'string' ||\n typeof record.headSeq !== 'number' ||\n !Number.isSafeInteger(record.headSeq) ||\n record.headSeq < 0\n )\n return undefined;\n return record as CommitNotification;\n};\n\nexport const attachLocalSync = async <TSchema extends ObjectSchema<object>>(\n input: AttachLocalSyncOptions<TSchema>\n): Promise<LocalSync> => {\n const runtime = input.runtime;\n const databaseName = requiredString(input.database, 'database');\n const documentId = requiredString(input.documentId, 'documentId');\n const schemaVersion = positiveInteger(input.schemaVersion ?? 1, 'schemaVersion');\n const lockManager = locks();\n const BroadcastChannel = channelConstructor();\n const timeline = await openIndexedDbTimeline(databaseName).catch(error => {\n throw normalizeLocalSyncError(error, 'unavailable', 'Unable to open local sync storage.');\n });\n\n let channel: Channel | undefined;\n let unsubscribe: (() => void) | undefined;\n let driver: RuntimeWriteDriverLease | undefined;\n try {\n const stored = await timeline.initialize(\n documentId,\n schemaVersion,\n json(runtime.snapshot(), 'runtime snapshot')\n );\n let headSeq = stored.checkpointSeq;\n let checkpointSeq = stored.checkpointSeq;\n let role: 'leader' | 'follower' = 'follower';\n let fault: LocalSyncError | undefined;\n let closing = false;\n let disposed = false;\n let releaseLeadership: (() => void) | undefined;\n let leadershipLease: Promise<void> | undefined;\n let queued: Promise<void> = Promise.resolve();\n let snapshot: LocalSyncState = Object.freeze({ status: role, headSeq, checkpointSeq });\n let stateRevision = 0;\n const stateListeners = new Set<() => void>();\n const publishState = (): void => {\n const next: LocalSyncState = disposed\n ? { status: 'disposed' }\n : fault\n ? { status: 'error', headSeq, checkpointSeq, error: fault }\n : { status: role, headSeq, checkpointSeq };\n if (\n snapshot.status === next.status &&\n (snapshot.status === 'disposed' ||\n (next.status !== 'disposed' &&\n snapshot.headSeq === next.headSeq &&\n snapshot.checkpointSeq === next.checkpointSeq &&\n (snapshot.status !== 'error' ||\n (next.status === 'error' && snapshot.error === next.error))))\n )\n return;\n snapshot = Object.freeze(next);\n stateRevision++;\n for (const listener of [...stateListeners]) {\n if (!stateListeners.has(listener)) continue;\n try {\n listener();\n } catch {\n // Consumer state listeners are isolated from synchronization state.\n }\n }\n };\n\n const fail = (error: unknown): LocalSyncError => {\n const failure = normalizeLocalSyncError(\n error,\n 'unavailable',\n 'Local synchronization failed.'\n );\n if (fault || disposed) return fault ?? failure;\n fault = failure;\n publishState();\n try {\n input.onError?.(failure);\n } catch {\n // Error reporting cannot repair or replace the original synchronization failure.\n }\n return failure;\n };\n\n const assertOpen = (): void => {\n if (closing || disposed)\n throw new LocalSyncError('disposed', 'Local sync has been disposed.');\n if (fault) throw fault;\n };\n\n const assertLeader = (intent: RuntimeWriteIntent): void => {\n assertOpen();\n if (role !== 'leader')\n throw new LocalSyncError(\n 'read-only',\n 'This tab is following the local document and cannot write until it becomes the leader.'\n );\n if (intent.kind === 'replace' || (intent.kind === 'apply' && intent.source === 'remote'))\n throw new LocalSyncError(\n 'unsupported-operation',\n 'Local sync appends committed changes. runtime.replace() and externally supplied remote changes are unavailable while it is attached.'\n );\n };\n\n const runRuntime = <TResult>(run: () => TResult): TResult => driver?.run(run) ?? run();\n const replayRuntime = <TResult>(run: () => TResult, message: string): TResult => {\n try {\n return runRuntime(run);\n } catch (error) {\n throw normalizeLocalSyncError(error, 'consistency', message);\n }\n };\n\n const applyStored = (commit: StoredCommit): void => {\n if (commit.seq !== headSeq + 1)\n throw new LocalSyncError('consistency', 'Local commit log contains a sequence gap.');\n const result = replayRuntime(\n () =>\n runtime.apply(commit.changes, {\n expectedRevision: runtime.revision(),\n source: 'remote',\n }),\n 'A stored local ChangeSet could not be replayed.'\n );\n if (result.status !== 'committed')\n throw new LocalSyncError('consistency', 'A stored local ChangeSet could not be applied.');\n headSeq = commit.seq;\n };\n\n const restore = async (reset = false): Promise<void> => {\n const current = await timeline.read(documentId);\n if (current.schemaVersion !== schemaVersion)\n throw new LocalSyncError(\n 'consistency',\n 'Local document schema changed while this attachment was open.'\n );\n if (headSeq > current.headSeq)\n throw new LocalSyncError('consistency', 'The local timeline moved behind this attachment.');\n if (reset || headSeq < current.checkpointSeq) {\n const result = replayRuntime(\n () => runtime.replace(current.checkpoint as Infer<TSchema>, { source: 'remote' }),\n 'The local checkpoint could not be restored.'\n );\n if (result.status === 'rejected')\n throw new LocalSyncError('consistency', 'The local checkpoint could not be restored.');\n headSeq = current.checkpointSeq;\n }\n const tail = await timeline.tail(documentId, headSeq);\n for (const commit of tail) applyStored(commit);\n if (headSeq !== current.headSeq)\n throw new LocalSyncError(\n 'consistency',\n 'Local commit log does not reach its recorded head sequence.'\n );\n checkpointSeq = current.checkpointSeq;\n publishState();\n };\n\n const enqueue = (run: () => Promise<void>): Promise<void> => {\n const next = queued.then(run, run);\n queued = next.then(\n () => undefined,\n error => {\n fail(error);\n }\n );\n return next;\n };\n\n const persist = async (changes: ChangeSet): Promise<void> => {\n const storedCommit = await timeline.append({\n documentId,\n expectedHeadSeq: headSeq,\n changes,\n });\n if (storedCommit.seq !== headSeq + 1)\n throw new LocalSyncError(\n 'consistency',\n 'A local commit was assigned an unexpected sequence.'\n );\n headSeq = storedCommit.seq;\n publishState();\n channel?.postMessage({\n kind: 'commit',\n documentId,\n headSeq,\n } satisfies CommitNotification);\n };\n\n const record = (commit: DocumentCommit<TSchema>): void => {\n try {\n const changes = jsonChanges(\n commit.changes,\n 'local commit changes',\n input.changeLimits ?? {}\n );\n void enqueue(() => persist(changes)).catch(() => undefined);\n } catch (error) {\n fail(error);\n }\n };\n\n const holdLeadership = (): Promise<void> =>\n new Promise(resolve => {\n releaseLeadership = resolve;\n });\n\n const lead = async (activated?: () => void): Promise<void> => {\n if (closing || disposed || fault) return;\n await restore();\n if (closing || disposed || fault) return;\n role = 'leader';\n publishState();\n activated?.();\n try {\n await holdLeadership();\n } finally {\n releaseLeadership = undefined;\n if (!closing && !disposed && !fault) {\n role = 'follower';\n publishState();\n }\n }\n };\n\n const watchLeadership = (): void => {\n leadershipLease = lockManager\n .request(`doxum:${documentId}`, { mode: 'exclusive' }, async () => {\n try {\n await lead();\n } catch (error) {\n fail(error);\n }\n })\n .catch(error => {\n if (!closing && !disposed) fail(error);\n });\n };\n\n const claimInitialLeadership = async (): Promise<void> => {\n let resolve!: (value: boolean) => void;\n const claimed = new Promise<boolean>(done => {\n resolve = done;\n });\n leadershipLease = lockManager\n .request(`doxum:${documentId}`, { mode: 'exclusive', ifAvailable: true }, async lock => {\n if (lock === null) {\n resolve(false);\n return;\n }\n try {\n await lead(() => resolve(true));\n } catch (error) {\n fail(error);\n resolve(false);\n }\n })\n .catch(error => {\n if (!closing && !disposed) fail(error);\n resolve(false);\n });\n if (!(await claimed) && !fault && !closing && !disposed) watchLeadership();\n };\n\n await restore(true);\n // The timeline, not a runtime constructed before attachment, is the baseline\n // for local undo. Even an identical restored snapshot must discard prior history.\n runtime.history.clear();\n driver = installRuntimeWriteDriver(runtime, { assertWritable: assertLeader });\n channel = new BroadcastChannel(`doxum:${databaseName}:${documentId}`);\n unsubscribe = runtime.subscribe(commit => {\n if (\n (commit.source === 'local' || commit.source === 'system' || commit.source === 'history') &&\n !closing &&\n !disposed\n )\n record(commit);\n });\n channel.onmessage = event => {\n const message = notification(event.data);\n if (\n !message ||\n message.documentId !== documentId ||\n message.headSeq <= headSeq ||\n closing ||\n disposed ||\n fault\n )\n return;\n void enqueue(restore).catch(() => undefined);\n };\n await claimInitialLeadership();\n if (fault) throw fault;\n\n const localSync: LocalSync = {\n state: Object.freeze({\n current: () => snapshot,\n revision: () => stateRevision,\n subscribe: (listener: () => void) => {\n if (disposed) throw new LocalSyncError('disposed', 'Local sync has been disposed.');\n stateListeners.add(listener);\n return () => {\n stateListeners.delete(listener);\n };\n },\n }),\n flush: async (): Promise<void> => {\n assertOpen();\n try {\n await enqueue(restore);\n } catch (error) {\n throw fail(error);\n }\n assertOpen();\n },\n dispose: async (): Promise<void> => {\n if (disposed) return;\n if (closing) {\n await queued;\n return;\n }\n closing = true;\n unsubscribe?.();\n await queued;\n releaseLeadership?.();\n if (role === 'leader') await leadershipLease;\n disposed = true;\n driver?.dispose();\n channel?.close();\n timeline.close();\n publishState();\n stateListeners.clear();\n },\n };\n return Object.freeze(localSync);\n } catch (error) {\n unsubscribe?.();\n driver?.dispose();\n channel?.close();\n timeline.close();\n throw normalizeLocalSyncError(error, 'unavailable', 'Unable to attach local sync.');\n }\n};\n"],"mappings":"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"}
|