@earendil-works/chord 0.86.1 → 0.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../../src/state/draft.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,MAAM,MAAM,KAAK,CAAC,CAAC,EAAE,KAAK,SAAS,SAAS,OAAO,EAAE,GAAG,EAAE,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,GACpF,CAAC,GACD,CAAC,SAAS,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GACzC,CAAC,GACD,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GACtC,CAAC,GACD,CAAC,SAAS,MAAM,GACf;IAAE,CAAC,UAAU,GAAG,IAAI,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,OAAO,CAAC,CAAC;CAAE,GAClE,CAAC,CAAC;AAER,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI;IAChC,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CACvB,CAAC;AAwBF,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,eAAe,CAAC,CAAC,CAAC,CAgCpH;AAED,qEAAqE;AACrE,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAEvF","sourcesContent":["/** A mutable transaction-scoped view of a JSON value, preserving tuple positions. */\nexport type Draft<T, Depth extends readonly unknown[] = []> = Depth[\"length\"] extends 8\n\t? T\n\t: T extends null | boolean | number | string\n\t\t? T\n\t\t: T extends (...args: never[]) => unknown\n\t\t\t? T\n\t\t\t: T extends object\n\t\t\t\t? { -readonly [Key in keyof T]: Draft<T[Key], [...Depth, unknown]> }\n\t\t\t\t: T;\n\nexport type ProduceMetadata<T> = {\n\tvalue: T;\n\towned: WeakSet<object>;\n};\n\ntype Container = Record<string, unknown> | unknown[];\n\ntype DraftState = {\n\tbase: Container;\n\tcontext: DraftContext;\n\tcopy: Container | undefined;\n\tmethods: Map<string, unknown> | undefined;\n\tproxy: object;\n\ttarget: unknown[] | undefined;\n};\n\ntype DraftContext = {\n\tactive: boolean;\n\tcreated: DraftState[];\n\towned: WeakSet<object>;\n\tproxies: WeakMap<object, DraftState>;\n\tstates: WeakMap<object, DraftState>;\n};\n\nconst RELEASED_BASE: Container = Object.freeze({});\nconst ARRAY_MUTATORS = new Set([\"push\", \"pop\", \"shift\", \"unshift\", \"splice\", \"sort\", \"reverse\", \"fill\", \"copyWithin\"]);\n\n/** Run a mutation recipe and return its value with transaction ownership metadata. */\nexport function produceWithMetadata<T extends object>(base: T, recipe: (draft: Draft<T>) => void): ProduceMetadata<T> {\n\tconst context: DraftContext = {\n\t\tactive: true,\n\t\tcreated: [],\n\t\towned: new WeakSet<object>(),\n\t\tproxies: new WeakMap<object, DraftState>(),\n\t\tstates: new WeakMap<object, DraftState>(),\n\t};\n\tconst root = getState(context, base as Container);\n\ttry {\n\t\tconst outcome = (recipe as (draft: Draft<T>) => unknown)(root.proxy as Draft<T>);\n\t\tif (isPromiseLike(outcome)) {\n\t\t\tvoid Promise.resolve(outcome).catch(() => undefined);\n\t\t\tthrow new TypeError(\"Replicated state change callbacks must be synchronous\");\n\t\t}\n\t\treturn {\n\t\t\tvalue: finalize(root, new Set<DraftState>(), new WeakMap<DraftState, Container>()) as T,\n\t\t\towned: context.owned,\n\t\t};\n\t} finally {\n\t\tcontext.active = false;\n\t\tfor (const state of context.created) {\n\t\t\tstate.base = RELEASED_BASE;\n\t\t\tstate.copy = undefined;\n\t\t\tstate.methods = undefined;\n\t\t\tstate.proxy = RELEASED_BASE;\n\t\t\tstate.target = undefined;\n\t\t}\n\t\tcontext.created.length = 0;\n\t\tcontext.proxies = new WeakMap();\n\t\tcontext.states = new WeakMap();\n\t}\n}\n\n/** Run a mutation recipe against a copy-on-write draft of `base`. */\nexport function produce<T extends object>(base: T, recipe: (draft: Draft<T>) => void): T {\n\treturn produceWithMetadata(base, recipe).value;\n}\n\nfunction getState(context: DraftContext, base: Container): DraftState {\n\tconst existing = context.states.get(base);\n\tif (existing !== undefined) return existing;\n\n\tconst target = Array.isArray(base) ? [] : undefined;\n\tif (target !== undefined) Reflect.set(target, \"length\", base.length);\n\tconst state: DraftState = {\n\t\tbase,\n\t\tcontext,\n\t\tcopy: undefined,\n\t\tmethods: undefined,\n\t\tproxy: target ?? {},\n\t\ttarget,\n\t};\n\tstate.proxy = new Proxy(target ?? state, createHandler(state));\n\tcontext.states.set(base, state);\n\tcontext.proxies.set(state.proxy, state);\n\tcontext.created.push(state);\n\treturn state;\n}\n\ntype DraftHandler = ProxyHandler<object> & { state: DraftState };\n\nconst handlerPrototype: ProxyHandler<object> = {\n\tdeleteProperty(this: DraftHandler, _target, property): boolean {\n\t\tconst state = this.state;\n\t\tassertStringWrite(property);\n\t\tconst current = currentValue(state);\n\t\tif (Array.isArray(current)) throw new TypeError(\"Draft arrays cannot contain holes\");\n\t\tif (!Object.hasOwn(current, property)) return true;\n\t\tconst deleted = Reflect.deleteProperty(ensureCopy(state), property);\n\t\tsyncArrayTarget(state);\n\t\treturn deleted;\n\t},\n\tdefineProperty(this: DraftHandler): never {\n\t\tassertActive(this.state.context);\n\t\tthrow new TypeError(\"Defining draft properties is not supported\");\n\t},\n\tget(this: DraftHandler, _target, property): unknown {\n\t\tconst state = this.state;\n\t\tconst current = currentValue(state);\n\t\tconst value = Reflect.get(current, property, state.proxy);\n\t\tif (Array.isArray(current) && typeof property === \"string\" && ARRAY_MUTATORS.has(property)) {\n\t\t\treturn arrayMutator(state, property, value);\n\t\t}\n\t\treturn draftValue(state.context, value);\n\t},\n\tgetOwnPropertyDescriptor(this: DraftHandler, _target, property): PropertyDescriptor | undefined {\n\t\tconst state = this.state;\n\t\tconst current = currentValue(state);\n\t\tconst descriptor = Reflect.getOwnPropertyDescriptor(current, property);\n\t\tif (descriptor === undefined) return undefined;\n\t\tif (Array.isArray(current) && property === \"length\") {\n\t\t\tsyncArrayTarget(state);\n\t\t\treturn Reflect.getOwnPropertyDescriptor(state.target!, property);\n\t\t}\n\t\treturn {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: descriptor.enumerable,\n\t\t\tvalue: \"value\" in descriptor ? draftValue(state.context, descriptor.value) : undefined,\n\t\t\twritable: true,\n\t\t};\n\t},\n\tgetPrototypeOf(this: DraftHandler): object | null {\n\t\tassertActive(this.state.context);\n\t\treturn Object.getPrototypeOf(this.state.base);\n\t},\n\thas(this: DraftHandler, _target, property): boolean {\n\t\treturn Reflect.has(currentValue(this.state), property);\n\t},\n\tisExtensible(this: DraftHandler): boolean {\n\t\tassertActive(this.state.context);\n\t\treturn true;\n\t},\n\townKeys(this: DraftHandler): ArrayLike<string | symbol> {\n\t\treturn Reflect.ownKeys(currentValue(this.state));\n\t},\n\tpreventExtensions(this: DraftHandler): never {\n\t\tassertActive(this.state.context);\n\t\tthrow new TypeError(\"Drafts cannot be made non-extensible\");\n\t},\n\tset(this: DraftHandler, _target, property, value): boolean {\n\t\tconst state = this.state;\n\t\tassertStringWrite(property);\n\t\tconst current = currentValue(state);\n\t\tlet stored: unknown;\n\t\tif (isContainer(value)) {\n\t\t\tstored = cloneAssigned(value, state.context, new Set<object>());\n\t\t} else {\n\t\t\tassertJsonPrimitive(value);\n\t\t\tstored = value;\n\t\t}\n\t\tif (Object.hasOwn(current, property) && Object.is(Reflect.get(current, property), stored)) return true;\n\t\tconst copy = ensureCopy(state);\n\t\tif (Array.isArray(copy) && property === \"length\") {\n\t\t\tconst written = Reflect.set(copy, property, stored);\n\t\t\tsyncArrayTarget(state);\n\t\t\treturn written;\n\t\t}\n\t\tObject.defineProperty(copy, property, {\n\t\t\tvalue: stored,\n\t\t\twritable: true,\n\t\t\tenumerable: true,\n\t\t\tconfigurable: true,\n\t\t});\n\t\tsyncArrayTarget(state);\n\t\treturn true;\n\t},\n\tsetPrototypeOf(this: DraftHandler): never {\n\t\tassertActive(this.state.context);\n\t\tthrow new TypeError(\"Changing a draft prototype is not supported\");\n\t},\n};\n\nfunction createHandler(state: DraftState): ProxyHandler<object> {\n\tconst handler = Object.create(handlerPrototype) as DraftHandler;\n\thandler.state = state;\n\treturn handler;\n}\n\nfunction arrayMutator(state: DraftState, property: string, value: unknown): unknown {\n\tassertActive(state.context);\n\tconst cached = state.methods?.get(property);\n\tif (cached !== undefined) return cached;\n\tif (typeof value !== \"function\") return draftValue(state.context, value);\n\tconst wrapped = function (this: unknown, ...args: unknown[]): unknown {\n\t\tassertActive(state.context);\n\t\tconst receiver = isContainer(this) ? state.context.proxies.get(this) : undefined;\n\t\tif (receiver === undefined) return Reflect.apply(value, this, args);\n\t\tconst target = ensureCopy(receiver);\n\t\tif (!Array.isArray(target)) throw new TypeError(\"Array mutator receiver is not an array draft\");\n\t\ttry {\n\t\t\treturn mutateArray(receiver, target, property, args);\n\t\t} finally {\n\t\t\tsyncArrayTarget(receiver);\n\t\t}\n\t};\n\tif (state.methods === undefined) state.methods = new Map();\n\tstate.methods.set(property, wrapped);\n\treturn wrapped;\n}\n\nfunction mutateArray(state: DraftState, target: unknown[], property: string, args: unknown[]): unknown {\n\tconst { context } = state;\n\tswitch (property) {\n\t\tcase \"push\": {\n\t\t\tconst items = cloneArrayItems(args, context);\n\t\t\tfor (const item of items) defineArrayValue(target, target.length, item);\n\t\t\treturn target.length;\n\t\t}\n\t\tcase \"pop\": {\n\t\t\tif (target.length === 0) return undefined;\n\t\t\tconst index = target.length - 1;\n\t\t\tconst removed = target[index];\n\t\t\tReflect.deleteProperty(target, String(index));\n\t\t\ttarget.length = index;\n\t\t\treturn draftValue(context, removed);\n\t\t}\n\t\tcase \"shift\":\n\t\t\treturn draftValue(context, Reflect.apply(Array.prototype.shift, target, []));\n\t\tcase \"unshift\": {\n\t\t\tconst items = cloneArrayItems(args, context);\n\t\t\tif (hasInheritedGrowthIndex(target, target.length + items.length)) {\n\t\t\t\tspliceArray(target, items, 0, 0, context);\n\t\t\t\treturn target.length;\n\t\t\t}\n\t\t\treturn Reflect.apply(Array.prototype.unshift, target, items);\n\t\t}\n\t\tcase \"splice\": {\n\t\t\tconst length = target.length;\n\t\t\tconst start = args.length === 0 ? 0 : clampArrayIndex(toIntegerOrInfinity(args[0]), length);\n\t\t\tconst remove =\n\t\t\t\targs.length === 0\n\t\t\t\t\t? 0\n\t\t\t\t\t: args.length === 1\n\t\t\t\t\t\t? length - start\n\t\t\t\t\t\t: Math.min(Math.max(toIntegerOrInfinity(args[1]), 0), length - start);\n\t\t\tconst items = cloneArrayItems(args.slice(2), context);\n\t\t\tif (\n\t\t\t\t!mayRunCoercionCode(args[0]) &&\n\t\t\t\t!mayRunCoercionCode(args[1]) &&\n\t\t\t\t!hasInheritedGrowthIndex(target, length - remove + items.length)\n\t\t\t) {\n\t\t\t\tconst removed = Reflect.apply(Array.prototype.splice, target, [start, remove, ...items]) as unknown[];\n\t\t\t\treturn removed.map((item) => draftValue(context, item));\n\t\t\t}\n\t\t\treturn spliceArray(target, items, start, remove, context, length);\n\t\t}\n\t\tcase \"sort\": {\n\t\t\tconst comparator = args[0];\n\t\t\tif (comparator !== undefined && typeof comparator !== \"function\") {\n\t\t\t\tReflect.apply(Array.prototype.sort, target, args);\n\t\t\t} else {\n\t\t\t\tReflect.apply(Array.prototype.sort, target, [\n\t\t\t\t\t(left: unknown, right: unknown): number => {\n\t\t\t\t\t\tconst draftedLeft = draftValue(context, left);\n\t\t\t\t\t\tconst draftedRight = draftValue(context, right);\n\t\t\t\t\t\tif (typeof comparator === \"function\") {\n\t\t\t\t\t\t\treturn Reflect.apply(comparator, undefined, [draftedLeft, draftedRight]) as number;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst leftString = String(draftedLeft);\n\t\t\t\t\t\tconst rightString = String(draftedRight);\n\t\t\t\t\t\treturn leftString < rightString ? -1 : leftString > rightString ? 1 : 0;\n\t\t\t\t\t},\n\t\t\t\t]);\n\t\t\t}\n\t\t\treturn state.proxy;\n\t\t}\n\t\tcase \"reverse\":\n\t\t\tReflect.apply(Array.prototype.reverse, target, []);\n\t\t\treturn state.proxy;\n\t\tcase \"fill\": {\n\t\t\tconst length = target.length;\n\t\t\tconst start = args.length > 1 ? clampArrayIndex(toIntegerOrInfinity(args[1]), length) : 0;\n\t\t\tconst end =\n\t\t\t\targs.length > 2 && args[2] !== undefined ? clampArrayIndex(toIntegerOrInfinity(args[2]), length) : length;\n\t\t\tif (end <= start) return state.proxy;\n\t\t\tconst supplied = args[0];\n\t\t\tfor (let index = start; index < end; index++) {\n\t\t\t\tconst item = isContainer(supplied)\n\t\t\t\t\t? cloneAssigned(supplied, context, new Set<object>())\n\t\t\t\t\t: assertJsonPrimitive(supplied);\n\t\t\t\tdefineArrayValue(target, index, item);\n\t\t\t}\n\t\t\treturn state.proxy;\n\t\t}\n\t\tcase \"copyWithin\": {\n\t\t\tconst length = target.length;\n\t\t\tconst to = clampArrayIndex(toIntegerOrInfinity(args[0]), length);\n\t\t\tconst from = clampArrayIndex(toIntegerOrInfinity(args[1]), length);\n\t\t\tconst end =\n\t\t\t\targs.length > 2 && args[2] !== undefined ? clampArrayIndex(toIntegerOrInfinity(args[2]), length) : length;\n\t\t\tconst count = Math.min(Math.max(end - from, 0), length - to);\n\t\t\tconst source = target.slice(from, from + count);\n\t\t\tfor (let offset = 0; offset < count; offset++) {\n\t\t\t\tconst item = source[offset];\n\t\t\t\tconst itemState = isContainer(item) ? context.states.get(item) : undefined;\n\t\t\t\tconst current = (itemState?.proxy as Container | undefined) ?? item;\n\t\t\t\tconst copy = isContainer(current)\n\t\t\t\t\t? cloneAssigned(current, context, new Set<object>())\n\t\t\t\t\t: assertJsonPrimitive(current);\n\t\t\t\tdefineArrayValue(target, to + offset, copy);\n\t\t\t}\n\t\t\treturn state.proxy;\n\t\t}\n\t}\n\tthrow new TypeError(`Unsupported array mutator: ${property}`);\n}\n\nfunction cloneArrayItems(items: readonly unknown[], context: DraftContext): unknown[] {\n\treturn items.map((item) => {\n\t\tif (isContainer(item)) return cloneAssigned(item, context, new Set<object>());\n\t\tassertJsonPrimitive(item);\n\t\treturn item;\n\t});\n}\n\nfunction hasInheritedGrowthIndex(target: unknown[], nextLength: number): boolean {\n\tfor (let index = target.length; index < nextLength; index++) {\n\t\tif (index in target) return true;\n\t}\n\treturn false;\n}\n\nfunction mayRunCoercionCode(value: unknown): boolean {\n\treturn (typeof value === \"object\" && value !== null) || typeof value === \"function\";\n}\n\nfunction spliceArray(\n\ttarget: unknown[],\n\titems: readonly unknown[],\n\tindex: number,\n\tremove: number,\n\tcontext: DraftContext,\n\tlength = target.length,\n): unknown[] {\n\tconst removed = new Array<unknown>(remove);\n\tfor (let offset = 0; offset < remove; offset++) {\n\t\tconst source = index + offset;\n\t\tif (!(source in target)) continue;\n\t\tdefineArrayValue(removed, offset, draftValue(context, target[source]));\n\t}\n\tif (items.length < remove) {\n\t\tfor (let source = index + remove; source < length; source++) {\n\t\t\tdefineArrayValue(target, source - remove + items.length, target[source]);\n\t\t}\n\t} else if (items.length > remove) {\n\t\tfor (let source = length - 1; source >= index + remove; source--) {\n\t\t\tdefineArrayValue(target, source - remove + items.length, target[source]);\n\t\t}\n\t}\n\tfor (let offset = 0; offset < items.length; offset++) defineArrayValue(target, index + offset, items[offset]);\n\ttarget.length = length - remove + items.length;\n\treturn removed;\n}\n\nfunction defineArrayValue(target: unknown[], index: number, value: unknown): void {\n\tObject.defineProperty(target, index, {\n\t\tvalue,\n\t\twritable: true,\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t});\n}\n\nfunction toIntegerOrInfinity(value: unknown): number {\n\tconst number = +(value as unknown as number);\n\tif (Number.isNaN(number) || number === 0) return 0;\n\treturn Number.isFinite(number) ? Math.trunc(number) : number;\n}\n\nfunction clampArrayIndex(value: number, length: number): number {\n\tif (value === Number.NEGATIVE_INFINITY) return 0;\n\tif (value < 0) return Math.max(length + value, 0);\n\treturn Math.min(value, length);\n}\n\nfunction currentValue(state: DraftState): Container {\n\tassertActive(state.context);\n\treturn state.copy ?? state.base;\n}\n\nfunction ensureCopy(state: DraftState): Container {\n\tif (state.copy !== undefined) return state.copy;\n\tif (state.context.owned.has(state.base)) {\n\t\tstate.copy = state.base;\n\t} else {\n\t\tstate.copy = shallowOwnedCopy(state.base, state.context);\n\t}\n\treturn state.copy;\n}\n\nfunction shallowOwnedCopy(value: Container, context: DraftContext): Container {\n\tif (Array.isArray(value)) {\n\t\tconst copy = value.slice();\n\t\tcontext.owned.add(copy);\n\t\treturn copy;\n\t}\n\tconst copy = Object.create(Object.getPrototypeOf(value)) as Record<string, unknown>;\n\tfor (const key of Object.keys(value)) {\n\t\tObject.defineProperty(copy, key, {\n\t\t\tvalue: value[key],\n\t\t\twritable: true,\n\t\t\tenumerable: true,\n\t\t\tconfigurable: true,\n\t\t});\n\t}\n\tcontext.owned.add(copy);\n\treturn copy;\n}\n\nfunction cloneAssigned(value: Container, context: DraftContext, ancestors: Set<object>): Container {\n\tif (ancestors.has(value)) throw new TypeError(\"Assigned JSON values cannot contain cycles\");\n\tancestors.add(value);\n\ttry {\n\t\tif (Array.isArray(value)) {\n\t\t\tassertDenseArray(value);\n\t\t\tconst clone: unknown[] = [];\n\t\t\tcontext.owned.add(clone);\n\t\t\tfor (let index = 0; index < value.length; index++) {\n\t\t\t\tclone.push(cloneAssignedValue(Reflect.get(value, String(index)), context, ancestors));\n\t\t\t}\n\t\t\treturn clone;\n\t\t}\n\n\t\tconst prototype = Object.getPrototypeOf(value);\n\t\tif (prototype !== Object.prototype && prototype !== null) {\n\t\t\tthrow new TypeError(\"Assigned JSON containers must be plain objects or arrays\");\n\t\t}\n\t\tconst clone = Object.create(prototype) as Record<string, unknown>;\n\t\tcontext.owned.add(clone);\n\t\tfor (const key of Reflect.ownKeys(value)) {\n\t\t\tif (typeof key === \"symbol\") throw new TypeError(\"Assigned JSON objects cannot have symbol properties\");\n\t\t\tconst descriptor = Reflect.getOwnPropertyDescriptor(value, key);\n\t\t\tif (descriptor === undefined || !descriptor.enumerable || !(\"value\" in descriptor)) {\n\t\t\t\tthrow new TypeError(\"Assigned JSON objects must contain enumerable data properties\");\n\t\t\t}\n\t\t\tObject.defineProperty(clone, key, {\n\t\t\t\tvalue: cloneAssignedValue(descriptor.value, context, ancestors),\n\t\t\t\twritable: true,\n\t\t\t\tenumerable: true,\n\t\t\t\tconfigurable: true,\n\t\t\t});\n\t\t}\n\t\treturn clone;\n\t} finally {\n\t\tancestors.delete(value);\n\t}\n}\n\nfunction cloneAssignedValue(value: unknown, context: DraftContext, ancestors: Set<object>): unknown {\n\tif (isContainer(value)) return cloneAssigned(value, context, ancestors);\n\tassertJsonPrimitive(value);\n\treturn value;\n}\n\nfunction assertJsonPrimitive(value: unknown): null | boolean | number | string {\n\tif (value === undefined) throw new TypeError(\"Assigned values cannot be undefined\");\n\tif (\n\t\tvalue !== null &&\n\t\ttypeof value !== \"string\" &&\n\t\ttypeof value !== \"boolean\" &&\n\t\t!(typeof value === \"number\" && Number.isFinite(value))\n\t) {\n\t\tthrow new TypeError(\"Assigned values must be strict JSON values\");\n\t}\n\treturn value;\n}\n\nfunction draftValue(context: DraftContext, value: unknown): unknown {\n\tif (!isContainer(value)) return value;\n\treturn getState(context, value).proxy;\n}\n\nfunction isContainer(value: unknown): value is Container {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n\treturn (\n\t\t((typeof value === \"object\" && value !== null) || typeof value === \"function\") &&\n\t\ttypeof (value as { then?: unknown }).then === \"function\"\n\t);\n}\n\nfunction assertActive(context: DraftContext): void {\n\tif (!context.active) throw new TypeError(\"Cannot use a draft outside its change callback\");\n}\n\nfunction assertStringWrite(property: string | symbol): asserts property is string {\n\tif (typeof property === \"symbol\") throw new TypeError(\"Symbol writes are not supported\");\n}\n\nfunction syncArrayTarget(state: DraftState): void {\n\tconst current = currentValue(state);\n\tif (Array.isArray(current)) Reflect.set(state.target!, \"length\", current.length);\n}\n\nfunction finalize(\n\tstate: DraftState,\n\tfinalizing: Set<DraftState>,\n\tfinalized: WeakMap<DraftState, Container>,\n): Container {\n\tconst cached = finalized.get(state);\n\tif (cached !== undefined) return cached;\n\tif (finalizing.has(state)) throw new TypeError(\"Cyclic draft state is not supported\");\n\tfinalizing.add(state);\n\ttry {\n\t\tif (state.copy !== undefined && shallowEqual(state.base, state.copy)) state.copy = undefined;\n\t\tconst current = currentValue(state);\n\t\tlet result = current;\n\t\tfor (const key of Object.keys(current)) {\n\t\t\tconst value = Reflect.get(current, key);\n\t\t\tif (!isContainer(value)) continue;\n\t\t\tconst child = state.context.states.get(value);\n\t\t\tif (child === undefined) continue;\n\t\t\tconst finalizedChild = finalize(child, finalizing, finalized);\n\t\t\tif (finalizedChild === value) continue;\n\t\t\tif (result === state.base) result = shallowOwnedCopy(state.base, state.context);\n\t\t\tObject.defineProperty(result, key, {\n\t\t\t\tvalue: finalizedChild,\n\t\t\t\twritable: true,\n\t\t\t\tenumerable: true,\n\t\t\t\tconfigurable: true,\n\t\t\t});\n\t\t}\n\t\tif (state.copy !== undefined && Array.isArray(result)) assertDenseArray(result);\n\t\tfinalized.set(state, result);\n\t\treturn result;\n\t} finally {\n\t\tfinalizing.delete(state);\n\t}\n}\n\nfunction shallowEqual(left: Container, right: Container): boolean {\n\tif (Array.isArray(left) !== Array.isArray(right)) return false;\n\tif (Array.isArray(left) && Array.isArray(right) && left.length !== right.length) return false;\n\tconst leftKeys = Object.keys(left);\n\tconst rightKeys = Object.keys(right);\n\tif (leftKeys.length !== rightKeys.length) return false;\n\tconst leftRecord = left as Record<string, unknown>;\n\tconst rightRecord = right as Record<string, unknown>;\n\tfor (const key of leftKeys) {\n\t\tif (!Object.hasOwn(right, key) || !Object.is(leftRecord[key], rightRecord[key])) return false;\n\t}\n\treturn true;\n}\n\nfunction assertDenseArray(value: unknown[]): void {\n\tconst keys = Reflect.ownKeys(value);\n\tif (keys.length !== value.length + 1 || keys.some((key) => typeof key !== \"string\")) {\n\t\tthrow new TypeError(\"Draft arrays must be dense and contain only indexed entries\");\n\t}\n\tfor (let index = 0; index < value.length; index++) {\n\t\tif (!Object.hasOwn(value, index) || value[index] === undefined) {\n\t\t\tthrow new TypeError(\"Draft arrays cannot contain holes or undefined entries\");\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,540 @@
1
+ const RELEASED_BASE = Object.freeze({});
2
+ const ARRAY_MUTATORS = new Set(["push", "pop", "shift", "unshift", "splice", "sort", "reverse", "fill", "copyWithin"]);
3
+ /** Run a mutation recipe and return its value with transaction ownership metadata. */
4
+ export function produceWithMetadata(base, recipe) {
5
+ const context = {
6
+ active: true,
7
+ created: [],
8
+ owned: new WeakSet(),
9
+ proxies: new WeakMap(),
10
+ states: new WeakMap(),
11
+ };
12
+ const root = getState(context, base);
13
+ try {
14
+ const outcome = recipe(root.proxy);
15
+ if (isPromiseLike(outcome)) {
16
+ void Promise.resolve(outcome).catch(() => undefined);
17
+ throw new TypeError("Replicated state change callbacks must be synchronous");
18
+ }
19
+ return {
20
+ value: finalize(root, new Set(), new WeakMap()),
21
+ owned: context.owned,
22
+ };
23
+ }
24
+ finally {
25
+ context.active = false;
26
+ for (const state of context.created) {
27
+ state.base = RELEASED_BASE;
28
+ state.copy = undefined;
29
+ state.methods = undefined;
30
+ state.proxy = RELEASED_BASE;
31
+ state.target = undefined;
32
+ }
33
+ context.created.length = 0;
34
+ context.proxies = new WeakMap();
35
+ context.states = new WeakMap();
36
+ }
37
+ }
38
+ /** Run a mutation recipe against a copy-on-write draft of `base`. */
39
+ export function produce(base, recipe) {
40
+ return produceWithMetadata(base, recipe).value;
41
+ }
42
+ function getState(context, base) {
43
+ const existing = context.states.get(base);
44
+ if (existing !== undefined)
45
+ return existing;
46
+ const target = Array.isArray(base) ? [] : undefined;
47
+ if (target !== undefined)
48
+ Reflect.set(target, "length", base.length);
49
+ const state = {
50
+ base,
51
+ context,
52
+ copy: undefined,
53
+ methods: undefined,
54
+ proxy: target ?? {},
55
+ target,
56
+ };
57
+ state.proxy = new Proxy(target ?? state, createHandler(state));
58
+ context.states.set(base, state);
59
+ context.proxies.set(state.proxy, state);
60
+ context.created.push(state);
61
+ return state;
62
+ }
63
+ const handlerPrototype = {
64
+ deleteProperty(_target, property) {
65
+ const state = this.state;
66
+ assertStringWrite(property);
67
+ const current = currentValue(state);
68
+ if (Array.isArray(current))
69
+ throw new TypeError("Draft arrays cannot contain holes");
70
+ if (!Object.hasOwn(current, property))
71
+ return true;
72
+ const deleted = Reflect.deleteProperty(ensureCopy(state), property);
73
+ syncArrayTarget(state);
74
+ return deleted;
75
+ },
76
+ defineProperty() {
77
+ assertActive(this.state.context);
78
+ throw new TypeError("Defining draft properties is not supported");
79
+ },
80
+ get(_target, property) {
81
+ const state = this.state;
82
+ const current = currentValue(state);
83
+ const value = Reflect.get(current, property, state.proxy);
84
+ if (Array.isArray(current) && typeof property === "string" && ARRAY_MUTATORS.has(property)) {
85
+ return arrayMutator(state, property, value);
86
+ }
87
+ return draftValue(state.context, value);
88
+ },
89
+ getOwnPropertyDescriptor(_target, property) {
90
+ const state = this.state;
91
+ const current = currentValue(state);
92
+ const descriptor = Reflect.getOwnPropertyDescriptor(current, property);
93
+ if (descriptor === undefined)
94
+ return undefined;
95
+ if (Array.isArray(current) && property === "length") {
96
+ syncArrayTarget(state);
97
+ return Reflect.getOwnPropertyDescriptor(state.target, property);
98
+ }
99
+ return {
100
+ configurable: true,
101
+ enumerable: descriptor.enumerable,
102
+ value: "value" in descriptor ? draftValue(state.context, descriptor.value) : undefined,
103
+ writable: true,
104
+ };
105
+ },
106
+ getPrototypeOf() {
107
+ assertActive(this.state.context);
108
+ return Object.getPrototypeOf(this.state.base);
109
+ },
110
+ has(_target, property) {
111
+ return Reflect.has(currentValue(this.state), property);
112
+ },
113
+ isExtensible() {
114
+ assertActive(this.state.context);
115
+ return true;
116
+ },
117
+ ownKeys() {
118
+ return Reflect.ownKeys(currentValue(this.state));
119
+ },
120
+ preventExtensions() {
121
+ assertActive(this.state.context);
122
+ throw new TypeError("Drafts cannot be made non-extensible");
123
+ },
124
+ set(_target, property, value) {
125
+ const state = this.state;
126
+ assertStringWrite(property);
127
+ const current = currentValue(state);
128
+ let stored;
129
+ if (isContainer(value)) {
130
+ stored = cloneAssigned(value, state.context, new Set());
131
+ }
132
+ else {
133
+ assertJsonPrimitive(value);
134
+ stored = value;
135
+ }
136
+ if (Object.hasOwn(current, property) && Object.is(Reflect.get(current, property), stored))
137
+ return true;
138
+ const copy = ensureCopy(state);
139
+ if (Array.isArray(copy) && property === "length") {
140
+ const written = Reflect.set(copy, property, stored);
141
+ syncArrayTarget(state);
142
+ return written;
143
+ }
144
+ Object.defineProperty(copy, property, {
145
+ value: stored,
146
+ writable: true,
147
+ enumerable: true,
148
+ configurable: true,
149
+ });
150
+ syncArrayTarget(state);
151
+ return true;
152
+ },
153
+ setPrototypeOf() {
154
+ assertActive(this.state.context);
155
+ throw new TypeError("Changing a draft prototype is not supported");
156
+ },
157
+ };
158
+ function createHandler(state) {
159
+ const handler = Object.create(handlerPrototype);
160
+ handler.state = state;
161
+ return handler;
162
+ }
163
+ function arrayMutator(state, property, value) {
164
+ assertActive(state.context);
165
+ const cached = state.methods?.get(property);
166
+ if (cached !== undefined)
167
+ return cached;
168
+ if (typeof value !== "function")
169
+ return draftValue(state.context, value);
170
+ const wrapped = function (...args) {
171
+ assertActive(state.context);
172
+ const receiver = isContainer(this) ? state.context.proxies.get(this) : undefined;
173
+ if (receiver === undefined)
174
+ return Reflect.apply(value, this, args);
175
+ const target = ensureCopy(receiver);
176
+ if (!Array.isArray(target))
177
+ throw new TypeError("Array mutator receiver is not an array draft");
178
+ try {
179
+ return mutateArray(receiver, target, property, args);
180
+ }
181
+ finally {
182
+ syncArrayTarget(receiver);
183
+ }
184
+ };
185
+ if (state.methods === undefined)
186
+ state.methods = new Map();
187
+ state.methods.set(property, wrapped);
188
+ return wrapped;
189
+ }
190
+ function mutateArray(state, target, property, args) {
191
+ const { context } = state;
192
+ switch (property) {
193
+ case "push": {
194
+ const items = cloneArrayItems(args, context);
195
+ for (const item of items)
196
+ defineArrayValue(target, target.length, item);
197
+ return target.length;
198
+ }
199
+ case "pop": {
200
+ if (target.length === 0)
201
+ return undefined;
202
+ const index = target.length - 1;
203
+ const removed = target[index];
204
+ Reflect.deleteProperty(target, String(index));
205
+ target.length = index;
206
+ return draftValue(context, removed);
207
+ }
208
+ case "shift":
209
+ return draftValue(context, Reflect.apply(Array.prototype.shift, target, []));
210
+ case "unshift": {
211
+ const items = cloneArrayItems(args, context);
212
+ if (hasInheritedGrowthIndex(target, target.length + items.length)) {
213
+ spliceArray(target, items, 0, 0, context);
214
+ return target.length;
215
+ }
216
+ return Reflect.apply(Array.prototype.unshift, target, items);
217
+ }
218
+ case "splice": {
219
+ const length = target.length;
220
+ const start = args.length === 0 ? 0 : clampArrayIndex(toIntegerOrInfinity(args[0]), length);
221
+ const remove = args.length === 0
222
+ ? 0
223
+ : args.length === 1
224
+ ? length - start
225
+ : Math.min(Math.max(toIntegerOrInfinity(args[1]), 0), length - start);
226
+ const items = cloneArrayItems(args.slice(2), context);
227
+ if (!mayRunCoercionCode(args[0]) &&
228
+ !mayRunCoercionCode(args[1]) &&
229
+ !hasInheritedGrowthIndex(target, length - remove + items.length)) {
230
+ const removed = Reflect.apply(Array.prototype.splice, target, [start, remove, ...items]);
231
+ return removed.map((item) => draftValue(context, item));
232
+ }
233
+ return spliceArray(target, items, start, remove, context, length);
234
+ }
235
+ case "sort": {
236
+ const comparator = args[0];
237
+ if (comparator !== undefined && typeof comparator !== "function") {
238
+ Reflect.apply(Array.prototype.sort, target, args);
239
+ }
240
+ else {
241
+ Reflect.apply(Array.prototype.sort, target, [
242
+ (left, right) => {
243
+ const draftedLeft = draftValue(context, left);
244
+ const draftedRight = draftValue(context, right);
245
+ if (typeof comparator === "function") {
246
+ return Reflect.apply(comparator, undefined, [draftedLeft, draftedRight]);
247
+ }
248
+ const leftString = String(draftedLeft);
249
+ const rightString = String(draftedRight);
250
+ return leftString < rightString ? -1 : leftString > rightString ? 1 : 0;
251
+ },
252
+ ]);
253
+ }
254
+ return state.proxy;
255
+ }
256
+ case "reverse":
257
+ Reflect.apply(Array.prototype.reverse, target, []);
258
+ return state.proxy;
259
+ case "fill": {
260
+ const length = target.length;
261
+ const start = args.length > 1 ? clampArrayIndex(toIntegerOrInfinity(args[1]), length) : 0;
262
+ const end = args.length > 2 && args[2] !== undefined ? clampArrayIndex(toIntegerOrInfinity(args[2]), length) : length;
263
+ if (end <= start)
264
+ return state.proxy;
265
+ const supplied = args[0];
266
+ for (let index = start; index < end; index++) {
267
+ const item = isContainer(supplied)
268
+ ? cloneAssigned(supplied, context, new Set())
269
+ : assertJsonPrimitive(supplied);
270
+ defineArrayValue(target, index, item);
271
+ }
272
+ return state.proxy;
273
+ }
274
+ case "copyWithin": {
275
+ const length = target.length;
276
+ const to = clampArrayIndex(toIntegerOrInfinity(args[0]), length);
277
+ const from = clampArrayIndex(toIntegerOrInfinity(args[1]), length);
278
+ const end = args.length > 2 && args[2] !== undefined ? clampArrayIndex(toIntegerOrInfinity(args[2]), length) : length;
279
+ const count = Math.min(Math.max(end - from, 0), length - to);
280
+ const source = target.slice(from, from + count);
281
+ for (let offset = 0; offset < count; offset++) {
282
+ const item = source[offset];
283
+ const itemState = isContainer(item) ? context.states.get(item) : undefined;
284
+ const current = itemState?.proxy ?? item;
285
+ const copy = isContainer(current)
286
+ ? cloneAssigned(current, context, new Set())
287
+ : assertJsonPrimitive(current);
288
+ defineArrayValue(target, to + offset, copy);
289
+ }
290
+ return state.proxy;
291
+ }
292
+ }
293
+ throw new TypeError(`Unsupported array mutator: ${property}`);
294
+ }
295
+ function cloneArrayItems(items, context) {
296
+ return items.map((item) => {
297
+ if (isContainer(item))
298
+ return cloneAssigned(item, context, new Set());
299
+ assertJsonPrimitive(item);
300
+ return item;
301
+ });
302
+ }
303
+ function hasInheritedGrowthIndex(target, nextLength) {
304
+ for (let index = target.length; index < nextLength; index++) {
305
+ if (index in target)
306
+ return true;
307
+ }
308
+ return false;
309
+ }
310
+ function mayRunCoercionCode(value) {
311
+ return (typeof value === "object" && value !== null) || typeof value === "function";
312
+ }
313
+ function spliceArray(target, items, index, remove, context, length = target.length) {
314
+ const removed = new Array(remove);
315
+ for (let offset = 0; offset < remove; offset++) {
316
+ const source = index + offset;
317
+ if (!(source in target))
318
+ continue;
319
+ defineArrayValue(removed, offset, draftValue(context, target[source]));
320
+ }
321
+ if (items.length < remove) {
322
+ for (let source = index + remove; source < length; source++) {
323
+ defineArrayValue(target, source - remove + items.length, target[source]);
324
+ }
325
+ }
326
+ else if (items.length > remove) {
327
+ for (let source = length - 1; source >= index + remove; source--) {
328
+ defineArrayValue(target, source - remove + items.length, target[source]);
329
+ }
330
+ }
331
+ for (let offset = 0; offset < items.length; offset++)
332
+ defineArrayValue(target, index + offset, items[offset]);
333
+ target.length = length - remove + items.length;
334
+ return removed;
335
+ }
336
+ function defineArrayValue(target, index, value) {
337
+ Object.defineProperty(target, index, {
338
+ value,
339
+ writable: true,
340
+ enumerable: true,
341
+ configurable: true,
342
+ });
343
+ }
344
+ function toIntegerOrInfinity(value) {
345
+ const number = +value;
346
+ if (Number.isNaN(number) || number === 0)
347
+ return 0;
348
+ return Number.isFinite(number) ? Math.trunc(number) : number;
349
+ }
350
+ function clampArrayIndex(value, length) {
351
+ if (value === Number.NEGATIVE_INFINITY)
352
+ return 0;
353
+ if (value < 0)
354
+ return Math.max(length + value, 0);
355
+ return Math.min(value, length);
356
+ }
357
+ function currentValue(state) {
358
+ assertActive(state.context);
359
+ return state.copy ?? state.base;
360
+ }
361
+ function ensureCopy(state) {
362
+ if (state.copy !== undefined)
363
+ return state.copy;
364
+ if (state.context.owned.has(state.base)) {
365
+ state.copy = state.base;
366
+ }
367
+ else {
368
+ state.copy = shallowOwnedCopy(state.base, state.context);
369
+ }
370
+ return state.copy;
371
+ }
372
+ function shallowOwnedCopy(value, context) {
373
+ if (Array.isArray(value)) {
374
+ const copy = value.slice();
375
+ context.owned.add(copy);
376
+ return copy;
377
+ }
378
+ const copy = Object.create(Object.getPrototypeOf(value));
379
+ for (const key of Object.keys(value)) {
380
+ Object.defineProperty(copy, key, {
381
+ value: value[key],
382
+ writable: true,
383
+ enumerable: true,
384
+ configurable: true,
385
+ });
386
+ }
387
+ context.owned.add(copy);
388
+ return copy;
389
+ }
390
+ function cloneAssigned(value, context, ancestors) {
391
+ if (ancestors.has(value))
392
+ throw new TypeError("Assigned JSON values cannot contain cycles");
393
+ ancestors.add(value);
394
+ try {
395
+ if (Array.isArray(value)) {
396
+ assertDenseArray(value);
397
+ const clone = [];
398
+ context.owned.add(clone);
399
+ for (let index = 0; index < value.length; index++) {
400
+ clone.push(cloneAssignedValue(Reflect.get(value, String(index)), context, ancestors));
401
+ }
402
+ return clone;
403
+ }
404
+ const prototype = Object.getPrototypeOf(value);
405
+ if (prototype !== Object.prototype && prototype !== null) {
406
+ throw new TypeError("Assigned JSON containers must be plain objects or arrays");
407
+ }
408
+ const clone = Object.create(prototype);
409
+ context.owned.add(clone);
410
+ for (const key of Reflect.ownKeys(value)) {
411
+ if (typeof key === "symbol")
412
+ throw new TypeError("Assigned JSON objects cannot have symbol properties");
413
+ const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
414
+ if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor)) {
415
+ throw new TypeError("Assigned JSON objects must contain enumerable data properties");
416
+ }
417
+ Object.defineProperty(clone, key, {
418
+ value: cloneAssignedValue(descriptor.value, context, ancestors),
419
+ writable: true,
420
+ enumerable: true,
421
+ configurable: true,
422
+ });
423
+ }
424
+ return clone;
425
+ }
426
+ finally {
427
+ ancestors.delete(value);
428
+ }
429
+ }
430
+ function cloneAssignedValue(value, context, ancestors) {
431
+ if (isContainer(value))
432
+ return cloneAssigned(value, context, ancestors);
433
+ assertJsonPrimitive(value);
434
+ return value;
435
+ }
436
+ function assertJsonPrimitive(value) {
437
+ if (value === undefined)
438
+ throw new TypeError("Assigned values cannot be undefined");
439
+ if (value !== null &&
440
+ typeof value !== "string" &&
441
+ typeof value !== "boolean" &&
442
+ !(typeof value === "number" && Number.isFinite(value))) {
443
+ throw new TypeError("Assigned values must be strict JSON values");
444
+ }
445
+ return value;
446
+ }
447
+ function draftValue(context, value) {
448
+ if (!isContainer(value))
449
+ return value;
450
+ return getState(context, value).proxy;
451
+ }
452
+ function isContainer(value) {
453
+ return typeof value === "object" && value !== null;
454
+ }
455
+ function isPromiseLike(value) {
456
+ return (((typeof value === "object" && value !== null) || typeof value === "function") &&
457
+ typeof value.then === "function");
458
+ }
459
+ function assertActive(context) {
460
+ if (!context.active)
461
+ throw new TypeError("Cannot use a draft outside its change callback");
462
+ }
463
+ function assertStringWrite(property) {
464
+ if (typeof property === "symbol")
465
+ throw new TypeError("Symbol writes are not supported");
466
+ }
467
+ function syncArrayTarget(state) {
468
+ const current = currentValue(state);
469
+ if (Array.isArray(current))
470
+ Reflect.set(state.target, "length", current.length);
471
+ }
472
+ function finalize(state, finalizing, finalized) {
473
+ const cached = finalized.get(state);
474
+ if (cached !== undefined)
475
+ return cached;
476
+ if (finalizing.has(state))
477
+ throw new TypeError("Cyclic draft state is not supported");
478
+ finalizing.add(state);
479
+ try {
480
+ if (state.copy !== undefined && shallowEqual(state.base, state.copy))
481
+ state.copy = undefined;
482
+ const current = currentValue(state);
483
+ let result = current;
484
+ for (const key of Object.keys(current)) {
485
+ const value = Reflect.get(current, key);
486
+ if (!isContainer(value))
487
+ continue;
488
+ const child = state.context.states.get(value);
489
+ if (child === undefined)
490
+ continue;
491
+ const finalizedChild = finalize(child, finalizing, finalized);
492
+ if (finalizedChild === value)
493
+ continue;
494
+ if (result === state.base)
495
+ result = shallowOwnedCopy(state.base, state.context);
496
+ Object.defineProperty(result, key, {
497
+ value: finalizedChild,
498
+ writable: true,
499
+ enumerable: true,
500
+ configurable: true,
501
+ });
502
+ }
503
+ if (state.copy !== undefined && Array.isArray(result))
504
+ assertDenseArray(result);
505
+ finalized.set(state, result);
506
+ return result;
507
+ }
508
+ finally {
509
+ finalizing.delete(state);
510
+ }
511
+ }
512
+ function shallowEqual(left, right) {
513
+ if (Array.isArray(left) !== Array.isArray(right))
514
+ return false;
515
+ if (Array.isArray(left) && Array.isArray(right) && left.length !== right.length)
516
+ return false;
517
+ const leftKeys = Object.keys(left);
518
+ const rightKeys = Object.keys(right);
519
+ if (leftKeys.length !== rightKeys.length)
520
+ return false;
521
+ const leftRecord = left;
522
+ const rightRecord = right;
523
+ for (const key of leftKeys) {
524
+ if (!Object.hasOwn(right, key) || !Object.is(leftRecord[key], rightRecord[key]))
525
+ return false;
526
+ }
527
+ return true;
528
+ }
529
+ function assertDenseArray(value) {
530
+ const keys = Reflect.ownKeys(value);
531
+ if (keys.length !== value.length + 1 || keys.some((key) => typeof key !== "string")) {
532
+ throw new TypeError("Draft arrays must be dense and contain only indexed entries");
533
+ }
534
+ for (let index = 0; index < value.length; index++) {
535
+ if (!Object.hasOwn(value, index) || value[index] === undefined) {
536
+ throw new TypeError("Draft arrays cannot contain holes or undefined entries");
537
+ }
538
+ }
539
+ }
540
+ //# sourceMappingURL=draft.js.map