doxum 0.1.7 → 0.1.9

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.
Files changed (70) hide show
  1. package/README.md +217 -296
  2. package/dist/contract-BNStLbSE.d.ts +441 -0
  3. package/dist/contract-CIU5FCC1.d.cts +441 -0
  4. package/dist/driver-BlR81Dqg.js +200 -0
  5. package/dist/driver-BlR81Dqg.js.map +1 -0
  6. package/dist/driver-xOIkwrB8.cjs +241 -0
  7. package/dist/driver-xOIkwrB8.cjs.map +1 -0
  8. package/dist/index.cjs +666 -2222
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +10 -20
  11. package/dist/index.d.ts +10 -18
  12. package/dist/index.js +634 -2189
  13. package/dist/index.js.map +1 -1
  14. package/dist/{integration-CZwCwFBS.cjs → integration-C87tjRop.cjs} +4 -9
  15. package/dist/integration-C87tjRop.cjs.map +1 -0
  16. package/dist/{integration-B56u1l9V.js → integration-D5XCBLJ8.js} +2 -7
  17. package/dist/integration-D5XCBLJ8.js.map +1 -0
  18. package/dist/integration.cjs +11 -11
  19. package/dist/integration.d.cts +10 -4
  20. package/dist/integration.d.ts +10 -4
  21. package/dist/integration.js +4 -3
  22. package/dist/issue-DVaGQGeP.js +576 -0
  23. package/dist/issue-DVaGQGeP.js.map +1 -0
  24. package/dist/issue-DhrNdQNg.cjs +797 -0
  25. package/dist/issue-DhrNdQNg.cjs.map +1 -0
  26. package/dist/local-sync.cjs +67 -39
  27. package/dist/local-sync.cjs.map +1 -1
  28. package/dist/local-sync.d.cts +10 -10
  29. package/dist/local-sync.d.ts +10 -10
  30. package/dist/local-sync.js +66 -38
  31. package/dist/local-sync.js.map +1 -1
  32. package/dist/react.cjs +23 -4
  33. package/dist/react.cjs.map +1 -1
  34. package/dist/react.d.cts +8 -3
  35. package/dist/react.d.ts +8 -3
  36. package/dist/react.js +21 -6
  37. package/dist/react.js.map +1 -1
  38. package/dist/store-1Uob0Ghk.cjs +2750 -0
  39. package/dist/store-1Uob0Ghk.cjs.map +1 -0
  40. package/dist/store-CD0KdGsq.d.cts +262 -0
  41. package/dist/store-D7QH6Rzw.js +2511 -0
  42. package/dist/store-D7QH6Rzw.js.map +1 -0
  43. package/dist/store-cp5CpfCy.d.ts +262 -0
  44. package/package.json +1 -1
  45. package/skills/doxum-runtime/SKILL.md +26 -44
  46. package/skills/doxum-runtime/references/guide.en.md +95 -359
  47. package/skills/doxum-runtime/references/guide.zh-CN.md +80 -285
  48. package/skills/doxum-runtime/references/invariants.en.md +42 -165
  49. package/skills/doxum-runtime/references/invariants.zh-CN.md +34 -97
  50. package/skills/doxum-runtime/references/patterns.en.md +73 -225
  51. package/skills/doxum-runtime/references/patterns.zh-CN.md +73 -179
  52. package/dist/chunk-pbuEa-1d.js +0 -13
  53. package/dist/contract-DNZ4D53r.d.ts +0 -563
  54. package/dist/contract-j3SLGAwh.d.cts +0 -563
  55. package/dist/driver-CNxqMVFH.cjs +0 -69
  56. package/dist/driver-CNxqMVFH.cjs.map +0 -1
  57. package/dist/driver-CbzfW5MR.js +0 -46
  58. package/dist/driver-CbzfW5MR.js.map +0 -1
  59. package/dist/integration-B56u1l9V.js.map +0 -1
  60. package/dist/integration-CZwCwFBS.cjs.map +0 -1
  61. package/dist/ownership-CY0nPXGF.cjs +0 -304
  62. package/dist/ownership-CY0nPXGF.cjs.map +0 -1
  63. package/dist/ownership-CduRygE7.js +0 -245
  64. package/dist/ownership-CduRygE7.js.map +0 -1
  65. package/dist/runtime-0mOFbe_H.cjs +0 -2439
  66. package/dist/runtime-0mOFbe_H.cjs.map +0 -1
  67. package/dist/runtime-B22tuj9A.d.cts +0 -150
  68. package/dist/runtime-BFmhzPpZ.d.ts +0 -150
  69. package/dist/runtime-DF1q9Gje.js +0 -2129
  70. package/dist/runtime-DF1q9Gje.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-DhrNdQNg.cjs","names":["isRecord","isRecord","contains","anchor.indexedKeys","isTree"],"sources":["../core/src/profile.ts","../core/src/mutation/anchor.ts","../core/src/value/ownership.ts","../core/src/mutation/tree.ts","../core/src/address.ts","../core/src/mutation/issue.ts"],"sourcesContent":["const counters = () => ({\n projection: {\n sourceEvents: 0,\n scheduledNodes: 0,\n processedNodes: 0,\n touchedKeys: 0,\n changedKeys: 0,\n publishedNodes: 0,\n flushes: 0,\n },\n copy: { structures: 0 },\n equality: { calls: 0, containers: 0 },\n recorder: {\n facts: 0,\n groups: 0,\n transitions: 0,\n absorbed: 0,\n orderSnapshots: 0,\n orderCaptures: 0,\n orderItems: 0,\n publishedOrderItems: 0,\n treeNodes: 0,\n sealed: 0,\n indexedGroups: 0,\n },\n access: { scopes: 0, proxies: 0, snapshots: 0, addresses: 0, resolutions: 0 },\n address: {\n schemaSteps: 0,\n documentSteps: 0,\n prefixComparisons: 0,\n segmentsCompared: 0,\n arraysCopied: 0,\n listIndexes: 0,\n listItems: 0,\n },\n impact: { affectsChecks: 0, indexes: 0 },\n collectionView: { mappedItems: 0, idsScanned: 0, arraysCopied: 0 },\n materialized: { updated: 0, rebuilt: 0, notifications: 0 },\n});\ntype Counters = ReturnType<typeof counters>;\ntype DeepReadonly<T> = { readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K] };\nexport type ProfileSnapshot = DeepReadonly<Counters>;\nlet active: Counters | undefined;\nconst freeze = <T extends object>(value: T): DeepReadonly<T> => {\n Object.values(value).forEach(child => {\n if (child && typeof child === 'object') freeze(child);\n });\n return Object.freeze(value) as DeepReadonly<T>;\n};\nexport const profile = {\n projection: (key: keyof Counters['projection'], amount = 1) => {\n if (active) active.projection[key] += amount;\n },\n recorder: (key: keyof Counters['recorder'], amount = 1) => {\n if (active) active.recorder[key] += amount;\n },\n access: (key: keyof Counters['access']) => {\n if (active) active.access[key]++;\n },\n copy: {\n structure: () => {\n if (active) active.copy.structures++;\n },\n },\n equality: {\n call: () => {\n if (active) active.equality.calls++;\n },\n container: () => {\n if (active) active.equality.containers++;\n },\n },\n address: {\n listIndex: (items: number) => {\n if (active) {\n active.address.listIndexes++;\n active.address.listItems += items;\n }\n },\n schemaStep: () => {\n if (active) active.address.schemaSteps++;\n },\n documentStep: () => {\n if (active) active.address.documentSteps++;\n },\n arrayCopied: () => {\n if (active) active.address.arraysCopied++;\n },\n prefixComparison: () => {\n if (active) active.address.prefixComparisons++;\n },\n segmentCompared: () => {\n if (active) active.address.segmentsCompared++;\n },\n },\n impact: {\n index: () => {\n if (active) active.impact.indexes++;\n },\n affects: () => {\n if (active) active.impact.affectsChecks++;\n },\n },\n collectionView: {\n mapped: () => {\n if (active) active.collectionView.mappedItems++;\n },\n idsScanned: (amount = 1) => {\n if (active) active.collectionView.idsScanned += amount;\n },\n arrayCopied: () => {\n if (active) active.collectionView.arraysCopied++;\n },\n },\n materialized: {\n updated: () => {\n if (active) active.materialized.updated++;\n },\n rebuilt: () => {\n if (active) active.materialized.rebuilt++;\n },\n notification: () => {\n if (active) active.materialized.notifications++;\n },\n },\n};\nexport type ProfileSession = { snapshot(): ProfileSnapshot; stop(): ProfileSnapshot };\nexport const startProfile = (): ProfileSession => {\n if (active) throw new Error('A document profile session is already active.');\n const value = counters();\n active = value;\n const snapshot = () => freeze(structuredClone(value));\n return {\n snapshot,\n stop: () => {\n if (active === value) active = undefined;\n return snapshot();\n },\n };\n};\nexport const measureProfile = <T>(run: () => T): { value: T; profile: ProfileSnapshot } => {\n const session = startProfile();\n try {\n return { value: run(), profile: session.stop() };\n } finally {\n session.stop();\n }\n};\n","import type { DocumentAnchor } from '../schema';\nimport { profile } from '../profile';\n\nexport const equal = (left: readonly string[], right: Keys): boolean => {\n profile.equality.call();\n if (left === right) return true;\n profile.equality.container();\n if (left.length !== right.length) return false;\n for (let i = 0; i < left.length; i++) if (left[i] !== keyAt(right, i)) return false;\n return true;\n};\n\nexport type KeyOrder = {\n readonly length: number;\n readonly at: (index: number) => string | undefined;\n readonly index: (key: string) => number;\n};\n\ntype Keys = readonly string[] | KeyOrder;\n\nconst length = (keys: Keys): number => keys.length;\nconst keyAt = (keys: Keys, index: number): string | undefined =>\n Array.isArray(keys) ? keys[index] : keys.at(index);\nconst keyIndex = (keys: Keys, key: string): number =>\n Array.isArray(keys) ? keys.indexOf(key) : (keys as KeyOrder).index(key);\n\nexport const keys = (values: readonly unknown[], keyOf: (value: unknown) => string): KeyOrder => ({\n length: values.length,\n at: index => (index >= 0 && index < values.length ? keyOf(values[index]) : undefined),\n index: key => {\n for (let index = 0; index < values.length; index += 1)\n if (keyOf(values[index]) === key) return index;\n return -1;\n },\n});\n\nconst listIndexes = new WeakMap<\n readonly unknown[],\n { keyOf: (value: unknown) => string; order: KeyOrder }\n>();\n\n/** Canonical lists keep their key positions until a structural write invalidates them. */\nexport const indexedKeys = (\n values: readonly unknown[],\n keyOf: (value: unknown) => string\n): KeyOrder => {\n const cached = listIndexes.get(values);\n if (cached?.keyOf === keyOf) return cached.order;\n let positions: Map<string, number> | undefined;\n const order: KeyOrder = {\n length: values.length,\n at: index => (index >= 0 && index < values.length ? keyOf(values[index]) : undefined),\n index: key => {\n if (!positions) {\n positions = new Map();\n for (let i = 0; i < values.length; i++) positions.set(keyOf(values[i]), i);\n profile.address.listIndex(values.length);\n }\n return positions.get(key) ?? -1;\n },\n };\n listIndexes.set(values, { keyOf, order });\n return order;\n};\n\n/** Structural sequence writes own invalidation, including rollback writes. */\nexport const insert = (values: unknown[], index: number, value: unknown): void => {\n values.splice(index, 0, value);\n listIndexes.delete(values);\n};\n\nexport const remove = (values: unknown[], index: number): void => {\n values.splice(index, 1);\n listIndexes.delete(values);\n};\n\nexport const move = (values: unknown[], from: number, to: number): void => {\n const [value] = values.splice(from, 1);\n values.splice(to, 0, value);\n listIndexes.delete(values);\n};\n\nexport const install = (\n values: unknown[],\n keyOf: (value: unknown) => string,\n order: readonly string[]\n): void => {\n const byKey = new Map<string, unknown>();\n for (const value of values) byKey.set(keyOf(value), value);\n values.length = order.length;\n for (let index = 0; index < order.length; index++) values[index] = byKey.get(order[index]);\n listIndexes.delete(values);\n};\n\nexport const matches = (order: readonly string[], members: readonly string[]): boolean => {\n if (order.length !== members.length) return false;\n const ids = new Set(order);\n return ids.size === order.length && members.every(id => ids.has(id));\n};\n\nexport const index = (keys: Keys, anchor?: DocumentAnchor): number => {\n if (!anchor) return length(keys);\n if ('at' in anchor) return anchor.at === 'start' ? 0 : length(keys);\n if ('before' in anchor) return keyIndex(keys, anchor.before);\n return keyIndex(keys, anchor.after) + 1;\n};\n\nexport const at = (keys: Keys, position: number): DocumentAnchor =>\n position <= 0 ? { at: 'start' } : { after: keyAt(keys, position - 1) as string };\n\nexport const valid = (keys: Keys, anchor?: DocumentAnchor): boolean => {\n if (!anchor) return true;\n if (typeof anchor !== 'object' || anchor === null) return false;\n if (Number('at' in anchor) + Number('before' in anchor) + Number('after' in anchor) !== 1)\n return false;\n if ('at' in anchor) return anchor.at === 'start' || anchor.at === 'end';\n return keyIndex(keys, 'before' in anchor ? anchor.before : anchor.after) >= 0;\n};\n\nexport const afterRemove = (keys: Keys, removedIndex: number, anchor?: DocumentAnchor): number => {\n const position = index(keys, anchor);\n return position > removedIndex ? position - 1 : position;\n};\n\nexport const validPositions = (length: number, positions: readonly number[]): boolean => {\n let previous = -1;\n for (let index = 0; index < positions.length; index += 1) {\n const position = positions[index];\n if (\n !Number.isInteger(position) ||\n position < 0 ||\n position <= previous ||\n position > length + index\n )\n return false;\n previous = position;\n }\n return true;\n};\n\nexport const restore = (\n keys: string[],\n inserted: readonly string[],\n positions: readonly number[]\n): void => {\n const currentLength = keys.length;\n if (inserted.length !== positions.length || !validPositions(currentLength, positions))\n throw new Error('Ordered restore positions are invalid.');\n let readIndex = currentLength - 1;\n let insertedIndex = inserted.length - 1;\n keys.length = currentLength + inserted.length;\n for (let writeIndex = keys.length - 1; writeIndex >= 0; writeIndex -= 1) {\n if (insertedIndex >= 0 && positions[insertedIndex] === writeIndex) {\n keys[writeIndex] = inserted[insertedIndex];\n insertedIndex -= 1;\n } else {\n keys[writeIndex] = keys[readIndex];\n readIndex -= 1;\n }\n }\n};\n","export const isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (value === null || typeof value !== 'object') return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n};\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n","import { isRecord } from '../value/ownership';\nexport type MutableTreeNode = { parentId?: string; children: string[]; value?: unknown };\nexport type MutableTree = { rootId?: string; nodes: Record<string, MutableTreeNode> };\nconst node = (tree: MutableTree, id: string) =>\n Object.hasOwn(tree.nodes, id) ? tree.nodes[id] : undefined;\nexport const is = (value: unknown): value is MutableTree =>\n isRecord(value) && isRecord(value.nodes);\n\nexport const validNode = (value: unknown): value is MutableTreeNode => {\n if (!isRecord(value) || !Array.isArray(value.children)) return false;\n if (value.parentId !== undefined && typeof value.parentId !== 'string') return false;\n const children = new Set<string>();\n for (const id of value.children) {\n if (typeof id !== 'string' || children.has(id)) return false;\n children.add(id);\n }\n return true;\n};\n\nexport const contains = (tree: MutableTree, id: string): boolean => Object.hasOwn(tree.nodes, id);\n\nexport const parent = (tree: MutableTree, id: string): string | undefined =>\n node(tree, id)?.parentId;\n\nexport const children = (tree: MutableTree, id: string): readonly string[] | undefined =>\n node(tree, id)?.children;\n\nexport const validate = (value: unknown): value is MutableTree => {\n if (!is(value)) return false;\n const ids = Object.keys(value.nodes);\n if (ids.length === 0) return value.rootId === undefined;\n if (typeof value.rootId !== 'string' || !Object.hasOwn(value.nodes, value.rootId)) return false;\n\n for (const id of ids) {\n const entry = value.nodes[id];\n if (!validNode(entry)) return false;\n if (id === value.rootId) {\n if (entry.parentId !== undefined) return false;\n } else if (entry.parentId === undefined || !Object.hasOwn(value.nodes, entry.parentId))\n return false;\n for (const child of entry.children) {\n const childNode = value.nodes[child];\n if (!childNode || childNode.parentId !== id) return false;\n }\n }\n\n const seen = new Set<string>();\n const stack = [value.rootId];\n while (stack.length) {\n const id = stack.pop() as string;\n if (seen.has(id)) return false;\n seen.add(id);\n const entry = value.nodes[id];\n if (!entry) return false;\n for (const child of entry.children) stack.push(child);\n }\n return seen.size === ids.length;\n};\n\nexport type TreePosition = { readonly parentId?: string; readonly index?: number };\n","import type { DocumentAddress, DocumentNode, ObjectNode, ObjectShape } from './schema';\nimport * as anchor from './mutation/anchor';\nimport { profile } from './profile';\nimport type { MutableTree } from './mutation/tree';\nimport { is as isTree } from './mutation/tree';\n\n/** The only resolved address representation used inside the runtime. */\nexport type AddressRef = {\n readonly path: number;\n readonly address: DocumentAddress;\n};\n\ntype RegistryNode = {\n readonly static: Map<string, RegistryNode>;\n dynamic?: RegistryNode;\n path?: number;\n};\n\ntype Registry = {\n readonly root: RegistryNode;\n nextPath: number;\n};\n\nconst registries = new WeakMap<object, Registry>();\nconst registryNode = (): RegistryNode => ({ static: new Map() });\nconst registryFor = (schema: ObjectNode): Registry => {\n const cached = registries.get(schema as object);\n if (cached) return cached;\n const registry: Registry = { root: registryNode(), nextPath: 0 };\n registries.set(schema as object, registry);\n return registry;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst variantNode = (\n node: Extract<DocumentNode, { kind: 'variant' }>,\n value: unknown\n): DocumentNode | undefined => {\n const tag = isRecord(value) && typeof value[node.tag] === 'string' ? value[node.tag] : undefined;\n return node.variants[String(tag ?? Object.keys(node.variants)[0] ?? '')];\n};\n\nconst step = (\n nodeInput: DocumentNode | undefined,\n value: unknown,\n segment: string\n): DocumentNode | undefined => {\n let node = nodeInput;\n if (!node) return undefined;\n if (node.kind === 'variant') {\n node = variantNode(node, value);\n }\n if (!node) return undefined;\n if (node.kind === 'object')\n return Object.hasOwn(node.shape, segment) ? node.shape[segment] : undefined;\n if (node.kind === 'table' || node.kind === 'map') return node.value;\n // List items use stable keys; tree topology remains a structural unit.\n if (node.kind === 'list') return node.value;\n return undefined;\n};\n\nconst pathFor = (\n schema: ObjectNode,\n address: DocumentAddress,\n document?: unknown\n): number | undefined => {\n const registry = registryFor(schema);\n let trie = registry.root;\n let node: DocumentNode | undefined = schema;\n let value: unknown = document;\n for (const segment of address) {\n profile.address.schemaStep();\n profile.address.documentStep();\n const resolved = step(node, value, segment);\n if (!resolved) return undefined;\n const next =\n node &&\n (node.kind === 'table' || node.kind === 'map' || node.kind === 'list' || node.kind === 'tree')\n ? (trie.dynamic ??= registryNode())\n : (() => {\n const existing = trie.static.get(segment);\n if (existing) return existing;\n const created = registryNode();\n trie.static.set(segment, created);\n return created;\n })();\n trie = next;\n value = readSegment(value, segment, node);\n node = resolved;\n }\n if (trie.path === undefined) trie.path = registry.nextPath++;\n return trie.path;\n};\n\nexport const resolveAddress = (\n schema: ObjectNode,\n address: DocumentAddress,\n document?: unknown\n): AddressRef | undefined => {\n if (!Array.isArray(address)) return undefined;\n for (const segment of address) if (typeof segment !== 'string') return undefined;\n profile.address.arrayCopied();\n const owned = Object.freeze(address.slice()) as DocumentAddress;\n const path = pathFor(schema, owned, document);\n return path === undefined ? undefined : { path, address: owned };\n};\n\nexport const nodeAt = (\n schema: DocumentNode,\n address: DocumentAddress,\n document?: unknown\n): DocumentNode | undefined => {\n let node: DocumentNode | undefined = schema;\n let value = document;\n for (const segment of address) {\n const resolved = step(node, value, segment);\n value = readSegment(value, segment, node);\n node = resolved;\n if (!node) return undefined;\n }\n return node;\n};\n\nexport const readSegment = (value: unknown, segment: string, node?: DocumentNode): unknown => {\n if (!isRecord(value) && !Array.isArray(value)) return undefined;\n if (node?.kind === 'list' && Array.isArray(value))\n return value[anchor.indexedKeys(value, node.keyOf).index(segment)];\n if (node?.kind === 'table' && isRecord(value) && isRecord(value.byId)) {\n return Object.hasOwn(value.byId, segment) ? value.byId[segment] : undefined;\n }\n return Object.prototype.hasOwnProperty.call(value, segment)\n ? (value as Record<string, unknown>)[segment]\n : undefined;\n};\n\nexport const read = (root: unknown, address: DocumentAddress, schema: DocumentNode): unknown => {\n let value = root;\n let node: DocumentNode | undefined = schema;\n for (const segment of address) {\n const next = step(node, value, segment);\n value = readSegment(value, segment, node);\n node = next;\n if (value === undefined) return undefined;\n }\n return value;\n};\n\nexport type ResolvedAddress = {\n readonly parent: Record<string, unknown> | unknown[];\n readonly key: string | number;\n readonly node: DocumentNode;\n};\n\ntype MemberDefinition = {\n readonly node: DocumentNode;\n readonly field: boolean;\n};\nexport type FixedMember = MemberDefinition & {\n readonly kind: 'fixed';\n readonly key: string;\n readonly slot: number;\n};\ntype CollectionMember = MemberDefinition & { readonly kind: 'entry' };\nexport type CompiledMember = FixedMember | CollectionMember;\nexport type FixedLayout = {\n readonly kind: 'fixed';\n readonly members: ReadonlyMap<string, FixedMember>;\n};\ntype DynamicLayout = { readonly kind: 'dynamic'; readonly entry: CollectionMember };\nexport type MemberLayout = FixedLayout | DynamicLayout;\nconst compiledObjects = new WeakMap<object, FixedLayout>();\nconst compiledEntries = new WeakMap<DocumentNode, DynamicLayout>();\nexport const compiledShape = (node: DocumentNode, value: unknown): FixedLayout | undefined => {\n const branch = node.kind === 'variant' ? variantNode(node, value) : node;\n if (branch?.kind !== 'object') return undefined;\n const shape: ObjectShape = branch.shape;\n let layout = compiledObjects.get(shape);\n if (!layout) {\n const compiled = new Map<string, FixedMember>();\n for (const key of Object.keys(shape).sort()) {\n profile.address.schemaStep();\n compiled.set(key, {\n kind: 'fixed',\n key,\n node: shape[key],\n field: shape[key].kind === 'field',\n slot: compiled.size,\n });\n }\n layout = { kind: 'fixed', members: compiled };\n compiledObjects.set(shape, layout);\n }\n return layout;\n};\n\n/** Immutable location facts, valid only for the resolving session generation. */\nexport type ResolvedContainer = {\n readonly owner: object;\n readonly generation: number;\n readonly at: DocumentAddress;\n readonly node: Exclude<DocumentNode, { kind: 'tree' }>;\n readonly parent: Record<string, unknown> | unknown[];\n readonly value: unknown;\n readonly layout: MemberLayout;\n};\nexport type ResolvedTreeContainer = {\n readonly owner: object;\n readonly generation: number;\n readonly at: DocumentAddress;\n readonly node: Extract<DocumentNode, { kind: 'tree' }>;\n readonly value: MutableTree;\n};\nexport const resolveContainer = (\n owner: object,\n generation: number,\n at: DocumentAddress,\n node: DocumentNode | undefined,\n value: unknown\n): ResolvedContainer | undefined => {\n if (!node || (!isRecord(value) && !Array.isArray(value))) return undefined;\n const parent = node.kind === 'table' && isRecord(value) ? value.byId : value;\n if (!isRecord(parent) && !Array.isArray(parent)) return undefined;\n if (node.kind === 'tree') return undefined;\n let layout: MemberLayout;\n if (node.kind === 'map' || node.kind === 'table' || node.kind === 'list') {\n let dynamic = compiledEntries.get(node);\n if (!dynamic) {\n dynamic = {\n kind: 'dynamic',\n entry: { kind: 'entry', node: node.value, field: node.value.kind === 'field' },\n };\n compiledEntries.set(node, dynamic);\n }\n layout = dynamic;\n } else {\n const fixed = compiledShape(node, value);\n if (!fixed) return undefined;\n layout = fixed;\n }\n return { owner, generation, at, node, parent, value, layout };\n};\nexport const resolveTreeContainer = (\n owner: object,\n generation: number,\n at: DocumentAddress,\n node: DocumentNode | undefined,\n value: unknown\n): ResolvedTreeContainer | undefined =>\n node?.kind === 'tree' && isTree(value) ? { owner, generation, at, node, value } : undefined;\nexport const memberKey = (\n container: { readonly node: DocumentNode; readonly parent: Record<string, unknown> | unknown[] },\n key: string\n): string | number =>\n container.node.kind === 'list'\n ? anchor.indexedKeys(container.parent as unknown[], container.node.keyOf).index(key)\n : key;\n\ntype ResolutionPrefix = {\n segment: string;\n node: DocumentNode;\n value: unknown;\n};\n\n/** One recent path per transaction; structural writes invalidate all retained locations. */\nexport const createAddressResolver = (schema: DocumentNode, root: unknown) => {\n const prefix: ResolutionPrefix[] = [];\n return {\n container: (owner: object, generation: number, at: DocumentAddress) => {\n if (!at.length) return resolveContainer(owner, generation, at, schema, root);\n const location = resolveWithPrefix(schema, root, at, prefix);\n if (!location || !Object.hasOwn(location.parent, location.key)) return undefined;\n return resolveContainer(\n owner,\n generation,\n at,\n location.node,\n (location.parent as Record<string | number, unknown>)[location.key]\n );\n },\n invalidate: () => {\n prefix.length = 0;\n },\n };\n};\n\n/** A subtree root is a value, not a fabricated member of an ObjectNode. */\nexport const resolveValue = (\n schema: DocumentNode,\n root: unknown,\n address: DocumentAddress,\n prefix?: ResolutionPrefix[]\n): { node: DocumentNode; value: unknown } | undefined => {\n if (!address.length) return { node: schema, value: root };\n const location = resolveWithPrefix(schema, root, address, prefix);\n return (\n location && {\n node: location.node,\n value: Object.hasOwn(location.parent, location.key)\n ? (location.parent as Record<string | number, unknown>)[location.key]\n : undefined,\n }\n );\n};\n\n/** Resolves schema and document location in one address traversal. */\nexport const resolveLocated = (\n schema: DocumentNode,\n root: unknown,\n address: DocumentAddress\n): ResolvedAddress | undefined => resolveWithPrefix(schema, root, address);\n\nconst resolveWithPrefix = (\n schema: DocumentNode,\n root: unknown,\n address: DocumentAddress,\n prefix?: ResolutionPrefix[]\n): ResolvedAddress | undefined => {\n if (address.length === 0) return undefined;\n let node: DocumentNode | undefined = schema;\n let current: unknown = root;\n let start = 0;\n if (prefix) {\n while (\n start < prefix.length &&\n start < address.length - 1 &&\n prefix[start].segment === address[start]\n )\n start++;\n prefix.length = start;\n if (start) {\n const retained = prefix[start - 1];\n node = retained.node;\n current = retained.value;\n }\n }\n let cacheable = true;\n for (let index = start; index < address.length - 1; index += 1) {\n profile.address.schemaStep();\n profile.address.documentStep();\n const resolved = step(node, current, address[index]);\n if (!resolved) return undefined;\n if (node?.kind !== 'object' && node?.kind !== 'table' && node?.kind !== 'map')\n cacheable = false;\n current = readSegment(current, address[index], node);\n node = resolved;\n if (prefix && cacheable)\n prefix.push({\n segment: address[index],\n node: resolved,\n value: current,\n });\n if (!node) return undefined;\n }\n const last = address[address.length - 1];\n return resolveChild(node, current, last);\n};\n\n/** Resolves one member from an already current schema/container pair. */\nexport const resolveChild = (\n node: DocumentNode | undefined,\n current: unknown,\n last: string\n): ResolvedAddress | undefined => {\n profile.address.schemaStep();\n profile.address.documentStep();\n const resolved = step(node, current, last);\n if (!resolved || (!isRecord(current) && !Array.isArray(current))) return undefined;\n if (node?.kind === 'table' && isRecord(current) && isRecord(current.byId))\n return {\n node: resolved,\n parent: current.byId,\n key: last,\n };\n return {\n node: resolved,\n parent: current,\n key:\n node?.kind === 'list' && Array.isArray(current)\n ? anchor.indexedKeys(current, node.keyOf).index(last)\n : last,\n };\n};\n\nexport const contains = (parent: DocumentAddress, child: DocumentAddress): boolean => {\n profile.address.prefixComparison();\n if (parent.length > child.length) return false;\n for (let index = 0; index < parent.length; index += 1) {\n profile.address.segmentCompared();\n if (parent[index] !== child[index]) return false;\n }\n return true;\n};\n\nexport const overlaps = (a: DocumentAddress, b: DocumentAddress): boolean =>\n contains(a, b) || contains(b, a);\n\nexport const debugKey = (address: DocumentAddress): string => {\n let result = '';\n for (let index = 0; index < address.length; index += 1) {\n if (index > 0) result += '/';\n result += address[index].replaceAll('~', '~~').replaceAll('/', '~/');\n }\n return result;\n};\n\nexport const same = (a: AddressRef, b: AddressRef): boolean => {\n if (a.path !== b.path || a.address.length !== b.address.length) return false;\n for (let index = 0; index < a.address.length; index += 1) {\n profile.address.segmentCompared();\n if (a.address[index] !== b.address[index]) return false;\n }\n return true;\n};\n\nexport type { DocumentAddress } from './schema';\n\ntype AddressIndexNode<T> = {\n readonly children: Map<string, AddressIndexNode<T>>;\n readonly values: Set<T>;\n};\n\n/** An index over canonical address segments, shared by impact and subscriptions. */\nexport class AddressIndex<T> {\n private readonly root: AddressIndexNode<T> = { children: new Map(), values: new Set() };\n\n add(address: DocumentAddress, value: T, member?: string): void {\n let node = this.root;\n for (let i = 0; i < address.length + (member === undefined ? 0 : 1); i++) {\n const segment = i < address.length ? address[i] : member!;\n let child = node.children.get(segment);\n if (!child) {\n child = { children: new Map(), values: new Set() };\n node.children.set(segment, child);\n }\n node = child;\n }\n node.values.add(value);\n }\n\n delete(address: DocumentAddress, value: T): void {\n const parents: AddressIndexNode<T>[] = [];\n let node = this.root;\n for (const segment of address) {\n const child = node.children.get(segment);\n if (!child) return;\n parents.push(node);\n node = child;\n }\n node.values.delete(value);\n for (let i = address.length - 1; i >= 0 && !node.values.size && !node.children.size; i--) {\n node = parents[i];\n node.children.delete(address[i]);\n }\n }\n\n exact(address: DocumentAddress): ReadonlySet<T> | undefined {\n let node = this.root;\n for (const segment of address) {\n const child = node.children.get(segment);\n if (!child) return undefined;\n node = child;\n }\n return node.values;\n }\n\n hasAncestor(address: DocumentAddress, strict = false): boolean {\n let node = this.root;\n for (let i = 0; i < address.length; i++) {\n if (node.values.size) return true;\n const child = node.children.get(address[i]);\n if (!child) return false;\n node = child;\n }\n return !strict && node.values.size > 0;\n }\n\n someAncestor(address: DocumentAddress, predicate: (value: T) => boolean): boolean {\n let node = this.root;\n for (let i = 0; ; i++) {\n for (const value of node.values) if (predicate(value)) return true;\n if (i === address.length) return false;\n const child = node.children.get(address[i]);\n if (!child) return false;\n node = child;\n }\n }\n\n overlaps(address: DocumentAddress): boolean {\n let node = this.root;\n for (const segment of address) {\n if (node.values.size) return true;\n const child = node.children.get(segment);\n if (!child) return false;\n node = child;\n }\n return node.values.size > 0 || node.children.size > 0;\n }\n\n hasDescendant(address: DocumentAddress): boolean {\n let node = this.root;\n for (const segment of address) {\n const child = node.children.get(segment);\n if (!child) return false;\n node = child;\n }\n return node.values.size > 0 || node.children.size > 0;\n }\n\n query(\n visit: (value: T) => void,\n relation: 'overlap' | 'ancestors' | 'descendants' = 'overlap'\n ): (address: DocumentAddress, members?: readonly { readonly key: string }[]) => void {\n const visited = new Set<AddressIndexNode<T>>();\n const subtrees = new Set<AddressIndexNode<T>>();\n const values = (node: AddressIndexNode<T>) => {\n if (visited.has(node)) return;\n visited.add(node);\n node.values.forEach(visit);\n };\n const descend = (current: AddressIndexNode<T>): void => {\n if (subtrees.has(current)) return;\n subtrees.add(current);\n values(current);\n current.children.forEach(descend);\n };\n const terminal = (node: AddressIndexNode<T>) =>\n relation === 'ancestors' ? values(node) : descend(node);\n return (address, members) => {\n let node = this.root;\n for (const segment of address) {\n if (subtrees.has(node)) return;\n if (relation !== 'descendants') values(node);\n const child = node.children.get(segment);\n if (!child) return;\n node = child;\n }\n if (!members) terminal(node);\n else {\n if (relation !== 'descendants') values(node);\n for (const member of members) {\n const child = node.children.get(member.key);\n if (child) terminal(child);\n }\n }\n };\n }\n\n clear(): void {\n this.root.values.clear();\n this.root.children.clear();\n }\n}\n","import type { DocumentAddress } from '../schema';\nimport type { ParseIssue } from '../schema-value';\n\nexport type MutationIssueCode =\n | 'invalid-address'\n | 'invalid-value'\n | 'invalid-key'\n | 'invalid-changes'\n | 'baseline-mismatch'\n | 'required-field'\n | 'invalid-anchor'\n | 'duplicate-entity'\n | 'missing-entity'\n | 'invalid-collection'\n | 'invalid-list-keys'\n | 'invalid-list-key'\n | 'duplicate-list-item'\n | 'missing-list-item'\n | 'invalid-tree'\n | 'duplicate-tree-node'\n | 'missing-tree-parent'\n | 'invalid-tree-index'\n | 'missing-tree-node'\n | 'tree-cycle';\nexport type MutationIssue = {\n readonly source: 'mutation';\n readonly code: MutationIssueCode;\n readonly address: DocumentAddress;\n readonly message: string;\n};\nexport const at = (\n address: DocumentAddress,\n code: MutationIssueCode,\n message: string\n): MutationIssue =>\n Object.freeze({ source: 'mutation', code, address: Object.freeze([...address]), message });\nexport class MutationRejected extends Error {\n constructor(readonly issue: MutationIssue) {\n super(issue.message);\n }\n}\nexport const fail = (address: DocumentAddress, code: MutationIssueCode, message: string): never => {\n throw new MutationRejected(at(address, code, message));\n};\n\nexport const invalidValue = (issue: ParseIssue, address = issue.address): never =>\n fail(address, issue.code === 'missing-validator' ? 'invalid-value' : issue.code, issue.message);\n"],"mappings":"AAiDA,MAAa,UAAU;CACrB,aAAa,KAAmC,SAAS,MAAM;CAG/D,WAAW,KAAiC,SAAS,MAAM;CAG3D,SAAS,QAAkC;CAG3C,MAAM,EACJ,iBAAiB,IAGlB;CACD,UAAU;EACR,YAAY;EAGZ,iBAAiB;EAGlB;CACD,SAAS;EACP,YAAY,UAAkB;EAM9B,kBAAkB;EAGlB,oBAAoB;EAGpB,mBAAmB;EAGnB,wBAAwB;EAGxB,uBAAuB;EAGxB;CACD,QAAQ;EACN,aAAa;EAGb,eAAe;EAGhB;CACD,gBAAgB;EACd,cAAc;EAGd,aAAa,SAAS,MAAM;EAG5B,mBAAmB;EAGpB;CACD,cAAc;EACZ,eAAe;EAGf,eAAe;EAGf,oBAAoB;EAGrB;CACF;;;AC1HD,MAAa,SAAS,MAAyB,UAAyB;AACtE,SAAQ,SAAS,MAAM;AACvB,KAAI,SAAS,MAAO,QAAO;AAC3B,SAAQ,SAAS,WAAW;AAC5B,KAAI,KAAK,WAAW,MAAM,OAAQ,QAAO;AACzC,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAK,KAAI,KAAK,OAAO,MAAM,OAAO,EAAE,CAAE,QAAO;AAC9E,QAAO;;AAWT,MAAM,UAAU,SAAuB,KAAK;AAC5C,MAAM,SAAS,MAAY,UACzB,MAAM,QAAQ,KAAK,GAAG,KAAK,SAAS,KAAK,GAAG,MAAM;AACpD,MAAM,YAAY,MAAY,QAC5B,MAAM,QAAQ,KAAK,GAAG,KAAK,QAAQ,IAAI,GAAI,KAAkB,MAAM,IAAI;AAEzE,MAAa,QAAQ,QAA4B,WAAiD;CAChG,QAAQ,OAAO;CACf,KAAI,UAAU,SAAS,KAAK,QAAQ,OAAO,SAAS,MAAM,OAAO,OAAO,GAAG,KAAA;CAC3E,QAAO,QAAO;AACZ,OAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS,EAClD,KAAI,MAAM,OAAO,OAAO,KAAK,IAAK,QAAO;AAC3C,SAAO;;CAEV;AAED,MAAM,8BAAc,IAAI,SAGrB;;AAGH,MAAa,eACX,QACA,UACa;CACb,MAAM,SAAS,YAAY,IAAI,OAAO;AACtC,KAAI,QAAQ,UAAU,MAAO,QAAO,OAAO;CAC3C,IAAI;CACJ,MAAM,QAAkB;EACtB,QAAQ,OAAO;EACf,KAAI,UAAU,SAAS,KAAK,QAAQ,OAAO,SAAS,MAAM,OAAO,OAAO,GAAG,KAAA;EAC3E,QAAO,QAAO;AACZ,OAAI,CAAC,WAAW;AACd,gCAAY,IAAI,KAAK;AACrB,SAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,IAAK,WAAU,IAAI,MAAM,OAAO,GAAG,EAAE,EAAE;AAC1E,YAAQ,QAAQ,UAAU,OAAO,OAAO;;AAE1C,UAAO,UAAU,IAAI,IAAI,IAAI;;EAEhC;AACD,aAAY,IAAI,QAAQ;EAAE;EAAO;EAAO,CAAC;AACzC,QAAO;;;AAIT,MAAa,UAAU,QAAmB,OAAe,UAAyB;AAChF,QAAO,OAAO,OAAO,GAAG,MAAM;AAC9B,aAAY,OAAO,OAAO;;AAG5B,MAAa,UAAU,QAAmB,UAAwB;AAChE,QAAO,OAAO,OAAO,EAAE;AACvB,aAAY,OAAO,OAAO;;AAG5B,MAAa,QAAQ,QAAmB,MAAc,OAAqB;CACzE,MAAM,CAAC,SAAS,OAAO,OAAO,MAAM,EAAE;AACtC,QAAO,OAAO,IAAI,GAAG,MAAM;AAC3B,aAAY,OAAO,OAAO;;AAG5B,MAAa,WACX,QACA,OACA,UACS;CACT,MAAM,wBAAQ,IAAI,KAAsB;AACxC,MAAK,MAAM,SAAS,OAAQ,OAAM,IAAI,MAAM,MAAM,EAAE,MAAM;AAC1D,QAAO,SAAS,MAAM;AACtB,MAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,QAAS,QAAO,SAAS,MAAM,IAAI,MAAM,OAAO;AAC1F,aAAY,OAAO,OAAO;;AAG5B,MAAa,WAAW,OAA0B,YAAwC;AACxF,KAAI,MAAM,WAAW,QAAQ,OAAQ,QAAO;CAC5C,MAAM,MAAM,IAAI,IAAI,MAAM;AAC1B,QAAO,IAAI,SAAS,MAAM,UAAU,QAAQ,OAAM,OAAM,IAAI,IAAI,GAAG,CAAC;;AAGtE,MAAa,SAAS,MAAY,WAAoC;AACpE,KAAI,CAAC,OAAQ,QAAO,OAAO,KAAK;AAChC,KAAI,QAAQ,OAAQ,QAAO,OAAO,OAAO,UAAU,IAAI,OAAO,KAAK;AACnE,KAAI,YAAY,OAAQ,QAAO,SAAS,MAAM,OAAO,OAAO;AAC5D,QAAO,SAAS,MAAM,OAAO,MAAM,GAAG;;AAMxC,MAAa,SAAS,MAAY,WAAqC;AACrE,KAAI,CAAC,OAAQ,QAAO;AACpB,KAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAC1D,KAAI,OAAO,QAAQ,OAAO,GAAG,OAAO,YAAY,OAAO,GAAG,OAAO,WAAW,OAAO,KAAK,EACtF,QAAO;AACT,KAAI,QAAQ,OAAQ,QAAO,OAAO,OAAO,WAAW,OAAO,OAAO;AAClE,QAAO,SAAS,MAAM,YAAY,SAAS,OAAO,SAAS,OAAO,MAAM,IAAI;;AAG9E,MAAa,eAAe,MAAY,cAAsB,WAAoC;CAChG,MAAM,WAAW,MAAM,MAAM,OAAO;AACpC,QAAO,WAAW,eAAe,WAAW,IAAI;;;;ACzHlD,MAAa,iBAAiB,UAAqD;AACjF,KAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;CACxD,MAAM,YAAY,OAAO,eAAe,MAAM;AAC9C,QAAO,cAAc,OAAO,aAAa,cAAc;;AAGzD,MAAaA,cAAY,UACvB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;;ACJtE,MAAM,QAAQ,MAAmB,OAC/B,OAAO,OAAO,KAAK,OAAO,GAAG,GAAG,KAAK,MAAM,MAAM,KAAA;AACnD,MAAa,MAAM,UACjBC,WAAS,MAAM,IAAIA,WAAS,MAAM,MAAM;AAE1C,MAAa,aAAa,UAA6C;AACrE,KAAI,CAACA,WAAS,MAAM,IAAI,CAAC,MAAM,QAAQ,MAAM,SAAS,CAAE,QAAO;AAC/D,KAAI,MAAM,aAAa,KAAA,KAAa,OAAO,MAAM,aAAa,SAAU,QAAO;CAC/E,MAAM,2BAAW,IAAI,KAAa;AAClC,MAAK,MAAM,MAAM,MAAM,UAAU;AAC/B,MAAI,OAAO,OAAO,YAAY,SAAS,IAAI,GAAG,CAAE,QAAO;AACvD,WAAS,IAAI,GAAG;;AAElB,QAAO;;AAGT,MAAaC,cAAY,MAAmB,OAAwB,OAAO,OAAO,KAAK,OAAO,GAAG;AAEjG,MAAa,UAAU,MAAmB,OACxC,KAAK,MAAM,GAAG,EAAE;AAElB,MAAa,YAAY,MAAmB,OAC1C,KAAK,MAAM,GAAG,EAAE;AAElB,MAAa,YAAY,UAAyC;AAChE,KAAI,CAAC,GAAG,MAAM,CAAE,QAAO;CACvB,MAAM,MAAM,OAAO,KAAK,MAAM,MAAM;AACpC,KAAI,IAAI,WAAW,EAAG,QAAO,MAAM,WAAW,KAAA;AAC9C,KAAI,OAAO,MAAM,WAAW,YAAY,CAAC,OAAO,OAAO,MAAM,OAAO,MAAM,OAAO,CAAE,QAAO;AAE1F,MAAK,MAAM,MAAM,KAAK;EACpB,MAAM,QAAQ,MAAM,MAAM;AAC1B,MAAI,CAAC,UAAU,MAAM,CAAE,QAAO;AAC9B,MAAI,OAAO,MAAM;OACX,MAAM,aAAa,KAAA,EAAW,QAAO;aAChC,MAAM,aAAa,KAAA,KAAa,CAAC,OAAO,OAAO,MAAM,OAAO,MAAM,SAAS,CACpF,QAAO;AACT,OAAK,MAAM,SAAS,MAAM,UAAU;GAClC,MAAM,YAAY,MAAM,MAAM;AAC9B,OAAI,CAAC,aAAa,UAAU,aAAa,GAAI,QAAO;;;CAIxD,MAAM,uBAAO,IAAI,KAAa;CAC9B,MAAM,QAAQ,CAAC,MAAM,OAAO;AAC5B,QAAO,MAAM,QAAQ;EACnB,MAAM,KAAK,MAAM,KAAK;AACtB,MAAI,KAAK,IAAI,GAAG,CAAE,QAAO;AACzB,OAAK,IAAI,GAAG;EACZ,MAAM,QAAQ,MAAM,MAAM;AAC1B,MAAI,CAAC,MAAO,QAAO;AACnB,OAAK,MAAM,SAAS,MAAM,SAAU,OAAM,KAAK,MAAM;;AAEvD,QAAO,KAAK,SAAS,IAAI;;;;ACjC3B,MAAM,6BAAa,IAAI,SAA2B;AAClD,MAAM,sBAAoC,EAAE,wBAAQ,IAAI,KAAK,EAAE;AAC/D,MAAM,eAAe,WAAiC;CACpD,MAAM,SAAS,WAAW,IAAI,OAAiB;AAC/C,KAAI,OAAQ,QAAO;CACnB,MAAM,WAAqB;EAAE,MAAM,cAAc;EAAE,UAAU;EAAG;AAChE,YAAW,IAAI,QAAkB,SAAS;AAC1C,QAAO;;AAGT,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;AAEtE,MAAM,eACJ,MACA,UAC6B;CAC7B,MAAM,MAAM,SAAS,MAAM,IAAI,OAAO,MAAM,KAAK,SAAS,WAAW,MAAM,KAAK,OAAO,KAAA;AACvF,QAAO,KAAK,SAAS,OAAO,OAAO,OAAO,KAAK,KAAK,SAAS,CAAC,MAAM,GAAG;;AAGzE,MAAM,QACJ,WACA,OACA,YAC6B;CAC7B,IAAI,OAAO;AACX,KAAI,CAAC,KAAM,QAAO,KAAA;AAClB,KAAI,KAAK,SAAS,UAChB,QAAO,YAAY,MAAM,MAAM;AAEjC,KAAI,CAAC,KAAM,QAAO,KAAA;AAClB,KAAI,KAAK,SAAS,SAChB,QAAO,OAAO,OAAO,KAAK,OAAO,QAAQ,GAAG,KAAK,MAAM,WAAW,KAAA;AACpE,KAAI,KAAK,SAAS,WAAW,KAAK,SAAS,MAAO,QAAO,KAAK;AAE9D,KAAI,KAAK,SAAS,OAAQ,QAAO,KAAK;;AAIxC,MAAM,WACJ,QACA,SACA,aACuB;CACvB,MAAM,WAAW,YAAY,OAAO;CACpC,IAAI,OAAO,SAAS;CACpB,IAAI,OAAiC;CACrC,IAAI,QAAiB;AACrB,MAAK,MAAM,WAAW,SAAS;AAC7B,UAAQ,QAAQ,YAAY;AAC5B,UAAQ,QAAQ,cAAc;EAC9B,MAAM,WAAW,KAAK,MAAM,OAAO,QAAQ;AAC3C,MAAI,CAAC,SAAU,QAAO,KAAA;AAYtB,SAVE,SACC,KAAK,SAAS,WAAW,KAAK,SAAS,SAAS,KAAK,SAAS,UAAU,KAAK,SAAS,UAClF,KAAK,YAAY,cAAc,UACzB;GACL,MAAM,WAAW,KAAK,OAAO,IAAI,QAAQ;AACzC,OAAI,SAAU,QAAO;GACrB,MAAM,UAAU,cAAc;AAC9B,QAAK,OAAO,IAAI,SAAS,QAAQ;AACjC,UAAO;MACL;AAEV,UAAQ,YAAY,OAAO,SAAS,KAAK;AACzC,SAAO;;AAET,KAAI,KAAK,SAAS,KAAA,EAAW,MAAK,OAAO,SAAS;AAClD,QAAO,KAAK;;AAGd,MAAa,kBACX,QACA,SACA,aAC2B;AAC3B,KAAI,CAAC,MAAM,QAAQ,QAAQ,CAAE,QAAO,KAAA;AACpC,MAAK,MAAM,WAAW,QAAS,KAAI,OAAO,YAAY,SAAU,QAAO,KAAA;AACvE,SAAQ,QAAQ,aAAa;CAC7B,MAAM,QAAQ,OAAO,OAAO,QAAQ,OAAO,CAAC;CAC5C,MAAM,OAAO,QAAQ,QAAQ,OAAO,SAAS;AAC7C,QAAO,SAAS,KAAA,IAAY,KAAA,IAAY;EAAE;EAAM,SAAS;EAAO;;AAGlE,MAAa,UACX,QACA,SACA,aAC6B;CAC7B,IAAI,OAAiC;CACrC,IAAI,QAAQ;AACZ,MAAK,MAAM,WAAW,SAAS;EAC7B,MAAM,WAAW,KAAK,MAAM,OAAO,QAAQ;AAC3C,UAAQ,YAAY,OAAO,SAAS,KAAK;AACzC,SAAO;AACP,MAAI,CAAC,KAAM,QAAO,KAAA;;AAEpB,QAAO;;AAGT,MAAa,eAAe,OAAgB,SAAiB,SAAiC;AAC5F,KAAI,CAAC,SAAS,MAAM,IAAI,CAAC,MAAM,QAAQ,MAAM,CAAE,QAAO,KAAA;AACtD,KAAI,MAAM,SAAS,UAAU,MAAM,QAAQ,MAAM,CAC/C,QAAO,MAAMC,YAAmB,OAAO,KAAK,MAAM,CAAC,MAAM,QAAQ;AACnE,KAAI,MAAM,SAAS,WAAW,SAAS,MAAM,IAAI,SAAS,MAAM,KAAK,CACnE,QAAO,OAAO,OAAO,MAAM,MAAM,QAAQ,GAAG,MAAM,KAAK,WAAW,KAAA;AAEpE,QAAO,OAAO,UAAU,eAAe,KAAK,OAAO,QAAQ,GACtD,MAAkC,WACnC,KAAA;;AAGN,MAAa,QAAQ,MAAe,SAA0B,WAAkC;CAC9F,IAAI,QAAQ;CACZ,IAAI,OAAiC;AACrC,MAAK,MAAM,WAAW,SAAS;EAC7B,MAAM,OAAO,KAAK,MAAM,OAAO,QAAQ;AACvC,UAAQ,YAAY,OAAO,SAAS,KAAK;AACzC,SAAO;AACP,MAAI,UAAU,KAAA,EAAW,QAAO,KAAA;;AAElC,QAAO;;AA0BT,MAAM,kCAAkB,IAAI,SAA8B;AAC1D,MAAM,kCAAkB,IAAI,SAAsC;AAClE,MAAa,iBAAiB,MAAoB,UAA4C;CAC5F,MAAM,SAAS,KAAK,SAAS,YAAY,YAAY,MAAM,MAAM,GAAG;AACpE,KAAI,QAAQ,SAAS,SAAU,QAAO,KAAA;CACtC,MAAM,QAAqB,OAAO;CAClC,IAAI,SAAS,gBAAgB,IAAI,MAAM;AACvC,KAAI,CAAC,QAAQ;EACX,MAAM,2BAAW,IAAI,KAA0B;AAC/C,OAAK,MAAM,OAAO,OAAO,KAAK,MAAM,CAAC,MAAM,EAAE;AAC3C,WAAQ,QAAQ,YAAY;AAC5B,YAAS,IAAI,KAAK;IAChB,MAAM;IACN;IACA,MAAM,MAAM;IACZ,OAAO,MAAM,KAAK,SAAS;IAC3B,MAAM,SAAS;IAChB,CAAC;;AAEJ,WAAS;GAAE,MAAM;GAAS,SAAS;GAAU;AAC7C,kBAAgB,IAAI,OAAO,OAAO;;AAEpC,QAAO;;AAoBT,MAAa,oBACX,OACA,YACA,IACA,MACA,UACkC;AAClC,KAAI,CAAC,QAAS,CAAC,SAAS,MAAM,IAAI,CAAC,MAAM,QAAQ,MAAM,CAAG,QAAO,KAAA;CACjE,MAAM,SAAS,KAAK,SAAS,WAAW,SAAS,MAAM,GAAG,MAAM,OAAO;AACvE,KAAI,CAAC,SAAS,OAAO,IAAI,CAAC,MAAM,QAAQ,OAAO,CAAE,QAAO,KAAA;AACxD,KAAI,KAAK,SAAS,OAAQ,QAAO,KAAA;CACjC,IAAI;AACJ,KAAI,KAAK,SAAS,SAAS,KAAK,SAAS,WAAW,KAAK,SAAS,QAAQ;EACxE,IAAI,UAAU,gBAAgB,IAAI,KAAK;AACvC,MAAI,CAAC,SAAS;AACZ,aAAU;IACR,MAAM;IACN,OAAO;KAAE,MAAM;KAAS,MAAM,KAAK;KAAO,OAAO,KAAK,MAAM,SAAS;KAAS;IAC/E;AACD,mBAAgB,IAAI,MAAM,QAAQ;;AAEpC,WAAS;QACJ;EACL,MAAM,QAAQ,cAAc,MAAM,MAAM;AACxC,MAAI,CAAC,MAAO,QAAO,KAAA;AACnB,WAAS;;AAEX,QAAO;EAAE;EAAO;EAAY;EAAI;EAAM;EAAQ;EAAO;EAAQ;;AAE/D,MAAa,wBACX,OACA,YACA,IACA,MACA,UAEA,MAAM,SAAS,UAAUC,GAAO,MAAM,GAAG;CAAE;CAAO;CAAY;CAAI;CAAM;CAAO,GAAG,KAAA;AACpF,MAAa,aACX,WACA,QAEA,UAAU,KAAK,SAAS,SACpBD,YAAmB,UAAU,QAAqB,UAAU,KAAK,MAAM,CAAC,MAAM,IAAI,GAClF;;AASN,MAAa,yBAAyB,QAAsB,SAAkB;CAC5E,MAAM,SAA6B,EAAE;AACrC,QAAO;EACL,YAAY,OAAe,YAAoB,OAAwB;AACrE,OAAI,CAAC,GAAG,OAAQ,QAAO,iBAAiB,OAAO,YAAY,IAAI,QAAQ,KAAK;GAC5E,MAAM,WAAW,kBAAkB,QAAQ,MAAM,IAAI,OAAO;AAC5D,OAAI,CAAC,YAAY,CAAC,OAAO,OAAO,SAAS,QAAQ,SAAS,IAAI,CAAE,QAAO,KAAA;AACvE,UAAO,iBACL,OACA,YACA,IACA,SAAS,MACR,SAAS,OAA4C,SAAS,KAChE;;EAEH,kBAAkB;AAChB,UAAO,SAAS;;EAEnB;;;AAIH,MAAa,gBACX,QACA,MACA,SACA,WACuD;AACvD,KAAI,CAAC,QAAQ,OAAQ,QAAO;EAAE,MAAM;EAAQ,OAAO;EAAM;CACzD,MAAM,WAAW,kBAAkB,QAAQ,MAAM,SAAS,OAAO;AACjE,QACE,YAAY;EACV,MAAM,SAAS;EACf,OAAO,OAAO,OAAO,SAAS,QAAQ,SAAS,IAAI,GAC9C,SAAS,OAA4C,SAAS,OAC/D,KAAA;EACL;;;AAKL,MAAa,kBACX,QACA,MACA,YACgC,kBAAkB,QAAQ,MAAM,QAAQ;AAE1E,MAAM,qBACJ,QACA,MACA,SACA,WACgC;AAChC,KAAI,QAAQ,WAAW,EAAG,QAAO,KAAA;CACjC,IAAI,OAAiC;CACrC,IAAI,UAAmB;CACvB,IAAI,QAAQ;AACZ,KAAI,QAAQ;AACV,SACE,QAAQ,OAAO,UACf,QAAQ,QAAQ,SAAS,KACzB,OAAO,OAAO,YAAY,QAAQ,OAElC;AACF,SAAO,SAAS;AAChB,MAAI,OAAO;GACT,MAAM,WAAW,OAAO,QAAQ;AAChC,UAAO,SAAS;AAChB,aAAU,SAAS;;;CAGvB,IAAI,YAAY;AAChB,MAAK,IAAI,QAAQ,OAAO,QAAQ,QAAQ,SAAS,GAAG,SAAS,GAAG;AAC9D,UAAQ,QAAQ,YAAY;AAC5B,UAAQ,QAAQ,cAAc;EAC9B,MAAM,WAAW,KAAK,MAAM,SAAS,QAAQ,OAAO;AACpD,MAAI,CAAC,SAAU,QAAO,KAAA;AACtB,MAAI,MAAM,SAAS,YAAY,MAAM,SAAS,WAAW,MAAM,SAAS,MACtE,aAAY;AACd,YAAU,YAAY,SAAS,QAAQ,QAAQ,KAAK;AACpD,SAAO;AACP,MAAI,UAAU,UACZ,QAAO,KAAK;GACV,SAAS,QAAQ;GACjB,MAAM;GACN,OAAO;GACR,CAAC;AACJ,MAAI,CAAC,KAAM,QAAO,KAAA;;CAEpB,MAAM,OAAO,QAAQ,QAAQ,SAAS;AACtC,QAAO,aAAa,MAAM,SAAS,KAAK;;;AAI1C,MAAa,gBACX,MACA,SACA,SACgC;AAChC,SAAQ,QAAQ,YAAY;AAC5B,SAAQ,QAAQ,cAAc;CAC9B,MAAM,WAAW,KAAK,MAAM,SAAS,KAAK;AAC1C,KAAI,CAAC,YAAa,CAAC,SAAS,QAAQ,IAAI,CAAC,MAAM,QAAQ,QAAQ,CAAG,QAAO,KAAA;AACzE,KAAI,MAAM,SAAS,WAAW,SAAS,QAAQ,IAAI,SAAS,QAAQ,KAAK,CACvE,QAAO;EACL,MAAM;EACN,QAAQ,QAAQ;EAChB,KAAK;EACN;AACH,QAAO;EACL,MAAM;EACN,QAAQ;EACR,KACE,MAAM,SAAS,UAAU,MAAM,QAAQ,QAAQ,GAC3CA,YAAmB,SAAS,KAAK,MAAM,CAAC,MAAM,KAAK,GACnD;EACP;;AAGH,MAAa,YAAY,QAAyB,UAAoC;AACpF,SAAQ,QAAQ,kBAAkB;AAClC,KAAI,OAAO,SAAS,MAAM,OAAQ,QAAO;AACzC,MAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS,GAAG;AACrD,UAAQ,QAAQ,iBAAiB;AACjC,MAAI,OAAO,WAAW,MAAM,OAAQ,QAAO;;AAE7C,QAAO;;AAGT,MAAa,YAAY,GAAoB,MAC3C,SAAS,GAAG,EAAE,IAAI,SAAS,GAAG,EAAE;AAElC,MAAa,YAAY,YAAqC;CAC5D,IAAI,SAAS;AACb,MAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,QAAQ,SAAS,GAAG;AACtD,MAAI,QAAQ,EAAG,WAAU;AACzB,YAAU,QAAQ,OAAO,WAAW,KAAK,KAAK,CAAC,WAAW,KAAK,KAAK;;AAEtE,QAAO;;;AAoBT,IAAa,eAAb,MAA6B;CAC3B,OAA6C;EAAE,0BAAU,IAAI,KAAK;EAAE,wBAAQ,IAAI,KAAK;EAAE;CAEvF,IAAI,SAA0B,OAAU,QAAuB;EAC7D,IAAI,OAAO,KAAK;AAChB,OAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,UAAU,WAAW,KAAA,IAAY,IAAI,IAAI,KAAK;GACxE,MAAM,UAAU,IAAI,QAAQ,SAAS,QAAQ,KAAK;GAClD,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ;AACtC,OAAI,CAAC,OAAO;AACV,YAAQ;KAAE,0BAAU,IAAI,KAAK;KAAE,wBAAQ,IAAI,KAAK;KAAE;AAClD,SAAK,SAAS,IAAI,SAAS,MAAM;;AAEnC,UAAO;;AAET,OAAK,OAAO,IAAI,MAAM;;CAGxB,OAAO,SAA0B,OAAgB;EAC/C,MAAM,UAAiC,EAAE;EACzC,IAAI,OAAO,KAAK;AAChB,OAAK,MAAM,WAAW,SAAS;GAC7B,MAAM,QAAQ,KAAK,SAAS,IAAI,QAAQ;AACxC,OAAI,CAAC,MAAO;AACZ,WAAQ,KAAK,KAAK;AAClB,UAAO;;AAET,OAAK,OAAO,OAAO,MAAM;AACzB,OAAK,IAAI,IAAI,QAAQ,SAAS,GAAG,KAAK,KAAK,CAAC,KAAK,OAAO,QAAQ,CAAC,KAAK,SAAS,MAAM,KAAK;AACxF,UAAO,QAAQ;AACf,QAAK,SAAS,OAAO,QAAQ,GAAG;;;CAIpC,MAAM,SAAsD;EAC1D,IAAI,OAAO,KAAK;AAChB,OAAK,MAAM,WAAW,SAAS;GAC7B,MAAM,QAAQ,KAAK,SAAS,IAAI,QAAQ;AACxC,OAAI,CAAC,MAAO,QAAO,KAAA;AACnB,UAAO;;AAET,SAAO,KAAK;;CAGd,YAAY,SAA0B,SAAS,OAAgB;EAC7D,IAAI,OAAO,KAAK;AAChB,OAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,OAAI,KAAK,OAAO,KAAM,QAAO;GAC7B,MAAM,QAAQ,KAAK,SAAS,IAAI,QAAQ,GAAG;AAC3C,OAAI,CAAC,MAAO,QAAO;AACnB,UAAO;;AAET,SAAO,CAAC,UAAU,KAAK,OAAO,OAAO;;CAGvC,aAAa,SAA0B,WAA2C;EAChF,IAAI,OAAO,KAAK;AAChB,OAAK,IAAI,IAAI,IAAK,KAAK;AACrB,QAAK,MAAM,SAAS,KAAK,OAAQ,KAAI,UAAU,MAAM,CAAE,QAAO;AAC9D,OAAI,MAAM,QAAQ,OAAQ,QAAO;GACjC,MAAM,QAAQ,KAAK,SAAS,IAAI,QAAQ,GAAG;AAC3C,OAAI,CAAC,MAAO,QAAO;AACnB,UAAO;;;CAIX,SAAS,SAAmC;EAC1C,IAAI,OAAO,KAAK;AAChB,OAAK,MAAM,WAAW,SAAS;AAC7B,OAAI,KAAK,OAAO,KAAM,QAAO;GAC7B,MAAM,QAAQ,KAAK,SAAS,IAAI,QAAQ;AACxC,OAAI,CAAC,MAAO,QAAO;AACnB,UAAO;;AAET,SAAO,KAAK,OAAO,OAAO,KAAK,KAAK,SAAS,OAAO;;CAGtD,cAAc,SAAmC;EAC/C,IAAI,OAAO,KAAK;AAChB,OAAK,MAAM,WAAW,SAAS;GAC7B,MAAM,QAAQ,KAAK,SAAS,IAAI,QAAQ;AACxC,OAAI,CAAC,MAAO,QAAO;AACnB,UAAO;;AAET,SAAO,KAAK,OAAO,OAAO,KAAK,KAAK,SAAS,OAAO;;CAGtD,MACE,OACA,WAAoD,WAC+B;EACnF,MAAM,0BAAU,IAAI,KAA0B;EAC9C,MAAM,2BAAW,IAAI,KAA0B;EAC/C,MAAM,UAAU,SAA8B;AAC5C,OAAI,QAAQ,IAAI,KAAK,CAAE;AACvB,WAAQ,IAAI,KAAK;AACjB,QAAK,OAAO,QAAQ,MAAM;;EAE5B,MAAM,WAAW,YAAuC;AACtD,OAAI,SAAS,IAAI,QAAQ,CAAE;AAC3B,YAAS,IAAI,QAAQ;AACrB,UAAO,QAAQ;AACf,WAAQ,SAAS,QAAQ,QAAQ;;EAEnC,MAAM,YAAY,SAChB,aAAa,cAAc,OAAO,KAAK,GAAG,QAAQ,KAAK;AACzD,UAAQ,SAAS,YAAY;GAC3B,IAAI,OAAO,KAAK;AAChB,QAAK,MAAM,WAAW,SAAS;AAC7B,QAAI,SAAS,IAAI,KAAK,CAAE;AACxB,QAAI,aAAa,cAAe,QAAO,KAAK;IAC5C,MAAM,QAAQ,KAAK,SAAS,IAAI,QAAQ;AACxC,QAAI,CAAC,MAAO;AACZ,WAAO;;AAET,OAAI,CAAC,QAAS,UAAS,KAAK;QACvB;AACH,QAAI,aAAa,cAAe,QAAO,KAAK;AAC5C,SAAK,MAAM,UAAU,SAAS;KAC5B,MAAM,QAAQ,KAAK,SAAS,IAAI,OAAO,IAAI;AAC3C,SAAI,MAAO,UAAS,MAAM;;;;;CAMlC,QAAc;AACZ,OAAK,KAAK,OAAO,OAAO;AACxB,OAAK,KAAK,SAAS,OAAO;;;;;ACzgB9B,MAAa,MACX,SACA,MACA,YAEA,OAAO,OAAO;CAAE,QAAQ;CAAY;CAAM,SAAS,OAAO,OAAO,CAAC,GAAG,QAAQ,CAAC;CAAE;CAAS,CAAC;AAC5F,IAAa,mBAAb,cAAsC,MAAM;CAC1C,YAAY,OAA+B;AACzC,QAAM,MAAM,QAAQ;AADD,OAAA,QAAA;;;AAIvB,MAAa,QAAQ,SAA0B,MAAyB,YAA2B;AACjG,OAAM,IAAI,iBAAiB,GAAG,SAAS,MAAM,QAAQ,CAAC;;AAGxD,MAAa,gBAAgB,OAAmB,UAAU,MAAM,YAC9D,KAAK,SAAS,MAAM,SAAS,sBAAsB,kBAAkB,MAAM,MAAM,MAAM,QAAQ"}
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_ownership = require("./ownership-CY0nPXGF.cjs");
3
- const require_driver = require("./driver-CNxqMVFH.cjs");
2
+ const require_issue = require("./issue-DhrNdQNg.cjs");
3
+ const require_driver = require("./driver-xOIkwrB8.cjs");
4
4
  //#region core/src/local-sync/contract.ts
5
5
  var LocalSyncUnavailableError = class extends Error {
6
6
  constructor(capability) {
@@ -28,7 +28,7 @@ var LocalSyncReadOnlyError = class extends Error {
28
28
  };
29
29
  var LocalSyncUnsupportedOperationError = class extends Error {
30
30
  constructor() {
31
- super("Local sync persists operation commands. runtime.replace() and externally supplied remote operations are unavailable while it is attached.");
31
+ super("Local sync appends committed changes. runtime.replace() and externally supplied remote changes are unavailable while it is attached.");
32
32
  this.name = "LocalSyncUnsupportedOperationError";
33
33
  }
34
34
  };
@@ -40,8 +40,8 @@ var LocalSyncDisposedError = class extends Error {
40
40
  };
41
41
  //#endregion
42
42
  //#region core/src/local-sync/json.ts
43
- const defaultJsonCommandLimits = Object.freeze({
44
- maxOperations: 1e3,
43
+ const defaultJsonChangeLimits = Object.freeze({
44
+ maxChanges: 1e3,
45
45
  maxBytes: 1e6,
46
46
  maxDepth: 64,
47
47
  maxStringLength: 256e3
@@ -59,10 +59,10 @@ const limit = (value, fallback, label) => {
59
59
  throw new TypeError(`${label} must be a positive safe integer.`);
60
60
  };
61
61
  const resolveLimits = (input) => ({
62
- maxOperations: limit(input?.maxOperations, defaultJsonCommandLimits.maxOperations, "maxOperations"),
63
- maxBytes: limit(input?.maxBytes, defaultJsonCommandLimits.maxBytes, "maxBytes"),
64
- maxDepth: limit(input?.maxDepth, defaultJsonCommandLimits.maxDepth, "maxDepth"),
65
- maxStringLength: limit(input?.maxStringLength, defaultJsonCommandLimits.maxStringLength, "maxStringLength")
62
+ maxChanges: limit(input?.maxChanges, defaultJsonChangeLimits.maxChanges, "maxChanges"),
63
+ maxBytes: limit(input?.maxBytes, defaultJsonChangeLimits.maxBytes, "maxBytes"),
64
+ maxDepth: limit(input?.maxDepth, defaultJsonChangeLimits.maxDepth, "maxDepth"),
65
+ maxStringLength: limit(input?.maxStringLength, defaultJsonChangeLimits.maxStringLength, "maxStringLength")
66
66
  });
67
67
  const validate = (value, path, limits, depth, ancestors) => {
68
68
  if (limits && depth > limits.maxDepth) throw new LocalSyncDataError(`${describePath(path)} exceeds the maximum JSON depth.`);
@@ -79,17 +79,25 @@ const validate = (value, path, limits, depth, ancestors) => {
79
79
  if (ancestors.has(value)) throw new LocalSyncDataError(`${describePath(path)} contains a cycle.`);
80
80
  ancestors.add(value);
81
81
  try {
82
- for (let index = 0; index < value.length; index += 1) validate(value[index], [...path, String(index)], limits, depth + 1, ancestors);
82
+ for (let index = 0; index < value.length; index += 1) {
83
+ path.push(String(index));
84
+ validate(value[index], path, limits, depth + 1, ancestors);
85
+ path.pop();
86
+ }
83
87
  } finally {
84
88
  ancestors.delete(value);
85
89
  }
86
90
  return value;
87
91
  }
88
- if (require_ownership.isPlainObject(value)) {
92
+ if (require_issue.isPlainObject(value)) {
89
93
  if (ancestors.has(value)) throw new LocalSyncDataError(`${describePath(path)} contains a cycle.`);
90
94
  ancestors.add(value);
91
95
  try {
92
- for (const [key, entry] of Object.entries(value)) validate(entry, [...path, key], limits, depth + 1, ancestors);
96
+ for (const key of Object.keys(value)) {
97
+ path.push(key);
98
+ validate(value[key], path, limits, depth + 1, ancestors);
99
+ path.pop();
100
+ }
93
101
  } finally {
94
102
  ancestors.delete(value);
95
103
  }
@@ -98,21 +106,28 @@ const validate = (value, path, limits, depth, ancestors) => {
98
106
  throw new LocalSyncDataError(`${describePath(path)} must be JSON data.`);
99
107
  };
100
108
  const json = (value, label) => validate(value, [label], void 0, 0, /* @__PURE__ */ new WeakSet());
101
- const jsonArray = (value, label, input) => {
102
- const limits = resolveLimits(input);
103
- const parsed = validate(value, [label], limits, 0, /* @__PURE__ */ new WeakSet());
104
- if (!Array.isArray(parsed)) throw new LocalSyncDataError(`${label} must be a JSON array.`);
105
- if (parsed.length > limits.maxOperations) throw new LocalSyncDataError(`${label} exceeds the maximum operation count.`);
106
- const serialized = JSON.stringify(parsed);
107
- if (new TextEncoder().encode(serialized).byteLength > limits.maxBytes) throw new LocalSyncDataError(`${label} exceeds the maximum command size.`);
108
- return parsed;
109
+ const jsonChanges = (value, label, input) => {
110
+ const limits = input === void 0 ? void 0 : resolveLimits(input);
111
+ let changes;
112
+ try {
113
+ changes = require_driver.decodeChanges(value);
114
+ } catch {
115
+ throw new LocalSyncDataError(`${label} contains an invalid ChangeSet.`);
116
+ }
117
+ if (limits && require_driver.changeCount(changes) > limits.maxChanges) throw new LocalSyncDataError(`${label} exceeds the maximum change count.`);
118
+ validate(changes.changes, [label], limits, 0, /* @__PURE__ */ new WeakSet());
119
+ if (limits) {
120
+ const serialized = JSON.stringify(changes.changes);
121
+ if (new TextEncoder().encode(serialized).byteLength > limits.maxBytes) throw new LocalSyncDataError(`${label} exceeds the maximum ChangeSet size.`);
122
+ }
123
+ return changes;
109
124
  };
110
125
  //#endregion
111
126
  //#region core/src/local-sync/timeline.ts
112
- const DATABASE_VERSION = 2;
127
+ const DATABASE_VERSION = 5;
128
+ const FORMAT_VERSION = 3;
113
129
  const DOCUMENTS = "documents";
114
130
  const COMMITS = "commits";
115
- const ACTORS = "actors";
116
131
  const request = (value) => new Promise((resolve, reject) => {
117
132
  value.onsuccess = () => resolve(value.result);
118
133
  value.onerror = () => reject(value.error ?? /* @__PURE__ */ new Error("IndexedDB request failed."));
@@ -144,11 +159,12 @@ const positiveInteger$1 = (value, label) => {
144
159
  throw new LocalSyncConsistencyError(`${label} is malformed in IndexedDB.`);
145
160
  };
146
161
  const documentRecord = (value) => {
147
- if (!require_ownership.isRecord(value)) throw new LocalSyncConsistencyError("Document record is malformed in IndexedDB.");
162
+ if (!require_issue.isRecord(value) || value.formatVersion !== FORMAT_VERSION) throw new LocalSyncConsistencyError("Document record is malformed in IndexedDB.");
148
163
  const checkpointSeq = nonNegativeInteger(value.checkpointSeq, "document.checkpointSeq");
149
164
  const headSeq = nonNegativeInteger(value.headSeq, "document.headSeq");
150
165
  if (checkpointSeq > headSeq) throw new LocalSyncConsistencyError("Document checkpoint exceeds its head sequence.");
151
166
  return Object.freeze({
167
+ formatVersion: FORMAT_VERSION,
152
168
  documentId: string(value.documentId, "document.documentId"),
153
169
  schemaVersion: positiveInteger$1(value.schemaVersion, "document.schemaVersion"),
154
170
  checkpointSeq,
@@ -157,24 +173,30 @@ const documentRecord = (value) => {
157
173
  });
158
174
  };
159
175
  const commitRecord = (value) => {
160
- if (!require_ownership.isRecord(value)) throw new LocalSyncConsistencyError("Commit record is malformed in IndexedDB.");
176
+ if (!require_issue.isRecord(value) || value.formatVersion !== FORMAT_VERSION) throw new LocalSyncConsistencyError("Commit record is malformed in IndexedDB.");
161
177
  return Object.freeze({
178
+ formatVersion: FORMAT_VERSION,
162
179
  documentId: string(value.documentId, "commit.documentId"),
163
180
  seq: positiveInteger$1(value.seq, "commit.seq"),
164
- operations: jsonArray(value.operations, "commit.operations"),
181
+ changes: jsonChanges({ changes: value.changes }, "commit.changes"),
165
182
  createdAt: nonNegativeInteger(value.createdAt, "commit.createdAt")
166
183
  });
167
184
  };
168
185
  const openIndexedDbTimeline = async (databaseName) => {
169
186
  const database = await new Promise((resolve, reject) => {
187
+ let incompatible = false;
170
188
  const open = factory().open(databaseName, DATABASE_VERSION);
171
- open.onerror = () => reject(open.error ?? /* @__PURE__ */ new Error("Unable to open IndexedDB."));
189
+ open.onerror = () => reject(incompatible ? new LocalSyncConsistencyError("Unsupported Doxum storage format. Existing data was left unchanged.") : open.error ?? /* @__PURE__ */ new Error("Unable to open IndexedDB."));
172
190
  open.onblocked = () => reject(/* @__PURE__ */ new Error(`IndexedDB database '${databaseName}' is blocked.`));
173
- open.onupgradeneeded = () => {
191
+ open.onupgradeneeded = (event) => {
192
+ if (event.oldVersion !== 0) {
193
+ incompatible = true;
194
+ open.transaction?.abort();
195
+ return;
196
+ }
174
197
  const db = open.result;
175
198
  if (!db.objectStoreNames.contains(DOCUMENTS)) db.createObjectStore(DOCUMENTS, { keyPath: "documentId" });
176
199
  if (!db.objectStoreNames.contains(COMMITS)) db.createObjectStore(COMMITS, { keyPath: ["documentId", "seq"] });
177
- if (db.objectStoreNames.contains(ACTORS)) db.deleteObjectStore(ACTORS);
178
200
  };
179
201
  open.onsuccess = () => resolve(open.result);
180
202
  });
@@ -195,6 +217,7 @@ const openIndexedDbTimeline = async (databaseName) => {
195
217
  return record;
196
218
  }
197
219
  const record = {
220
+ formatVersion: FORMAT_VERSION,
198
221
  documentId,
199
222
  schemaVersion,
200
223
  checkpointSeq: 0,
@@ -222,18 +245,22 @@ const openIndexedDbTimeline = async (databaseName) => {
222
245
  const transaction = database.transaction([DOCUMENTS, COMMITS], "readwrite");
223
246
  const documents = transaction.objectStore(DOCUMENTS);
224
247
  const current = await readDocument(transaction, input.documentId);
225
- if (current.headSeq !== input.expectedHeadSeq) throw new LocalSyncConsistencyError("Local document advanced before the leader could append its command.");
248
+ if (current.headSeq !== input.expectedHeadSeq) throw new LocalSyncConsistencyError("Local document advanced before the leader could append its commit.");
226
249
  const commit = {
250
+ formatVersion: FORMAT_VERSION,
227
251
  documentId: input.documentId,
228
252
  seq: current.headSeq + 1,
229
- operations: input.operations,
253
+ changes: input.changes,
230
254
  createdAt: Date.now()
231
255
  };
232
256
  documents.put({
233
257
  ...current,
234
258
  headSeq: commit.seq
235
259
  });
236
- transaction.objectStore(COMMITS).put(commit);
260
+ transaction.objectStore(COMMITS).put({
261
+ ...commit,
262
+ changes: commit.changes.changes
263
+ });
237
264
  await complete(transaction);
238
265
  return Object.freeze(commit);
239
266
  },
@@ -340,10 +367,11 @@ const attachLocalSync = async (input) => {
340
367
  const runRuntime = (run) => driver?.run(run) ?? run();
341
368
  const applyStored = (commit) => {
342
369
  if (commit.seq !== headSeq + 1) throw new LocalSyncConsistencyError("Local commit log contains a sequence gap.");
343
- if (runRuntime(() => runtime.apply(commit.operations, {
370
+ if (runRuntime(() => runtime.apply(commit.changes, {
371
+ expectedRevision: runtime.revision(),
344
372
  source: "remote",
345
373
  history: false
346
- })).status !== "committed") throw new LocalSyncConsistencyError("A stored local command could not be applied.");
374
+ })).status !== "committed") throw new LocalSyncConsistencyError("A stored local ChangeSet could not be applied.");
347
375
  headSeq = commit.seq;
348
376
  };
349
377
  const restore = async (reset = false) => {
@@ -367,13 +395,13 @@ const attachLocalSync = async (input) => {
367
395
  });
368
396
  return next;
369
397
  };
370
- const persist = async (operations) => {
398
+ const persist = async (changes) => {
371
399
  const storedCommit = await timeline.append({
372
400
  documentId,
373
401
  expectedHeadSeq: headSeq,
374
- operations
402
+ changes
375
403
  });
376
- if (storedCommit.seq !== headSeq + 1) throw new LocalSyncConsistencyError("A local command was assigned an unexpected sequence.");
404
+ if (storedCommit.seq !== headSeq + 1) throw new LocalSyncConsistencyError("A local commit was assigned an unexpected sequence.");
377
405
  headSeq = storedCommit.seq;
378
406
  publishState();
379
407
  channel?.postMessage({
@@ -384,8 +412,8 @@ const attachLocalSync = async (input) => {
384
412
  };
385
413
  const record = (commit) => {
386
414
  try {
387
- const operations = jsonArray(commit.operations, "local command operations", input.commandLimits);
388
- enqueue(() => persist(operations)).catch(() => void 0);
415
+ const changes = jsonChanges(commit.changes, "local commit changes", input.changeLimits ?? {});
416
+ enqueue(() => persist(changes)).catch(() => void 0);
389
417
  } catch (error) {
390
418
  fail(error);
391
419
  }
@@ -519,6 +547,6 @@ exports.LocalSyncSchemaError = LocalSyncSchemaError;
519
547
  exports.LocalSyncUnavailableError = LocalSyncUnavailableError;
520
548
  exports.LocalSyncUnsupportedOperationError = LocalSyncUnsupportedOperationError;
521
549
  exports.attachLocalSync = attachLocalSync;
522
- exports.defaultJsonCommandLimits = defaultJsonCommandLimits;
550
+ exports.defaultJsonChangeLimits = defaultJsonChangeLimits;
523
551
 
524
552
  //# sourceMappingURL=local-sync.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"local-sync.cjs","names":["isPlainObject","positiveInteger","isRecord","installRuntimeWriteDriver"],"sources":["../core/src/local-sync/contract.ts","../core/src/local-sync/json.ts","../core/src/local-sync/timeline.ts","../core/src/local-sync/session.ts"],"sourcesContent":["import type { DocumentSchema } from '../schema';\nimport type { DocumentRuntime } from '../runtime/contract';\nimport type { JsonCommandLimits } from './json';\nimport type { Readable } from '../projection/readable';\n\nexport class LocalSyncUnavailableError extends Error {\n constructor(capability: 'IndexedDB' | 'Web Locks' | 'BroadcastChannel') {\n super(`${capability} is required by doxum/local-sync in this environment.`);\n this.name = 'LocalSyncUnavailableError';\n }\n}\n\nexport class LocalSyncSchemaError extends Error {\n constructor(documentId: string, expected: number, actual: number) {\n super(\n `Local document '${documentId}' uses schema version ${actual}, but version ${expected} was requested.`\n );\n this.name = 'LocalSyncSchemaError';\n }\n}\n\nexport class LocalSyncConsistencyError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'LocalSyncConsistencyError';\n }\n}\n\nexport class LocalSyncReadOnlyError extends Error {\n constructor() {\n super('This tab is following the local document and cannot write until it becomes the leader.');\n this.name = 'LocalSyncReadOnlyError';\n }\n}\n\nexport class LocalSyncUnsupportedOperationError extends Error {\n constructor() {\n super(\n 'Local sync persists operation commands. runtime.replace() and externally supplied remote operations are unavailable while it is attached.'\n );\n this.name = 'LocalSyncUnsupportedOperationError';\n }\n}\n\nexport class LocalSyncDisposedError extends Error {\n constructor() {\n super('Local sync has been disposed.');\n this.name = 'LocalSyncDisposedError';\n }\n}\n\nexport type LocalSyncState =\n | {\n readonly status: 'leader' | 'follower';\n readonly headSeq: number;\n readonly checkpointSeq: number;\n }\n | {\n readonly status: 'error';\n readonly headSeq: number;\n readonly checkpointSeq: number;\n readonly error: unknown;\n }\n | { readonly status: 'disposed' };\n\nexport type LocalSync = {\n readonly state: Readable<LocalSyncState>;\n flush(): Promise<void>;\n dispose(): Promise<void>;\n};\n\nexport type AttachLocalSyncOptions<TSchema extends DocumentSchema> = {\n readonly runtime: DocumentRuntime<TSchema>;\n readonly database: string;\n readonly documentId: string;\n readonly schemaVersion?: number;\n readonly commandLimits?: JsonCommandLimits;\n readonly onError?: (error: unknown) => void;\n};\n","import { isPlainObject } from '../value/ownership';\n\nexport type JsonPrimitive = null | boolean | number | string;\nexport type JsonValue =\n JsonPrimitive | readonly JsonValue[] | { readonly [key: string]: JsonValue };\n\nexport type JsonCommandLimits = {\n readonly maxOperations?: number;\n readonly maxBytes?: number;\n readonly maxDepth?: number;\n readonly maxStringLength?: number;\n};\n\ntype ResolvedJsonLimits = {\n readonly maxOperations: number;\n readonly maxBytes: number;\n readonly maxDepth: number;\n readonly maxStringLength: number;\n};\n\nexport const defaultJsonCommandLimits: Readonly<ResolvedJsonLimits> = Object.freeze({\n maxOperations: 1_000,\n maxBytes: 1_000_000,\n maxDepth: 64,\n maxStringLength: 256_000,\n});\n\nexport class LocalSyncDataError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'LocalSyncDataError';\n }\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: JsonCommandLimits | undefined): ResolvedJsonLimits => ({\n maxOperations: limit(\n input?.maxOperations,\n defaultJsonCommandLimits.maxOperations,\n 'maxOperations'\n ),\n maxBytes: limit(input?.maxBytes, defaultJsonCommandLimits.maxBytes, 'maxBytes'),\n maxDepth: limit(input?.maxDepth, defaultJsonCommandLimits.maxDepth, 'maxDepth'),\n maxStringLength: limit(\n input?.maxStringLength,\n defaultJsonCommandLimits.maxStringLength,\n 'maxStringLength'\n ),\n});\n\nconst validate = (\n value: unknown,\n path: readonly string[],\n limits: ResolvedJsonLimits | undefined,\n depth: number,\n ancestors: WeakSet<object>\n): JsonValue => {\n if (limits && depth > limits.maxDepth)\n throw new LocalSyncDataError(`${describePath(path)} exceeds the maximum JSON depth.`);\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 LocalSyncDataError(`${describePath(path)} exceeds the maximum string length.`);\n }\n if (typeof value === 'number') {\n if (Number.isFinite(value)) return value;\n throw new LocalSyncDataError(`${describePath(path)} contains a non-finite number.`);\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value))\n throw new LocalSyncDataError(`${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (let index = 0; index < value.length; index += 1)\n validate(value[index], [...path, String(index)], limits, depth + 1, ancestors);\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 LocalSyncDataError(`${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (const [key, entry] of Object.entries(value))\n validate(entry, [...path, key], limits, depth + 1, ancestors);\n } finally {\n ancestors.delete(value);\n }\n return value as { readonly [key: string]: JsonValue };\n }\n throw new LocalSyncDataError(`${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 jsonArray = (\n value: unknown,\n label: string,\n input?: JsonCommandLimits\n): readonly JsonValue[] => {\n const limits = resolveLimits(input);\n const parsed = validate(value, [label], limits, 0, new WeakSet());\n if (!Array.isArray(parsed)) throw new LocalSyncDataError(`${label} must be a JSON array.`);\n if (parsed.length > limits.maxOperations)\n throw new LocalSyncDataError(`${label} exceeds the maximum operation count.`);\n const serialized = JSON.stringify(parsed);\n if (new TextEncoder().encode(serialized).byteLength > limits.maxBytes)\n throw new LocalSyncDataError(`${label} exceeds the maximum command size.`);\n return parsed;\n};\n","import {\n LocalSyncConsistencyError,\n LocalSyncSchemaError,\n LocalSyncUnavailableError,\n} from './contract';\nimport { json, jsonArray, type JsonValue } from './json';\nimport { isRecord } from '../value/ownership';\n\nconst DATABASE_VERSION = 2;\nconst DOCUMENTS = 'documents';\nconst COMMITS = 'commits';\nconst ACTORS = 'actors';\n\nexport type StoredDocument = {\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 documentId: string;\n readonly seq: number;\n readonly operations: readonly JsonValue[];\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 = () => reject(value.error ?? new Error('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(transaction.error ?? new Error('IndexedDB transaction was aborted.'));\n transaction.onerror = () =>\n reject(transaction.error ?? new Error('IndexedDB transaction failed.'));\n });\n\nconst factory = (): IDBFactory => {\n if (!globalThis.indexedDB) throw new LocalSyncUnavailableError('IndexedDB');\n return globalThis.indexedDB;\n};\n\nconst keyRange = (): typeof IDBKeyRange => {\n if (!globalThis.IDBKeyRange) throw new LocalSyncUnavailableError('IndexedDB');\n return globalThis.IDBKeyRange;\n};\n\nconst string = (value: unknown, label: string): string => {\n if (typeof value === 'string') return value;\n throw new LocalSyncConsistencyError(`${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 LocalSyncConsistencyError(`${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 LocalSyncConsistencyError(`${label} is malformed in IndexedDB.`);\n};\n\nconst documentRecord = (value: unknown): StoredDocument => {\n if (!isRecord(value))\n throw new LocalSyncConsistencyError('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 LocalSyncConsistencyError('Document checkpoint exceeds its head sequence.');\n return Object.freeze({\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))\n throw new LocalSyncConsistencyError('Commit record is malformed in IndexedDB.');\n return Object.freeze({\n documentId: string(value.documentId, 'commit.documentId'),\n seq: positiveInteger(value.seq, 'commit.seq'),\n operations: jsonArray(value.operations, 'commit.operations'),\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 operations: readonly JsonValue[];\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 const open = factory().open(databaseName, DATABASE_VERSION);\n open.onerror = () => reject(open.error ?? new Error('Unable to open IndexedDB.'));\n open.onblocked = () => reject(new Error(`IndexedDB database '${databaseName}' is blocked.`));\n open.onupgradeneeded = () => {\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 if (db.objectStoreNames.contains(ACTORS)) db.deleteObjectStore(ACTORS);\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 LocalSyncConsistencyError(`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 LocalSyncSchemaError(documentId, schemaVersion, record.schemaVersion);\n await complete(transaction);\n return record;\n }\n const record: StoredDocument = {\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 LocalSyncConsistencyError(\n 'Local document advanced before the leader could append its command.'\n );\n const commit: StoredCommit = {\n documentId: input.documentId,\n seq: current.headSeq + 1,\n operations: input.operations,\n createdAt: Date.now(),\n };\n documents.put({ ...current, headSeq: commit.seq });\n transaction.objectStore(COMMITS).put(commit);\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, DocumentRuntime, OperationResult } from '../runtime/contract';\nimport type { DocumentSchema, ReadonlyDocument } from '../schema';\nimport {\n LocalSyncConsistencyError,\n LocalSyncDisposedError,\n LocalSyncReadOnlyError,\n LocalSyncUnsupportedOperationError,\n LocalSyncUnavailableError,\n type AttachLocalSyncOptions,\n type LocalSync,\n type LocalSyncState,\n} from './contract';\nimport { json, jsonArray, type JsonValue } from './json';\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 LocalSyncUnavailableError('Web Locks');\n return value as LockManager;\n};\n\nconst channelConstructor = (): ChannelConstructor => {\n const value: unknown = globalThis.BroadcastChannel;\n if (typeof value !== 'function') throw new LocalSyncUnavailableError('BroadcastChannel');\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 DocumentSchema>(\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);\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: unknown;\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 (error) {\n try {\n input.onError?.(error);\n } catch {\n /* A listener cannot fail persistence. */\n }\n }\n }\n };\n\n const fail = (error: unknown): void => {\n if (fault || disposed) return;\n fault = error;\n publishState();\n try {\n input.onError?.(error);\n } catch {\n // Error reporting cannot repair or replace the original synchronization failure.\n }\n };\n\n const assertOpen = (): void => {\n if (closing || disposed) throw new LocalSyncDisposedError();\n if (fault) throw fault;\n };\n\n const assertLeader = (intent: RuntimeWriteIntent): void => {\n assertOpen();\n if (role !== 'leader') throw new LocalSyncReadOnlyError();\n if (intent.kind === 'replace' || (intent.kind === 'apply' && intent.source === 'remote'))\n throw new LocalSyncUnsupportedOperationError();\n };\n\n const runRuntime = <TResult>(run: () => TResult): TResult => driver?.run(run) ?? run();\n\n const applyStored = (commit: StoredCommit): void => {\n if (commit.seq !== headSeq + 1)\n throw new LocalSyncConsistencyError('Local commit log contains a sequence gap.');\n const result = runRuntime(() =>\n runtime.apply(commit.operations, { source: 'remote', history: false })\n );\n if (result.status !== 'committed')\n throw new LocalSyncConsistencyError('A stored local command 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 LocalSyncConsistencyError(\n 'Local document schema changed while this attachment was open.'\n );\n if (headSeq > current.headSeq)\n throw new LocalSyncConsistencyError('The local timeline moved behind this attachment.');\n if (reset || headSeq < current.checkpointSeq) {\n const result = runRuntime(() =>\n runtime.replace(current.checkpoint as ReadonlyDocument<TSchema>, { source: 'remote' })\n );\n if (result.status === 'rejected')\n throw new LocalSyncConsistencyError('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 LocalSyncConsistencyError(\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 (operations: readonly JsonValue[]): Promise<void> => {\n const storedCommit = await timeline.append({\n documentId,\n expectedHeadSeq: headSeq,\n operations,\n });\n if (storedCommit.seq !== headSeq + 1)\n throw new LocalSyncConsistencyError('A local command was assigned an unexpected sequence.');\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 operations = jsonArray(\n commit.operations,\n 'local command operations',\n input.commandLimits\n );\n void enqueue(() => persist(operations)).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 LocalSyncDisposedError();\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 fail(error);\n throw 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 error;\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"local-sync.cjs","names":["isPlainObject","decodeChanges","changeCount","positiveInteger","isRecord","installRuntimeWriteDriver"],"sources":["../core/src/local-sync/contract.ts","../core/src/local-sync/json.ts","../core/src/local-sync/timeline.ts","../core/src/local-sync/session.ts"],"sourcesContent":["import type { ObjectNode } from '../schema';\nimport type { DocumentRuntime } from '../runtime/contract';\nimport type { JsonChangeLimits } from './json';\nimport type { Readable } from '../projection/readable';\n\nexport class LocalSyncUnavailableError extends Error {\n constructor(capability: 'IndexedDB' | 'Web Locks' | 'BroadcastChannel') {\n super(`${capability} is required by doxum/local-sync in this environment.`);\n this.name = 'LocalSyncUnavailableError';\n }\n}\n\nexport class LocalSyncSchemaError extends Error {\n constructor(documentId: string, expected: number, actual: number) {\n super(\n `Local document '${documentId}' uses schema version ${actual}, but version ${expected} was requested.`\n );\n this.name = 'LocalSyncSchemaError';\n }\n}\n\nexport class LocalSyncConsistencyError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'LocalSyncConsistencyError';\n }\n}\n\nexport class LocalSyncReadOnlyError extends Error {\n constructor() {\n super('This tab is following the local document and cannot write until it becomes the leader.');\n this.name = 'LocalSyncReadOnlyError';\n }\n}\n\nexport class LocalSyncUnsupportedOperationError extends Error {\n constructor() {\n super(\n 'Local sync appends committed changes. runtime.replace() and externally supplied remote changes are unavailable while it is attached.'\n );\n this.name = 'LocalSyncUnsupportedOperationError';\n }\n}\n\nexport class LocalSyncDisposedError extends Error {\n constructor() {\n super('Local sync has been disposed.');\n this.name = 'LocalSyncDisposedError';\n }\n}\n\nexport type LocalSyncState =\n | {\n readonly status: 'leader' | 'follower';\n readonly headSeq: number;\n readonly checkpointSeq: number;\n }\n | {\n readonly status: 'error';\n readonly headSeq: number;\n readonly checkpointSeq: number;\n readonly error: unknown;\n }\n | { readonly status: 'disposed' };\n\nexport type LocalSync = {\n readonly state: Readable<LocalSyncState>;\n flush(): Promise<void>;\n dispose(): Promise<void>;\n};\n\nexport type AttachLocalSyncOptions<TSchema extends ObjectNode> = {\n readonly runtime: DocumentRuntime<TSchema>;\n readonly database: string;\n readonly documentId: string;\n readonly schemaVersion?: number;\n /** Admission limits for new local commits; durable replay does not reapply them. */\n readonly changeLimits?: JsonChangeLimits;\n readonly onError?: (error: unknown) => void;\n};\n","import { isPlainObject } from '../value/ownership';\nimport { changeCount, decodeChanges } from '../mutation/changes';\nimport type { ChangeSet } from '../changes';\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\nexport class LocalSyncDataError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'LocalSyncDataError';\n }\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 LocalSyncDataError(`${describePath(path)} exceeds the maximum JSON depth.`);\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 LocalSyncDataError(`${describePath(path)} exceeds the maximum string length.`);\n }\n if (typeof value === 'number') {\n if (Number.isFinite(value)) return value;\n throw new LocalSyncDataError(`${describePath(path)} contains a non-finite number.`);\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value))\n throw new LocalSyncDataError(`${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 LocalSyncDataError(`${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 LocalSyncDataError(`${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 = decodeChanges(value);\n } catch {\n throw new LocalSyncDataError(`${label} contains an invalid ChangeSet.`);\n }\n if (limits && changeCount(changes) > limits.maxChanges)\n throw new LocalSyncDataError(`${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 LocalSyncDataError(`${label} exceeds the maximum ChangeSet size.`);\n }\n return changes;\n};\n","import {\n LocalSyncConsistencyError,\n LocalSyncSchemaError,\n LocalSyncUnavailableError,\n} from './contract';\nimport { json, jsonChanges, type JsonValue } from './json';\nimport type { ChangeSet } from '../changes';\nimport { isRecord } from '../value/ownership';\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 = () => reject(value.error ?? new Error('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(transaction.error ?? new Error('IndexedDB transaction was aborted.'));\n transaction.onerror = () =>\n reject(transaction.error ?? new Error('IndexedDB transaction failed.'));\n });\n\nconst factory = (): IDBFactory => {\n if (!globalThis.indexedDB) throw new LocalSyncUnavailableError('IndexedDB');\n return globalThis.indexedDB;\n};\n\nconst keyRange = (): typeof IDBKeyRange => {\n if (!globalThis.IDBKeyRange) throw new LocalSyncUnavailableError('IndexedDB');\n return globalThis.IDBKeyRange;\n};\n\nconst string = (value: unknown, label: string): string => {\n if (typeof value === 'string') return value;\n throw new LocalSyncConsistencyError(`${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 LocalSyncConsistencyError(`${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 LocalSyncConsistencyError(`${label} is malformed in IndexedDB.`);\n};\n\nconst documentRecord = (value: unknown): StoredDocument => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncConsistencyError('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 LocalSyncConsistencyError('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 LocalSyncConsistencyError('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 LocalSyncConsistencyError(\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 LocalSyncConsistencyError(`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 LocalSyncSchemaError(documentId, schemaVersion, record.schemaVersion);\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 LocalSyncConsistencyError(\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 { ObjectNode, Infer } from '../schema';\nimport {\n LocalSyncConsistencyError,\n LocalSyncDisposedError,\n LocalSyncReadOnlyError,\n LocalSyncUnsupportedOperationError,\n LocalSyncUnavailableError,\n type AttachLocalSyncOptions,\n type LocalSync,\n type LocalSyncState,\n} from './contract';\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 LocalSyncUnavailableError('Web Locks');\n return value as LockManager;\n};\n\nconst channelConstructor = (): ChannelConstructor => {\n const value: unknown = globalThis.BroadcastChannel;\n if (typeof value !== 'function') throw new LocalSyncUnavailableError('BroadcastChannel');\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 ObjectNode>(\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);\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: unknown;\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 (error) {\n try {\n input.onError?.(error);\n } catch {\n /* A listener cannot fail persistence. */\n }\n }\n }\n };\n\n const fail = (error: unknown): void => {\n if (fault || disposed) return;\n fault = error;\n publishState();\n try {\n input.onError?.(error);\n } catch {\n // Error reporting cannot repair or replace the original synchronization failure.\n }\n };\n\n const assertOpen = (): void => {\n if (closing || disposed) throw new LocalSyncDisposedError();\n if (fault) throw fault;\n };\n\n const assertLeader = (intent: RuntimeWriteIntent): void => {\n assertOpen();\n if (role !== 'leader') throw new LocalSyncReadOnlyError();\n if (intent.kind === 'replace' || (intent.kind === 'apply' && intent.source === 'remote'))\n throw new LocalSyncUnsupportedOperationError();\n };\n\n const runRuntime = <TResult>(run: () => TResult): TResult => driver?.run(run) ?? run();\n\n const applyStored = (commit: StoredCommit): void => {\n if (commit.seq !== headSeq + 1)\n throw new LocalSyncConsistencyError('Local commit log contains a sequence gap.');\n const result = runRuntime(() =>\n runtime.apply(commit.changes, {\n expectedRevision: runtime.revision(),\n source: 'remote',\n history: false,\n })\n );\n if (result.status !== 'committed')\n throw new LocalSyncConsistencyError('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 LocalSyncConsistencyError(\n 'Local document schema changed while this attachment was open.'\n );\n if (headSeq > current.headSeq)\n throw new LocalSyncConsistencyError('The local timeline moved behind this attachment.');\n if (reset || headSeq < current.checkpointSeq) {\n const result = runRuntime(() =>\n runtime.replace(current.checkpoint as Infer<TSchema>, { source: 'remote' })\n );\n if (result.status === 'rejected')\n throw new LocalSyncConsistencyError('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 LocalSyncConsistencyError(\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 LocalSyncConsistencyError('A local commit was assigned an unexpected sequence.');\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 LocalSyncDisposedError();\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 fail(error);\n throw 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 error;\n }\n};\n"],"mappings":"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"}
@@ -1,23 +1,23 @@
1
- import { c as DocumentRuntime, v as Readable, wt as DocumentSchema } from "./contract-j3SLGAwh.cjs";
1
+ import { Q as ObjectNode, l as DocumentRuntime, v as Readable } from "./contract-CIU5FCC1.cjs";
2
2
 
3
3
  //#region core/src/local-sync/json.d.ts
4
4
  type JsonPrimitive = null | boolean | number | string;
5
5
  type JsonValue = JsonPrimitive | readonly JsonValue[] | {
6
6
  readonly [key: string]: JsonValue;
7
7
  };
8
- type JsonCommandLimits = {
9
- readonly maxOperations?: number;
8
+ type JsonChangeLimits = {
9
+ readonly maxChanges?: number;
10
10
  readonly maxBytes?: number;
11
11
  readonly maxDepth?: number;
12
12
  readonly maxStringLength?: number;
13
13
  };
14
14
  type ResolvedJsonLimits = {
15
- readonly maxOperations: number;
15
+ readonly maxChanges: number;
16
16
  readonly maxBytes: number;
17
17
  readonly maxDepth: number;
18
18
  readonly maxStringLength: number;
19
19
  };
20
- declare const defaultJsonCommandLimits: Readonly<ResolvedJsonLimits>;
20
+ declare const defaultJsonChangeLimits: Readonly<ResolvedJsonLimits>;
21
21
  declare class LocalSyncDataError extends Error {
22
22
  constructor(message: string);
23
23
  }
@@ -58,17 +58,17 @@ type LocalSync = {
58
58
  flush(): Promise<void>;
59
59
  dispose(): Promise<void>;
60
60
  };
61
- type AttachLocalSyncOptions<TSchema extends DocumentSchema> = {
61
+ type AttachLocalSyncOptions<TSchema extends ObjectNode> = {
62
62
  readonly runtime: DocumentRuntime<TSchema>;
63
63
  readonly database: string;
64
64
  readonly documentId: string;
65
- readonly schemaVersion?: number;
66
- readonly commandLimits?: JsonCommandLimits;
65
+ readonly schemaVersion?: number; /** Admission limits for new local commits; durable replay does not reapply them. */
66
+ readonly changeLimits?: JsonChangeLimits;
67
67
  readonly onError?: (error: unknown) => void;
68
68
  };
69
69
  //#endregion
70
70
  //#region core/src/local-sync/session.d.ts
71
- declare const attachLocalSync: <TSchema extends DocumentSchema>(input: AttachLocalSyncOptions<TSchema>) => Promise<LocalSync>;
71
+ declare const attachLocalSync: <TSchema extends ObjectNode>(input: AttachLocalSyncOptions<TSchema>) => Promise<LocalSync>;
72
72
  //#endregion
73
- export { type AttachLocalSyncOptions, type JsonCommandLimits, type JsonPrimitive, type JsonValue, type LocalSync, LocalSyncConsistencyError, LocalSyncDataError, LocalSyncDisposedError, LocalSyncReadOnlyError, LocalSyncSchemaError, type LocalSyncState, LocalSyncUnavailableError, LocalSyncUnsupportedOperationError, attachLocalSync, defaultJsonCommandLimits };
73
+ export { type AttachLocalSyncOptions, type JsonChangeLimits, type JsonPrimitive, type JsonValue, type LocalSync, LocalSyncConsistencyError, LocalSyncDataError, LocalSyncDisposedError, LocalSyncReadOnlyError, LocalSyncSchemaError, type LocalSyncState, LocalSyncUnavailableError, LocalSyncUnsupportedOperationError, attachLocalSync, defaultJsonChangeLimits };
74
74
  //# sourceMappingURL=local-sync.d.cts.map