@aihu/reactive 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +2 -1
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/helpers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers/index.ts"],"mappings":";;;;;AAgBA;iBAAgB,QAAA,mCAA2C,CAAA,CAAA,CAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,GAAI,MAAA,CAAO,CAAA,CAAE,CAAA
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers/index.ts"],"mappings":";;;;;AAgBA;iBAAgB,QAAA,mCAA2C,CAAA,CAAA,CAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,GAAI,MAAA,CAAO,CAAA,CAAE,CAAA;AAA5D;AAAmC;AAAnC,iBAUR,SAAA,kBAAA,CAA4B,CAAA,EAAG,CAAA,iBAAkB,CAAA,GAAI,MAAA,CAAO,CAAA,CAAE,CAAA;AAVM;AAAT;AAAM;AAAG,iBAqBpE,UAAA,kBAAA,CAA6B,MAAA,EAAQ,MAAA,CAAO,CAAA,IAAK,CAAA;AArBxC;AAAkB;AAAlB,iBAuDT,YAAA,mCAA+C,CAAA,CAAA,CAAG,CAAA,EAAG,CAAA,KAAM,IAAA,EAAM,CAAA,KAAM,IAAA,CAAK,CAAA,EAAG,CAAA;AAvD9B;AAAH;AAAG,iBAgFjD,YAAA,mCAA+C,CAAA,CAAA,CAAG,CAAA,EAAG,CAAA,KAAM,IAAA,EAAM,CAAA,KAAM,IAAA,CAAK,CAAA,EAAG,CAAA;AAhF3B;AAAO;AAAO;AAAE;AAAC;AAAjB,iBA4GpD,gBAAA,kBAAA,CAAmC,EAAA,QAAU,CAAA,GAAI,CAAC"}
|
package/dist/helpers.js
CHANGED
package/dist/helpers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","names":[],"sources":["../src/helpers/index.ts"],"sourcesContent":["/**\n * `@aihu/reactive/helpers` — the tree ↔ tuple bridge (design\n * docs/plans/2026-07-24-deep-reactivity.md §4.2).\n *\n * Imports the core (`@aihu/reactive`) by package NAME rather than a\n * relative path, and that self-import is marked `external` in\n * rolldown.config.ts (mirroring `@aihu/signals` being external to the\n * core row) — so this entry measures only the helper-specific code, never\n * a duplicate copy of the trap machinery.\n */\nimport { reactive, reconcile } from '@aihu/reactive'\nimport { effect, type Signal } from '@aihu/signals'\n\n/** Lens a single property as a signal tuple — the tree → tuple bridge.\n * Reads track the SAME per-key node the proxy's `get` trap would create\n * (it IS that trap); writes go through the proxy's `set` trap. */\nexport function toSignal<T extends object, K extends keyof T>(t: T, k: K): Signal<T[K]> {\n const read = () => t[k]\n const write = (next: unknown) => {\n t[k] = typeof next === 'function' ? (next as (prev: T[K]) => T[K])(t[k]) : (next as T[K])\n }\n return [read, write] as unknown as Signal<T[K]>\n}\n\n/** Every own key as a signal tuple. Destructure-safe (each tuple is a live\n * lens). */\nexport function toSignals<T extends object>(t: T): { [K in keyof T]: Signal<T[K]> } {\n const out = {} as { [K in keyof T]: Signal<T[K]> }\n for (const k of Object.keys(t) as Array<keyof T>) {\n out[k] = toSignal(t, k)\n }\n return out\n}\n\n/** Signal-of-object → reactive-looking view. Whole-value read granularity;\n * writes go through the tuple's setter with a shallow copy. The tuple →\n * tree bridge. */\nexport function toReactive<T extends object>(source: Signal<T>): T {\n const [read, write] = source\n return new Proxy({} as T, {\n get(_t, key) {\n return (read() as Record<PropertyKey, unknown>)[key]\n },\n set(_t, key, value) {\n write((prev) => ({ ...(prev as object), [key]: value }) as T)\n return true\n },\n has(_t, key) {\n return key in (read() as object)\n },\n deleteProperty(_t, key) {\n write((prev) => {\n const next = { ...(prev as object) } as Record<PropertyKey, unknown>\n delete next[key]\n return next as T\n })\n return true\n },\n ownKeys() {\n return Reflect.ownKeys(read() as object)\n },\n getOwnPropertyDescriptor(_t, key) {\n const obj = read() as Record<PropertyKey, unknown>\n if (!(key in obj)) return undefined\n return { enumerable: true, configurable: true, value: obj[key] }\n },\n }) as T\n}\n\n/** Read-through view over a subset of keys — no copies, tracking is\n * preserved (each read forwards to the source proxy's own trap). */\nexport function reactivePick<T extends object, K extends keyof T>(s: T, ...keys: K[]): Pick<T, K> {\n const keySet = new Set<PropertyKey>(keys)\n return new Proxy({} as Pick<T, K>, {\n get(_t, key) {\n return keySet.has(key) ? (s as Record<PropertyKey, unknown>)[key] : undefined\n },\n has(_t, key) {\n return keySet.has(key) && key in (s as object)\n },\n ownKeys() {\n return [...keySet] as (string | symbol)[]\n },\n getOwnPropertyDescriptor(_t, key) {\n if (!keySet.has(key)) return undefined\n return {\n enumerable: true,\n configurable: true,\n value: (s as Record<PropertyKey, unknown>)[key],\n }\n },\n }) as Pick<T, K>\n}\n\n/** Read-through view omitting a subset of keys — no copies, tracking is\n * preserved. */\nexport function reactiveOmit<T extends object, K extends keyof T>(s: T, ...keys: K[]): Omit<T, K> {\n const omitSet = new Set<PropertyKey>(keys)\n return new Proxy({} as Omit<T, K>, {\n get(_t, key) {\n return omitSet.has(key) ? undefined : (s as Record<PropertyKey, unknown>)[key]\n },\n has(_t, key) {\n return !omitSet.has(key) && key in (s as object)\n },\n ownKeys() {\n return Reflect.ownKeys(s as object).filter((k) => !omitSet.has(k))\n },\n getOwnPropertyDescriptor(_t, key) {\n if (omitSet.has(key)) return undefined\n return {\n enumerable: true,\n configurable: true,\n value: (s as Record<PropertyKey, unknown>)[key],\n }\n },\n }) as Omit<T, K>\n}\n\n/** A reactive object kept in sync with `fn()` by an effect + `reconcile`.\n * Scope-owned: `effect()` registers with the current scope like any other\n * effect, so the enclosing `effectScope` disposes it. Per-key granularity\n * — consumers that read one key only re-run when THAT key changes,\n * because `reconcile` notifies only the keys that actually changed. */\nexport function reactiveComputed<T extends object>(fn: () => T): T {\n const target = reactive({} as T)\n effect(() => {\n const next = fn()\n reconcile(target, next)\n })\n return target\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,SAAgB,SAA8C,GAAM,GAAoB;CACtF,MAAM,aAAa,EAAE;CACrB,MAAM,SAAS,SAAkB;EAC/B,EAAE,KAAK,OAAO,SAAS,aAAc,KAA8B,EAAE,
|
|
1
|
+
{"version":3,"file":"helpers.js","names":[],"sources":["../src/helpers/index.ts"],"sourcesContent":["/**\n * `@aihu/reactive/helpers` — the tree ↔ tuple bridge (design\n * docs/plans/2026-07-24-deep-reactivity.md §4.2).\n *\n * Imports the core (`@aihu/reactive`) by package NAME rather than a\n * relative path, and that self-import is marked `external` in\n * rolldown.config.ts (mirroring `@aihu/signals` being external to the\n * core row) — so this entry measures only the helper-specific code, never\n * a duplicate copy of the trap machinery.\n */\nimport { reactive, reconcile } from '@aihu/reactive'\nimport { effect, type Signal } from '@aihu/signals'\n\n/** Lens a single property as a signal tuple — the tree → tuple bridge.\n * Reads track the SAME per-key node the proxy's `get` trap would create\n * (it IS that trap); writes go through the proxy's `set` trap. */\nexport function toSignal<T extends object, K extends keyof T>(t: T, k: K): Signal<T[K]> {\n const read = () => t[k]\n const write = (next: unknown) => {\n t[k] = typeof next === 'function' ? (next as (prev: T[K]) => T[K])(t[k]) : (next as T[K])\n }\n return [read, write] as unknown as Signal<T[K]>\n}\n\n/** Every own key as a signal tuple. Destructure-safe (each tuple is a live\n * lens). */\nexport function toSignals<T extends object>(t: T): { [K in keyof T]: Signal<T[K]> } {\n const out = {} as { [K in keyof T]: Signal<T[K]> }\n for (const k of Object.keys(t) as Array<keyof T>) {\n out[k] = toSignal(t, k)\n }\n return out\n}\n\n/** Signal-of-object → reactive-looking view. Whole-value read granularity;\n * writes go through the tuple's setter with a shallow copy. The tuple →\n * tree bridge. */\nexport function toReactive<T extends object>(source: Signal<T>): T {\n const [read, write] = source\n return new Proxy({} as T, {\n get(_t, key) {\n return (read() as Record<PropertyKey, unknown>)[key]\n },\n set(_t, key, value) {\n write((prev) => ({ ...(prev as object), [key]: value }) as T)\n return true\n },\n has(_t, key) {\n return key in (read() as object)\n },\n deleteProperty(_t, key) {\n write((prev) => {\n const next = { ...(prev as object) } as Record<PropertyKey, unknown>\n delete next[key]\n return next as T\n })\n return true\n },\n ownKeys() {\n return Reflect.ownKeys(read() as object)\n },\n getOwnPropertyDescriptor(_t, key) {\n const obj = read() as Record<PropertyKey, unknown>\n if (!(key in obj)) return undefined\n return { enumerable: true, configurable: true, value: obj[key] }\n },\n }) as T\n}\n\n/** Read-through view over a subset of keys — no copies, tracking is\n * preserved (each read forwards to the source proxy's own trap). */\nexport function reactivePick<T extends object, K extends keyof T>(s: T, ...keys: K[]): Pick<T, K> {\n const keySet = new Set<PropertyKey>(keys)\n return new Proxy({} as Pick<T, K>, {\n get(_t, key) {\n return keySet.has(key) ? (s as Record<PropertyKey, unknown>)[key] : undefined\n },\n has(_t, key) {\n return keySet.has(key) && key in (s as object)\n },\n ownKeys() {\n return [...keySet] as (string | symbol)[]\n },\n getOwnPropertyDescriptor(_t, key) {\n if (!keySet.has(key)) return undefined\n return {\n enumerable: true,\n configurable: true,\n value: (s as Record<PropertyKey, unknown>)[key],\n }\n },\n }) as Pick<T, K>\n}\n\n/** Read-through view omitting a subset of keys — no copies, tracking is\n * preserved. */\nexport function reactiveOmit<T extends object, K extends keyof T>(s: T, ...keys: K[]): Omit<T, K> {\n const omitSet = new Set<PropertyKey>(keys)\n return new Proxy({} as Omit<T, K>, {\n get(_t, key) {\n return omitSet.has(key) ? undefined : (s as Record<PropertyKey, unknown>)[key]\n },\n has(_t, key) {\n return !omitSet.has(key) && key in (s as object)\n },\n ownKeys() {\n return Reflect.ownKeys(s as object).filter((k) => !omitSet.has(k))\n },\n getOwnPropertyDescriptor(_t, key) {\n if (omitSet.has(key)) return undefined\n return {\n enumerable: true,\n configurable: true,\n value: (s as Record<PropertyKey, unknown>)[key],\n }\n },\n }) as Omit<T, K>\n}\n\n/** A reactive object kept in sync with `fn()` by an effect + `reconcile`.\n * Scope-owned: `effect()` registers with the current scope like any other\n * effect, so the enclosing `effectScope` disposes it. Per-key granularity\n * — consumers that read one key only re-run when THAT key changes,\n * because `reconcile` notifies only the keys that actually changed. */\nexport function reactiveComputed<T extends object>(fn: () => T): T {\n const target = reactive({} as T)\n effect(() => {\n const next = fn()\n reconcile(target, next)\n })\n return target\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,SAAgB,SAA8C,GAAM,GAAoB;CACtF,MAAM,aAAa,EAAE;CACrB,MAAM,SAAS,SAAkB;EAC/B,EAAE,KAAK,OAAO,SAAS,aAAc,KAA8B,EAAE,EAAE,IAAK;CAC9E;CACA,OAAO,CAAC,MAAM,KAAK;AACrB;;;AAIA,SAAgB,UAA4B,GAAwC;CAClF,MAAM,MAAM,CAAC;CACb,KAAK,MAAM,KAAK,OAAO,KAAK,CAAC,GAC3B,IAAI,KAAK,SAAS,GAAG,CAAC;CAExB,OAAO;AACT;;;;AAKA,SAAgB,WAA6B,QAAsB;CACjE,MAAM,CAAC,MAAM,SAAS;CACtB,OAAO,IAAI,MAAM,CAAC,GAAQ;EACxB,IAAI,IAAI,KAAK;GACX,OAAQ,KAAK,CAAC,CAAkC;EAClD;EACA,IAAI,IAAI,KAAK,OAAO;GAClB,OAAO,UAAU;IAAE,GAAI;KAAkB,MAAM;GAAM,EAAO;GAC5D,OAAO;EACT;EACA,IAAI,IAAI,KAAK;GACX,OAAO,OAAQ,KAAK;EACtB;EACA,eAAe,IAAI,KAAK;GACtB,OAAO,SAAS;IACd,MAAM,OAAO,EAAE,GAAI,KAAgB;IACnC,OAAO,KAAK;IACZ,OAAO;GACT,CAAC;GACD,OAAO;EACT;EACA,UAAU;GACR,OAAO,QAAQ,QAAQ,KAAK,CAAW;EACzC;EACA,yBAAyB,IAAI,KAAK;GAChC,MAAM,MAAM,KAAK;GACjB,IAAI,EAAE,OAAO,MAAM,OAAO,KAAA;GAC1B,OAAO;IAAE,YAAY;IAAM,cAAc;IAAM,OAAO,IAAI;GAAK;EACjE;CACF,CAAC;AACH;;;AAIA,SAAgB,aAAkD,GAAM,GAAG,MAAuB;CAChG,MAAM,SAAS,IAAI,IAAiB,IAAI;CACxC,OAAO,IAAI,MAAM,CAAC,GAAiB;EACjC,IAAI,IAAI,KAAK;GACX,OAAO,OAAO,IAAI,GAAG,IAAK,EAAmC,OAAO,KAAA;EACtE;EACA,IAAI,IAAI,KAAK;GACX,OAAO,OAAO,IAAI,GAAG,KAAK,OAAQ;EACpC;EACA,UAAU;GACR,OAAO,CAAC,GAAG,MAAM;EACnB;EACA,yBAAyB,IAAI,KAAK;GAChC,IAAI,CAAC,OAAO,IAAI,GAAG,GAAG,OAAO,KAAA;GAC7B,OAAO;IACL,YAAY;IACZ,cAAc;IACd,OAAQ,EAAmC;GAC7C;EACF;CACF,CAAC;AACH;;;AAIA,SAAgB,aAAkD,GAAM,GAAG,MAAuB;CAChG,MAAM,UAAU,IAAI,IAAiB,IAAI;CACzC,OAAO,IAAI,MAAM,CAAC,GAAiB;EACjC,IAAI,IAAI,KAAK;GACX,OAAO,QAAQ,IAAI,GAAG,IAAI,KAAA,IAAa,EAAmC;EAC5E;EACA,IAAI,IAAI,KAAK;GACX,OAAO,CAAC,QAAQ,IAAI,GAAG,KAAK,OAAQ;EACtC;EACA,UAAU;GACR,OAAO,QAAQ,QAAQ,CAAW,CAAC,CAAC,QAAQ,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;EACnE;EACA,yBAAyB,IAAI,KAAK;GAChC,IAAI,QAAQ,IAAI,GAAG,GAAG,OAAO,KAAA;GAC7B,OAAO;IACL,YAAY;IACZ,cAAc;IACd,OAAQ,EAAmC;GAC7C;EACF;CACF,CAAC;AACH;;;;;;AAOA,SAAgB,iBAAmC,IAAgB;CACjE,MAAM,SAAS,SAAS,CAAC,CAAM;CAC/B,aAAa;EACX,MAAM,OAAO,GAAG;EAChB,UAAU,QAAQ,IAAI;CACxB,CAAC;CACD,OAAO;AACT"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/internal.ts"],"mappings":";;AA8PA
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/internal.ts"],"mappings":";;AA8PA;AAAwB;AAAA;AAAgC;AAA/B;iBAAT,QAAA,kBAAA,CAA2B,MAAA,EAAQ,CAAA,GAAI,CAAC;AAAb;AAAA,iBAa3B,UAAA,CAAW,KAAc;AAbe;AAAA;AAaxD;AAbwD,iBAoBxC,MAAA,GAAA,CAAU,KAAA,EAAO,CAAA,GAAI,CAAC;AAPZ;AAAA;AAAe;AAOzC;AAP0B,iBAmBV,MAAA,kBAAA,CAAyB,MAAA,EAAQ,CAAA,EAAG,MAAA,GAAS,KAAA,EAAO,CAAC;AAAA,KAMhE,gBAAA;EAAqB,GAAA,GAAM,WAAW,KAAK,IAAA;AAAA;AAlBf;AAAP;AAAW;AAAC;AAAA;AAYtC;AAAsB;AAZW,iBAuHjB,SAAA,kBAAA,CAA4B,MAAA,EAAQ,CAAA,EAAG,IAAA,EAAM,CAAA,EAAG,OAAA,GAAU,gBAAA"}
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ const KEYS = Symbol("aihu-reactive-keys");
|
|
|
30
30
|
* §2.6: "array mutating methods run inside batch()"). Intercepted directly
|
|
31
31
|
* in the `get` trap rather than tracked as a plain property read: nobody
|
|
32
32
|
* meaningfully subscribes to the identity of the `push` function itself. */
|
|
33
|
-
const ARRAY_MUTATORS = new Set([
|
|
33
|
+
const ARRAY_MUTATORS = /* @__PURE__ */ new Set([
|
|
34
34
|
"push",
|
|
35
35
|
"pop",
|
|
36
36
|
"shift",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/internal.ts"],"sourcesContent":["/**\n * `@aihu/reactive` — fine-grained Proxy-backed deep reactive trees on\n * `@aihu/signals` (docs/plans/2026-07-24-deep-reactivity.md).\n *\n * Mechanism (design §2.6/§2.7): a Solid-shaped node model — lazily allocated\n * per-(object, key) tracking nodes, one `signal(0, { equals: false })` per\n * touched key used as a pure version token (the VALUE lives on the raw\n * object; the node only carries subscription edges) — with Vue-shaped\n * write ergonomics: plain `obj.key = value` assignment through the `set`\n * trap, no `setStore(...)` path tuples.\n *\n * `@aihu/signals` is the sole dependency and is marked `external` in\n * rolldown.config.ts — this package adds zero bytes to the signals core row.\n */\nimport { batch, type Signal, signal } from '@aihu/signals'\n\n// ───────── Identity maps (design §2.6) ─────────\n\n/** raw → proxy. Stable so `reactive(o) === reactive(o)`. */\nconst wrapMap = new WeakMap<object, object>()\n/** proxy → raw. `unwrap()` is a single lookup here — O(1), no traversal. */\nconst rawMap = new WeakMap<object, object>()\n/** raw → per-key tracking node map. Lazily populated (design §2.7): a node\n * is allocated on the FIRST get-trap touch of a key, tracked or not. */\nconst nodeMap = new WeakMap<object, Map<PropertyKey, Signal<number>>>()\n\n/** @internal — sentinel key for the per-object \"shape\" node: notified on\n * property add/delete, tracked by `ownKeys`/`has` (design §2.6). Never\n * collides with a real property key (own module-local symbol). */\nconst KEYS: unique symbol = Symbol('aihu-reactive-keys')\n\n/** Array mutator methods that touch more than one index/length slot — run\n * inside `batch()` so e.g. `arr.push(a, b)` is one flush, not N (design\n * §2.6: \"array mutating methods run inside batch()\"). Intercepted directly\n * in the `get` trap rather than tracked as a plain property read: nobody\n * meaningfully subscribes to the identity of the `push` function itself. */\nconst ARRAY_MUTATORS = new Set<PropertyKey>([\n 'push',\n 'pop',\n 'shift',\n 'unshift',\n 'splice',\n 'sort',\n 'reverse',\n 'fill',\n 'copyWithin',\n])\n\nfunction getNodes(raw: object): Map<PropertyKey, Signal<number>> {\n let nodes = nodeMap.get(raw)\n if (nodes === undefined) {\n nodes = new Map()\n nodeMap.set(raw, nodes)\n }\n return nodes\n}\n\nfunction getOrCreateNode(\n nodes: Map<PropertyKey, Signal<number>>,\n key: PropertyKey,\n): Signal<number> {\n let node = nodes.get(key)\n if (node === undefined) {\n node = signal(0, { equals: false })\n nodes.set(key, node)\n }\n return node\n}\n\nfunction isArrayIndexKey(key: PropertyKey): boolean {\n if (typeof key !== 'string') return false\n if (key === '') return false\n const n = Number(key)\n return Number.isInteger(n) && n >= 0 && String(n) === key\n}\n\n/** Solid's `isWrappable`, minus the collection-type carve-outs this design\n * doesn't need (design §2.2, §2.6): plain objects and arrays, not frozen. */\nfunction isWrappable(v: object): boolean {\n if (Object.isFrozen(v)) return false\n if (Array.isArray(v)) return true\n const proto = Object.getPrototypeOf(v)\n return proto === Object.prototype || proto === null\n}\n\nfunction isPlainObjectLike(v: unknown): v is Record<PropertyKey, unknown> {\n if (v === null || typeof v !== 'object' || Array.isArray(v)) return false\n const proto = Object.getPrototypeOf(v)\n return proto === Object.prototype || proto === null\n}\n\nfunction sameContainerShape(a: unknown, b: unknown): boolean {\n if (Array.isArray(a) && Array.isArray(b)) return true\n return isPlainObjectLike(a) && isPlainObjectLike(b)\n}\n\n/** Recursively replace nested reactive proxies found inside a freshly\n * assigned plain container with their raw counterparts, IN PLACE, before\n * that container is stored on the raw tree (design §2.6/§8.4: \"the raw\n * tree never contains proxies\"). Plain `unwrap()` is a single WeakMap\n * lookup and only strips a DIRECTLY-assigned proxy — `outer.box = { inner:\n * someProxy }` would otherwise smuggle `someProxy` in under `box.inner`\n * since `box` itself was never a proxy. Walks only wrappable containers\n * (same class `isWrappable` recognizes); a `seen` WeakSet guards against\n * cyclic user data. */\nfunction unwrapDeep(value: unknown, seen?: WeakSet<object>): unknown {\n if (value === null || typeof value !== 'object') return value\n const raw = rawMap.get(value as object)\n const container = raw !== undefined ? raw : (value as object)\n if (!isWrappable(container)) return container\n const visited = seen ?? new WeakSet<object>()\n if (visited.has(container)) return container\n visited.add(container)\n const rec = container as Record<PropertyKey, unknown>\n for (const k of Reflect.ownKeys(rec)) {\n const v = rec[k]\n if (v !== null && typeof v === 'object') {\n rec[k] = unwrapDeep(v, visited)\n }\n }\n return container\n}\n\n// ───────── Proxy traps (design §2.6) ─────────\n\nconst handlers: ProxyHandler<object> = {\n get(target, key, receiver) {\n if (Array.isArray(target) && ARRAY_MUTATORS.has(key)) {\n const fn = (target as unknown as Record<PropertyKey, (...a: unknown[]) => unknown>)[key] as (\n ...a: unknown[]\n ) => unknown\n // Apply with `this = receiver` (the proxy, not the raw target) so the\n // method's own internal index/length writes route through OUR `set`\n // trap — that's what makes `batch()` here collapse them into one\n // flush instead of bypassing tracking entirely.\n return (...args: unknown[]) => batch(() => fn.apply(receiver, args))\n }\n // First-touch allocation, tracked or not (design §2.7): the read() call\n // below is the ordinary signal reader — `if (currentObserver !== null)\n // linkAdd(...)` — so an untracked read allocates the node but no edge.\n getOrCreateNode(getNodes(target), key)[0]()\n const res = (target as Record<PropertyKey, unknown>)[key]\n return res !== null && typeof res === 'object' ? reactive(res as object) : res\n },\n\n set(target, key, value) {\n const isArray = Array.isArray(target)\n const rawValue = unwrapDeep(value)\n const record = target as Record<PropertyKey, unknown>\n // Add-detection MUST run before the equality short-circuit below:\n // assigning `undefined` to a key that does not yet exist has to still\n // create it (plain-JS parity, design §2.1/§2.6) — `oldValue` for a\n // missing key also reads as `undefined`, so checking `hadKey` first is\n // what tells the two cases apart.\n const hadKey = key in target\n const oldValue = record[key]\n // Equality short-circuit (design §2.6) — same Object.is rule and the\n // same \"no allocation, no notify\" shape signal.ts's write() already\n // applies to every tuple write. `obj.x = obj.x` is a correct no-op.\n if (hadKey && Object.is(rawValue, oldValue)) return true\n\n const nodes = getNodes(target)\n\n // Array length assignment is the one write that can silently drop (or\n // reintroduce) index properties without ever routing through\n // `deleteProperty` — handle it explicitly so effects subscribed to a\n // dropped index are notified. This is the exact path `reconcile()`'s\n // truncation uses (`proxy.length = next.length`), so fixing it here\n // fixes reconcile too (design §8.10's index-tracking model).\n if (isArray && key === 'length') {\n const oldLen = oldValue as number\n record.length = rawValue as number\n const newLen = record.length as number\n const lengthNode = getOrCreateNode(nodes, 'length')\n if (newLen < oldLen) {\n batch(() => {\n lengthNode[1]((v) => v + 1)\n for (let i = newLen; i < oldLen; i++) {\n const idxNode = nodes.get(String(i))\n if (idxNode !== undefined) idxNode[1]((v) => v + 1)\n }\n })\n } else {\n lengthNode[1]((v) => v + 1)\n }\n return true\n }\n\n record[key] = rawValue\n const keyNode = getOrCreateNode(nodes, key)\n if (hadKey) {\n // Plain value write on an existing key touches exactly one node —\n // no batch needed (matches tuple-write semantics: one write, one\n // flush).\n keyNode[1]((v) => v + 1)\n return true\n }\n // Add path: property add, or an array index write past the current\n // length. Both touch the key's own node AND a shape companion node\n // (KEYS for objects / length for array-index adds) — batched together\n // so the trap never produces two back-to-back synchronous drains for\n // one authored assignment (design §2.6). `ownKeys`/`has` track BOTH\n // nodes for arrays (below), so bumping `length` alone still wakes\n // Object.keys/for-in/`in` watchers on an index add.\n const companion = getOrCreateNode(nodes, isArray && isArrayIndexKey(key) ? 'length' : KEYS)\n batch(() => {\n keyNode[1]((v) => v + 1)\n companion[1]((v) => v + 1)\n })\n return true\n },\n\n has(target, key) {\n const nodes = getNodes(target)\n getOrCreateNode(nodes, KEYS)[0]()\n // Arrays notify index adds via the `length` node, not KEYS (see\n // `set`) — track it too so `'k' in arr` reacts to shape changes made\n // through an index write, not only through defineProperty-shaped adds.\n if (Array.isArray(target)) getOrCreateNode(nodes, 'length')[0]()\n return key in target\n },\n\n deleteProperty(target, key) {\n const had = key in target\n const ok = delete (target as Record<PropertyKey, unknown>)[key]\n if (had && ok) {\n const nodes = getNodes(target)\n const keyNode = nodes.get(key)\n const keysNode = getOrCreateNode(nodes, KEYS)\n batch(() => {\n if (keyNode !== undefined) keyNode[1]((v) => v + 1)\n keysNode[1]((v) => v + 1)\n })\n }\n return ok\n },\n\n ownKeys(target) {\n const nodes = getNodes(target)\n getOrCreateNode(nodes, KEYS)[0]()\n // See `has` above: array index adds bump `length`, not KEYS.\n if (Array.isArray(target)) getOrCreateNode(nodes, 'length')[0]()\n return Reflect.ownKeys(target)\n },\n}\n\n// ───────── Public core API (design §4.1) ─────────\n\n/**\n * Wrap a plain object/array in a fine-grained reactive tree. Idempotent and\n * identity-stable: `reactive(o) === reactive(o)`, `reactive(reactive(o)) ===\n * reactive(o)`. Non-wrappable values (Date, Map, Set, class instances,\n * frozen objects, primitives) are returned as-is.\n */\nexport function reactive<T extends object>(source: T): T {\n if (source === null || typeof source !== 'object') return source\n if (rawMap.has(source as object)) return source // already a proxy\n const cached = wrapMap.get(source as object)\n if (cached !== undefined) return cached as T\n if (!isWrappable(source as object)) return source\n const proxy = new Proxy(source as object, handlers) as T\n wrapMap.set(source as object, proxy as object)\n rawMap.set(proxy as object, source as object)\n return proxy\n}\n\n/** True for a proxy produced by `reactive()`. */\nexport function isReactive(value: unknown): boolean {\n return value !== null && typeof value === 'object' && rawMap.has(value as object)\n}\n\n/** The raw object behind a reactive proxy (O(1), no traversal — writes\n * unwrap, so the raw tree never contains proxies). Non-proxies pass\n * through unchanged. */\nexport function unwrap<T>(value: T): T {\n if (value !== null && typeof value === 'object') {\n const raw = rawMap.get(value as object)\n if (raw !== undefined) return raw as T\n }\n return value\n}\n\n/** Apply many writes as ONE flush. Equivalent to `batch(() => recipe(target))`\n * — the \"draft\" IS the reactive proxy; writes apply immediately (design\n * §8.6: NOT an Immer draft — a throwing recipe leaves partial writes, same\n * non-atomic-on-error posture `batch()` already documents). */\nexport function mutate<T extends object>(target: T, recipe: (draft: T) => void): void {\n batch(() => recipe(target))\n}\n\n// ───────── reconcile (design §4.1, §7.2, §9) ─────────\n\ntype ReconcileOptions = { key?: PropertyKey | ((item: unknown) => unknown) }\n\nfunction reconcileInto(proxy: object, next: unknown, options?: ReconcileOptions): void {\n const raw = unwrap(proxy)\n if (Array.isArray(raw) && Array.isArray(next)) {\n // `key` must apply at every array encountered during the recursion,\n // not only a top-level array (design intent: `reconcile(state, payload,\n // { key: 'id' })` where `state.rows` is a nested array) — falling back\n // to index matching here would silently re-identity rows on a reorder.\n if (options?.key !== undefined) {\n reconcileArrayKeyed(proxy as unknown[], raw, next, options)\n return\n }\n for (let i = 0; i < next.length; i++) {\n reconcileField(proxy, i, (raw as unknown[])[i], next[i], i < raw.length, options)\n }\n if (next.length < raw.length) (proxy as unknown[]).length = next.length\n return\n }\n if (isPlainObjectLike(raw) && isPlainObjectLike(next)) {\n for (const k of Reflect.ownKeys(next)) {\n reconcileField(proxy, k, raw[k], next[k], Object.hasOwn(raw, k), options)\n }\n for (const k of Reflect.ownKeys(raw)) {\n if (!Object.hasOwn(next, k)) {\n delete (proxy as Record<PropertyKey, unknown>)[k]\n }\n }\n }\n}\n\n/** Reconcile one field. Reads for comparison come from the RAW values\n * (never through the proxy) so this can run safely inside the tracking\n * effect that drives `reactiveComputed` without the write-side of this\n * same reconcile becoming a read-side dependency of itself. Only the\n * write path (`proxy[key] = …`) touches the proxy.\n *\n * `hadKey` mirrors the `set` trap's own add-detection fix: `curVal` for a\n * key genuinely absent from `raw` reads as `undefined`, same as an\n * explicit `undefined` value would — `Object.is` alone can't tell \"already\n * undefined\" from \"never existed\" apart, so a payload adding an\n * explicitly-`undefined`-valued key would otherwise be silently dropped as\n * a no-op. */\nfunction reconcileField(\n proxy: object,\n key: PropertyKey,\n curVal: unknown,\n nextVal: unknown,\n hadKey: boolean,\n options?: ReconcileOptions,\n): void {\n if (sameContainerShape(curVal, nextVal)) {\n reconcileInto(reactive(curVal as object), nextVal, options)\n } else if (!hadKey || !Object.is(curVal, nextVal)) {\n ;(proxy as Record<PropertyKey, unknown>)[key] = nextVal\n }\n}\n\nfunction reconcileArrayKeyed(\n proxy: unknown[],\n raw: unknown[],\n next: unknown[],\n options: ReconcileOptions,\n): void {\n const keyOpt = options.key as PropertyKey | ((item: unknown) => unknown)\n const idOf =\n typeof keyOpt === 'function'\n ? keyOpt\n : (item: unknown) => (item as Record<PropertyKey, unknown> | null)?.[keyOpt]\n const byKey = new Map<unknown, unknown>()\n for (const item of raw) byKey.set(idOf(item), item)\n const merged: unknown[] = new Array(next.length)\n for (let i = 0; i < next.length; i++) {\n const nextItem = next[i]\n const id = idOf(nextItem)\n const match = byKey.get(id)\n if (match !== undefined && sameContainerShape(match, nextItem)) {\n reconcileInto(reactive(match as object), nextItem, options)\n merged[i] = match\n // Consume the match: a duplicate key in `next` must NOT alias a\n // second array slot onto the same raw object (that would silently\n // make `proxy[i] === proxy[j]`, so a later write to one row mutates\n // the other). Unmatched duplicates fall through to a fresh value.\n byKey.delete(id)\n } else {\n merged[i] = nextItem\n }\n }\n for (let i = 0; i < merged.length; i++) {\n if (!Object.is(raw[i], merged[i])) proxy[i] = merged[i]\n }\n if (merged.length < raw.length) proxy.length = merged.length\n}\n\n/**\n * Merge `next` into `target` in place, preserving node identity for\n * unchanged values and notifying ONLY changed paths. The hydration /\n * refetch primitive. `key` controls array item matching (default: index),\n * applied recursively to every array reconcile encounters, not only a\n * top-level one.\n */\nexport function reconcile<T extends object>(target: T, next: T, options?: ReconcileOptions): void {\n const proxy = reactive(target)\n batch(() => {\n reconcileInto(proxy as object, next, options)\n })\n}\n\n// ───────── Test-only introspection ─────────\n//\n// NOT re-exported from index.ts (the \".\" entry stays exactly the public\n// API above) — reached only via a direct relative import from tests, the\n// same pattern packages/signals/src/signal.ts uses for `__hostOf` /\n// `__inspectGraph`. Since index.ts never references this binding, it is\n// dead-code-eliminated out of the measured `dist/index.js` (design §5,\n// §12 acceptance #1/#2 — this must never move the size row).\n\n/** @internal — test-only: the read function of the per-key tracking node\n * for `(raw, key)`, or `undefined` if that key has never been touched\n * through the proxy's `get`/`set` trap. Compose with `@aihu/signals`'\n * `__hostOf`/`__inspectGraph` to assert §2.7's allocation contract. */\nexport function __nodeOf(raw: object, key: PropertyKey): (() => number) | undefined {\n return nodeMap.get(raw)?.get(key)?.[0]\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/internal.ts"],"sourcesContent":["/**\n * `@aihu/reactive` — fine-grained Proxy-backed deep reactive trees on\n * `@aihu/signals` (docs/plans/2026-07-24-deep-reactivity.md).\n *\n * Mechanism (design §2.6/§2.7): a Solid-shaped node model — lazily allocated\n * per-(object, key) tracking nodes, one `signal(0, { equals: false })` per\n * touched key used as a pure version token (the VALUE lives on the raw\n * object; the node only carries subscription edges) — with Vue-shaped\n * write ergonomics: plain `obj.key = value` assignment through the `set`\n * trap, no `setStore(...)` path tuples.\n *\n * `@aihu/signals` is the sole dependency and is marked `external` in\n * rolldown.config.ts — this package adds zero bytes to the signals core row.\n */\nimport { batch, type Signal, signal } from '@aihu/signals'\n\n// ───────── Identity maps (design §2.6) ─────────\n\n/** raw → proxy. Stable so `reactive(o) === reactive(o)`. */\nconst wrapMap = new WeakMap<object, object>()\n/** proxy → raw. `unwrap()` is a single lookup here — O(1), no traversal. */\nconst rawMap = new WeakMap<object, object>()\n/** raw → per-key tracking node map. Lazily populated (design §2.7): a node\n * is allocated on the FIRST get-trap touch of a key, tracked or not. */\nconst nodeMap = new WeakMap<object, Map<PropertyKey, Signal<number>>>()\n\n/** @internal — sentinel key for the per-object \"shape\" node: notified on\n * property add/delete, tracked by `ownKeys`/`has` (design §2.6). Never\n * collides with a real property key (own module-local symbol). */\nconst KEYS: unique symbol = Symbol('aihu-reactive-keys')\n\n/** Array mutator methods that touch more than one index/length slot — run\n * inside `batch()` so e.g. `arr.push(a, b)` is one flush, not N (design\n * §2.6: \"array mutating methods run inside batch()\"). Intercepted directly\n * in the `get` trap rather than tracked as a plain property read: nobody\n * meaningfully subscribes to the identity of the `push` function itself. */\nconst ARRAY_MUTATORS = new Set<PropertyKey>([\n 'push',\n 'pop',\n 'shift',\n 'unshift',\n 'splice',\n 'sort',\n 'reverse',\n 'fill',\n 'copyWithin',\n])\n\nfunction getNodes(raw: object): Map<PropertyKey, Signal<number>> {\n let nodes = nodeMap.get(raw)\n if (nodes === undefined) {\n nodes = new Map()\n nodeMap.set(raw, nodes)\n }\n return nodes\n}\n\nfunction getOrCreateNode(\n nodes: Map<PropertyKey, Signal<number>>,\n key: PropertyKey,\n): Signal<number> {\n let node = nodes.get(key)\n if (node === undefined) {\n node = signal(0, { equals: false })\n nodes.set(key, node)\n }\n return node\n}\n\nfunction isArrayIndexKey(key: PropertyKey): boolean {\n if (typeof key !== 'string') return false\n if (key === '') return false\n const n = Number(key)\n return Number.isInteger(n) && n >= 0 && String(n) === key\n}\n\n/** Solid's `isWrappable`, minus the collection-type carve-outs this design\n * doesn't need (design §2.2, §2.6): plain objects and arrays, not frozen. */\nfunction isWrappable(v: object): boolean {\n if (Object.isFrozen(v)) return false\n if (Array.isArray(v)) return true\n const proto = Object.getPrototypeOf(v)\n return proto === Object.prototype || proto === null\n}\n\nfunction isPlainObjectLike(v: unknown): v is Record<PropertyKey, unknown> {\n if (v === null || typeof v !== 'object' || Array.isArray(v)) return false\n const proto = Object.getPrototypeOf(v)\n return proto === Object.prototype || proto === null\n}\n\nfunction sameContainerShape(a: unknown, b: unknown): boolean {\n if (Array.isArray(a) && Array.isArray(b)) return true\n return isPlainObjectLike(a) && isPlainObjectLike(b)\n}\n\n/** Recursively replace nested reactive proxies found inside a freshly\n * assigned plain container with their raw counterparts, IN PLACE, before\n * that container is stored on the raw tree (design §2.6/§8.4: \"the raw\n * tree never contains proxies\"). Plain `unwrap()` is a single WeakMap\n * lookup and only strips a DIRECTLY-assigned proxy — `outer.box = { inner:\n * someProxy }` would otherwise smuggle `someProxy` in under `box.inner`\n * since `box` itself was never a proxy. Walks only wrappable containers\n * (same class `isWrappable` recognizes); a `seen` WeakSet guards against\n * cyclic user data. */\nfunction unwrapDeep(value: unknown, seen?: WeakSet<object>): unknown {\n if (value === null || typeof value !== 'object') return value\n const raw = rawMap.get(value as object)\n const container = raw !== undefined ? raw : (value as object)\n if (!isWrappable(container)) return container\n const visited = seen ?? new WeakSet<object>()\n if (visited.has(container)) return container\n visited.add(container)\n const rec = container as Record<PropertyKey, unknown>\n for (const k of Reflect.ownKeys(rec)) {\n const v = rec[k]\n if (v !== null && typeof v === 'object') {\n rec[k] = unwrapDeep(v, visited)\n }\n }\n return container\n}\n\n// ───────── Proxy traps (design §2.6) ─────────\n\nconst handlers: ProxyHandler<object> = {\n get(target, key, receiver) {\n if (Array.isArray(target) && ARRAY_MUTATORS.has(key)) {\n const fn = (target as unknown as Record<PropertyKey, (...a: unknown[]) => unknown>)[key] as (\n ...a: unknown[]\n ) => unknown\n // Apply with `this = receiver` (the proxy, not the raw target) so the\n // method's own internal index/length writes route through OUR `set`\n // trap — that's what makes `batch()` here collapse them into one\n // flush instead of bypassing tracking entirely.\n return (...args: unknown[]) => batch(() => fn.apply(receiver, args))\n }\n // First-touch allocation, tracked or not (design §2.7): the read() call\n // below is the ordinary signal reader — `if (currentObserver !== null)\n // linkAdd(...)` — so an untracked read allocates the node but no edge.\n getOrCreateNode(getNodes(target), key)[0]()\n const res = (target as Record<PropertyKey, unknown>)[key]\n return res !== null && typeof res === 'object' ? reactive(res as object) : res\n },\n\n set(target, key, value) {\n const isArray = Array.isArray(target)\n const rawValue = unwrapDeep(value)\n const record = target as Record<PropertyKey, unknown>\n // Add-detection MUST run before the equality short-circuit below:\n // assigning `undefined` to a key that does not yet exist has to still\n // create it (plain-JS parity, design §2.1/§2.6) — `oldValue` for a\n // missing key also reads as `undefined`, so checking `hadKey` first is\n // what tells the two cases apart.\n const hadKey = key in target\n const oldValue = record[key]\n // Equality short-circuit (design §2.6) — same Object.is rule and the\n // same \"no allocation, no notify\" shape signal.ts's write() already\n // applies to every tuple write. `obj.x = obj.x` is a correct no-op.\n if (hadKey && Object.is(rawValue, oldValue)) return true\n\n const nodes = getNodes(target)\n\n // Array length assignment is the one write that can silently drop (or\n // reintroduce) index properties without ever routing through\n // `deleteProperty` — handle it explicitly so effects subscribed to a\n // dropped index are notified. This is the exact path `reconcile()`'s\n // truncation uses (`proxy.length = next.length`), so fixing it here\n // fixes reconcile too (design §8.10's index-tracking model).\n if (isArray && key === 'length') {\n const oldLen = oldValue as number\n record.length = rawValue as number\n const newLen = record.length as number\n const lengthNode = getOrCreateNode(nodes, 'length')\n if (newLen < oldLen) {\n batch(() => {\n lengthNode[1]((v) => v + 1)\n for (let i = newLen; i < oldLen; i++) {\n const idxNode = nodes.get(String(i))\n if (idxNode !== undefined) idxNode[1]((v) => v + 1)\n }\n })\n } else {\n lengthNode[1]((v) => v + 1)\n }\n return true\n }\n\n record[key] = rawValue\n const keyNode = getOrCreateNode(nodes, key)\n if (hadKey) {\n // Plain value write on an existing key touches exactly one node —\n // no batch needed (matches tuple-write semantics: one write, one\n // flush).\n keyNode[1]((v) => v + 1)\n return true\n }\n // Add path: property add, or an array index write past the current\n // length. Both touch the key's own node AND a shape companion node\n // (KEYS for objects / length for array-index adds) — batched together\n // so the trap never produces two back-to-back synchronous drains for\n // one authored assignment (design §2.6). `ownKeys`/`has` track BOTH\n // nodes for arrays (below), so bumping `length` alone still wakes\n // Object.keys/for-in/`in` watchers on an index add.\n const companion = getOrCreateNode(nodes, isArray && isArrayIndexKey(key) ? 'length' : KEYS)\n batch(() => {\n keyNode[1]((v) => v + 1)\n companion[1]((v) => v + 1)\n })\n return true\n },\n\n has(target, key) {\n const nodes = getNodes(target)\n getOrCreateNode(nodes, KEYS)[0]()\n // Arrays notify index adds via the `length` node, not KEYS (see\n // `set`) — track it too so `'k' in arr` reacts to shape changes made\n // through an index write, not only through defineProperty-shaped adds.\n if (Array.isArray(target)) getOrCreateNode(nodes, 'length')[0]()\n return key in target\n },\n\n deleteProperty(target, key) {\n const had = key in target\n const ok = delete (target as Record<PropertyKey, unknown>)[key]\n if (had && ok) {\n const nodes = getNodes(target)\n const keyNode = nodes.get(key)\n const keysNode = getOrCreateNode(nodes, KEYS)\n batch(() => {\n if (keyNode !== undefined) keyNode[1]((v) => v + 1)\n keysNode[1]((v) => v + 1)\n })\n }\n return ok\n },\n\n ownKeys(target) {\n const nodes = getNodes(target)\n getOrCreateNode(nodes, KEYS)[0]()\n // See `has` above: array index adds bump `length`, not KEYS.\n if (Array.isArray(target)) getOrCreateNode(nodes, 'length')[0]()\n return Reflect.ownKeys(target)\n },\n}\n\n// ───────── Public core API (design §4.1) ─────────\n\n/**\n * Wrap a plain object/array in a fine-grained reactive tree. Idempotent and\n * identity-stable: `reactive(o) === reactive(o)`, `reactive(reactive(o)) ===\n * reactive(o)`. Non-wrappable values (Date, Map, Set, class instances,\n * frozen objects, primitives) are returned as-is.\n */\nexport function reactive<T extends object>(source: T): T {\n if (source === null || typeof source !== 'object') return source\n if (rawMap.has(source as object)) return source // already a proxy\n const cached = wrapMap.get(source as object)\n if (cached !== undefined) return cached as T\n if (!isWrappable(source as object)) return source\n const proxy = new Proxy(source as object, handlers) as T\n wrapMap.set(source as object, proxy as object)\n rawMap.set(proxy as object, source as object)\n return proxy\n}\n\n/** True for a proxy produced by `reactive()`. */\nexport function isReactive(value: unknown): boolean {\n return value !== null && typeof value === 'object' && rawMap.has(value as object)\n}\n\n/** The raw object behind a reactive proxy (O(1), no traversal — writes\n * unwrap, so the raw tree never contains proxies). Non-proxies pass\n * through unchanged. */\nexport function unwrap<T>(value: T): T {\n if (value !== null && typeof value === 'object') {\n const raw = rawMap.get(value as object)\n if (raw !== undefined) return raw as T\n }\n return value\n}\n\n/** Apply many writes as ONE flush. Equivalent to `batch(() => recipe(target))`\n * — the \"draft\" IS the reactive proxy; writes apply immediately (design\n * §8.6: NOT an Immer draft — a throwing recipe leaves partial writes, same\n * non-atomic-on-error posture `batch()` already documents). */\nexport function mutate<T extends object>(target: T, recipe: (draft: T) => void): void {\n batch(() => recipe(target))\n}\n\n// ───────── reconcile (design §4.1, §7.2, §9) ─────────\n\ntype ReconcileOptions = { key?: PropertyKey | ((item: unknown) => unknown) }\n\nfunction reconcileInto(proxy: object, next: unknown, options?: ReconcileOptions): void {\n const raw = unwrap(proxy)\n if (Array.isArray(raw) && Array.isArray(next)) {\n // `key` must apply at every array encountered during the recursion,\n // not only a top-level array (design intent: `reconcile(state, payload,\n // { key: 'id' })` where `state.rows` is a nested array) — falling back\n // to index matching here would silently re-identity rows on a reorder.\n if (options?.key !== undefined) {\n reconcileArrayKeyed(proxy as unknown[], raw, next, options)\n return\n }\n for (let i = 0; i < next.length; i++) {\n reconcileField(proxy, i, (raw as unknown[])[i], next[i], i < raw.length, options)\n }\n if (next.length < raw.length) (proxy as unknown[]).length = next.length\n return\n }\n if (isPlainObjectLike(raw) && isPlainObjectLike(next)) {\n for (const k of Reflect.ownKeys(next)) {\n reconcileField(proxy, k, raw[k], next[k], Object.hasOwn(raw, k), options)\n }\n for (const k of Reflect.ownKeys(raw)) {\n if (!Object.hasOwn(next, k)) {\n delete (proxy as Record<PropertyKey, unknown>)[k]\n }\n }\n }\n}\n\n/** Reconcile one field. Reads for comparison come from the RAW values\n * (never through the proxy) so this can run safely inside the tracking\n * effect that drives `reactiveComputed` without the write-side of this\n * same reconcile becoming a read-side dependency of itself. Only the\n * write path (`proxy[key] = …`) touches the proxy.\n *\n * `hadKey` mirrors the `set` trap's own add-detection fix: `curVal` for a\n * key genuinely absent from `raw` reads as `undefined`, same as an\n * explicit `undefined` value would — `Object.is` alone can't tell \"already\n * undefined\" from \"never existed\" apart, so a payload adding an\n * explicitly-`undefined`-valued key would otherwise be silently dropped as\n * a no-op. */\nfunction reconcileField(\n proxy: object,\n key: PropertyKey,\n curVal: unknown,\n nextVal: unknown,\n hadKey: boolean,\n options?: ReconcileOptions,\n): void {\n if (sameContainerShape(curVal, nextVal)) {\n reconcileInto(reactive(curVal as object), nextVal, options)\n } else if (!hadKey || !Object.is(curVal, nextVal)) {\n ;(proxy as Record<PropertyKey, unknown>)[key] = nextVal\n }\n}\n\nfunction reconcileArrayKeyed(\n proxy: unknown[],\n raw: unknown[],\n next: unknown[],\n options: ReconcileOptions,\n): void {\n const keyOpt = options.key as PropertyKey | ((item: unknown) => unknown)\n const idOf =\n typeof keyOpt === 'function'\n ? keyOpt\n : (item: unknown) => (item as Record<PropertyKey, unknown> | null)?.[keyOpt]\n const byKey = new Map<unknown, unknown>()\n for (const item of raw) byKey.set(idOf(item), item)\n const merged: unknown[] = new Array(next.length)\n for (let i = 0; i < next.length; i++) {\n const nextItem = next[i]\n const id = idOf(nextItem)\n const match = byKey.get(id)\n if (match !== undefined && sameContainerShape(match, nextItem)) {\n reconcileInto(reactive(match as object), nextItem, options)\n merged[i] = match\n // Consume the match: a duplicate key in `next` must NOT alias a\n // second array slot onto the same raw object (that would silently\n // make `proxy[i] === proxy[j]`, so a later write to one row mutates\n // the other). Unmatched duplicates fall through to a fresh value.\n byKey.delete(id)\n } else {\n merged[i] = nextItem\n }\n }\n for (let i = 0; i < merged.length; i++) {\n if (!Object.is(raw[i], merged[i])) proxy[i] = merged[i]\n }\n if (merged.length < raw.length) proxy.length = merged.length\n}\n\n/**\n * Merge `next` into `target` in place, preserving node identity for\n * unchanged values and notifying ONLY changed paths. The hydration /\n * refetch primitive. `key` controls array item matching (default: index),\n * applied recursively to every array reconcile encounters, not only a\n * top-level one.\n */\nexport function reconcile<T extends object>(target: T, next: T, options?: ReconcileOptions): void {\n const proxy = reactive(target)\n batch(() => {\n reconcileInto(proxy as object, next, options)\n })\n}\n\n// ───────── Test-only introspection ─────────\n//\n// NOT re-exported from index.ts (the \".\" entry stays exactly the public\n// API above) — reached only via a direct relative import from tests, the\n// same pattern packages/signals/src/signal.ts uses for `__hostOf` /\n// `__inspectGraph`. Since index.ts never references this binding, it is\n// dead-code-eliminated out of the measured `dist/index.js` (design §5,\n// §12 acceptance #1/#2 — this must never move the size row).\n\n/** @internal — test-only: the read function of the per-key tracking node\n * for `(raw, key)`, or `undefined` if that key has never been touched\n * through the proxy's `get`/`set` trap. Compose with `@aihu/signals`'\n * `__hostOf`/`__inspectGraph` to assert §2.7's allocation contract. */\nexport function __nodeOf(raw: object, key: PropertyKey): (() => number) | undefined {\n return nodeMap.get(raw)?.get(key)?.[0]\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aihu/reactive",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
],
|
|
24
24
|
"sideEffects": false,
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@aihu/signals": "0.5.
|
|
26
|
+
"@aihu/signals": "^0.5.1"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "rm -rf dist && rolldown -c",
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
"typecheck": "tsc --noEmit",
|
|
32
32
|
"prepublishOnly": "bun run build"
|
|
33
33
|
},
|
|
34
|
-
"description": "Fine-grained Proxy-backed deep reactive trees on aihu signals
|
|
34
|
+
"description": "Fine-grained Proxy-backed deep reactive trees on aihu signals \u2014 lazy per-(object,key) tracking nodes, plain-assignment writes, mutate/reconcile.",
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
|
37
|
-
"url": "git+https://github.com/
|
|
37
|
+
"url": "git+https://github.com/aihu-project/aihu-dom.git",
|
|
38
38
|
"directory": "packages/reactive"
|
|
39
39
|
},
|
|
40
|
-
"homepage": "https://github.com/
|
|
41
|
-
"bugs": "https://github.com/
|
|
40
|
+
"homepage": "https://github.com/aihu-project/aihu-dom/tree/main/packages/reactive#readme",
|
|
41
|
+
"bugs": "https://github.com/aihu-project/aihu-dom/issues",
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public"
|
|
44
44
|
}
|