fict 0.2.0 → 0.2.2

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.
@@ -53,12 +53,22 @@ function $store(initialValue) {
53
53
  boundMethods.set(prop, { ref: currentValue, bound });
54
54
  return bound;
55
55
  }
56
+ {
57
+ const boundMethods = BOUND_METHOD_CACHE.get(target);
58
+ if (boundMethods && boundMethods.has(prop)) {
59
+ boundMethods.delete(prop);
60
+ if (boundMethods.size === 0) {
61
+ BOUND_METHOD_CACHE.delete(target);
62
+ }
63
+ }
64
+ }
56
65
  if (typeof currentValue === "object" && currentValue !== null) {
57
66
  return $store(currentValue);
58
67
  }
59
68
  return currentValue;
60
69
  },
61
70
  set(target, prop, newValue, receiver) {
71
+ const oldLength = Array.isArray(target) && prop === "length" ? target.length : void 0;
62
72
  const oldValue = Reflect.get(target, prop, receiver);
63
73
  const hadKey = Object.prototype.hasOwnProperty.call(target, prop);
64
74
  if (oldValue === newValue && hadKey) {
@@ -68,6 +78,9 @@ function $store(initialValue) {
68
78
  const boundMethods = BOUND_METHOD_CACHE.get(target);
69
79
  if (boundMethods && boundMethods.has(prop)) {
70
80
  boundMethods.delete(prop);
81
+ if (boundMethods.size === 0) {
82
+ BOUND_METHOD_CACHE.delete(target);
83
+ }
71
84
  }
72
85
  const signals = SIGNAL_CACHE.get(target);
73
86
  if (signals && signals[prop]) {
@@ -83,6 +96,18 @@ function $store(initialValue) {
83
96
  }
84
97
  }
85
98
  if (Array.isArray(target) && prop === "length") {
99
+ const nextLength = target.length;
100
+ if (typeof oldLength === "number" && nextLength < oldLength) {
101
+ const signals2 = SIGNAL_CACHE.get(target);
102
+ if (signals2) {
103
+ for (let i = nextLength; i < oldLength; i += 1) {
104
+ const key = String(i);
105
+ if (signals2[key]) {
106
+ signals2[key](void 0);
107
+ }
108
+ }
109
+ }
110
+ }
86
111
  triggerIteration(target);
87
112
  }
88
113
  return result;
@@ -98,6 +123,9 @@ function $store(initialValue) {
98
123
  const boundMethods = BOUND_METHOD_CACHE.get(target);
99
124
  if (boundMethods && boundMethods.has(prop)) {
100
125
  boundMethods.delete(prop);
126
+ if (boundMethods.size === 0) {
127
+ BOUND_METHOD_CACHE.delete(target);
128
+ }
101
129
  }
102
130
  triggerIteration(target);
103
131
  }
@@ -117,5 +145,5 @@ function $store(initialValue) {
117
145
  }
118
146
 
119
147
  export { $store };
120
- //# sourceMappingURL=chunk-QPBAYI4X.js.map
121
- //# sourceMappingURL=chunk-QPBAYI4X.js.map
148
+ //# sourceMappingURL=chunk-YSNNU4OE.js.map
149
+ //# sourceMappingURL=chunk-YSNNU4OE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/store.ts"],"names":["signals"],"mappings":";;;AA4BA,IAAM,WAAA,uBAAkB,OAAA,EAAyB;AAGjD,IAAM,YAAA,uBAAmB,OAAA,EAA0D;AAGnF,IAAM,kBAAA,uBAAyB,OAAA,EAAwD;AAGvF,IAAM,WAAA,GAAc,OAAO,SAAS,CAAA;AAMpC,SAAS,SAAA,CAAU,QAAgB,IAAA,EAAwC;AACzE,EAAA,IAAI,OAAA,GAAU,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AACrC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAA,GAAU,EAAC;AACX,IAAA,YAAA,CAAa,GAAA,CAAI,QAAQ,OAAO,CAAA;AAAA,EAClC;AACA,EAAA,IAAI,CAAC,OAAA,CAAQ,IAAI,CAAA,EAAG;AAClB,IAAA,MAAM,OAAA,GAAU,IAAA,KAAS,WAAA,GAAc,CAAA,GAAK,OAA2B,IAAI,CAAA;AAC3E,IAAA,OAAA,CAAQ,IAAI,CAAA,GAAI,YAAA,CAAa,OAAO,CAAA;AAAA,EACtC;AACA,EAAA,OAAO,QAAQ,IAAI,CAAA;AACrB;AAMA,SAAS,iBAAiB,MAAA,EAAsB;AAC9C,EAAA,MAAM,OAAA,GAAU,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AACvC,EAAA,IAAI,OAAA,IAAW,OAAA,CAAQ,WAAW,CAAA,EAAG;AACnC,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,WAAW,CAAA,EAAE;AACrC,IAAA,OAAA,CAAQ,WAAW,CAAA,CAAE,OAAA,GAAU,CAAC,CAAA;AAAA,EAClC;AACF;AA+BO,SAAS,OAAyB,YAAA,EAAoB;AAC3D,EAAA,IAAI,OAAO,YAAA,KAAiB,QAAA,IAAY,YAAA,KAAiB,IAAA,EAAM;AAC7D,IAAA,OAAO,YAAA;AAAA,EACT;AAEA,EAAA,IAAI,WAAA,CAAY,GAAA,CAAI,YAAY,CAAA,EAAG;AACjC,IAAA,OAAO,WAAA,CAAY,IAAI,YAAY,CAAA;AAAA,EACrC;AAEA,EAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,CAAM,YAAA,EAAc;AAAA,IACpC,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,QAAA,EAAU;AAG1B,MAAA,MAAM,MAAA,GAAS,SAAA,CAAU,MAAA,EAAQ,IAAI,CAAA;AACrC,MAAA,MAAM,eAAe,MAAA,EAAO;AAE5B,MAAA,MAAM,eAAe,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,YAAY,KAAK,CAAA;AAChE,MAAA,IAAI,iBAAiB,YAAA,EAAc;AAIjC,QAAA,MAAA,CAAO,YAAY,CAAA;AAAA,MACrB;AAEA,MAAA,IAAI,OAAO,iBAAiB,UAAA,EAAY;AACtC,QAAA,IAAI,YAAA,GAAe,kBAAA,CAAmB,GAAA,CAAI,MAAM,CAAA;AAChD,QAAA,IAAI,CAAC,YAAA,EAAc;AACjB,UAAA,YAAA,uBAAmB,GAAA,EAAI;AACvB,UAAA,kBAAA,CAAmB,GAAA,CAAI,QAAQ,YAAY,CAAA;AAAA,QAC7C;AACA,QAAA,MAAM,MAAA,GAAS,YAAA,CAAa,GAAA,CAAI,IAAI,CAAA;AACpC,QAAA,IAAI,MAAA,IAAU,MAAA,CAAO,GAAA,KAAQ,YAAA,EAAc;AACzC,UAAA,OAAO,MAAA,CAAO,KAAA;AAAA,QAChB;AAEA,QAAA,MAAM,KAAA,GAAS,YAAA,CAAuB,IAAA,CAAK,QAAA,IAAY,KAAK,CAAA;AAC5D,QAAA,YAAA,CAAa,IAAI,IAAA,EAAM,EAAE,GAAA,EAAK,YAAA,EAAuB,OAAO,CAAA;AAC5D,QAAA,OAAO,KAAA;AAAA,MACT;AACA,MAAA;AACE,QAAA,MAAM,YAAA,GAAe,kBAAA,CAAmB,GAAA,CAAI,MAAM,CAAA;AAClD,QAAA,IAAI,YAAA,IAAgB,YAAA,CAAa,GAAA,CAAI,IAAI,CAAA,EAAG;AAC1C,UAAA,YAAA,CAAa,OAAO,IAAI,CAAA;AACxB,UAAA,IAAI,YAAA,CAAa,SAAS,CAAA,EAAG;AAC3B,YAAA,kBAAA,CAAmB,OAAO,MAAM,CAAA;AAAA,UAClC;AAAA,QACF;AAAA,MACF;AAGA,MAAA,IAAI,OAAO,YAAA,KAAiB,QAAA,IAAY,YAAA,KAAiB,IAAA,EAAM;AAC7D,QAAA,OAAO,OAAO,YAAuC,CAAA;AAAA,MACvD;AAGA,MAAA,OAAO,YAAA;AAAA,IACT,CAAA;AAAA,IAEA,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,QAAA,EAAU,QAAA,EAAU;AACpC,MAAA,MAAM,SAAA,GAAY,MAAM,OAAA,CAAQ,MAAM,KAAK,IAAA,KAAS,QAAA,GAAW,OAAO,MAAA,GAAS,MAAA;AAC/E,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,QAAQ,CAAA;AACnD,MAAA,MAAM,SAAS,MAAA,CAAO,SAAA,CAAU,cAAA,CAAe,IAAA,CAAK,QAAQ,IAAI,CAAA;AAGhE,MAAA,IAAI,QAAA,KAAa,YAAY,MAAA,EAAQ;AACnC,QAAA,OAAO,IAAA;AAAA,MACT;AAEA,MAAA,MAAM,SAAS,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,UAAU,QAAQ,CAAA;AAG3D,MAAA,MAAM,YAAA,GAAe,kBAAA,CAAmB,GAAA,CAAI,MAAM,CAAA;AAClD,MAAA,IAAI,YAAA,IAAgB,YAAA,CAAa,GAAA,CAAI,IAAI,CAAA,EAAG;AAC1C,QAAA,YAAA,CAAa,OAAO,IAAI,CAAA;AACxB,QAAA,IAAI,YAAA,CAAa,SAAS,CAAA,EAAG;AAC3B,UAAA,kBAAA,CAAmB,OAAO,MAAM,CAAA;AAAA,QAClC;AAAA,MACF;AAGA,MAAA,MAAM,OAAA,GAAU,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AACvC,MAAA,IAAI,OAAA,IAAW,OAAA,CAAQ,IAAI,CAAA,EAAG;AAC5B,QAAA,OAAA,CAAQ,IAAI,EAAE,QAAQ,CAAA;AAAA,MACxB;AAGA,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA,gBAAA,CAAiB,MAAM,CAAA;AAAA,MACzB;AAIA,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,IAAK,SAAS,QAAA,EAAU;AAC9C,QAAA,MAAMA,QAAAA,GAAU,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AACvC,QAAA,IAAIA,QAAAA,IAAWA,SAAQ,MAAA,EAAQ;AAC7B,UAAAA,QAAAA,CAAQ,MAAA,CAAO,MAAA,CAAO,MAAM,CAAA;AAAA,QAC9B;AAAA,MACF;AAGA,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,IAAK,SAAS,QAAA,EAAU;AAC9C,QAAA,MAAM,aAAa,MAAA,CAAO,MAAA;AAC1B,QAAA,IAAI,OAAO,SAAA,KAAc,QAAA,IAAY,UAAA,GAAa,SAAA,EAAW;AAC3D,UAAA,MAAMA,QAAAA,GAAU,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AACvC,UAAA,IAAIA,QAAAA,EAAS;AACX,YAAA,KAAA,IAAS,CAAA,GAAI,UAAA,EAAY,CAAA,GAAI,SAAA,EAAW,KAAK,CAAA,EAAG;AAC9C,cAAA,MAAM,GAAA,GAAM,OAAO,CAAC,CAAA;AACpB,cAAA,IAAIA,QAAAA,CAAQ,GAAG,CAAA,EAAG;AAChB,gBAAAA,QAAAA,CAAQ,GAAG,CAAA,CAAE,MAAS,CAAA;AAAA,cACxB;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,QAAA,gBAAA,CAAiB,MAAM,CAAA;AAAA,MACzB;AAEA,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IAEA,cAAA,CAAe,QAAQ,IAAA,EAAM;AAC3B,MAAA,MAAM,SAAS,MAAA,CAAO,SAAA,CAAU,cAAA,CAAe,IAAA,CAAK,QAAQ,IAAI,CAAA;AAChE,MAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,cAAA,CAAe,MAAA,EAAQ,IAAI,CAAA;AAElD,MAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,QAAA,MAAM,OAAA,GAAU,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AACvC,QAAA,IAAI,OAAA,IAAW,OAAA,CAAQ,IAAI,CAAA,EAAG;AAC5B,UAAA,OAAA,CAAQ,IAAI,EAAE,MAAS,CAAA;AAAA,QACzB;AAGA,QAAA,MAAM,YAAA,GAAe,kBAAA,CAAmB,GAAA,CAAI,MAAM,CAAA;AAClD,QAAA,IAAI,YAAA,IAAgB,YAAA,CAAa,GAAA,CAAI,IAAI,CAAA,EAAG;AAC1C,UAAA,YAAA,CAAa,OAAO,IAAI,CAAA;AACxB,UAAA,IAAI,YAAA,CAAa,SAAS,CAAA,EAAG;AAC3B,YAAA,kBAAA,CAAmB,OAAO,MAAM,CAAA;AAAA,UAClC;AAAA,QACF;AAEA,QAAA,gBAAA,CAAiB,MAAM,CAAA;AAAA,MACzB;AAEA,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IAEA,QAAQ,MAAA,EAAQ;AACd,MAAA,SAAA,CAAU,MAAA,EAAQ,WAAW,CAAA,EAAE;AAC/B,MAAA,OAAO,OAAA,CAAQ,QAAQ,MAAM,CAAA;AAAA,IAC/B,CAAA;AAAA,IAEA,GAAA,CAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,SAAA,CAAU,MAAA,EAAQ,IAAI,CAAA,EAAE;AACxB,MAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,IAAI,CAAA;AAAA,IACjC;AAAA,GACD,CAAA;AAED,EAAA,WAAA,CAAY,GAAA,CAAI,cAAc,KAAK,CAAA;AACnC,EAAA,OAAO,KAAA;AACT","file":"chunk-YSNNU4OE.js","sourcesContent":["/**\n * @fileoverview Deep reactive store implementation for Fict.\n *\n * $store creates a deeply reactive proxy that tracks property access at the path level.\n * Unlike $state (which is shallow), $store allows direct mutation of nested properties.\n *\n * @example\n * ```typescript\n * const user = $store({ name: 'Alice', address: { city: 'London' } })\n * user.address.city = 'Paris' // Fine-grained reactive update\n * ```\n */\n\nimport { createSignal, type Signal } from '@fictjs/runtime/advanced'\n\n/** Function type for bound methods */\ntype AnyFn = (...args: unknown[]) => unknown\n\n/** Cache entry for bound methods to preserve identity */\ninterface BoundMethodEntry {\n ref: AnyFn\n bound: AnyFn\n}\n\n/** Type for objects with indexable properties */\ntype IndexableObject = Record<string | symbol, unknown>\n\n/** Cache of proxied objects to avoid duplicate proxies */\nconst PROXY_CACHE = new WeakMap<object, unknown>()\n\n/** Cache of signals per object property */\nconst SIGNAL_CACHE = new WeakMap<object, Record<string | symbol, Signal<unknown>>>()\n\n/** Cache of bound methods to preserve function identity across reads */\nconst BOUND_METHOD_CACHE = new WeakMap<object, Map<string | symbol, BoundMethodEntry>>()\n\n/** Special key for tracking iteration (Object.keys, for-in, etc.) */\nconst ITERATE_KEY = Symbol('iterate')\n\n/**\n * Get or create a signal for a specific property on a target object.\n * @internal\n */\nfunction getSignal(target: object, prop: string | symbol): Signal<unknown> {\n let signals = SIGNAL_CACHE.get(target)\n if (!signals) {\n signals = {}\n SIGNAL_CACHE.set(target, signals)\n }\n if (!signals[prop]) {\n const initial = prop === ITERATE_KEY ? 0 : (target as IndexableObject)[prop]\n signals[prop] = createSignal(initial)\n }\n return signals[prop]\n}\n\n/**\n * Trigger iteration signal to notify consumers that keys have changed.\n * @internal\n */\nfunction triggerIteration(target: object): void {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[ITERATE_KEY]) {\n const current = signals[ITERATE_KEY]() as number\n signals[ITERATE_KEY](current + 1)\n }\n}\n\n/**\n * Create a deep reactive store using Proxy.\n *\n * Unlike `$state` (which is shallow and compiler-transformed), `$store` provides:\n * - **Deep reactivity**: Nested objects are automatically wrapped in proxies\n * - **Direct mutation**: Modify properties directly without spread operators\n * - **Path-level tracking**: Only components reading changed paths re-render\n *\n * @param initialValue - The initial object to make reactive\n * @returns A reactive proxy of the object\n *\n * @example\n * ```tsx\n * import { $store } from 'fict'\n *\n * const form = $store({\n * user: { name: '', email: '' },\n * settings: { theme: 'light' }\n * })\n *\n * // Direct mutation works\n * form.user.name = 'Alice'\n *\n * // In JSX - only updates when form.user.name changes\n * <input value={form.user.name} />\n * ```\n *\n * @public\n */\nexport function $store<T extends object>(initialValue: T): T {\n if (typeof initialValue !== 'object' || initialValue === null) {\n return initialValue\n }\n\n if (PROXY_CACHE.has(initialValue)) {\n return PROXY_CACHE.get(initialValue) as T\n }\n\n const proxy = new Proxy(initialValue, {\n get(target, prop, receiver) {\n // Always touch the signal so reference changes to this property are tracked,\n // even if the value is an object we proxy further.\n const signal = getSignal(target, prop)\n const trackedValue = signal()\n\n const currentValue = Reflect.get(target, prop, receiver ?? proxy)\n if (currentValue !== trackedValue) {\n // If the value has changed (e.g. via direct mutation of the underlying object not via proxy),\n // we update the signal to keep it in sync.\n // Note: This is a bit of a heuristic. Ideally all mutations go through proxy.\n signal(currentValue)\n }\n\n if (typeof currentValue === 'function') {\n let boundMethods = BOUND_METHOD_CACHE.get(target)\n if (!boundMethods) {\n boundMethods = new Map()\n BOUND_METHOD_CACHE.set(target, boundMethods)\n }\n const cached = boundMethods.get(prop)\n if (cached && cached.ref === currentValue) {\n return cached.bound\n }\n\n const bound = (currentValue as AnyFn).bind(receiver ?? proxy)\n boundMethods.set(prop, { ref: currentValue as AnyFn, bound })\n return bound\n }\n {\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n if (boundMethods.size === 0) {\n BOUND_METHOD_CACHE.delete(target)\n }\n }\n }\n\n // If the value is an object/array, we recursively wrap it in a store\n if (typeof currentValue === 'object' && currentValue !== null) {\n return $store(currentValue as Record<string, unknown>)\n }\n\n // For primitives (and functions), we return the signal value (which tracks the read)\n return currentValue\n },\n\n set(target, prop, newValue, receiver) {\n const oldLength = Array.isArray(target) && prop === 'length' ? target.length : undefined\n const oldValue = Reflect.get(target, prop, receiver)\n const hadKey = Object.prototype.hasOwnProperty.call(target, prop)\n\n // If value hasn't changed, do nothing\n if (oldValue === newValue && hadKey) {\n return true\n }\n\n const result = Reflect.set(target, prop, newValue, receiver)\n\n // IMPORTANT: Clear bound method cache BEFORE updating the signal\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n if (boundMethods.size === 0) {\n BOUND_METHOD_CACHE.delete(target)\n }\n }\n\n // Update the signal if it exists\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[prop]) {\n signals[prop](newValue)\n }\n\n // If new property, trigger iteration update\n if (!hadKey) {\n triggerIteration(target)\n }\n\n // Ensure array length subscribers are notified even if the native push/pop\n // doesn't trigger a separate set trap for \"length\" (defensive).\n if (Array.isArray(target) && prop !== 'length') {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals.length) {\n signals.length(target.length)\n }\n }\n\n // If it's an array and length changed implicitly, we might need to handle it.\n if (Array.isArray(target) && prop === 'length') {\n const nextLength = target.length\n if (typeof oldLength === 'number' && nextLength < oldLength) {\n const signals = SIGNAL_CACHE.get(target)\n if (signals) {\n for (let i = nextLength; i < oldLength; i += 1) {\n const key = String(i)\n if (signals[key]) {\n signals[key](undefined)\n }\n }\n }\n }\n triggerIteration(target)\n }\n\n return result\n },\n\n deleteProperty(target, prop) {\n const hadKey = Object.prototype.hasOwnProperty.call(target, prop)\n const result = Reflect.deleteProperty(target, prop)\n\n if (result && hadKey) {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[prop]) {\n signals[prop](undefined)\n }\n\n // Clear bound method cache\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n if (boundMethods.size === 0) {\n BOUND_METHOD_CACHE.delete(target)\n }\n }\n\n triggerIteration(target)\n }\n\n return result\n },\n\n ownKeys(target) {\n getSignal(target, ITERATE_KEY)()\n return Reflect.ownKeys(target)\n },\n\n has(target, prop) {\n getSignal(target, prop)()\n return Reflect.has(target, prop)\n },\n })\n\n PROXY_CACHE.set(initialValue, proxy)\n return proxy\n}\n"]}
package/dist/index.cjs CHANGED
@@ -56,12 +56,22 @@ function $store(initialValue) {
56
56
  boundMethods.set(prop, { ref: currentValue, bound });
57
57
  return bound;
58
58
  }
59
+ {
60
+ const boundMethods = BOUND_METHOD_CACHE.get(target);
61
+ if (boundMethods && boundMethods.has(prop)) {
62
+ boundMethods.delete(prop);
63
+ if (boundMethods.size === 0) {
64
+ BOUND_METHOD_CACHE.delete(target);
65
+ }
66
+ }
67
+ }
59
68
  if (typeof currentValue === "object" && currentValue !== null) {
60
69
  return $store(currentValue);
61
70
  }
62
71
  return currentValue;
63
72
  },
64
73
  set(target, prop, newValue, receiver) {
74
+ const oldLength = Array.isArray(target) && prop === "length" ? target.length : void 0;
65
75
  const oldValue = Reflect.get(target, prop, receiver);
66
76
  const hadKey = Object.prototype.hasOwnProperty.call(target, prop);
67
77
  if (oldValue === newValue && hadKey) {
@@ -71,6 +81,9 @@ function $store(initialValue) {
71
81
  const boundMethods = BOUND_METHOD_CACHE.get(target);
72
82
  if (boundMethods && boundMethods.has(prop)) {
73
83
  boundMethods.delete(prop);
84
+ if (boundMethods.size === 0) {
85
+ BOUND_METHOD_CACHE.delete(target);
86
+ }
74
87
  }
75
88
  const signals = SIGNAL_CACHE.get(target);
76
89
  if (signals && signals[prop]) {
@@ -86,6 +99,18 @@ function $store(initialValue) {
86
99
  }
87
100
  }
88
101
  if (Array.isArray(target) && prop === "length") {
102
+ const nextLength = target.length;
103
+ if (typeof oldLength === "number" && nextLength < oldLength) {
104
+ const signals2 = SIGNAL_CACHE.get(target);
105
+ if (signals2) {
106
+ for (let i = nextLength; i < oldLength; i += 1) {
107
+ const key = String(i);
108
+ if (signals2[key]) {
109
+ signals2[key](void 0);
110
+ }
111
+ }
112
+ }
113
+ }
89
114
  triggerIteration(target);
90
115
  }
91
116
  return result;
@@ -101,6 +126,9 @@ function $store(initialValue) {
101
126
  const boundMethods = BOUND_METHOD_CACHE.get(target);
102
127
  if (boundMethods && boundMethods.has(prop)) {
103
128
  boundMethods.delete(prop);
129
+ if (boundMethods.size === 0) {
130
+ BOUND_METHOD_CACHE.delete(target);
131
+ }
104
132
  }
105
133
  triggerIteration(target);
106
134
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/store.ts","../src/index.ts"],"names":["createSignal","signals"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @fileoverview Deep reactive store implementation for Fict.\n *\n * $store creates a deeply reactive proxy that tracks property access at the path level.\n * Unlike $state (which is shallow), $store allows direct mutation of nested properties.\n *\n * @example\n * ```typescript\n * const user = $store({ name: 'Alice', address: { city: 'London' } })\n * user.address.city = 'Paris' // Fine-grained reactive update\n * ```\n */\n\nimport { createSignal, type Signal } from '@fictjs/runtime/advanced'\n\n/** Function type for bound methods */\ntype AnyFn = (...args: unknown[]) => unknown\n\n/** Cache entry for bound methods to preserve identity */\ninterface BoundMethodEntry {\n ref: AnyFn\n bound: AnyFn\n}\n\n/** Type for objects with indexable properties */\ntype IndexableObject = Record<string | symbol, unknown>\n\n/** Cache of proxied objects to avoid duplicate proxies */\nconst PROXY_CACHE = new WeakMap<object, unknown>()\n\n/** Cache of signals per object property */\nconst SIGNAL_CACHE = new WeakMap<object, Record<string | symbol, Signal<unknown>>>()\n\n/** Cache of bound methods to preserve function identity across reads */\nconst BOUND_METHOD_CACHE = new WeakMap<object, Map<string | symbol, BoundMethodEntry>>()\n\n/** Special key for tracking iteration (Object.keys, for-in, etc.) */\nconst ITERATE_KEY = Symbol('iterate')\n\n/**\n * Get or create a signal for a specific property on a target object.\n * @internal\n */\nfunction getSignal(target: object, prop: string | symbol): Signal<unknown> {\n let signals = SIGNAL_CACHE.get(target)\n if (!signals) {\n signals = {}\n SIGNAL_CACHE.set(target, signals)\n }\n if (!signals[prop]) {\n const initial = prop === ITERATE_KEY ? 0 : (target as IndexableObject)[prop]\n signals[prop] = createSignal(initial)\n }\n return signals[prop]\n}\n\n/**\n * Trigger iteration signal to notify consumers that keys have changed.\n * @internal\n */\nfunction triggerIteration(target: object): void {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[ITERATE_KEY]) {\n const current = signals[ITERATE_KEY]() as number\n signals[ITERATE_KEY](current + 1)\n }\n}\n\n/**\n * Create a deep reactive store using Proxy.\n *\n * Unlike `$state` (which is shallow and compiler-transformed), `$store` provides:\n * - **Deep reactivity**: Nested objects are automatically wrapped in proxies\n * - **Direct mutation**: Modify properties directly without spread operators\n * - **Path-level tracking**: Only components reading changed paths re-render\n *\n * @param initialValue - The initial object to make reactive\n * @returns A reactive proxy of the object\n *\n * @example\n * ```tsx\n * import { $store } from 'fict'\n *\n * const form = $store({\n * user: { name: '', email: '' },\n * settings: { theme: 'light' }\n * })\n *\n * // Direct mutation works\n * form.user.name = 'Alice'\n *\n * // In JSX - only updates when form.user.name changes\n * <input value={form.user.name} />\n * ```\n *\n * @public\n */\nexport function $store<T extends object>(initialValue: T): T {\n if (typeof initialValue !== 'object' || initialValue === null) {\n return initialValue\n }\n\n if (PROXY_CACHE.has(initialValue)) {\n return PROXY_CACHE.get(initialValue) as T\n }\n\n const proxy = new Proxy(initialValue, {\n get(target, prop, receiver) {\n // Always touch the signal so reference changes to this property are tracked,\n // even if the value is an object we proxy further.\n const signal = getSignal(target, prop)\n const trackedValue = signal()\n\n const currentValue = Reflect.get(target, prop, receiver ?? proxy)\n if (currentValue !== trackedValue) {\n // If the value has changed (e.g. via direct mutation of the underlying object not via proxy),\n // we update the signal to keep it in sync.\n // Note: This is a bit of a heuristic. Ideally all mutations go through proxy.\n signal(currentValue)\n }\n\n if (typeof currentValue === 'function') {\n let boundMethods = BOUND_METHOD_CACHE.get(target)\n if (!boundMethods) {\n boundMethods = new Map()\n BOUND_METHOD_CACHE.set(target, boundMethods)\n }\n const cached = boundMethods.get(prop)\n if (cached && cached.ref === currentValue) {\n return cached.bound\n }\n\n const bound = (currentValue as AnyFn).bind(receiver ?? proxy)\n boundMethods.set(prop, { ref: currentValue as AnyFn, bound })\n return bound\n }\n\n // If the value is an object/array, we recursively wrap it in a store\n if (typeof currentValue === 'object' && currentValue !== null) {\n return $store(currentValue as Record<string, unknown>)\n }\n\n // For primitives (and functions), we return the signal value (which tracks the read)\n return currentValue\n },\n\n set(target, prop, newValue, receiver) {\n const oldValue = Reflect.get(target, prop, receiver)\n const hadKey = Object.prototype.hasOwnProperty.call(target, prop)\n\n // If value hasn't changed, do nothing\n if (oldValue === newValue && hadKey) {\n return true\n }\n\n const result = Reflect.set(target, prop, newValue, receiver)\n\n // IMPORTANT: Clear bound method cache BEFORE updating the signal\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n }\n\n // Update the signal if it exists\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[prop]) {\n signals[prop](newValue)\n }\n\n // If new property, trigger iteration update\n if (!hadKey) {\n triggerIteration(target)\n }\n\n // Ensure array length subscribers are notified even if the native push/pop\n // doesn't trigger a separate set trap for \"length\" (defensive).\n if (Array.isArray(target) && prop !== 'length') {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals.length) {\n signals.length(target.length)\n }\n }\n\n // If it's an array and length changed implicitly, we might need to handle it.\n // But usually 'length' is set explicitly or handled by the runtime.\n if (Array.isArray(target) && prop === 'length') {\n triggerIteration(target)\n }\n\n return result\n },\n\n deleteProperty(target, prop) {\n const hadKey = Object.prototype.hasOwnProperty.call(target, prop)\n const result = Reflect.deleteProperty(target, prop)\n\n if (result && hadKey) {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[prop]) {\n signals[prop](undefined)\n }\n\n // Clear bound method cache\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n }\n\n triggerIteration(target)\n }\n\n return result\n },\n\n ownKeys(target) {\n getSignal(target, ITERATE_KEY)()\n return Reflect.ownKeys(target)\n },\n\n has(target, prop) {\n getSignal(target, prop)()\n return Reflect.has(target, prop)\n },\n })\n\n PROXY_CACHE.set(initialValue, proxy)\n return proxy\n}\n","/**\n * @fileoverview Fict Framework - Complete API\n *\n * This is the main entry point for the Fict framework.\n *\n * ## Recommended Import Pattern (v1.0+)\n *\n * ```typescript\n * // Core public API (most users need only this)\n * // Use $state in components (compiler-transformed)\n * // Use $store for cross-component shared state\n * import { $store, render } from 'fict'\n *\n * // Async utilities\n * import { resource, lazy } from 'fict/plus'\n *\n * // Advanced APIs (escape hatches, library authors)\n * import { createSignal, createContext, createScope } from 'fict/advanced'\n * ```\n *\n * ## State Management Guide\n *\n * | Use Case | API |\n * |----------|-----|\n * | Component-local state | `$state` (compiler-transformed) |\n * | Derived values / side effects | JS + auto-derived + `$effect` |\n * | Cross-component (large objects, deep mutation) | `$store` |\n * | Cross-component (scalar/lightweight, library-level) | `createSignal` (advanced) |\n * | Cross-component (subtree scope, SSR isolation) | `Context` (advanced) |\n *\n * @public\n * @packageDocumentation\n */\n\n// Re-export everything from runtime\nexport * from '@fictjs/runtime'\n\n// Re-export commonly used advanced APIs for convenience\nexport { createSelector, createScope, runInScope } from '@fictjs/runtime/advanced'\n\n// ============================================================================\n// Convenience Aliases\n// ============================================================================\n\n/**\n * Alias for createMemo.\n * Creates a memoized value that only recomputes when dependencies change.\n *\n * @example\n * ```tsx\n * const fullName = $memo(() => firstName + ' ' + lastName)\n * ```\n *\n * @public\n */\nexport { createMemo as $memo } from '@fictjs/runtime'\n\n// ============================================================================\n// Deep Reactive Store (Proxy-based)\n// ============================================================================\n\n/**\n * Create a deep reactive store using Proxy.\n * Unlike createStore, $store allows direct mutation.\n *\n * @example\n * ```tsx\n * const user = $store({ name: 'Alice', address: { city: 'Beijing' } })\n * user.name = 'Bob' // Reactive update\n * user.address.city = 'Shanghai' // Deep reactive\n * ```\n *\n * @public\n */\nexport { $store } from './store'\n\n// ============================================================================\n// Compiler Macros (transformed at compile time)\n// ============================================================================\n\n/**\n * Compiler macro for reactive state.\n * This is transformed at compile time and should never be called at runtime.\n *\n * @example\n * ```tsx\n * let count = $state(0)\n * count++ // Reactive update\n * ```\n *\n * @public\n */\nexport function $state<T>(_initialValue: T): T {\n // This function is never called at runtime - the compiler transforms it\n throw new Error('$state() is a compiler macro and should be transformed at compile time')\n}\n\n/**\n * Compiler macro for reactive effects.\n * This is transformed at compile time and should never be called at runtime.\n *\n * @example\n * ```tsx\n * $effect(() => {\n * console.log('count changed:', count)\n * })\n * ```\n *\n * @public\n */\nexport function $effect(_fn: () => void | (() => void)): void {\n // This function is never called at runtime - the compiler transforms it\n throw new Error('$effect() is a compiler macro and should be transformed at compile time')\n}\n"]}
1
+ {"version":3,"sources":["../src/store.ts","../src/index.ts"],"names":["createSignal","signals"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @fileoverview Deep reactive store implementation for Fict.\n *\n * $store creates a deeply reactive proxy that tracks property access at the path level.\n * Unlike $state (which is shallow), $store allows direct mutation of nested properties.\n *\n * @example\n * ```typescript\n * const user = $store({ name: 'Alice', address: { city: 'London' } })\n * user.address.city = 'Paris' // Fine-grained reactive update\n * ```\n */\n\nimport { createSignal, type Signal } from '@fictjs/runtime/advanced'\n\n/** Function type for bound methods */\ntype AnyFn = (...args: unknown[]) => unknown\n\n/** Cache entry for bound methods to preserve identity */\ninterface BoundMethodEntry {\n ref: AnyFn\n bound: AnyFn\n}\n\n/** Type for objects with indexable properties */\ntype IndexableObject = Record<string | symbol, unknown>\n\n/** Cache of proxied objects to avoid duplicate proxies */\nconst PROXY_CACHE = new WeakMap<object, unknown>()\n\n/** Cache of signals per object property */\nconst SIGNAL_CACHE = new WeakMap<object, Record<string | symbol, Signal<unknown>>>()\n\n/** Cache of bound methods to preserve function identity across reads */\nconst BOUND_METHOD_CACHE = new WeakMap<object, Map<string | symbol, BoundMethodEntry>>()\n\n/** Special key for tracking iteration (Object.keys, for-in, etc.) */\nconst ITERATE_KEY = Symbol('iterate')\n\n/**\n * Get or create a signal for a specific property on a target object.\n * @internal\n */\nfunction getSignal(target: object, prop: string | symbol): Signal<unknown> {\n let signals = SIGNAL_CACHE.get(target)\n if (!signals) {\n signals = {}\n SIGNAL_CACHE.set(target, signals)\n }\n if (!signals[prop]) {\n const initial = prop === ITERATE_KEY ? 0 : (target as IndexableObject)[prop]\n signals[prop] = createSignal(initial)\n }\n return signals[prop]\n}\n\n/**\n * Trigger iteration signal to notify consumers that keys have changed.\n * @internal\n */\nfunction triggerIteration(target: object): void {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[ITERATE_KEY]) {\n const current = signals[ITERATE_KEY]() as number\n signals[ITERATE_KEY](current + 1)\n }\n}\n\n/**\n * Create a deep reactive store using Proxy.\n *\n * Unlike `$state` (which is shallow and compiler-transformed), `$store` provides:\n * - **Deep reactivity**: Nested objects are automatically wrapped in proxies\n * - **Direct mutation**: Modify properties directly without spread operators\n * - **Path-level tracking**: Only components reading changed paths re-render\n *\n * @param initialValue - The initial object to make reactive\n * @returns A reactive proxy of the object\n *\n * @example\n * ```tsx\n * import { $store } from 'fict'\n *\n * const form = $store({\n * user: { name: '', email: '' },\n * settings: { theme: 'light' }\n * })\n *\n * // Direct mutation works\n * form.user.name = 'Alice'\n *\n * // In JSX - only updates when form.user.name changes\n * <input value={form.user.name} />\n * ```\n *\n * @public\n */\nexport function $store<T extends object>(initialValue: T): T {\n if (typeof initialValue !== 'object' || initialValue === null) {\n return initialValue\n }\n\n if (PROXY_CACHE.has(initialValue)) {\n return PROXY_CACHE.get(initialValue) as T\n }\n\n const proxy = new Proxy(initialValue, {\n get(target, prop, receiver) {\n // Always touch the signal so reference changes to this property are tracked,\n // even if the value is an object we proxy further.\n const signal = getSignal(target, prop)\n const trackedValue = signal()\n\n const currentValue = Reflect.get(target, prop, receiver ?? proxy)\n if (currentValue !== trackedValue) {\n // If the value has changed (e.g. via direct mutation of the underlying object not via proxy),\n // we update the signal to keep it in sync.\n // Note: This is a bit of a heuristic. Ideally all mutations go through proxy.\n signal(currentValue)\n }\n\n if (typeof currentValue === 'function') {\n let boundMethods = BOUND_METHOD_CACHE.get(target)\n if (!boundMethods) {\n boundMethods = new Map()\n BOUND_METHOD_CACHE.set(target, boundMethods)\n }\n const cached = boundMethods.get(prop)\n if (cached && cached.ref === currentValue) {\n return cached.bound\n }\n\n const bound = (currentValue as AnyFn).bind(receiver ?? proxy)\n boundMethods.set(prop, { ref: currentValue as AnyFn, bound })\n return bound\n }\n {\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n if (boundMethods.size === 0) {\n BOUND_METHOD_CACHE.delete(target)\n }\n }\n }\n\n // If the value is an object/array, we recursively wrap it in a store\n if (typeof currentValue === 'object' && currentValue !== null) {\n return $store(currentValue as Record<string, unknown>)\n }\n\n // For primitives (and functions), we return the signal value (which tracks the read)\n return currentValue\n },\n\n set(target, prop, newValue, receiver) {\n const oldLength = Array.isArray(target) && prop === 'length' ? target.length : undefined\n const oldValue = Reflect.get(target, prop, receiver)\n const hadKey = Object.prototype.hasOwnProperty.call(target, prop)\n\n // If value hasn't changed, do nothing\n if (oldValue === newValue && hadKey) {\n return true\n }\n\n const result = Reflect.set(target, prop, newValue, receiver)\n\n // IMPORTANT: Clear bound method cache BEFORE updating the signal\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n if (boundMethods.size === 0) {\n BOUND_METHOD_CACHE.delete(target)\n }\n }\n\n // Update the signal if it exists\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[prop]) {\n signals[prop](newValue)\n }\n\n // If new property, trigger iteration update\n if (!hadKey) {\n triggerIteration(target)\n }\n\n // Ensure array length subscribers are notified even if the native push/pop\n // doesn't trigger a separate set trap for \"length\" (defensive).\n if (Array.isArray(target) && prop !== 'length') {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals.length) {\n signals.length(target.length)\n }\n }\n\n // If it's an array and length changed implicitly, we might need to handle it.\n if (Array.isArray(target) && prop === 'length') {\n const nextLength = target.length\n if (typeof oldLength === 'number' && nextLength < oldLength) {\n const signals = SIGNAL_CACHE.get(target)\n if (signals) {\n for (let i = nextLength; i < oldLength; i += 1) {\n const key = String(i)\n if (signals[key]) {\n signals[key](undefined)\n }\n }\n }\n }\n triggerIteration(target)\n }\n\n return result\n },\n\n deleteProperty(target, prop) {\n const hadKey = Object.prototype.hasOwnProperty.call(target, prop)\n const result = Reflect.deleteProperty(target, prop)\n\n if (result && hadKey) {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[prop]) {\n signals[prop](undefined)\n }\n\n // Clear bound method cache\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n if (boundMethods.size === 0) {\n BOUND_METHOD_CACHE.delete(target)\n }\n }\n\n triggerIteration(target)\n }\n\n return result\n },\n\n ownKeys(target) {\n getSignal(target, ITERATE_KEY)()\n return Reflect.ownKeys(target)\n },\n\n has(target, prop) {\n getSignal(target, prop)()\n return Reflect.has(target, prop)\n },\n })\n\n PROXY_CACHE.set(initialValue, proxy)\n return proxy\n}\n","/**\n * @fileoverview Fict Framework - Complete API\n *\n * This is the main entry point for the Fict framework.\n *\n * ## Recommended Import Pattern (v1.0+)\n *\n * ```typescript\n * // Core public API (most users need only this)\n * // Use $state in components (compiler-transformed)\n * // Use $store for cross-component shared state\n * import { $store, render } from 'fict'\n *\n * // Async utilities\n * import { resource, lazy } from 'fict/plus'\n *\n * // Advanced APIs (escape hatches, library authors)\n * import { createSignal, createContext, createScope } from 'fict/advanced'\n * ```\n *\n * ## State Management Guide\n *\n * | Use Case | API |\n * |----------|-----|\n * | Component-local state | `$state` (compiler-transformed) |\n * | Derived values / side effects | JS + auto-derived + `$effect` |\n * | Cross-component (large objects, deep mutation) | `$store` |\n * | Cross-component (scalar/lightweight, library-level) | `createSignal` (advanced) |\n * | Cross-component (subtree scope, SSR isolation) | `Context` (advanced) |\n *\n * @public\n * @packageDocumentation\n */\n\n// Re-export everything from runtime\nexport * from '@fictjs/runtime'\n\n// Re-export commonly used advanced APIs for convenience\nexport { createSelector, createScope, runInScope } from '@fictjs/runtime/advanced'\n\n// ============================================================================\n// Convenience Aliases\n// ============================================================================\n\n/**\n * Alias for createMemo.\n * Creates a memoized value that only recomputes when dependencies change.\n *\n * @example\n * ```tsx\n * const fullName = $memo(() => firstName + ' ' + lastName)\n * ```\n *\n * @public\n */\nexport { createMemo as $memo } from '@fictjs/runtime'\n\n// ============================================================================\n// Deep Reactive Store (Proxy-based)\n// ============================================================================\n\n/**\n * Create a deep reactive store using Proxy.\n * Unlike createStore, $store allows direct mutation.\n *\n * @example\n * ```tsx\n * const user = $store({ name: 'Alice', address: { city: 'Beijing' } })\n * user.name = 'Bob' // Reactive update\n * user.address.city = 'Shanghai' // Deep reactive\n * ```\n *\n * @public\n */\nexport { $store } from './store'\n\n// ============================================================================\n// Compiler Macros (transformed at compile time)\n// ============================================================================\n\n/**\n * Compiler macro for reactive state.\n * This is transformed at compile time and should never be called at runtime.\n *\n * @example\n * ```tsx\n * let count = $state(0)\n * count++ // Reactive update\n * ```\n *\n * @public\n */\nexport function $state<T>(_initialValue: T): T {\n // This function is never called at runtime - the compiler transforms it\n throw new Error('$state() is a compiler macro and should be transformed at compile time')\n}\n\n/**\n * Compiler macro for reactive effects.\n * This is transformed at compile time and should never be called at runtime.\n *\n * @example\n * ```tsx\n * $effect(() => {\n * console.log('count changed:', count)\n * })\n * ```\n *\n * @public\n */\nexport function $effect(_fn: () => void | (() => void)): void {\n // This function is never called at runtime - the compiler transforms it\n throw new Error('$effect() is a compiler macro and should be transformed at compile time')\n}\n"]}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { $store } from './chunk-QPBAYI4X.js';
1
+ export { $store } from './chunk-YSNNU4OE.js';
2
2
  export * from '@fictjs/runtime';
3
3
  export { createMemo as $memo } from '@fictjs/runtime';
4
4
  export { createScope, createSelector, runInScope } from '@fictjs/runtime/advanced';
package/dist/plus.cjs CHANGED
@@ -56,12 +56,22 @@ function $store(initialValue) {
56
56
  boundMethods.set(prop, { ref: currentValue, bound });
57
57
  return bound;
58
58
  }
59
+ {
60
+ const boundMethods = BOUND_METHOD_CACHE.get(target);
61
+ if (boundMethods && boundMethods.has(prop)) {
62
+ boundMethods.delete(prop);
63
+ if (boundMethods.size === 0) {
64
+ BOUND_METHOD_CACHE.delete(target);
65
+ }
66
+ }
67
+ }
59
68
  if (typeof currentValue === "object" && currentValue !== null) {
60
69
  return $store(currentValue);
61
70
  }
62
71
  return currentValue;
63
72
  },
64
73
  set(target, prop, newValue, receiver) {
74
+ const oldLength = Array.isArray(target) && prop === "length" ? target.length : void 0;
65
75
  const oldValue = Reflect.get(target, prop, receiver);
66
76
  const hadKey = Object.prototype.hasOwnProperty.call(target, prop);
67
77
  if (oldValue === newValue && hadKey) {
@@ -71,6 +81,9 @@ function $store(initialValue) {
71
81
  const boundMethods = BOUND_METHOD_CACHE.get(target);
72
82
  if (boundMethods && boundMethods.has(prop)) {
73
83
  boundMethods.delete(prop);
84
+ if (boundMethods.size === 0) {
85
+ BOUND_METHOD_CACHE.delete(target);
86
+ }
74
87
  }
75
88
  const signals = SIGNAL_CACHE.get(target);
76
89
  if (signals && signals[prop]) {
@@ -86,6 +99,18 @@ function $store(initialValue) {
86
99
  }
87
100
  }
88
101
  if (Array.isArray(target) && prop === "length") {
102
+ const nextLength = target.length;
103
+ if (typeof oldLength === "number" && nextLength < oldLength) {
104
+ const signals2 = SIGNAL_CACHE.get(target);
105
+ if (signals2) {
106
+ for (let i = nextLength; i < oldLength; i += 1) {
107
+ const key = String(i);
108
+ if (signals2[key]) {
109
+ signals2[key](void 0);
110
+ }
111
+ }
112
+ }
113
+ }
89
114
  triggerIteration(target);
90
115
  }
91
116
  return result;
@@ -101,6 +126,9 @@ function $store(initialValue) {
101
126
  const boundMethods = BOUND_METHOD_CACHE.get(target);
102
127
  if (boundMethods && boundMethods.has(prop)) {
103
128
  boundMethods.delete(prop);
129
+ if (boundMethods.size === 0) {
130
+ BOUND_METHOD_CACHE.delete(target);
131
+ }
104
132
  }
105
133
  triggerIteration(target);
106
134
  }
@@ -164,6 +192,7 @@ function resource(optionsOrFetcher) {
164
192
  generation: 0,
165
193
  expiresAt: void 0,
166
194
  inFlight: void 0,
195
+ inFlightArgs: void 0,
167
196
  controller: void 0
168
197
  };
169
198
  cache.set(key, state);
@@ -184,6 +213,9 @@ function resource(optionsOrFetcher) {
184
213
  entry.expiresAt = Number.isFinite(resolvedCacheOptions.ttlMs) ? Date.now() + resolvedCacheOptions.ttlMs : void 0;
185
214
  };
186
215
  const startFetch = (entry, key, args, isRevalidating = false) => {
216
+ if (entry.inFlight && entry.inFlightArgs === args) {
217
+ return;
218
+ }
187
219
  entry.controller?.abort();
188
220
  entry.inFlight = void 0;
189
221
  const controller = new AbortController();
@@ -225,9 +257,11 @@ function resource(optionsOrFetcher) {
225
257
  }
226
258
  }).finally(() => {
227
259
  entry.inFlight = void 0;
260
+ entry.inFlightArgs = void 0;
228
261
  entry.controller = void 0;
229
262
  });
230
263
  entry.inFlight = fetchPromise;
264
+ entry.inFlightArgs = args;
231
265
  runtime.onCleanup(() => {
232
266
  if (resolvedCacheOptions.mode === "none") {
233
267
  controller.abort();
@@ -263,6 +297,32 @@ function resource(optionsOrFetcher) {
263
297
  startFetch(entry, key, args);
264
298
  }
265
299
  };
300
+ const mutate = (argsAccessor, value, options) => {
301
+ const args = readArgs(argsAccessor);
302
+ const key = options?.key ?? computeKey(args);
303
+ const entry = ensureEntry(key);
304
+ const prevValue = entry.data();
305
+ const nextValue = typeof value === "function" ? value(prevValue) : value;
306
+ entry.controller?.abort();
307
+ entry.inFlight = void 0;
308
+ entry.inFlightArgs = void 0;
309
+ entry.generation += 1;
310
+ entry.data(nextValue);
311
+ entry.hasValue = true;
312
+ entry.status = "success";
313
+ entry.loading(false);
314
+ entry.error(void 0);
315
+ markExpiry(entry);
316
+ entry.lastArgs = args;
317
+ entry.lastVersion = entry.version();
318
+ if (entry.pendingToken) {
319
+ entry.pendingToken.resolve();
320
+ entry.pendingToken = null;
321
+ }
322
+ if (options?.revalidate) {
323
+ entry.version(entry.version() + 1);
324
+ }
325
+ };
266
326
  return {
267
327
  read(argsAccessor) {
268
328
  const entryRef = advanced.createSignal(null);
@@ -320,7 +380,8 @@ function resource(optionsOrFetcher) {
320
380
  };
321
381
  },
322
382
  invalidate,
323
- prefetch
383
+ prefetch,
384
+ mutate
324
385
  };
325
386
  }
326
387
  function lazy(loader, options = {}) {
package/dist/plus.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/store.ts","../src/resource.ts","../src/lazy.ts"],"names":["createSignal","signals","createSuspenseToken","onCleanup","entry","createEffect"],"mappings":"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","file":"plus.cjs","sourcesContent":["/**\n * @fileoverview Deep reactive store implementation for Fict.\n *\n * $store creates a deeply reactive proxy that tracks property access at the path level.\n * Unlike $state (which is shallow), $store allows direct mutation of nested properties.\n *\n * @example\n * ```typescript\n * const user = $store({ name: 'Alice', address: { city: 'London' } })\n * user.address.city = 'Paris' // Fine-grained reactive update\n * ```\n */\n\nimport { createSignal, type Signal } from '@fictjs/runtime/advanced'\n\n/** Function type for bound methods */\ntype AnyFn = (...args: unknown[]) => unknown\n\n/** Cache entry for bound methods to preserve identity */\ninterface BoundMethodEntry {\n ref: AnyFn\n bound: AnyFn\n}\n\n/** Type for objects with indexable properties */\ntype IndexableObject = Record<string | symbol, unknown>\n\n/** Cache of proxied objects to avoid duplicate proxies */\nconst PROXY_CACHE = new WeakMap<object, unknown>()\n\n/** Cache of signals per object property */\nconst SIGNAL_CACHE = new WeakMap<object, Record<string | symbol, Signal<unknown>>>()\n\n/** Cache of bound methods to preserve function identity across reads */\nconst BOUND_METHOD_CACHE = new WeakMap<object, Map<string | symbol, BoundMethodEntry>>()\n\n/** Special key for tracking iteration (Object.keys, for-in, etc.) */\nconst ITERATE_KEY = Symbol('iterate')\n\n/**\n * Get or create a signal for a specific property on a target object.\n * @internal\n */\nfunction getSignal(target: object, prop: string | symbol): Signal<unknown> {\n let signals = SIGNAL_CACHE.get(target)\n if (!signals) {\n signals = {}\n SIGNAL_CACHE.set(target, signals)\n }\n if (!signals[prop]) {\n const initial = prop === ITERATE_KEY ? 0 : (target as IndexableObject)[prop]\n signals[prop] = createSignal(initial)\n }\n return signals[prop]\n}\n\n/**\n * Trigger iteration signal to notify consumers that keys have changed.\n * @internal\n */\nfunction triggerIteration(target: object): void {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[ITERATE_KEY]) {\n const current = signals[ITERATE_KEY]() as number\n signals[ITERATE_KEY](current + 1)\n }\n}\n\n/**\n * Create a deep reactive store using Proxy.\n *\n * Unlike `$state` (which is shallow and compiler-transformed), `$store` provides:\n * - **Deep reactivity**: Nested objects are automatically wrapped in proxies\n * - **Direct mutation**: Modify properties directly without spread operators\n * - **Path-level tracking**: Only components reading changed paths re-render\n *\n * @param initialValue - The initial object to make reactive\n * @returns A reactive proxy of the object\n *\n * @example\n * ```tsx\n * import { $store } from 'fict'\n *\n * const form = $store({\n * user: { name: '', email: '' },\n * settings: { theme: 'light' }\n * })\n *\n * // Direct mutation works\n * form.user.name = 'Alice'\n *\n * // In JSX - only updates when form.user.name changes\n * <input value={form.user.name} />\n * ```\n *\n * @public\n */\nexport function $store<T extends object>(initialValue: T): T {\n if (typeof initialValue !== 'object' || initialValue === null) {\n return initialValue\n }\n\n if (PROXY_CACHE.has(initialValue)) {\n return PROXY_CACHE.get(initialValue) as T\n }\n\n const proxy = new Proxy(initialValue, {\n get(target, prop, receiver) {\n // Always touch the signal so reference changes to this property are tracked,\n // even if the value is an object we proxy further.\n const signal = getSignal(target, prop)\n const trackedValue = signal()\n\n const currentValue = Reflect.get(target, prop, receiver ?? proxy)\n if (currentValue !== trackedValue) {\n // If the value has changed (e.g. via direct mutation of the underlying object not via proxy),\n // we update the signal to keep it in sync.\n // Note: This is a bit of a heuristic. Ideally all mutations go through proxy.\n signal(currentValue)\n }\n\n if (typeof currentValue === 'function') {\n let boundMethods = BOUND_METHOD_CACHE.get(target)\n if (!boundMethods) {\n boundMethods = new Map()\n BOUND_METHOD_CACHE.set(target, boundMethods)\n }\n const cached = boundMethods.get(prop)\n if (cached && cached.ref === currentValue) {\n return cached.bound\n }\n\n const bound = (currentValue as AnyFn).bind(receiver ?? proxy)\n boundMethods.set(prop, { ref: currentValue as AnyFn, bound })\n return bound\n }\n\n // If the value is an object/array, we recursively wrap it in a store\n if (typeof currentValue === 'object' && currentValue !== null) {\n return $store(currentValue as Record<string, unknown>)\n }\n\n // For primitives (and functions), we return the signal value (which tracks the read)\n return currentValue\n },\n\n set(target, prop, newValue, receiver) {\n const oldValue = Reflect.get(target, prop, receiver)\n const hadKey = Object.prototype.hasOwnProperty.call(target, prop)\n\n // If value hasn't changed, do nothing\n if (oldValue === newValue && hadKey) {\n return true\n }\n\n const result = Reflect.set(target, prop, newValue, receiver)\n\n // IMPORTANT: Clear bound method cache BEFORE updating the signal\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n }\n\n // Update the signal if it exists\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[prop]) {\n signals[prop](newValue)\n }\n\n // If new property, trigger iteration update\n if (!hadKey) {\n triggerIteration(target)\n }\n\n // Ensure array length subscribers are notified even if the native push/pop\n // doesn't trigger a separate set trap for \"length\" (defensive).\n if (Array.isArray(target) && prop !== 'length') {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals.length) {\n signals.length(target.length)\n }\n }\n\n // If it's an array and length changed implicitly, we might need to handle it.\n // But usually 'length' is set explicitly or handled by the runtime.\n if (Array.isArray(target) && prop === 'length') {\n triggerIteration(target)\n }\n\n return result\n },\n\n deleteProperty(target, prop) {\n const hadKey = Object.prototype.hasOwnProperty.call(target, prop)\n const result = Reflect.deleteProperty(target, prop)\n\n if (result && hadKey) {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[prop]) {\n signals[prop](undefined)\n }\n\n // Clear bound method cache\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n }\n\n triggerIteration(target)\n }\n\n return result\n },\n\n ownKeys(target) {\n getSignal(target, ITERATE_KEY)()\n return Reflect.ownKeys(target)\n },\n\n has(target, prop) {\n getSignal(target, prop)()\n return Reflect.has(target, prop)\n },\n })\n\n PROXY_CACHE.set(initialValue, proxy)\n return proxy\n}\n","/**\n * @fileoverview Async data fetching with caching and Suspense support.\n *\n * The `resource` function creates a reactive data fetcher that:\n * - Automatically cancels in-flight requests when args change\n * - Supports Suspense for loading states\n * - Provides caching with TTL and stale-while-revalidate\n * - Handles errors gracefully\n */\n\nimport { createEffect, onCleanup, createSuspenseToken } from '@fictjs/runtime'\nimport { createSignal } from '@fictjs/runtime/advanced'\n\n/**\n * The result of reading a resource.\n *\n * @typeParam T - The type of data returned by the fetcher\n */\nexport interface ResourceResult<T> {\n /** The fetched data, or undefined if not yet loaded or on error */\n readonly data: T | undefined\n /** Whether the resource is currently loading (initial fetch or refetch) */\n readonly loading: boolean\n /**\n * Any error that occurred during fetching.\n * Type is unknown since errors can be any value in JavaScript.\n */\n readonly error: unknown\n /** Manually trigger a refetch of the resource */\n refresh: () => void\n}\n\n/**\n * Cache configuration options for a resource.\n */\nexport interface ResourceCacheOptions {\n /**\n * Caching mode:\n * - `'memory'`: Cache responses in memory (default)\n * - `'none'`: No caching, always refetch\n * @default 'memory'\n */\n mode?: 'memory' | 'none'\n\n /**\n * Time-to-live in milliseconds before cached data is considered stale.\n * @default Infinity\n */\n ttlMs?: number\n\n /**\n * If true, return stale cached data immediately while refetching in background.\n * @default false\n */\n staleWhileRevalidate?: boolean\n\n /**\n * If true, cache error responses as well.\n * @default false\n */\n cacheErrors?: boolean\n}\n\n/**\n * Configuration options for creating a resource.\n *\n * @typeParam T - The type of data returned by the fetcher\n * @typeParam Args - The type of arguments passed to the fetcher\n */\nexport interface ResourceOptions<T, Args> {\n /**\n * Custom cache key. Can be a static value or a function that computes\n * the key from the args. If not provided, args are used as the key.\n */\n key?: unknown | ((args: Args) => unknown)\n\n /**\n * The fetcher function that performs the async data retrieval.\n * Receives an AbortController signal for cancellation support.\n */\n fetch: (ctx: { signal: AbortSignal }, args: Args) => Promise<T>\n\n /**\n * If true, the resource will throw a Suspense token while loading,\n * enabling React-like Suspense boundaries.\n * @default false\n */\n suspense?: boolean\n\n /**\n * Cache configuration options.\n */\n cache?: ResourceCacheOptions\n\n /**\n * A value or reactive getter that, when changed, resets the resource.\n * Useful for clearing cache when certain conditions change.\n */\n reset?: unknown | (() => unknown)\n}\n\n/**\n * Return type of the resource factory.\n *\n * @typeParam T - The type of data returned by the fetcher\n * @typeParam Args - The type of arguments passed to the fetcher\n */\nexport interface Resource<T, Args> {\n /**\n * Read the resource data, triggering a fetch if needed.\n * Can accept static args or a reactive getter.\n *\n * @param argsAccessor - Arguments or a getter returning arguments\n */\n read(argsAccessor: (() => Args) | Args): ResourceResult<T>\n\n /**\n * Invalidate cached data, causing the next read to refetch.\n *\n * @param key - Optional specific key to invalidate. If omitted, invalidates all.\n */\n invalidate(key?: unknown): void\n\n /**\n * Prefetch data without reading it. Useful for eager loading.\n *\n * @param args - Arguments to pass to the fetcher\n * @param keyOverride - Optional cache key override\n */\n prefetch(args: Args, keyOverride?: unknown): void\n}\n\n/**\n * Resource status values for tracking fetch lifecycle.\n * @internal\n */\nexport type ResourceStatus = 'idle' | 'pending' | 'success' | 'error'\n\n/**\n * Internal cache entry for a resource.\n * Tracks the reactive state and metadata for a single cached fetch.\n *\n * @typeParam T - The type of data returned by the fetcher\n * @typeParam Args - The type of arguments passed to the fetcher\n * @internal\n */\ninterface ResourceEntry<T, Args> {\n /** Reactive signal for the fetched data */\n data: ReturnType<typeof createSignal<T | undefined>>\n /** Reactive signal for loading state */\n loading: ReturnType<typeof createSignal<boolean>>\n /** Reactive signal for error state */\n error: ReturnType<typeof createSignal<unknown>>\n /** Version counter for invalidation */\n version: ReturnType<typeof createSignal<number>>\n /** Suspense token when using suspense mode */\n pendingToken: ReturnType<typeof createSuspenseToken> | null\n /** Last used arguments for change detection */\n lastArgs: Args | undefined\n /** Last seen version for change detection */\n lastVersion: number\n /** Last reset token value for change detection */\n lastReset: unknown\n /** Whether we have a valid cached value */\n hasValue: boolean\n /** Current fetch status */\n status: ResourceStatus\n /** Generation counter to handle race conditions */\n generation: number\n /** Timestamp when the cached value expires */\n expiresAt: number | undefined\n /** Currently in-flight fetch promise */\n inFlight: Promise<void> | undefined\n /** AbortController for cancelling in-flight requests */\n controller: AbortController | undefined\n}\n\nconst defaultCacheOptions: Required<ResourceCacheOptions> = {\n mode: 'memory',\n ttlMs: Number.POSITIVE_INFINITY,\n staleWhileRevalidate: false,\n cacheErrors: false,\n}\n\n/**\n * Create a reactive async data resource.\n *\n * Resources handle async data fetching with automatic caching, cancellation,\n * and optional Suspense integration.\n *\n * @param optionsOrFetcher - A fetcher function or full configuration object\n * @returns A resource factory with read, invalidate, and prefetch methods\n *\n * @example\n * ```tsx\n * import { resource } from 'fict'\n *\n * // Simple fetcher\n * const userResource = resource(\n * ({ signal }, userId: string) =>\n * fetch(`/api/users/${userId}`, { signal }).then(r => r.json())\n * )\n *\n * // With full options\n * const postsResource = resource({\n * fetch: ({ signal }, userId: string) =>\n * fetch(`/api/users/${userId}/posts`, { signal }).then(r => r.json()),\n * suspense: true,\n * cache: {\n * ttlMs: 60_000,\n * staleWhileRevalidate: true,\n * },\n * })\n *\n * // Usage in component\n * function UserProfile({ userId }: { userId: string }) {\n * const { data, loading, error, refresh } = userResource.read(() => userId)\n *\n * if (loading) return <Spinner />\n * if (error) return <ErrorMessage error={error} />\n * return <div>{data.name}</div>\n * }\n * ```\n *\n * @public\n */\nexport function resource<T, Args = void>(\n optionsOrFetcher:\n | ((ctx: { signal: AbortSignal }, args: Args) => Promise<T>)\n | ResourceOptions<T, Args>,\n): Resource<T, Args> {\n const fetcher = typeof optionsOrFetcher === 'function' ? optionsOrFetcher : optionsOrFetcher.fetch\n const useSuspense = typeof optionsOrFetcher === 'object' && !!optionsOrFetcher.suspense\n const cacheOptions: ResourceCacheOptions =\n typeof optionsOrFetcher === 'object' ? (optionsOrFetcher.cache ?? {}) : {}\n const resolvedCacheOptions = { ...defaultCacheOptions, ...cacheOptions }\n const cache = new Map<unknown, ResourceEntry<T, Args>>()\n\n const readArgs = (argsAccessor: (() => Args) | Args): Args =>\n typeof argsAccessor === 'function' ? (argsAccessor as () => Args)() : argsAccessor\n\n const computeKey = (argsAccessor: (() => Args) | Args): unknown => {\n const argsValue = readArgs(argsAccessor)\n if (typeof optionsOrFetcher === 'object' && optionsOrFetcher.key !== undefined) {\n const key = optionsOrFetcher.key\n return typeof key === 'function' ? (key as (args: Args) => unknown)(argsValue) : key\n }\n return argsValue\n }\n\n const readResetToken = (): unknown => {\n if (typeof optionsOrFetcher !== 'object') return undefined\n const reset = optionsOrFetcher.reset\n if (typeof reset === 'function' && (reset as () => unknown).length === 0) {\n return (reset as () => unknown)()\n }\n return reset\n }\n\n const ensureEntry = (key: unknown): ResourceEntry<T, Args> => {\n let state = cache.get(key)\n if (!state) {\n state = {\n data: createSignal<T | undefined>(undefined),\n loading: createSignal<boolean>(false),\n error: createSignal<unknown>(undefined),\n version: createSignal(0),\n pendingToken: null,\n lastArgs: undefined,\n lastVersion: -1,\n lastReset: undefined,\n hasValue: false,\n status: 'idle',\n generation: 0,\n expiresAt: undefined,\n inFlight: undefined,\n controller: undefined,\n }\n cache.set(key, state)\n }\n return state!\n }\n\n const isExpired = (entry: ResourceEntry<T, Args>): boolean => {\n if (resolvedCacheOptions.mode === 'none') return true\n if (!Number.isFinite(resolvedCacheOptions.ttlMs)) return false\n if (entry.expiresAt === undefined) return false\n return entry.expiresAt < Date.now()\n }\n\n const markExpiry = (entry: ResourceEntry<T, Args>) => {\n if (resolvedCacheOptions.mode === 'none') {\n entry.expiresAt = Date.now() - 1\n return\n }\n entry.expiresAt = Number.isFinite(resolvedCacheOptions.ttlMs)\n ? Date.now() + resolvedCacheOptions.ttlMs\n : undefined\n }\n\n const startFetch = (\n entry: ResourceEntry<T, Args>,\n key: unknown,\n args: Args,\n isRevalidating = false,\n ) => {\n entry.controller?.abort()\n entry.inFlight = undefined\n const controller = new AbortController()\n entry.controller = controller\n entry.status = 'pending'\n // For stale-while-revalidate: don't show loading if we already have data to display\n if (!isRevalidating) {\n entry.loading(true)\n }\n entry.error(undefined)\n entry.generation += 1\n const currentGen = entry.generation\n\n const shouldSuspend = useSuspense && !entry.hasValue\n entry.pendingToken = shouldSuspend ? createSuspenseToken() : null\n\n const fetchPromise = fetcher({ signal: controller.signal }, args)\n .then(res => {\n if (controller.signal.aborted || entry.generation !== currentGen) return\n entry.data(res)\n entry.hasValue = true\n entry.status = 'success'\n entry.loading(false)\n markExpiry(entry)\n if (entry.pendingToken) {\n entry.pendingToken.resolve()\n entry.pendingToken = null\n }\n })\n .catch(err => {\n if (controller.signal.aborted || entry.generation !== currentGen) return\n entry.error(err)\n entry.status = 'error'\n entry.loading(false)\n if (resolvedCacheOptions.cacheErrors) {\n markExpiry(entry)\n } else {\n entry.expiresAt = Date.now() - 1\n entry.hasValue = false\n }\n if (entry.pendingToken) {\n entry.pendingToken.reject(err)\n entry.pendingToken = null\n }\n })\n .finally(() => {\n entry.inFlight = undefined\n entry.controller = undefined\n })\n\n entry.inFlight = fetchPromise\n\n onCleanup(() => {\n if (resolvedCacheOptions.mode === 'none') {\n controller.abort()\n cache.delete(key)\n }\n })\n }\n\n const invalidate = (key?: unknown) => {\n if (key === undefined) {\n cache.forEach(entry => {\n entry.controller?.abort()\n entry.version(entry.version() + 1)\n entry.expiresAt = Date.now() - 1\n })\n cache.clear()\n return\n }\n const entry = cache.get(key)\n if (entry) {\n entry.controller?.abort()\n entry.version(entry.version() + 1)\n entry.expiresAt = Date.now() - 1\n cache.delete(key)\n }\n }\n\n const prefetch = (args: Args, keyOverride?: unknown) => {\n const key = keyOverride ?? computeKey(args)\n const entry = ensureEntry(key)\n const usableData = entry.hasValue && !isExpired(entry)\n if (!usableData) {\n entry.lastArgs = args\n entry.lastVersion = entry.version()\n startFetch(entry, key, args)\n }\n }\n\n return {\n read(argsAccessor: (() => Args) | Args): ResourceResult<T> {\n const entryRef = createSignal<ResourceEntry<T, Args> | null>(null)\n\n createEffect(() => {\n const key = computeKey(argsAccessor)\n const entry = ensureEntry(key)\n entryRef(entry)\n const args = readArgs(argsAccessor)\n const currentVersion = entry.version()\n const expired = isExpired(entry)\n const argsChanged = entry.lastArgs !== args\n const versionChanged = entry.lastVersion !== currentVersion\n const resetToken = readResetToken()\n const resetChanged = entry.lastReset !== resetToken\n // For stale-while-revalidate: if we have cached data, don't treat expired as requiring immediate refetch\n // We'll handle the revalidation separately to show stale data without loading state\n const canUseStaleData =\n resolvedCacheOptions.staleWhileRevalidate && entry.hasValue && expired\n const shouldRefetch =\n (expired && !canUseStaleData) ||\n argsChanged ||\n versionChanged ||\n resetChanged ||\n (entry.status === 'error' && !resolvedCacheOptions.cacheErrors)\n\n entry.lastArgs = args\n entry.lastVersion = currentVersion\n entry.lastReset = resetToken\n\n if (shouldRefetch) {\n if (entry.inFlight && (argsChanged || versionChanged)) {\n entry.controller?.abort()\n entry.inFlight = undefined\n }\n if (resetChanged) {\n entry.hasValue = false\n entry.expiresAt = Date.now() - 1\n }\n startFetch(entry, key, args as Args)\n } else if (canUseStaleData && entry.inFlight === undefined) {\n // stale-while-revalidate: return stale data immediately, refresh in background\n // Pass isRevalidating=true to avoid showing loading state\n startFetch(entry, key, args as Args, true)\n }\n })\n\n return {\n get data() {\n const entry = entryRef()\n if (!entry) return undefined\n if (useSuspense && entry.pendingToken) {\n throw entry.pendingToken.token\n }\n return entry.data()\n },\n get loading() {\n const entry = entryRef()\n return entry ? entry.loading() : false\n },\n get error() {\n const entry = entryRef()\n return entry ? entry.error() : undefined\n },\n refresh: () => {\n const entry = entryRef()\n if (entry) entry.version(entry.version() + 1)\n },\n }\n },\n invalidate,\n prefetch,\n }\n}\n","/**\n * @fileoverview Lazy component loading with Suspense support.\n *\n * Creates a component that loads its implementation asynchronously,\n * suspending rendering until the module is loaded.\n */\n\nimport { createSuspenseToken } from '@fictjs/runtime'\nimport type { Component } from '@fictjs/runtime'\n\n/** Module shape expected from dynamic imports */\nexport interface LazyModule<TProps extends Record<string, unknown>> {\n default: Component<TProps>\n}\n\n/** Options for lazy loading behavior */\nexport interface LazyOptions {\n /**\n * Maximum number of retry attempts on load failure.\n * Set to 0 to disable retries (default behavior).\n * @default 0\n */\n maxRetries?: number\n\n /**\n * Delay in milliseconds between retry attempts.\n * Uses exponential backoff: delay * 2^(attempt - 1)\n * @default 1000\n */\n retryDelay?: number\n}\n\n/** Extended component with retry capability */\nexport interface LazyComponent<TProps extends Record<string, unknown>> extends Component<TProps> {\n /**\n * Reset the lazy component state, allowing it to retry loading.\n * Useful when used with ErrorBoundary reset functionality.\n */\n reset: () => void\n\n /**\n * Preload the component without rendering it.\n * Returns a promise that resolves when the component is loaded.\n */\n preload: () => Promise<void>\n}\n\n/**\n * Create a lazy component that suspends while loading.\n *\n * @param loader - Function that returns a promise resolving to the component module\n * @param options - Optional configuration for retry behavior\n * @returns A component that suspends during loading and supports retry on failure\n *\n * @example\n * ```tsx\n * import { lazy, Suspense } from 'fict'\n *\n * // Basic usage\n * const LazyChart = lazy(() => import('./Chart'))\n *\n * // With retry options\n * const LazyDashboard = lazy(() => import('./Dashboard'), {\n * maxRetries: 3,\n * retryDelay: 1000\n * })\n *\n * function App() {\n * return (\n * <Suspense fallback={<Loading />}>\n * <LazyChart />\n * </Suspense>\n * )\n * }\n *\n * // Reset on error (with ErrorBoundary)\n * <ErrorBoundary fallback={(err, reset) => (\n * <button onClick={() => { LazyChart.reset(); reset(); }}>Retry</button>\n * )}>\n * <LazyChart />\n * </ErrorBoundary>\n * ```\n *\n * @public\n */\nexport function lazy<TProps extends Record<string, unknown> = Record<string, unknown>>(\n loader: () => Promise<LazyModule<TProps> | { default: Component<TProps> }>,\n options: LazyOptions = {},\n): LazyComponent<TProps> {\n const { maxRetries = 0, retryDelay = 1000 } = options\n\n let loaded: Component<TProps> | null = null\n let loadError: unknown = null\n let loadingPromise: Promise<unknown> | null = null\n let pendingToken: ReturnType<typeof createSuspenseToken> | null = null\n let retryCount = 0\n\n const attemptLoad = (): Promise<void> => {\n return loader()\n .then(mod => {\n loaded = (mod as LazyModule<TProps>).default\n loadError = null\n retryCount = 0\n pendingToken?.resolve()\n })\n .catch((err: unknown) => {\n if (retryCount < maxRetries) {\n retryCount++\n const delay = retryDelay * Math.pow(2, retryCount - 1)\n return new Promise<void>(resolve => {\n setTimeout(() => {\n resolve(attemptLoad())\n }, delay)\n })\n }\n loadError = err\n pendingToken?.reject(err)\n return undefined\n })\n .finally(() => {\n loadingPromise = null\n pendingToken = null\n })\n }\n\n const component = ((props: TProps) => {\n if (loaded) {\n return loaded(props)\n }\n if (loadError) {\n throw loadError\n }\n if (!loadingPromise) {\n pendingToken = createSuspenseToken()\n loadingPromise = attemptLoad()\n }\n if (pendingToken) {\n throw pendingToken.token\n }\n // Should never hit if pendingToken exists, but fallback for type safety.\n throw new Error('Lazy component failed to start loading')\n }) as LazyComponent<TProps>\n\n /**\n * Reset the lazy component state, clearing any cached error.\n * Call this before triggering a re-render to retry loading.\n */\n component.reset = () => {\n loadError = null\n loadingPromise = null\n pendingToken = null\n retryCount = 0\n // Note: we don't clear `loaded` - if it was successfully loaded, keep it\n }\n\n /**\n * Preload the component without rendering.\n * Useful for eager loading on route prefetch.\n */\n component.preload = (): Promise<void> => {\n if (loaded) {\n return Promise.resolve()\n }\n if (loadingPromise) {\n return loadingPromise as Promise<void>\n }\n pendingToken = createSuspenseToken()\n loadingPromise = attemptLoad()\n return loadingPromise as Promise<void>\n }\n\n return component\n}\n"]}
1
+ {"version":3,"sources":["../src/store.ts","../src/resource.ts","../src/lazy.ts"],"names":["createSignal","signals","createSuspenseToken","onCleanup","entry","createEffect"],"mappings":"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","file":"plus.cjs","sourcesContent":["/**\n * @fileoverview Deep reactive store implementation for Fict.\n *\n * $store creates a deeply reactive proxy that tracks property access at the path level.\n * Unlike $state (which is shallow), $store allows direct mutation of nested properties.\n *\n * @example\n * ```typescript\n * const user = $store({ name: 'Alice', address: { city: 'London' } })\n * user.address.city = 'Paris' // Fine-grained reactive update\n * ```\n */\n\nimport { createSignal, type Signal } from '@fictjs/runtime/advanced'\n\n/** Function type for bound methods */\ntype AnyFn = (...args: unknown[]) => unknown\n\n/** Cache entry for bound methods to preserve identity */\ninterface BoundMethodEntry {\n ref: AnyFn\n bound: AnyFn\n}\n\n/** Type for objects with indexable properties */\ntype IndexableObject = Record<string | symbol, unknown>\n\n/** Cache of proxied objects to avoid duplicate proxies */\nconst PROXY_CACHE = new WeakMap<object, unknown>()\n\n/** Cache of signals per object property */\nconst SIGNAL_CACHE = new WeakMap<object, Record<string | symbol, Signal<unknown>>>()\n\n/** Cache of bound methods to preserve function identity across reads */\nconst BOUND_METHOD_CACHE = new WeakMap<object, Map<string | symbol, BoundMethodEntry>>()\n\n/** Special key for tracking iteration (Object.keys, for-in, etc.) */\nconst ITERATE_KEY = Symbol('iterate')\n\n/**\n * Get or create a signal for a specific property on a target object.\n * @internal\n */\nfunction getSignal(target: object, prop: string | symbol): Signal<unknown> {\n let signals = SIGNAL_CACHE.get(target)\n if (!signals) {\n signals = {}\n SIGNAL_CACHE.set(target, signals)\n }\n if (!signals[prop]) {\n const initial = prop === ITERATE_KEY ? 0 : (target as IndexableObject)[prop]\n signals[prop] = createSignal(initial)\n }\n return signals[prop]\n}\n\n/**\n * Trigger iteration signal to notify consumers that keys have changed.\n * @internal\n */\nfunction triggerIteration(target: object): void {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[ITERATE_KEY]) {\n const current = signals[ITERATE_KEY]() as number\n signals[ITERATE_KEY](current + 1)\n }\n}\n\n/**\n * Create a deep reactive store using Proxy.\n *\n * Unlike `$state` (which is shallow and compiler-transformed), `$store` provides:\n * - **Deep reactivity**: Nested objects are automatically wrapped in proxies\n * - **Direct mutation**: Modify properties directly without spread operators\n * - **Path-level tracking**: Only components reading changed paths re-render\n *\n * @param initialValue - The initial object to make reactive\n * @returns A reactive proxy of the object\n *\n * @example\n * ```tsx\n * import { $store } from 'fict'\n *\n * const form = $store({\n * user: { name: '', email: '' },\n * settings: { theme: 'light' }\n * })\n *\n * // Direct mutation works\n * form.user.name = 'Alice'\n *\n * // In JSX - only updates when form.user.name changes\n * <input value={form.user.name} />\n * ```\n *\n * @public\n */\nexport function $store<T extends object>(initialValue: T): T {\n if (typeof initialValue !== 'object' || initialValue === null) {\n return initialValue\n }\n\n if (PROXY_CACHE.has(initialValue)) {\n return PROXY_CACHE.get(initialValue) as T\n }\n\n const proxy = new Proxy(initialValue, {\n get(target, prop, receiver) {\n // Always touch the signal so reference changes to this property are tracked,\n // even if the value is an object we proxy further.\n const signal = getSignal(target, prop)\n const trackedValue = signal()\n\n const currentValue = Reflect.get(target, prop, receiver ?? proxy)\n if (currentValue !== trackedValue) {\n // If the value has changed (e.g. via direct mutation of the underlying object not via proxy),\n // we update the signal to keep it in sync.\n // Note: This is a bit of a heuristic. Ideally all mutations go through proxy.\n signal(currentValue)\n }\n\n if (typeof currentValue === 'function') {\n let boundMethods = BOUND_METHOD_CACHE.get(target)\n if (!boundMethods) {\n boundMethods = new Map()\n BOUND_METHOD_CACHE.set(target, boundMethods)\n }\n const cached = boundMethods.get(prop)\n if (cached && cached.ref === currentValue) {\n return cached.bound\n }\n\n const bound = (currentValue as AnyFn).bind(receiver ?? proxy)\n boundMethods.set(prop, { ref: currentValue as AnyFn, bound })\n return bound\n }\n {\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n if (boundMethods.size === 0) {\n BOUND_METHOD_CACHE.delete(target)\n }\n }\n }\n\n // If the value is an object/array, we recursively wrap it in a store\n if (typeof currentValue === 'object' && currentValue !== null) {\n return $store(currentValue as Record<string, unknown>)\n }\n\n // For primitives (and functions), we return the signal value (which tracks the read)\n return currentValue\n },\n\n set(target, prop, newValue, receiver) {\n const oldLength = Array.isArray(target) && prop === 'length' ? target.length : undefined\n const oldValue = Reflect.get(target, prop, receiver)\n const hadKey = Object.prototype.hasOwnProperty.call(target, prop)\n\n // If value hasn't changed, do nothing\n if (oldValue === newValue && hadKey) {\n return true\n }\n\n const result = Reflect.set(target, prop, newValue, receiver)\n\n // IMPORTANT: Clear bound method cache BEFORE updating the signal\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n if (boundMethods.size === 0) {\n BOUND_METHOD_CACHE.delete(target)\n }\n }\n\n // Update the signal if it exists\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[prop]) {\n signals[prop](newValue)\n }\n\n // If new property, trigger iteration update\n if (!hadKey) {\n triggerIteration(target)\n }\n\n // Ensure array length subscribers are notified even if the native push/pop\n // doesn't trigger a separate set trap for \"length\" (defensive).\n if (Array.isArray(target) && prop !== 'length') {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals.length) {\n signals.length(target.length)\n }\n }\n\n // If it's an array and length changed implicitly, we might need to handle it.\n if (Array.isArray(target) && prop === 'length') {\n const nextLength = target.length\n if (typeof oldLength === 'number' && nextLength < oldLength) {\n const signals = SIGNAL_CACHE.get(target)\n if (signals) {\n for (let i = nextLength; i < oldLength; i += 1) {\n const key = String(i)\n if (signals[key]) {\n signals[key](undefined)\n }\n }\n }\n }\n triggerIteration(target)\n }\n\n return result\n },\n\n deleteProperty(target, prop) {\n const hadKey = Object.prototype.hasOwnProperty.call(target, prop)\n const result = Reflect.deleteProperty(target, prop)\n\n if (result && hadKey) {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[prop]) {\n signals[prop](undefined)\n }\n\n // Clear bound method cache\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n if (boundMethods.size === 0) {\n BOUND_METHOD_CACHE.delete(target)\n }\n }\n\n triggerIteration(target)\n }\n\n return result\n },\n\n ownKeys(target) {\n getSignal(target, ITERATE_KEY)()\n return Reflect.ownKeys(target)\n },\n\n has(target, prop) {\n getSignal(target, prop)()\n return Reflect.has(target, prop)\n },\n })\n\n PROXY_CACHE.set(initialValue, proxy)\n return proxy\n}\n","/**\n * @fileoverview Async data fetching with caching and Suspense support.\n *\n * The `resource` function creates a reactive data fetcher that:\n * - Automatically cancels in-flight requests when args change\n * - Supports Suspense for loading states\n * - Provides caching with TTL and stale-while-revalidate\n * - Handles errors gracefully\n */\n\nimport { createEffect, onCleanup, createSuspenseToken } from '@fictjs/runtime'\nimport { createSignal } from '@fictjs/runtime/advanced'\n\n/**\n * The result of reading a resource.\n *\n * @typeParam T - The type of data returned by the fetcher\n */\nexport interface ResourceResult<T> {\n /** The fetched data, or undefined if not yet loaded or on error */\n readonly data: T | undefined\n /** Whether the resource is currently loading (initial fetch or refetch) */\n readonly loading: boolean\n /**\n * Any error that occurred during fetching.\n * Type is unknown since errors can be any value in JavaScript.\n */\n readonly error: unknown\n /** Manually trigger a refetch of the resource */\n refresh: () => void\n}\n\n/**\n * Cache configuration options for a resource.\n */\nexport interface ResourceCacheOptions {\n /**\n * Caching mode:\n * - `'memory'`: Cache responses in memory (default)\n * - `'none'`: No caching, always refetch\n * @default 'memory'\n */\n mode?: 'memory' | 'none'\n\n /**\n * Time-to-live in milliseconds before cached data is considered stale.\n * @default Infinity\n */\n ttlMs?: number\n\n /**\n * If true, return stale cached data immediately while refetching in background.\n * @default false\n */\n staleWhileRevalidate?: boolean\n\n /**\n * If true, cache error responses as well.\n * @default false\n */\n cacheErrors?: boolean\n}\n\n/**\n * Configuration options for creating a resource.\n *\n * @typeParam T - The type of data returned by the fetcher\n * @typeParam Args - The type of arguments passed to the fetcher\n */\nexport interface ResourceOptions<T, Args> {\n /**\n * Custom cache key. Can be a static value or a function that computes\n * the key from the args. If not provided, args are used as the key.\n */\n key?: unknown | ((args: Args) => unknown)\n\n /**\n * The fetcher function that performs the async data retrieval.\n * Receives an AbortController signal for cancellation support.\n */\n fetch: (ctx: { signal: AbortSignal }, args: Args) => Promise<T>\n\n /**\n * If true, the resource will throw a Suspense token while loading,\n * enabling React-like Suspense boundaries.\n * @default false\n */\n suspense?: boolean\n\n /**\n * Cache configuration options.\n */\n cache?: ResourceCacheOptions\n\n /**\n * A value or reactive getter that, when changed, resets the resource.\n * Useful for clearing cache when certain conditions change.\n */\n reset?: unknown | (() => unknown)\n}\n\n/**\n * Return type of the resource factory.\n *\n * @typeParam T - The type of data returned by the fetcher\n * @typeParam Args - The type of arguments passed to the fetcher\n */\nexport interface Resource<T, Args> {\n /**\n * Read the resource data, triggering a fetch if needed.\n * Can accept static args or a reactive getter.\n *\n * @param argsAccessor - Arguments or a getter returning arguments\n */\n read(argsAccessor: (() => Args) | Args): ResourceResult<T>\n\n /**\n * Invalidate cached data, causing the next read to refetch.\n *\n * @param key - Optional specific key to invalidate. If omitted, invalidates all.\n */\n invalidate(key?: unknown): void\n\n /**\n * Prefetch data without reading it. Useful for eager loading.\n *\n * @param args - Arguments to pass to the fetcher\n * @param keyOverride - Optional cache key override\n */\n prefetch(args: Args, keyOverride?: unknown): void\n\n /**\n * Optimistically update cached data for a given args/key.\n *\n * @param argsAccessor - Arguments or a getter returning arguments\n * @param value - New value or updater function\n * @param options - Optional settings (key override, revalidate)\n */\n mutate(\n argsAccessor: (() => Args) | Args,\n value: T | ((prev: T | undefined) => T),\n options?: { key?: unknown; revalidate?: boolean },\n ): void\n}\n\n/**\n * Resource status values for tracking fetch lifecycle.\n * @internal\n */\nexport type ResourceStatus = 'idle' | 'pending' | 'success' | 'error'\n\n/**\n * Internal cache entry for a resource.\n * Tracks the reactive state and metadata for a single cached fetch.\n *\n * @typeParam T - The type of data returned by the fetcher\n * @typeParam Args - The type of arguments passed to the fetcher\n * @internal\n */\ninterface ResourceEntry<T, Args> {\n /** Reactive signal for the fetched data */\n data: ReturnType<typeof createSignal<T | undefined>>\n /** Reactive signal for loading state */\n loading: ReturnType<typeof createSignal<boolean>>\n /** Reactive signal for error state */\n error: ReturnType<typeof createSignal<unknown>>\n /** Version counter for invalidation */\n version: ReturnType<typeof createSignal<number>>\n /** Suspense token when using suspense mode */\n pendingToken: ReturnType<typeof createSuspenseToken> | null\n /** Last used arguments for change detection */\n lastArgs: Args | undefined\n /** Last seen version for change detection */\n lastVersion: number\n /** Last reset token value for change detection */\n lastReset: unknown\n /** Whether we have a valid cached value */\n hasValue: boolean\n /** Current fetch status */\n status: ResourceStatus\n /** Generation counter to handle race conditions */\n generation: number\n /** Timestamp when the cached value expires */\n expiresAt: number | undefined\n /** Currently in-flight fetch promise */\n inFlight: Promise<void> | undefined\n /** Args used for the current in-flight request */\n inFlightArgs: Args | undefined\n /** AbortController for cancelling in-flight requests */\n controller: AbortController | undefined\n}\n\nconst defaultCacheOptions: Required<ResourceCacheOptions> = {\n mode: 'memory',\n ttlMs: Number.POSITIVE_INFINITY,\n staleWhileRevalidate: false,\n cacheErrors: false,\n}\n\n/**\n * Create a reactive async data resource.\n *\n * Resources handle async data fetching with automatic caching, cancellation,\n * and optional Suspense integration.\n *\n * @param optionsOrFetcher - A fetcher function or full configuration object\n * @returns A resource factory with read, invalidate, and prefetch methods\n *\n * @example\n * ```tsx\n * import { resource } from 'fict'\n *\n * // Simple fetcher\n * const userResource = resource(\n * ({ signal }, userId: string) =>\n * fetch(`/api/users/${userId}`, { signal }).then(r => r.json())\n * )\n *\n * // With full options\n * const postsResource = resource({\n * fetch: ({ signal }, userId: string) =>\n * fetch(`/api/users/${userId}/posts`, { signal }).then(r => r.json()),\n * suspense: true,\n * cache: {\n * ttlMs: 60_000,\n * staleWhileRevalidate: true,\n * },\n * })\n *\n * // Usage in component\n * function UserProfile({ userId }: { userId: string }) {\n * const { data, loading, error, refresh } = userResource.read(() => userId)\n *\n * if (loading) return <Spinner />\n * if (error) return <ErrorMessage error={error} />\n * return <div>{data.name}</div>\n * }\n * ```\n *\n * @public\n */\nexport function resource<T, Args = void>(\n optionsOrFetcher:\n | ((ctx: { signal: AbortSignal }, args: Args) => Promise<T>)\n | ResourceOptions<T, Args>,\n): Resource<T, Args> {\n const fetcher = typeof optionsOrFetcher === 'function' ? optionsOrFetcher : optionsOrFetcher.fetch\n const useSuspense = typeof optionsOrFetcher === 'object' && !!optionsOrFetcher.suspense\n const cacheOptions: ResourceCacheOptions =\n typeof optionsOrFetcher === 'object' ? (optionsOrFetcher.cache ?? {}) : {}\n const resolvedCacheOptions = { ...defaultCacheOptions, ...cacheOptions }\n const cache = new Map<unknown, ResourceEntry<T, Args>>()\n\n const readArgs = (argsAccessor: (() => Args) | Args): Args =>\n typeof argsAccessor === 'function' ? (argsAccessor as () => Args)() : argsAccessor\n\n const computeKey = (argsAccessor: (() => Args) | Args): unknown => {\n const argsValue = readArgs(argsAccessor)\n if (typeof optionsOrFetcher === 'object' && optionsOrFetcher.key !== undefined) {\n const key = optionsOrFetcher.key\n return typeof key === 'function' ? (key as (args: Args) => unknown)(argsValue) : key\n }\n return argsValue\n }\n\n const readResetToken = (): unknown => {\n if (typeof optionsOrFetcher !== 'object') return undefined\n const reset = optionsOrFetcher.reset\n if (typeof reset === 'function' && (reset as () => unknown).length === 0) {\n return (reset as () => unknown)()\n }\n return reset\n }\n\n const ensureEntry = (key: unknown): ResourceEntry<T, Args> => {\n let state = cache.get(key)\n if (!state) {\n state = {\n data: createSignal<T | undefined>(undefined),\n loading: createSignal<boolean>(false),\n error: createSignal<unknown>(undefined),\n version: createSignal(0),\n pendingToken: null,\n lastArgs: undefined,\n lastVersion: -1,\n lastReset: undefined,\n hasValue: false,\n status: 'idle',\n generation: 0,\n expiresAt: undefined,\n inFlight: undefined,\n inFlightArgs: undefined,\n controller: undefined,\n }\n cache.set(key, state)\n }\n return state!\n }\n\n const isExpired = (entry: ResourceEntry<T, Args>): boolean => {\n if (resolvedCacheOptions.mode === 'none') return true\n if (!Number.isFinite(resolvedCacheOptions.ttlMs)) return false\n if (entry.expiresAt === undefined) return false\n return entry.expiresAt < Date.now()\n }\n\n const markExpiry = (entry: ResourceEntry<T, Args>) => {\n if (resolvedCacheOptions.mode === 'none') {\n entry.expiresAt = Date.now() - 1\n return\n }\n entry.expiresAt = Number.isFinite(resolvedCacheOptions.ttlMs)\n ? Date.now() + resolvedCacheOptions.ttlMs\n : undefined\n }\n\n const startFetch = (\n entry: ResourceEntry<T, Args>,\n key: unknown,\n args: Args,\n isRevalidating = false,\n ) => {\n if (entry.inFlight && entry.inFlightArgs === args) {\n return\n }\n entry.controller?.abort()\n entry.inFlight = undefined\n const controller = new AbortController()\n entry.controller = controller\n entry.status = 'pending'\n // For stale-while-revalidate: don't show loading if we already have data to display\n if (!isRevalidating) {\n entry.loading(true)\n }\n entry.error(undefined)\n entry.generation += 1\n const currentGen = entry.generation\n\n const shouldSuspend = useSuspense && !entry.hasValue\n entry.pendingToken = shouldSuspend ? createSuspenseToken() : null\n\n const fetchPromise = fetcher({ signal: controller.signal }, args)\n .then(res => {\n if (controller.signal.aborted || entry.generation !== currentGen) return\n entry.data(res)\n entry.hasValue = true\n entry.status = 'success'\n entry.loading(false)\n markExpiry(entry)\n if (entry.pendingToken) {\n entry.pendingToken.resolve()\n entry.pendingToken = null\n }\n })\n .catch(err => {\n if (controller.signal.aborted || entry.generation !== currentGen) return\n entry.error(err)\n entry.status = 'error'\n entry.loading(false)\n if (resolvedCacheOptions.cacheErrors) {\n markExpiry(entry)\n } else {\n entry.expiresAt = Date.now() - 1\n entry.hasValue = false\n }\n if (entry.pendingToken) {\n entry.pendingToken.reject(err)\n entry.pendingToken = null\n }\n })\n .finally(() => {\n entry.inFlight = undefined\n entry.inFlightArgs = undefined\n entry.controller = undefined\n })\n\n entry.inFlight = fetchPromise\n entry.inFlightArgs = args\n\n onCleanup(() => {\n if (resolvedCacheOptions.mode === 'none') {\n controller.abort()\n cache.delete(key)\n }\n })\n }\n\n const invalidate = (key?: unknown) => {\n if (key === undefined) {\n cache.forEach(entry => {\n entry.controller?.abort()\n entry.version(entry.version() + 1)\n entry.expiresAt = Date.now() - 1\n })\n cache.clear()\n return\n }\n const entry = cache.get(key)\n if (entry) {\n entry.controller?.abort()\n entry.version(entry.version() + 1)\n entry.expiresAt = Date.now() - 1\n cache.delete(key)\n }\n }\n\n const prefetch = (args: Args, keyOverride?: unknown) => {\n const key = keyOverride ?? computeKey(args)\n const entry = ensureEntry(key)\n const usableData = entry.hasValue && !isExpired(entry)\n if (!usableData) {\n entry.lastArgs = args\n entry.lastVersion = entry.version()\n startFetch(entry, key, args)\n }\n }\n\n const mutate = (\n argsAccessor: (() => Args) | Args,\n value: T | ((prev: T | undefined) => T),\n options?: { key?: unknown; revalidate?: boolean },\n ) => {\n const args = readArgs(argsAccessor)\n const key = options?.key ?? computeKey(args)\n const entry = ensureEntry(key)\n const prevValue = entry.data()\n const nextValue =\n typeof value === 'function' ? (value as (prev: T | undefined) => T)(prevValue) : value\n\n entry.controller?.abort()\n entry.inFlight = undefined\n entry.inFlightArgs = undefined\n entry.generation += 1\n\n entry.data(nextValue)\n entry.hasValue = true\n entry.status = 'success'\n entry.loading(false)\n entry.error(undefined)\n markExpiry(entry)\n entry.lastArgs = args\n entry.lastVersion = entry.version()\n\n if (entry.pendingToken) {\n entry.pendingToken.resolve()\n entry.pendingToken = null\n }\n\n if (options?.revalidate) {\n entry.version(entry.version() + 1)\n }\n }\n\n return {\n read(argsAccessor: (() => Args) | Args): ResourceResult<T> {\n const entryRef = createSignal<ResourceEntry<T, Args> | null>(null)\n\n createEffect(() => {\n const key = computeKey(argsAccessor)\n const entry = ensureEntry(key)\n entryRef(entry)\n const args = readArgs(argsAccessor)\n const currentVersion = entry.version()\n const expired = isExpired(entry)\n const argsChanged = entry.lastArgs !== args\n const versionChanged = entry.lastVersion !== currentVersion\n const resetToken = readResetToken()\n const resetChanged = entry.lastReset !== resetToken\n // For stale-while-revalidate: if we have cached data, don't treat expired as requiring immediate refetch\n // We'll handle the revalidation separately to show stale data without loading state\n const canUseStaleData =\n resolvedCacheOptions.staleWhileRevalidate && entry.hasValue && expired\n const shouldRefetch =\n (expired && !canUseStaleData) ||\n argsChanged ||\n versionChanged ||\n resetChanged ||\n (entry.status === 'error' && !resolvedCacheOptions.cacheErrors)\n\n entry.lastArgs = args\n entry.lastVersion = currentVersion\n entry.lastReset = resetToken\n\n if (shouldRefetch) {\n if (entry.inFlight && (argsChanged || versionChanged)) {\n entry.controller?.abort()\n entry.inFlight = undefined\n }\n if (resetChanged) {\n entry.hasValue = false\n entry.expiresAt = Date.now() - 1\n }\n startFetch(entry, key, args as Args)\n } else if (canUseStaleData && entry.inFlight === undefined) {\n // stale-while-revalidate: return stale data immediately, refresh in background\n // Pass isRevalidating=true to avoid showing loading state\n startFetch(entry, key, args as Args, true)\n }\n })\n\n return {\n get data() {\n const entry = entryRef()\n if (!entry) return undefined\n if (useSuspense && entry.pendingToken) {\n throw entry.pendingToken.token\n }\n return entry.data()\n },\n get loading() {\n const entry = entryRef()\n return entry ? entry.loading() : false\n },\n get error() {\n const entry = entryRef()\n return entry ? entry.error() : undefined\n },\n refresh: () => {\n const entry = entryRef()\n if (entry) entry.version(entry.version() + 1)\n },\n }\n },\n invalidate,\n prefetch,\n mutate,\n }\n}\n","/**\n * @fileoverview Lazy component loading with Suspense support.\n *\n * Creates a component that loads its implementation asynchronously,\n * suspending rendering until the module is loaded.\n */\n\nimport { createSuspenseToken } from '@fictjs/runtime'\nimport type { Component } from '@fictjs/runtime'\n\n/** Module shape expected from dynamic imports */\nexport interface LazyModule<TProps extends Record<string, unknown>> {\n default: Component<TProps>\n}\n\n/** Options for lazy loading behavior */\nexport interface LazyOptions {\n /**\n * Maximum number of retry attempts on load failure.\n * Set to 0 to disable retries (default behavior).\n * @default 0\n */\n maxRetries?: number\n\n /**\n * Delay in milliseconds between retry attempts.\n * Uses exponential backoff: delay * 2^(attempt - 1)\n * @default 1000\n */\n retryDelay?: number\n}\n\n/** Extended component with retry capability */\nexport interface LazyComponent<TProps extends Record<string, unknown>> extends Component<TProps> {\n /**\n * Reset the lazy component state, allowing it to retry loading.\n * Useful when used with ErrorBoundary reset functionality.\n */\n reset: () => void\n\n /**\n * Preload the component without rendering it.\n * Returns a promise that resolves when the component is loaded.\n */\n preload: () => Promise<void>\n}\n\n/**\n * Create a lazy component that suspends while loading.\n *\n * @param loader - Function that returns a promise resolving to the component module\n * @param options - Optional configuration for retry behavior\n * @returns A component that suspends during loading and supports retry on failure\n *\n * @example\n * ```tsx\n * import { lazy, Suspense } from 'fict'\n *\n * // Basic usage\n * const LazyChart = lazy(() => import('./Chart'))\n *\n * // With retry options\n * const LazyDashboard = lazy(() => import('./Dashboard'), {\n * maxRetries: 3,\n * retryDelay: 1000\n * })\n *\n * function App() {\n * return (\n * <Suspense fallback={<Loading />}>\n * <LazyChart />\n * </Suspense>\n * )\n * }\n *\n * // Reset on error (with ErrorBoundary)\n * <ErrorBoundary fallback={(err, reset) => (\n * <button onClick={() => { LazyChart.reset(); reset(); }}>Retry</button>\n * )}>\n * <LazyChart />\n * </ErrorBoundary>\n * ```\n *\n * @public\n */\nexport function lazy<TProps extends Record<string, unknown> = Record<string, unknown>>(\n loader: () => Promise<LazyModule<TProps> | { default: Component<TProps> }>,\n options: LazyOptions = {},\n): LazyComponent<TProps> {\n const { maxRetries = 0, retryDelay = 1000 } = options\n\n let loaded: Component<TProps> | null = null\n let loadError: unknown = null\n let loadingPromise: Promise<unknown> | null = null\n let pendingToken: ReturnType<typeof createSuspenseToken> | null = null\n let retryCount = 0\n\n const attemptLoad = (): Promise<void> => {\n return loader()\n .then(mod => {\n loaded = (mod as LazyModule<TProps>).default\n loadError = null\n retryCount = 0\n pendingToken?.resolve()\n })\n .catch((err: unknown) => {\n if (retryCount < maxRetries) {\n retryCount++\n const delay = retryDelay * Math.pow(2, retryCount - 1)\n return new Promise<void>(resolve => {\n setTimeout(() => {\n resolve(attemptLoad())\n }, delay)\n })\n }\n loadError = err\n pendingToken?.reject(err)\n return undefined\n })\n .finally(() => {\n loadingPromise = null\n pendingToken = null\n })\n }\n\n const component = ((props: TProps) => {\n if (loaded) {\n return loaded(props)\n }\n if (loadError) {\n throw loadError\n }\n if (!loadingPromise) {\n pendingToken = createSuspenseToken()\n loadingPromise = attemptLoad()\n }\n if (pendingToken) {\n throw pendingToken.token\n }\n // Should never hit if pendingToken exists, but fallback for type safety.\n throw new Error('Lazy component failed to start loading')\n }) as LazyComponent<TProps>\n\n /**\n * Reset the lazy component state, clearing any cached error.\n * Call this before triggering a re-render to retry loading.\n */\n component.reset = () => {\n loadError = null\n loadingPromise = null\n pendingToken = null\n retryCount = 0\n // Note: we don't clear `loaded` - if it was successfully loaded, keep it\n }\n\n /**\n * Preload the component without rendering.\n * Useful for eager loading on route prefetch.\n */\n component.preload = (): Promise<void> => {\n if (loaded) {\n return Promise.resolve()\n }\n if (loadingPromise) {\n return loadingPromise as Promise<void>\n }\n pendingToken = createSuspenseToken()\n loadingPromise = attemptLoad()\n return loadingPromise as Promise<void>\n }\n\n return component\n}\n"]}
package/dist/plus.d.cts CHANGED
@@ -117,6 +117,17 @@ interface Resource<T, Args> {
117
117
  * @param keyOverride - Optional cache key override
118
118
  */
119
119
  prefetch(args: Args, keyOverride?: unknown): void;
120
+ /**
121
+ * Optimistically update cached data for a given args/key.
122
+ *
123
+ * @param argsAccessor - Arguments or a getter returning arguments
124
+ * @param value - New value or updater function
125
+ * @param options - Optional settings (key override, revalidate)
126
+ */
127
+ mutate(argsAccessor: (() => Args) | Args, value: T | ((prev: T | undefined) => T), options?: {
128
+ key?: unknown;
129
+ revalidate?: boolean;
130
+ }): void;
120
131
  }
121
132
  /**
122
133
  * Create a reactive async data resource.
package/dist/plus.d.ts CHANGED
@@ -117,6 +117,17 @@ interface Resource<T, Args> {
117
117
  * @param keyOverride - Optional cache key override
118
118
  */
119
119
  prefetch(args: Args, keyOverride?: unknown): void;
120
+ /**
121
+ * Optimistically update cached data for a given args/key.
122
+ *
123
+ * @param argsAccessor - Arguments or a getter returning arguments
124
+ * @param value - New value or updater function
125
+ * @param options - Optional settings (key override, revalidate)
126
+ */
127
+ mutate(argsAccessor: (() => Args) | Args, value: T | ((prev: T | undefined) => T), options?: {
128
+ key?: unknown;
129
+ revalidate?: boolean;
130
+ }): void;
120
131
  }
121
132
  /**
122
133
  * Create a reactive async data resource.
package/dist/plus.js CHANGED
@@ -1,4 +1,4 @@
1
- export { $store } from './chunk-QPBAYI4X.js';
1
+ export { $store } from './chunk-YSNNU4OE.js';
2
2
  import { createEffect, createSuspenseToken, onCleanup } from '@fictjs/runtime';
3
3
  import { createSignal } from '@fictjs/runtime/advanced';
4
4
 
@@ -48,6 +48,7 @@ function resource(optionsOrFetcher) {
48
48
  generation: 0,
49
49
  expiresAt: void 0,
50
50
  inFlight: void 0,
51
+ inFlightArgs: void 0,
51
52
  controller: void 0
52
53
  };
53
54
  cache.set(key, state);
@@ -68,6 +69,9 @@ function resource(optionsOrFetcher) {
68
69
  entry.expiresAt = Number.isFinite(resolvedCacheOptions.ttlMs) ? Date.now() + resolvedCacheOptions.ttlMs : void 0;
69
70
  };
70
71
  const startFetch = (entry, key, args, isRevalidating = false) => {
72
+ if (entry.inFlight && entry.inFlightArgs === args) {
73
+ return;
74
+ }
71
75
  entry.controller?.abort();
72
76
  entry.inFlight = void 0;
73
77
  const controller = new AbortController();
@@ -109,9 +113,11 @@ function resource(optionsOrFetcher) {
109
113
  }
110
114
  }).finally(() => {
111
115
  entry.inFlight = void 0;
116
+ entry.inFlightArgs = void 0;
112
117
  entry.controller = void 0;
113
118
  });
114
119
  entry.inFlight = fetchPromise;
120
+ entry.inFlightArgs = args;
115
121
  onCleanup(() => {
116
122
  if (resolvedCacheOptions.mode === "none") {
117
123
  controller.abort();
@@ -147,6 +153,32 @@ function resource(optionsOrFetcher) {
147
153
  startFetch(entry, key, args);
148
154
  }
149
155
  };
156
+ const mutate = (argsAccessor, value, options) => {
157
+ const args = readArgs(argsAccessor);
158
+ const key = options?.key ?? computeKey(args);
159
+ const entry = ensureEntry(key);
160
+ const prevValue = entry.data();
161
+ const nextValue = typeof value === "function" ? value(prevValue) : value;
162
+ entry.controller?.abort();
163
+ entry.inFlight = void 0;
164
+ entry.inFlightArgs = void 0;
165
+ entry.generation += 1;
166
+ entry.data(nextValue);
167
+ entry.hasValue = true;
168
+ entry.status = "success";
169
+ entry.loading(false);
170
+ entry.error(void 0);
171
+ markExpiry(entry);
172
+ entry.lastArgs = args;
173
+ entry.lastVersion = entry.version();
174
+ if (entry.pendingToken) {
175
+ entry.pendingToken.resolve();
176
+ entry.pendingToken = null;
177
+ }
178
+ if (options?.revalidate) {
179
+ entry.version(entry.version() + 1);
180
+ }
181
+ };
150
182
  return {
151
183
  read(argsAccessor) {
152
184
  const entryRef = createSignal(null);
@@ -204,7 +236,8 @@ function resource(optionsOrFetcher) {
204
236
  };
205
237
  },
206
238
  invalidate,
207
- prefetch
239
+ prefetch,
240
+ mutate
208
241
  };
209
242
  }
210
243
  function lazy(loader, options = {}) {
package/dist/plus.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/resource.ts","../src/lazy.ts"],"names":["entry","createSuspenseToken"],"mappings":"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","file":"plus.js","sourcesContent":["/**\n * @fileoverview Async data fetching with caching and Suspense support.\n *\n * The `resource` function creates a reactive data fetcher that:\n * - Automatically cancels in-flight requests when args change\n * - Supports Suspense for loading states\n * - Provides caching with TTL and stale-while-revalidate\n * - Handles errors gracefully\n */\n\nimport { createEffect, onCleanup, createSuspenseToken } from '@fictjs/runtime'\nimport { createSignal } from '@fictjs/runtime/advanced'\n\n/**\n * The result of reading a resource.\n *\n * @typeParam T - The type of data returned by the fetcher\n */\nexport interface ResourceResult<T> {\n /** The fetched data, or undefined if not yet loaded or on error */\n readonly data: T | undefined\n /** Whether the resource is currently loading (initial fetch or refetch) */\n readonly loading: boolean\n /**\n * Any error that occurred during fetching.\n * Type is unknown since errors can be any value in JavaScript.\n */\n readonly error: unknown\n /** Manually trigger a refetch of the resource */\n refresh: () => void\n}\n\n/**\n * Cache configuration options for a resource.\n */\nexport interface ResourceCacheOptions {\n /**\n * Caching mode:\n * - `'memory'`: Cache responses in memory (default)\n * - `'none'`: No caching, always refetch\n * @default 'memory'\n */\n mode?: 'memory' | 'none'\n\n /**\n * Time-to-live in milliseconds before cached data is considered stale.\n * @default Infinity\n */\n ttlMs?: number\n\n /**\n * If true, return stale cached data immediately while refetching in background.\n * @default false\n */\n staleWhileRevalidate?: boolean\n\n /**\n * If true, cache error responses as well.\n * @default false\n */\n cacheErrors?: boolean\n}\n\n/**\n * Configuration options for creating a resource.\n *\n * @typeParam T - The type of data returned by the fetcher\n * @typeParam Args - The type of arguments passed to the fetcher\n */\nexport interface ResourceOptions<T, Args> {\n /**\n * Custom cache key. Can be a static value or a function that computes\n * the key from the args. If not provided, args are used as the key.\n */\n key?: unknown | ((args: Args) => unknown)\n\n /**\n * The fetcher function that performs the async data retrieval.\n * Receives an AbortController signal for cancellation support.\n */\n fetch: (ctx: { signal: AbortSignal }, args: Args) => Promise<T>\n\n /**\n * If true, the resource will throw a Suspense token while loading,\n * enabling React-like Suspense boundaries.\n * @default false\n */\n suspense?: boolean\n\n /**\n * Cache configuration options.\n */\n cache?: ResourceCacheOptions\n\n /**\n * A value or reactive getter that, when changed, resets the resource.\n * Useful for clearing cache when certain conditions change.\n */\n reset?: unknown | (() => unknown)\n}\n\n/**\n * Return type of the resource factory.\n *\n * @typeParam T - The type of data returned by the fetcher\n * @typeParam Args - The type of arguments passed to the fetcher\n */\nexport interface Resource<T, Args> {\n /**\n * Read the resource data, triggering a fetch if needed.\n * Can accept static args or a reactive getter.\n *\n * @param argsAccessor - Arguments or a getter returning arguments\n */\n read(argsAccessor: (() => Args) | Args): ResourceResult<T>\n\n /**\n * Invalidate cached data, causing the next read to refetch.\n *\n * @param key - Optional specific key to invalidate. If omitted, invalidates all.\n */\n invalidate(key?: unknown): void\n\n /**\n * Prefetch data without reading it. Useful for eager loading.\n *\n * @param args - Arguments to pass to the fetcher\n * @param keyOverride - Optional cache key override\n */\n prefetch(args: Args, keyOverride?: unknown): void\n}\n\n/**\n * Resource status values for tracking fetch lifecycle.\n * @internal\n */\nexport type ResourceStatus = 'idle' | 'pending' | 'success' | 'error'\n\n/**\n * Internal cache entry for a resource.\n * Tracks the reactive state and metadata for a single cached fetch.\n *\n * @typeParam T - The type of data returned by the fetcher\n * @typeParam Args - The type of arguments passed to the fetcher\n * @internal\n */\ninterface ResourceEntry<T, Args> {\n /** Reactive signal for the fetched data */\n data: ReturnType<typeof createSignal<T | undefined>>\n /** Reactive signal for loading state */\n loading: ReturnType<typeof createSignal<boolean>>\n /** Reactive signal for error state */\n error: ReturnType<typeof createSignal<unknown>>\n /** Version counter for invalidation */\n version: ReturnType<typeof createSignal<number>>\n /** Suspense token when using suspense mode */\n pendingToken: ReturnType<typeof createSuspenseToken> | null\n /** Last used arguments for change detection */\n lastArgs: Args | undefined\n /** Last seen version for change detection */\n lastVersion: number\n /** Last reset token value for change detection */\n lastReset: unknown\n /** Whether we have a valid cached value */\n hasValue: boolean\n /** Current fetch status */\n status: ResourceStatus\n /** Generation counter to handle race conditions */\n generation: number\n /** Timestamp when the cached value expires */\n expiresAt: number | undefined\n /** Currently in-flight fetch promise */\n inFlight: Promise<void> | undefined\n /** AbortController for cancelling in-flight requests */\n controller: AbortController | undefined\n}\n\nconst defaultCacheOptions: Required<ResourceCacheOptions> = {\n mode: 'memory',\n ttlMs: Number.POSITIVE_INFINITY,\n staleWhileRevalidate: false,\n cacheErrors: false,\n}\n\n/**\n * Create a reactive async data resource.\n *\n * Resources handle async data fetching with automatic caching, cancellation,\n * and optional Suspense integration.\n *\n * @param optionsOrFetcher - A fetcher function or full configuration object\n * @returns A resource factory with read, invalidate, and prefetch methods\n *\n * @example\n * ```tsx\n * import { resource } from 'fict'\n *\n * // Simple fetcher\n * const userResource = resource(\n * ({ signal }, userId: string) =>\n * fetch(`/api/users/${userId}`, { signal }).then(r => r.json())\n * )\n *\n * // With full options\n * const postsResource = resource({\n * fetch: ({ signal }, userId: string) =>\n * fetch(`/api/users/${userId}/posts`, { signal }).then(r => r.json()),\n * suspense: true,\n * cache: {\n * ttlMs: 60_000,\n * staleWhileRevalidate: true,\n * },\n * })\n *\n * // Usage in component\n * function UserProfile({ userId }: { userId: string }) {\n * const { data, loading, error, refresh } = userResource.read(() => userId)\n *\n * if (loading) return <Spinner />\n * if (error) return <ErrorMessage error={error} />\n * return <div>{data.name}</div>\n * }\n * ```\n *\n * @public\n */\nexport function resource<T, Args = void>(\n optionsOrFetcher:\n | ((ctx: { signal: AbortSignal }, args: Args) => Promise<T>)\n | ResourceOptions<T, Args>,\n): Resource<T, Args> {\n const fetcher = typeof optionsOrFetcher === 'function' ? optionsOrFetcher : optionsOrFetcher.fetch\n const useSuspense = typeof optionsOrFetcher === 'object' && !!optionsOrFetcher.suspense\n const cacheOptions: ResourceCacheOptions =\n typeof optionsOrFetcher === 'object' ? (optionsOrFetcher.cache ?? {}) : {}\n const resolvedCacheOptions = { ...defaultCacheOptions, ...cacheOptions }\n const cache = new Map<unknown, ResourceEntry<T, Args>>()\n\n const readArgs = (argsAccessor: (() => Args) | Args): Args =>\n typeof argsAccessor === 'function' ? (argsAccessor as () => Args)() : argsAccessor\n\n const computeKey = (argsAccessor: (() => Args) | Args): unknown => {\n const argsValue = readArgs(argsAccessor)\n if (typeof optionsOrFetcher === 'object' && optionsOrFetcher.key !== undefined) {\n const key = optionsOrFetcher.key\n return typeof key === 'function' ? (key as (args: Args) => unknown)(argsValue) : key\n }\n return argsValue\n }\n\n const readResetToken = (): unknown => {\n if (typeof optionsOrFetcher !== 'object') return undefined\n const reset = optionsOrFetcher.reset\n if (typeof reset === 'function' && (reset as () => unknown).length === 0) {\n return (reset as () => unknown)()\n }\n return reset\n }\n\n const ensureEntry = (key: unknown): ResourceEntry<T, Args> => {\n let state = cache.get(key)\n if (!state) {\n state = {\n data: createSignal<T | undefined>(undefined),\n loading: createSignal<boolean>(false),\n error: createSignal<unknown>(undefined),\n version: createSignal(0),\n pendingToken: null,\n lastArgs: undefined,\n lastVersion: -1,\n lastReset: undefined,\n hasValue: false,\n status: 'idle',\n generation: 0,\n expiresAt: undefined,\n inFlight: undefined,\n controller: undefined,\n }\n cache.set(key, state)\n }\n return state!\n }\n\n const isExpired = (entry: ResourceEntry<T, Args>): boolean => {\n if (resolvedCacheOptions.mode === 'none') return true\n if (!Number.isFinite(resolvedCacheOptions.ttlMs)) return false\n if (entry.expiresAt === undefined) return false\n return entry.expiresAt < Date.now()\n }\n\n const markExpiry = (entry: ResourceEntry<T, Args>) => {\n if (resolvedCacheOptions.mode === 'none') {\n entry.expiresAt = Date.now() - 1\n return\n }\n entry.expiresAt = Number.isFinite(resolvedCacheOptions.ttlMs)\n ? Date.now() + resolvedCacheOptions.ttlMs\n : undefined\n }\n\n const startFetch = (\n entry: ResourceEntry<T, Args>,\n key: unknown,\n args: Args,\n isRevalidating = false,\n ) => {\n entry.controller?.abort()\n entry.inFlight = undefined\n const controller = new AbortController()\n entry.controller = controller\n entry.status = 'pending'\n // For stale-while-revalidate: don't show loading if we already have data to display\n if (!isRevalidating) {\n entry.loading(true)\n }\n entry.error(undefined)\n entry.generation += 1\n const currentGen = entry.generation\n\n const shouldSuspend = useSuspense && !entry.hasValue\n entry.pendingToken = shouldSuspend ? createSuspenseToken() : null\n\n const fetchPromise = fetcher({ signal: controller.signal }, args)\n .then(res => {\n if (controller.signal.aborted || entry.generation !== currentGen) return\n entry.data(res)\n entry.hasValue = true\n entry.status = 'success'\n entry.loading(false)\n markExpiry(entry)\n if (entry.pendingToken) {\n entry.pendingToken.resolve()\n entry.pendingToken = null\n }\n })\n .catch(err => {\n if (controller.signal.aborted || entry.generation !== currentGen) return\n entry.error(err)\n entry.status = 'error'\n entry.loading(false)\n if (resolvedCacheOptions.cacheErrors) {\n markExpiry(entry)\n } else {\n entry.expiresAt = Date.now() - 1\n entry.hasValue = false\n }\n if (entry.pendingToken) {\n entry.pendingToken.reject(err)\n entry.pendingToken = null\n }\n })\n .finally(() => {\n entry.inFlight = undefined\n entry.controller = undefined\n })\n\n entry.inFlight = fetchPromise\n\n onCleanup(() => {\n if (resolvedCacheOptions.mode === 'none') {\n controller.abort()\n cache.delete(key)\n }\n })\n }\n\n const invalidate = (key?: unknown) => {\n if (key === undefined) {\n cache.forEach(entry => {\n entry.controller?.abort()\n entry.version(entry.version() + 1)\n entry.expiresAt = Date.now() - 1\n })\n cache.clear()\n return\n }\n const entry = cache.get(key)\n if (entry) {\n entry.controller?.abort()\n entry.version(entry.version() + 1)\n entry.expiresAt = Date.now() - 1\n cache.delete(key)\n }\n }\n\n const prefetch = (args: Args, keyOverride?: unknown) => {\n const key = keyOverride ?? computeKey(args)\n const entry = ensureEntry(key)\n const usableData = entry.hasValue && !isExpired(entry)\n if (!usableData) {\n entry.lastArgs = args\n entry.lastVersion = entry.version()\n startFetch(entry, key, args)\n }\n }\n\n return {\n read(argsAccessor: (() => Args) | Args): ResourceResult<T> {\n const entryRef = createSignal<ResourceEntry<T, Args> | null>(null)\n\n createEffect(() => {\n const key = computeKey(argsAccessor)\n const entry = ensureEntry(key)\n entryRef(entry)\n const args = readArgs(argsAccessor)\n const currentVersion = entry.version()\n const expired = isExpired(entry)\n const argsChanged = entry.lastArgs !== args\n const versionChanged = entry.lastVersion !== currentVersion\n const resetToken = readResetToken()\n const resetChanged = entry.lastReset !== resetToken\n // For stale-while-revalidate: if we have cached data, don't treat expired as requiring immediate refetch\n // We'll handle the revalidation separately to show stale data without loading state\n const canUseStaleData =\n resolvedCacheOptions.staleWhileRevalidate && entry.hasValue && expired\n const shouldRefetch =\n (expired && !canUseStaleData) ||\n argsChanged ||\n versionChanged ||\n resetChanged ||\n (entry.status === 'error' && !resolvedCacheOptions.cacheErrors)\n\n entry.lastArgs = args\n entry.lastVersion = currentVersion\n entry.lastReset = resetToken\n\n if (shouldRefetch) {\n if (entry.inFlight && (argsChanged || versionChanged)) {\n entry.controller?.abort()\n entry.inFlight = undefined\n }\n if (resetChanged) {\n entry.hasValue = false\n entry.expiresAt = Date.now() - 1\n }\n startFetch(entry, key, args as Args)\n } else if (canUseStaleData && entry.inFlight === undefined) {\n // stale-while-revalidate: return stale data immediately, refresh in background\n // Pass isRevalidating=true to avoid showing loading state\n startFetch(entry, key, args as Args, true)\n }\n })\n\n return {\n get data() {\n const entry = entryRef()\n if (!entry) return undefined\n if (useSuspense && entry.pendingToken) {\n throw entry.pendingToken.token\n }\n return entry.data()\n },\n get loading() {\n const entry = entryRef()\n return entry ? entry.loading() : false\n },\n get error() {\n const entry = entryRef()\n return entry ? entry.error() : undefined\n },\n refresh: () => {\n const entry = entryRef()\n if (entry) entry.version(entry.version() + 1)\n },\n }\n },\n invalidate,\n prefetch,\n }\n}\n","/**\n * @fileoverview Lazy component loading with Suspense support.\n *\n * Creates a component that loads its implementation asynchronously,\n * suspending rendering until the module is loaded.\n */\n\nimport { createSuspenseToken } from '@fictjs/runtime'\nimport type { Component } from '@fictjs/runtime'\n\n/** Module shape expected from dynamic imports */\nexport interface LazyModule<TProps extends Record<string, unknown>> {\n default: Component<TProps>\n}\n\n/** Options for lazy loading behavior */\nexport interface LazyOptions {\n /**\n * Maximum number of retry attempts on load failure.\n * Set to 0 to disable retries (default behavior).\n * @default 0\n */\n maxRetries?: number\n\n /**\n * Delay in milliseconds between retry attempts.\n * Uses exponential backoff: delay * 2^(attempt - 1)\n * @default 1000\n */\n retryDelay?: number\n}\n\n/** Extended component with retry capability */\nexport interface LazyComponent<TProps extends Record<string, unknown>> extends Component<TProps> {\n /**\n * Reset the lazy component state, allowing it to retry loading.\n * Useful when used with ErrorBoundary reset functionality.\n */\n reset: () => void\n\n /**\n * Preload the component without rendering it.\n * Returns a promise that resolves when the component is loaded.\n */\n preload: () => Promise<void>\n}\n\n/**\n * Create a lazy component that suspends while loading.\n *\n * @param loader - Function that returns a promise resolving to the component module\n * @param options - Optional configuration for retry behavior\n * @returns A component that suspends during loading and supports retry on failure\n *\n * @example\n * ```tsx\n * import { lazy, Suspense } from 'fict'\n *\n * // Basic usage\n * const LazyChart = lazy(() => import('./Chart'))\n *\n * // With retry options\n * const LazyDashboard = lazy(() => import('./Dashboard'), {\n * maxRetries: 3,\n * retryDelay: 1000\n * })\n *\n * function App() {\n * return (\n * <Suspense fallback={<Loading />}>\n * <LazyChart />\n * </Suspense>\n * )\n * }\n *\n * // Reset on error (with ErrorBoundary)\n * <ErrorBoundary fallback={(err, reset) => (\n * <button onClick={() => { LazyChart.reset(); reset(); }}>Retry</button>\n * )}>\n * <LazyChart />\n * </ErrorBoundary>\n * ```\n *\n * @public\n */\nexport function lazy<TProps extends Record<string, unknown> = Record<string, unknown>>(\n loader: () => Promise<LazyModule<TProps> | { default: Component<TProps> }>,\n options: LazyOptions = {},\n): LazyComponent<TProps> {\n const { maxRetries = 0, retryDelay = 1000 } = options\n\n let loaded: Component<TProps> | null = null\n let loadError: unknown = null\n let loadingPromise: Promise<unknown> | null = null\n let pendingToken: ReturnType<typeof createSuspenseToken> | null = null\n let retryCount = 0\n\n const attemptLoad = (): Promise<void> => {\n return loader()\n .then(mod => {\n loaded = (mod as LazyModule<TProps>).default\n loadError = null\n retryCount = 0\n pendingToken?.resolve()\n })\n .catch((err: unknown) => {\n if (retryCount < maxRetries) {\n retryCount++\n const delay = retryDelay * Math.pow(2, retryCount - 1)\n return new Promise<void>(resolve => {\n setTimeout(() => {\n resolve(attemptLoad())\n }, delay)\n })\n }\n loadError = err\n pendingToken?.reject(err)\n return undefined\n })\n .finally(() => {\n loadingPromise = null\n pendingToken = null\n })\n }\n\n const component = ((props: TProps) => {\n if (loaded) {\n return loaded(props)\n }\n if (loadError) {\n throw loadError\n }\n if (!loadingPromise) {\n pendingToken = createSuspenseToken()\n loadingPromise = attemptLoad()\n }\n if (pendingToken) {\n throw pendingToken.token\n }\n // Should never hit if pendingToken exists, but fallback for type safety.\n throw new Error('Lazy component failed to start loading')\n }) as LazyComponent<TProps>\n\n /**\n * Reset the lazy component state, clearing any cached error.\n * Call this before triggering a re-render to retry loading.\n */\n component.reset = () => {\n loadError = null\n loadingPromise = null\n pendingToken = null\n retryCount = 0\n // Note: we don't clear `loaded` - if it was successfully loaded, keep it\n }\n\n /**\n * Preload the component without rendering.\n * Useful for eager loading on route prefetch.\n */\n component.preload = (): Promise<void> => {\n if (loaded) {\n return Promise.resolve()\n }\n if (loadingPromise) {\n return loadingPromise as Promise<void>\n }\n pendingToken = createSuspenseToken()\n loadingPromise = attemptLoad()\n return loadingPromise as Promise<void>\n }\n\n return component\n}\n"]}
1
+ {"version":3,"sources":["../src/resource.ts","../src/lazy.ts"],"names":["entry","createSuspenseToken"],"mappings":"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","file":"plus.js","sourcesContent":["/**\n * @fileoverview Async data fetching with caching and Suspense support.\n *\n * The `resource` function creates a reactive data fetcher that:\n * - Automatically cancels in-flight requests when args change\n * - Supports Suspense for loading states\n * - Provides caching with TTL and stale-while-revalidate\n * - Handles errors gracefully\n */\n\nimport { createEffect, onCleanup, createSuspenseToken } from '@fictjs/runtime'\nimport { createSignal } from '@fictjs/runtime/advanced'\n\n/**\n * The result of reading a resource.\n *\n * @typeParam T - The type of data returned by the fetcher\n */\nexport interface ResourceResult<T> {\n /** The fetched data, or undefined if not yet loaded or on error */\n readonly data: T | undefined\n /** Whether the resource is currently loading (initial fetch or refetch) */\n readonly loading: boolean\n /**\n * Any error that occurred during fetching.\n * Type is unknown since errors can be any value in JavaScript.\n */\n readonly error: unknown\n /** Manually trigger a refetch of the resource */\n refresh: () => void\n}\n\n/**\n * Cache configuration options for a resource.\n */\nexport interface ResourceCacheOptions {\n /**\n * Caching mode:\n * - `'memory'`: Cache responses in memory (default)\n * - `'none'`: No caching, always refetch\n * @default 'memory'\n */\n mode?: 'memory' | 'none'\n\n /**\n * Time-to-live in milliseconds before cached data is considered stale.\n * @default Infinity\n */\n ttlMs?: number\n\n /**\n * If true, return stale cached data immediately while refetching in background.\n * @default false\n */\n staleWhileRevalidate?: boolean\n\n /**\n * If true, cache error responses as well.\n * @default false\n */\n cacheErrors?: boolean\n}\n\n/**\n * Configuration options for creating a resource.\n *\n * @typeParam T - The type of data returned by the fetcher\n * @typeParam Args - The type of arguments passed to the fetcher\n */\nexport interface ResourceOptions<T, Args> {\n /**\n * Custom cache key. Can be a static value or a function that computes\n * the key from the args. If not provided, args are used as the key.\n */\n key?: unknown | ((args: Args) => unknown)\n\n /**\n * The fetcher function that performs the async data retrieval.\n * Receives an AbortController signal for cancellation support.\n */\n fetch: (ctx: { signal: AbortSignal }, args: Args) => Promise<T>\n\n /**\n * If true, the resource will throw a Suspense token while loading,\n * enabling React-like Suspense boundaries.\n * @default false\n */\n suspense?: boolean\n\n /**\n * Cache configuration options.\n */\n cache?: ResourceCacheOptions\n\n /**\n * A value or reactive getter that, when changed, resets the resource.\n * Useful for clearing cache when certain conditions change.\n */\n reset?: unknown | (() => unknown)\n}\n\n/**\n * Return type of the resource factory.\n *\n * @typeParam T - The type of data returned by the fetcher\n * @typeParam Args - The type of arguments passed to the fetcher\n */\nexport interface Resource<T, Args> {\n /**\n * Read the resource data, triggering a fetch if needed.\n * Can accept static args or a reactive getter.\n *\n * @param argsAccessor - Arguments or a getter returning arguments\n */\n read(argsAccessor: (() => Args) | Args): ResourceResult<T>\n\n /**\n * Invalidate cached data, causing the next read to refetch.\n *\n * @param key - Optional specific key to invalidate. If omitted, invalidates all.\n */\n invalidate(key?: unknown): void\n\n /**\n * Prefetch data without reading it. Useful for eager loading.\n *\n * @param args - Arguments to pass to the fetcher\n * @param keyOverride - Optional cache key override\n */\n prefetch(args: Args, keyOverride?: unknown): void\n\n /**\n * Optimistically update cached data for a given args/key.\n *\n * @param argsAccessor - Arguments or a getter returning arguments\n * @param value - New value or updater function\n * @param options - Optional settings (key override, revalidate)\n */\n mutate(\n argsAccessor: (() => Args) | Args,\n value: T | ((prev: T | undefined) => T),\n options?: { key?: unknown; revalidate?: boolean },\n ): void\n}\n\n/**\n * Resource status values for tracking fetch lifecycle.\n * @internal\n */\nexport type ResourceStatus = 'idle' | 'pending' | 'success' | 'error'\n\n/**\n * Internal cache entry for a resource.\n * Tracks the reactive state and metadata for a single cached fetch.\n *\n * @typeParam T - The type of data returned by the fetcher\n * @typeParam Args - The type of arguments passed to the fetcher\n * @internal\n */\ninterface ResourceEntry<T, Args> {\n /** Reactive signal for the fetched data */\n data: ReturnType<typeof createSignal<T | undefined>>\n /** Reactive signal for loading state */\n loading: ReturnType<typeof createSignal<boolean>>\n /** Reactive signal for error state */\n error: ReturnType<typeof createSignal<unknown>>\n /** Version counter for invalidation */\n version: ReturnType<typeof createSignal<number>>\n /** Suspense token when using suspense mode */\n pendingToken: ReturnType<typeof createSuspenseToken> | null\n /** Last used arguments for change detection */\n lastArgs: Args | undefined\n /** Last seen version for change detection */\n lastVersion: number\n /** Last reset token value for change detection */\n lastReset: unknown\n /** Whether we have a valid cached value */\n hasValue: boolean\n /** Current fetch status */\n status: ResourceStatus\n /** Generation counter to handle race conditions */\n generation: number\n /** Timestamp when the cached value expires */\n expiresAt: number | undefined\n /** Currently in-flight fetch promise */\n inFlight: Promise<void> | undefined\n /** Args used for the current in-flight request */\n inFlightArgs: Args | undefined\n /** AbortController for cancelling in-flight requests */\n controller: AbortController | undefined\n}\n\nconst defaultCacheOptions: Required<ResourceCacheOptions> = {\n mode: 'memory',\n ttlMs: Number.POSITIVE_INFINITY,\n staleWhileRevalidate: false,\n cacheErrors: false,\n}\n\n/**\n * Create a reactive async data resource.\n *\n * Resources handle async data fetching with automatic caching, cancellation,\n * and optional Suspense integration.\n *\n * @param optionsOrFetcher - A fetcher function or full configuration object\n * @returns A resource factory with read, invalidate, and prefetch methods\n *\n * @example\n * ```tsx\n * import { resource } from 'fict'\n *\n * // Simple fetcher\n * const userResource = resource(\n * ({ signal }, userId: string) =>\n * fetch(`/api/users/${userId}`, { signal }).then(r => r.json())\n * )\n *\n * // With full options\n * const postsResource = resource({\n * fetch: ({ signal }, userId: string) =>\n * fetch(`/api/users/${userId}/posts`, { signal }).then(r => r.json()),\n * suspense: true,\n * cache: {\n * ttlMs: 60_000,\n * staleWhileRevalidate: true,\n * },\n * })\n *\n * // Usage in component\n * function UserProfile({ userId }: { userId: string }) {\n * const { data, loading, error, refresh } = userResource.read(() => userId)\n *\n * if (loading) return <Spinner />\n * if (error) return <ErrorMessage error={error} />\n * return <div>{data.name}</div>\n * }\n * ```\n *\n * @public\n */\nexport function resource<T, Args = void>(\n optionsOrFetcher:\n | ((ctx: { signal: AbortSignal }, args: Args) => Promise<T>)\n | ResourceOptions<T, Args>,\n): Resource<T, Args> {\n const fetcher = typeof optionsOrFetcher === 'function' ? optionsOrFetcher : optionsOrFetcher.fetch\n const useSuspense = typeof optionsOrFetcher === 'object' && !!optionsOrFetcher.suspense\n const cacheOptions: ResourceCacheOptions =\n typeof optionsOrFetcher === 'object' ? (optionsOrFetcher.cache ?? {}) : {}\n const resolvedCacheOptions = { ...defaultCacheOptions, ...cacheOptions }\n const cache = new Map<unknown, ResourceEntry<T, Args>>()\n\n const readArgs = (argsAccessor: (() => Args) | Args): Args =>\n typeof argsAccessor === 'function' ? (argsAccessor as () => Args)() : argsAccessor\n\n const computeKey = (argsAccessor: (() => Args) | Args): unknown => {\n const argsValue = readArgs(argsAccessor)\n if (typeof optionsOrFetcher === 'object' && optionsOrFetcher.key !== undefined) {\n const key = optionsOrFetcher.key\n return typeof key === 'function' ? (key as (args: Args) => unknown)(argsValue) : key\n }\n return argsValue\n }\n\n const readResetToken = (): unknown => {\n if (typeof optionsOrFetcher !== 'object') return undefined\n const reset = optionsOrFetcher.reset\n if (typeof reset === 'function' && (reset as () => unknown).length === 0) {\n return (reset as () => unknown)()\n }\n return reset\n }\n\n const ensureEntry = (key: unknown): ResourceEntry<T, Args> => {\n let state = cache.get(key)\n if (!state) {\n state = {\n data: createSignal<T | undefined>(undefined),\n loading: createSignal<boolean>(false),\n error: createSignal<unknown>(undefined),\n version: createSignal(0),\n pendingToken: null,\n lastArgs: undefined,\n lastVersion: -1,\n lastReset: undefined,\n hasValue: false,\n status: 'idle',\n generation: 0,\n expiresAt: undefined,\n inFlight: undefined,\n inFlightArgs: undefined,\n controller: undefined,\n }\n cache.set(key, state)\n }\n return state!\n }\n\n const isExpired = (entry: ResourceEntry<T, Args>): boolean => {\n if (resolvedCacheOptions.mode === 'none') return true\n if (!Number.isFinite(resolvedCacheOptions.ttlMs)) return false\n if (entry.expiresAt === undefined) return false\n return entry.expiresAt < Date.now()\n }\n\n const markExpiry = (entry: ResourceEntry<T, Args>) => {\n if (resolvedCacheOptions.mode === 'none') {\n entry.expiresAt = Date.now() - 1\n return\n }\n entry.expiresAt = Number.isFinite(resolvedCacheOptions.ttlMs)\n ? Date.now() + resolvedCacheOptions.ttlMs\n : undefined\n }\n\n const startFetch = (\n entry: ResourceEntry<T, Args>,\n key: unknown,\n args: Args,\n isRevalidating = false,\n ) => {\n if (entry.inFlight && entry.inFlightArgs === args) {\n return\n }\n entry.controller?.abort()\n entry.inFlight = undefined\n const controller = new AbortController()\n entry.controller = controller\n entry.status = 'pending'\n // For stale-while-revalidate: don't show loading if we already have data to display\n if (!isRevalidating) {\n entry.loading(true)\n }\n entry.error(undefined)\n entry.generation += 1\n const currentGen = entry.generation\n\n const shouldSuspend = useSuspense && !entry.hasValue\n entry.pendingToken = shouldSuspend ? createSuspenseToken() : null\n\n const fetchPromise = fetcher({ signal: controller.signal }, args)\n .then(res => {\n if (controller.signal.aborted || entry.generation !== currentGen) return\n entry.data(res)\n entry.hasValue = true\n entry.status = 'success'\n entry.loading(false)\n markExpiry(entry)\n if (entry.pendingToken) {\n entry.pendingToken.resolve()\n entry.pendingToken = null\n }\n })\n .catch(err => {\n if (controller.signal.aborted || entry.generation !== currentGen) return\n entry.error(err)\n entry.status = 'error'\n entry.loading(false)\n if (resolvedCacheOptions.cacheErrors) {\n markExpiry(entry)\n } else {\n entry.expiresAt = Date.now() - 1\n entry.hasValue = false\n }\n if (entry.pendingToken) {\n entry.pendingToken.reject(err)\n entry.pendingToken = null\n }\n })\n .finally(() => {\n entry.inFlight = undefined\n entry.inFlightArgs = undefined\n entry.controller = undefined\n })\n\n entry.inFlight = fetchPromise\n entry.inFlightArgs = args\n\n onCleanup(() => {\n if (resolvedCacheOptions.mode === 'none') {\n controller.abort()\n cache.delete(key)\n }\n })\n }\n\n const invalidate = (key?: unknown) => {\n if (key === undefined) {\n cache.forEach(entry => {\n entry.controller?.abort()\n entry.version(entry.version() + 1)\n entry.expiresAt = Date.now() - 1\n })\n cache.clear()\n return\n }\n const entry = cache.get(key)\n if (entry) {\n entry.controller?.abort()\n entry.version(entry.version() + 1)\n entry.expiresAt = Date.now() - 1\n cache.delete(key)\n }\n }\n\n const prefetch = (args: Args, keyOverride?: unknown) => {\n const key = keyOverride ?? computeKey(args)\n const entry = ensureEntry(key)\n const usableData = entry.hasValue && !isExpired(entry)\n if (!usableData) {\n entry.lastArgs = args\n entry.lastVersion = entry.version()\n startFetch(entry, key, args)\n }\n }\n\n const mutate = (\n argsAccessor: (() => Args) | Args,\n value: T | ((prev: T | undefined) => T),\n options?: { key?: unknown; revalidate?: boolean },\n ) => {\n const args = readArgs(argsAccessor)\n const key = options?.key ?? computeKey(args)\n const entry = ensureEntry(key)\n const prevValue = entry.data()\n const nextValue =\n typeof value === 'function' ? (value as (prev: T | undefined) => T)(prevValue) : value\n\n entry.controller?.abort()\n entry.inFlight = undefined\n entry.inFlightArgs = undefined\n entry.generation += 1\n\n entry.data(nextValue)\n entry.hasValue = true\n entry.status = 'success'\n entry.loading(false)\n entry.error(undefined)\n markExpiry(entry)\n entry.lastArgs = args\n entry.lastVersion = entry.version()\n\n if (entry.pendingToken) {\n entry.pendingToken.resolve()\n entry.pendingToken = null\n }\n\n if (options?.revalidate) {\n entry.version(entry.version() + 1)\n }\n }\n\n return {\n read(argsAccessor: (() => Args) | Args): ResourceResult<T> {\n const entryRef = createSignal<ResourceEntry<T, Args> | null>(null)\n\n createEffect(() => {\n const key = computeKey(argsAccessor)\n const entry = ensureEntry(key)\n entryRef(entry)\n const args = readArgs(argsAccessor)\n const currentVersion = entry.version()\n const expired = isExpired(entry)\n const argsChanged = entry.lastArgs !== args\n const versionChanged = entry.lastVersion !== currentVersion\n const resetToken = readResetToken()\n const resetChanged = entry.lastReset !== resetToken\n // For stale-while-revalidate: if we have cached data, don't treat expired as requiring immediate refetch\n // We'll handle the revalidation separately to show stale data without loading state\n const canUseStaleData =\n resolvedCacheOptions.staleWhileRevalidate && entry.hasValue && expired\n const shouldRefetch =\n (expired && !canUseStaleData) ||\n argsChanged ||\n versionChanged ||\n resetChanged ||\n (entry.status === 'error' && !resolvedCacheOptions.cacheErrors)\n\n entry.lastArgs = args\n entry.lastVersion = currentVersion\n entry.lastReset = resetToken\n\n if (shouldRefetch) {\n if (entry.inFlight && (argsChanged || versionChanged)) {\n entry.controller?.abort()\n entry.inFlight = undefined\n }\n if (resetChanged) {\n entry.hasValue = false\n entry.expiresAt = Date.now() - 1\n }\n startFetch(entry, key, args as Args)\n } else if (canUseStaleData && entry.inFlight === undefined) {\n // stale-while-revalidate: return stale data immediately, refresh in background\n // Pass isRevalidating=true to avoid showing loading state\n startFetch(entry, key, args as Args, true)\n }\n })\n\n return {\n get data() {\n const entry = entryRef()\n if (!entry) return undefined\n if (useSuspense && entry.pendingToken) {\n throw entry.pendingToken.token\n }\n return entry.data()\n },\n get loading() {\n const entry = entryRef()\n return entry ? entry.loading() : false\n },\n get error() {\n const entry = entryRef()\n return entry ? entry.error() : undefined\n },\n refresh: () => {\n const entry = entryRef()\n if (entry) entry.version(entry.version() + 1)\n },\n }\n },\n invalidate,\n prefetch,\n mutate,\n }\n}\n","/**\n * @fileoverview Lazy component loading with Suspense support.\n *\n * Creates a component that loads its implementation asynchronously,\n * suspending rendering until the module is loaded.\n */\n\nimport { createSuspenseToken } from '@fictjs/runtime'\nimport type { Component } from '@fictjs/runtime'\n\n/** Module shape expected from dynamic imports */\nexport interface LazyModule<TProps extends Record<string, unknown>> {\n default: Component<TProps>\n}\n\n/** Options for lazy loading behavior */\nexport interface LazyOptions {\n /**\n * Maximum number of retry attempts on load failure.\n * Set to 0 to disable retries (default behavior).\n * @default 0\n */\n maxRetries?: number\n\n /**\n * Delay in milliseconds between retry attempts.\n * Uses exponential backoff: delay * 2^(attempt - 1)\n * @default 1000\n */\n retryDelay?: number\n}\n\n/** Extended component with retry capability */\nexport interface LazyComponent<TProps extends Record<string, unknown>> extends Component<TProps> {\n /**\n * Reset the lazy component state, allowing it to retry loading.\n * Useful when used with ErrorBoundary reset functionality.\n */\n reset: () => void\n\n /**\n * Preload the component without rendering it.\n * Returns a promise that resolves when the component is loaded.\n */\n preload: () => Promise<void>\n}\n\n/**\n * Create a lazy component that suspends while loading.\n *\n * @param loader - Function that returns a promise resolving to the component module\n * @param options - Optional configuration for retry behavior\n * @returns A component that suspends during loading and supports retry on failure\n *\n * @example\n * ```tsx\n * import { lazy, Suspense } from 'fict'\n *\n * // Basic usage\n * const LazyChart = lazy(() => import('./Chart'))\n *\n * // With retry options\n * const LazyDashboard = lazy(() => import('./Dashboard'), {\n * maxRetries: 3,\n * retryDelay: 1000\n * })\n *\n * function App() {\n * return (\n * <Suspense fallback={<Loading />}>\n * <LazyChart />\n * </Suspense>\n * )\n * }\n *\n * // Reset on error (with ErrorBoundary)\n * <ErrorBoundary fallback={(err, reset) => (\n * <button onClick={() => { LazyChart.reset(); reset(); }}>Retry</button>\n * )}>\n * <LazyChart />\n * </ErrorBoundary>\n * ```\n *\n * @public\n */\nexport function lazy<TProps extends Record<string, unknown> = Record<string, unknown>>(\n loader: () => Promise<LazyModule<TProps> | { default: Component<TProps> }>,\n options: LazyOptions = {},\n): LazyComponent<TProps> {\n const { maxRetries = 0, retryDelay = 1000 } = options\n\n let loaded: Component<TProps> | null = null\n let loadError: unknown = null\n let loadingPromise: Promise<unknown> | null = null\n let pendingToken: ReturnType<typeof createSuspenseToken> | null = null\n let retryCount = 0\n\n const attemptLoad = (): Promise<void> => {\n return loader()\n .then(mod => {\n loaded = (mod as LazyModule<TProps>).default\n loadError = null\n retryCount = 0\n pendingToken?.resolve()\n })\n .catch((err: unknown) => {\n if (retryCount < maxRetries) {\n retryCount++\n const delay = retryDelay * Math.pow(2, retryCount - 1)\n return new Promise<void>(resolve => {\n setTimeout(() => {\n resolve(attemptLoad())\n }, delay)\n })\n }\n loadError = err\n pendingToken?.reject(err)\n return undefined\n })\n .finally(() => {\n loadingPromise = null\n pendingToken = null\n })\n }\n\n const component = ((props: TProps) => {\n if (loaded) {\n return loaded(props)\n }\n if (loadError) {\n throw loadError\n }\n if (!loadingPromise) {\n pendingToken = createSuspenseToken()\n loadingPromise = attemptLoad()\n }\n if (pendingToken) {\n throw pendingToken.token\n }\n // Should never hit if pendingToken exists, but fallback for type safety.\n throw new Error('Lazy component failed to start loading')\n }) as LazyComponent<TProps>\n\n /**\n * Reset the lazy component state, clearing any cached error.\n * Call this before triggering a re-render to retry loading.\n */\n component.reset = () => {\n loadError = null\n loadingPromise = null\n pendingToken = null\n retryCount = 0\n // Note: we don't clear `loaded` - if it was successfully loaded, keep it\n }\n\n /**\n * Preload the component without rendering.\n * Useful for eager loading on route prefetch.\n */\n component.preload = (): Promise<void> => {\n if (loaded) {\n return Promise.resolve()\n }\n if (loadingPromise) {\n return loadingPromise as Promise<void>\n }\n pendingToken = createSuspenseToken()\n loadingPromise = attemptLoad()\n return loadingPromise as Promise<void>\n }\n\n return component\n}\n"]}
package/dist/slim.cjs ADDED
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ // src/slim.ts
4
+ function $state(_initialValue) {
5
+ throw new Error("$state() is a compiler macro and should be transformed at compile time");
6
+ }
7
+ function $effect(_fn) {
8
+ throw new Error("$effect() is a compiler macro and should be transformed at compile time");
9
+ }
10
+
11
+ exports.$effect = $effect;
12
+ exports.$state = $state;
13
+ //# sourceMappingURL=slim.cjs.map
14
+ //# sourceMappingURL=slim.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/slim.ts"],"names":[],"mappings":";;;AAcO,SAAS,OAAU,aAAA,EAAqB;AAC7C,EAAA,MAAM,IAAI,MAAM,wEAAwE,CAAA;AAC1F;AAMO,SAAS,QAAQ,GAAA,EAAsC;AAC5D,EAAA,MAAM,IAAI,MAAM,yEAAyE,CAAA;AAC3F","file":"slim.cjs","sourcesContent":["/**\n * @fileoverview Fict Slim entrypoint\n *\n * Exposes compiler macros only. Intended for users who want the smallest\n * runtime surface and rely on the compiler to erase macro calls.\n *\n * @public\n * @packageDocumentation\n */\n\n/**\n * Compiler macro for reactive state.\n * This is transformed at compile time and should never be called at runtime.\n */\nexport function $state<T>(_initialValue: T): T {\n throw new Error('$state() is a compiler macro and should be transformed at compile time')\n}\n\n/**\n * Compiler macro for reactive effects.\n * This is transformed at compile time and should never be called at runtime.\n */\nexport function $effect(_fn: () => void | (() => void)): void {\n throw new Error('$effect() is a compiler macro and should be transformed at compile time')\n}\n"]}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fileoverview Fict Slim entrypoint
3
+ *
4
+ * Exposes compiler macros only. Intended for users who want the smallest
5
+ * runtime surface and rely on the compiler to erase macro calls.
6
+ *
7
+ * @public
8
+ * @packageDocumentation
9
+ */
10
+ /**
11
+ * Compiler macro for reactive state.
12
+ * This is transformed at compile time and should never be called at runtime.
13
+ */
14
+ declare function $state<T>(_initialValue: T): T;
15
+ /**
16
+ * Compiler macro for reactive effects.
17
+ * This is transformed at compile time and should never be called at runtime.
18
+ */
19
+ declare function $effect(_fn: () => void | (() => void)): void;
20
+
21
+ export { $effect, $state };
package/dist/slim.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fileoverview Fict Slim entrypoint
3
+ *
4
+ * Exposes compiler macros only. Intended for users who want the smallest
5
+ * runtime surface and rely on the compiler to erase macro calls.
6
+ *
7
+ * @public
8
+ * @packageDocumentation
9
+ */
10
+ /**
11
+ * Compiler macro for reactive state.
12
+ * This is transformed at compile time and should never be called at runtime.
13
+ */
14
+ declare function $state<T>(_initialValue: T): T;
15
+ /**
16
+ * Compiler macro for reactive effects.
17
+ * This is transformed at compile time and should never be called at runtime.
18
+ */
19
+ declare function $effect(_fn: () => void | (() => void)): void;
20
+
21
+ export { $effect, $state };
package/dist/slim.js ADDED
@@ -0,0 +1,11 @@
1
+ // src/slim.ts
2
+ function $state(_initialValue) {
3
+ throw new Error("$state() is a compiler macro and should be transformed at compile time");
4
+ }
5
+ function $effect(_fn) {
6
+ throw new Error("$effect() is a compiler macro and should be transformed at compile time");
7
+ }
8
+
9
+ export { $effect, $state };
10
+ //# sourceMappingURL=slim.js.map
11
+ //# sourceMappingURL=slim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/slim.ts"],"names":[],"mappings":";AAcO,SAAS,OAAU,aAAA,EAAqB;AAC7C,EAAA,MAAM,IAAI,MAAM,wEAAwE,CAAA;AAC1F;AAMO,SAAS,QAAQ,GAAA,EAAsC;AAC5D,EAAA,MAAM,IAAI,MAAM,yEAAyE,CAAA;AAC3F","file":"slim.js","sourcesContent":["/**\n * @fileoverview Fict Slim entrypoint\n *\n * Exposes compiler macros only. Intended for users who want the smallest\n * runtime surface and rely on the compiler to erase macro calls.\n *\n * @public\n * @packageDocumentation\n */\n\n/**\n * Compiler macro for reactive state.\n * This is transformed at compile time and should never be called at runtime.\n */\nexport function $state<T>(_initialValue: T): T {\n throw new Error('$state() is a compiler macro and should be transformed at compile time')\n}\n\n/**\n * Compiler macro for reactive effects.\n * This is transformed at compile time and should never be called at runtime.\n */\nexport function $effect(_fn: () => void | (() => void)): void {\n throw new Error('$effect() is a compiler macro and should be transformed at compile time')\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fict",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Main package for the Fict framework",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -48,6 +48,11 @@
48
48
  "types": "./dist/advanced.d.ts",
49
49
  "import": "./dist/advanced.js",
50
50
  "require": "./dist/advanced.cjs"
51
+ },
52
+ "./slim": {
53
+ "types": "./dist/slim.d.ts",
54
+ "import": "./dist/slim.js",
55
+ "require": "./dist/slim.cjs"
51
56
  }
52
57
  },
53
58
  "sideEffects": false,
@@ -56,12 +61,12 @@
56
61
  "src"
57
62
  ],
58
63
  "dependencies": {
59
- "@fictjs/runtime": "0.2.0"
64
+ "@fictjs/runtime": "0.2.2"
60
65
  },
61
66
  "devDependencies": {
62
67
  "tsup": "^8.5.1",
63
68
  "vite": "^7.3.0",
64
- "@fictjs/vite-plugin": "0.2.0"
69
+ "@fictjs/vite-plugin": "0.2.2"
65
70
  },
66
71
  "scripts": {
67
72
  "build": "tsup",
package/src/resource.ts CHANGED
@@ -128,6 +128,19 @@ export interface Resource<T, Args> {
128
128
  * @param keyOverride - Optional cache key override
129
129
  */
130
130
  prefetch(args: Args, keyOverride?: unknown): void
131
+
132
+ /**
133
+ * Optimistically update cached data for a given args/key.
134
+ *
135
+ * @param argsAccessor - Arguments or a getter returning arguments
136
+ * @param value - New value or updater function
137
+ * @param options - Optional settings (key override, revalidate)
138
+ */
139
+ mutate(
140
+ argsAccessor: (() => Args) | Args,
141
+ value: T | ((prev: T | undefined) => T),
142
+ options?: { key?: unknown; revalidate?: boolean },
143
+ ): void
131
144
  }
132
145
 
133
146
  /**
@@ -171,6 +184,8 @@ interface ResourceEntry<T, Args> {
171
184
  expiresAt: number | undefined
172
185
  /** Currently in-flight fetch promise */
173
186
  inFlight: Promise<void> | undefined
187
+ /** Args used for the current in-flight request */
188
+ inFlightArgs: Args | undefined
174
189
  /** AbortController for cancelling in-flight requests */
175
190
  controller: AbortController | undefined
176
191
  }
@@ -274,6 +289,7 @@ export function resource<T, Args = void>(
274
289
  generation: 0,
275
290
  expiresAt: undefined,
276
291
  inFlight: undefined,
292
+ inFlightArgs: undefined,
277
293
  controller: undefined,
278
294
  }
279
295
  cache.set(key, state)
@@ -304,6 +320,9 @@ export function resource<T, Args = void>(
304
320
  args: Args,
305
321
  isRevalidating = false,
306
322
  ) => {
323
+ if (entry.inFlight && entry.inFlightArgs === args) {
324
+ return
325
+ }
307
326
  entry.controller?.abort()
308
327
  entry.inFlight = undefined
309
328
  const controller = new AbortController()
@@ -351,10 +370,12 @@ export function resource<T, Args = void>(
351
370
  })
352
371
  .finally(() => {
353
372
  entry.inFlight = undefined
373
+ entry.inFlightArgs = undefined
354
374
  entry.controller = undefined
355
375
  })
356
376
 
357
377
  entry.inFlight = fetchPromise
378
+ entry.inFlightArgs = args
358
379
 
359
380
  onCleanup(() => {
360
381
  if (resolvedCacheOptions.mode === 'none') {
@@ -394,6 +415,42 @@ export function resource<T, Args = void>(
394
415
  }
395
416
  }
396
417
 
418
+ const mutate = (
419
+ argsAccessor: (() => Args) | Args,
420
+ value: T | ((prev: T | undefined) => T),
421
+ options?: { key?: unknown; revalidate?: boolean },
422
+ ) => {
423
+ const args = readArgs(argsAccessor)
424
+ const key = options?.key ?? computeKey(args)
425
+ const entry = ensureEntry(key)
426
+ const prevValue = entry.data()
427
+ const nextValue =
428
+ typeof value === 'function' ? (value as (prev: T | undefined) => T)(prevValue) : value
429
+
430
+ entry.controller?.abort()
431
+ entry.inFlight = undefined
432
+ entry.inFlightArgs = undefined
433
+ entry.generation += 1
434
+
435
+ entry.data(nextValue)
436
+ entry.hasValue = true
437
+ entry.status = 'success'
438
+ entry.loading(false)
439
+ entry.error(undefined)
440
+ markExpiry(entry)
441
+ entry.lastArgs = args
442
+ entry.lastVersion = entry.version()
443
+
444
+ if (entry.pendingToken) {
445
+ entry.pendingToken.resolve()
446
+ entry.pendingToken = null
447
+ }
448
+
449
+ if (options?.revalidate) {
450
+ entry.version(entry.version() + 1)
451
+ }
452
+ }
453
+
397
454
  return {
398
455
  read(argsAccessor: (() => Args) | Args): ResourceResult<T> {
399
456
  const entryRef = createSignal<ResourceEntry<T, Args> | null>(null)
@@ -466,5 +523,6 @@ export function resource<T, Args = void>(
466
523
  },
467
524
  invalidate,
468
525
  prefetch,
526
+ mutate,
469
527
  }
470
528
  }
package/src/slim.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @fileoverview Fict Slim entrypoint
3
+ *
4
+ * Exposes compiler macros only. Intended for users who want the smallest
5
+ * runtime surface and rely on the compiler to erase macro calls.
6
+ *
7
+ * @public
8
+ * @packageDocumentation
9
+ */
10
+
11
+ /**
12
+ * Compiler macro for reactive state.
13
+ * This is transformed at compile time and should never be called at runtime.
14
+ */
15
+ export function $state<T>(_initialValue: T): T {
16
+ throw new Error('$state() is a compiler macro and should be transformed at compile time')
17
+ }
18
+
19
+ /**
20
+ * Compiler macro for reactive effects.
21
+ * This is transformed at compile time and should never be called at runtime.
22
+ */
23
+ export function $effect(_fn: () => void | (() => void)): void {
24
+ throw new Error('$effect() is a compiler macro and should be transformed at compile time')
25
+ }
package/src/store.ts CHANGED
@@ -134,6 +134,15 @@ export function $store<T extends object>(initialValue: T): T {
134
134
  boundMethods.set(prop, { ref: currentValue as AnyFn, bound })
135
135
  return bound
136
136
  }
137
+ {
138
+ const boundMethods = BOUND_METHOD_CACHE.get(target)
139
+ if (boundMethods && boundMethods.has(prop)) {
140
+ boundMethods.delete(prop)
141
+ if (boundMethods.size === 0) {
142
+ BOUND_METHOD_CACHE.delete(target)
143
+ }
144
+ }
145
+ }
137
146
 
138
147
  // If the value is an object/array, we recursively wrap it in a store
139
148
  if (typeof currentValue === 'object' && currentValue !== null) {
@@ -145,6 +154,7 @@ export function $store<T extends object>(initialValue: T): T {
145
154
  },
146
155
 
147
156
  set(target, prop, newValue, receiver) {
157
+ const oldLength = Array.isArray(target) && prop === 'length' ? target.length : undefined
148
158
  const oldValue = Reflect.get(target, prop, receiver)
149
159
  const hadKey = Object.prototype.hasOwnProperty.call(target, prop)
150
160
 
@@ -159,6 +169,9 @@ export function $store<T extends object>(initialValue: T): T {
159
169
  const boundMethods = BOUND_METHOD_CACHE.get(target)
160
170
  if (boundMethods && boundMethods.has(prop)) {
161
171
  boundMethods.delete(prop)
172
+ if (boundMethods.size === 0) {
173
+ BOUND_METHOD_CACHE.delete(target)
174
+ }
162
175
  }
163
176
 
164
177
  // Update the signal if it exists
@@ -182,8 +195,19 @@ export function $store<T extends object>(initialValue: T): T {
182
195
  }
183
196
 
184
197
  // If it's an array and length changed implicitly, we might need to handle it.
185
- // But usually 'length' is set explicitly or handled by the runtime.
186
198
  if (Array.isArray(target) && prop === 'length') {
199
+ const nextLength = target.length
200
+ if (typeof oldLength === 'number' && nextLength < oldLength) {
201
+ const signals = SIGNAL_CACHE.get(target)
202
+ if (signals) {
203
+ for (let i = nextLength; i < oldLength; i += 1) {
204
+ const key = String(i)
205
+ if (signals[key]) {
206
+ signals[key](undefined)
207
+ }
208
+ }
209
+ }
210
+ }
187
211
  triggerIteration(target)
188
212
  }
189
213
 
@@ -204,6 +228,9 @@ export function $store<T extends object>(initialValue: T): T {
204
228
  const boundMethods = BOUND_METHOD_CACHE.get(target)
205
229
  if (boundMethods && boundMethods.has(prop)) {
206
230
  boundMethods.delete(prop)
231
+ if (boundMethods.size === 0) {
232
+ BOUND_METHOD_CACHE.delete(target)
233
+ }
207
234
  }
208
235
 
209
236
  triggerIteration(target)
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/store.ts"],"names":["signals"],"mappings":"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","file":"chunk-QPBAYI4X.js","sourcesContent":["/**\n * @fileoverview Deep reactive store implementation for Fict.\n *\n * $store creates a deeply reactive proxy that tracks property access at the path level.\n * Unlike $state (which is shallow), $store allows direct mutation of nested properties.\n *\n * @example\n * ```typescript\n * const user = $store({ name: 'Alice', address: { city: 'London' } })\n * user.address.city = 'Paris' // Fine-grained reactive update\n * ```\n */\n\nimport { createSignal, type Signal } from '@fictjs/runtime/advanced'\n\n/** Function type for bound methods */\ntype AnyFn = (...args: unknown[]) => unknown\n\n/** Cache entry for bound methods to preserve identity */\ninterface BoundMethodEntry {\n ref: AnyFn\n bound: AnyFn\n}\n\n/** Type for objects with indexable properties */\ntype IndexableObject = Record<string | symbol, unknown>\n\n/** Cache of proxied objects to avoid duplicate proxies */\nconst PROXY_CACHE = new WeakMap<object, unknown>()\n\n/** Cache of signals per object property */\nconst SIGNAL_CACHE = new WeakMap<object, Record<string | symbol, Signal<unknown>>>()\n\n/** Cache of bound methods to preserve function identity across reads */\nconst BOUND_METHOD_CACHE = new WeakMap<object, Map<string | symbol, BoundMethodEntry>>()\n\n/** Special key for tracking iteration (Object.keys, for-in, etc.) */\nconst ITERATE_KEY = Symbol('iterate')\n\n/**\n * Get or create a signal for a specific property on a target object.\n * @internal\n */\nfunction getSignal(target: object, prop: string | symbol): Signal<unknown> {\n let signals = SIGNAL_CACHE.get(target)\n if (!signals) {\n signals = {}\n SIGNAL_CACHE.set(target, signals)\n }\n if (!signals[prop]) {\n const initial = prop === ITERATE_KEY ? 0 : (target as IndexableObject)[prop]\n signals[prop] = createSignal(initial)\n }\n return signals[prop]\n}\n\n/**\n * Trigger iteration signal to notify consumers that keys have changed.\n * @internal\n */\nfunction triggerIteration(target: object): void {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[ITERATE_KEY]) {\n const current = signals[ITERATE_KEY]() as number\n signals[ITERATE_KEY](current + 1)\n }\n}\n\n/**\n * Create a deep reactive store using Proxy.\n *\n * Unlike `$state` (which is shallow and compiler-transformed), `$store` provides:\n * - **Deep reactivity**: Nested objects are automatically wrapped in proxies\n * - **Direct mutation**: Modify properties directly without spread operators\n * - **Path-level tracking**: Only components reading changed paths re-render\n *\n * @param initialValue - The initial object to make reactive\n * @returns A reactive proxy of the object\n *\n * @example\n * ```tsx\n * import { $store } from 'fict'\n *\n * const form = $store({\n * user: { name: '', email: '' },\n * settings: { theme: 'light' }\n * })\n *\n * // Direct mutation works\n * form.user.name = 'Alice'\n *\n * // In JSX - only updates when form.user.name changes\n * <input value={form.user.name} />\n * ```\n *\n * @public\n */\nexport function $store<T extends object>(initialValue: T): T {\n if (typeof initialValue !== 'object' || initialValue === null) {\n return initialValue\n }\n\n if (PROXY_CACHE.has(initialValue)) {\n return PROXY_CACHE.get(initialValue) as T\n }\n\n const proxy = new Proxy(initialValue, {\n get(target, prop, receiver) {\n // Always touch the signal so reference changes to this property are tracked,\n // even if the value is an object we proxy further.\n const signal = getSignal(target, prop)\n const trackedValue = signal()\n\n const currentValue = Reflect.get(target, prop, receiver ?? proxy)\n if (currentValue !== trackedValue) {\n // If the value has changed (e.g. via direct mutation of the underlying object not via proxy),\n // we update the signal to keep it in sync.\n // Note: This is a bit of a heuristic. Ideally all mutations go through proxy.\n signal(currentValue)\n }\n\n if (typeof currentValue === 'function') {\n let boundMethods = BOUND_METHOD_CACHE.get(target)\n if (!boundMethods) {\n boundMethods = new Map()\n BOUND_METHOD_CACHE.set(target, boundMethods)\n }\n const cached = boundMethods.get(prop)\n if (cached && cached.ref === currentValue) {\n return cached.bound\n }\n\n const bound = (currentValue as AnyFn).bind(receiver ?? proxy)\n boundMethods.set(prop, { ref: currentValue as AnyFn, bound })\n return bound\n }\n\n // If the value is an object/array, we recursively wrap it in a store\n if (typeof currentValue === 'object' && currentValue !== null) {\n return $store(currentValue as Record<string, unknown>)\n }\n\n // For primitives (and functions), we return the signal value (which tracks the read)\n return currentValue\n },\n\n set(target, prop, newValue, receiver) {\n const oldValue = Reflect.get(target, prop, receiver)\n const hadKey = Object.prototype.hasOwnProperty.call(target, prop)\n\n // If value hasn't changed, do nothing\n if (oldValue === newValue && hadKey) {\n return true\n }\n\n const result = Reflect.set(target, prop, newValue, receiver)\n\n // IMPORTANT: Clear bound method cache BEFORE updating the signal\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n }\n\n // Update the signal if it exists\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[prop]) {\n signals[prop](newValue)\n }\n\n // If new property, trigger iteration update\n if (!hadKey) {\n triggerIteration(target)\n }\n\n // Ensure array length subscribers are notified even if the native push/pop\n // doesn't trigger a separate set trap for \"length\" (defensive).\n if (Array.isArray(target) && prop !== 'length') {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals.length) {\n signals.length(target.length)\n }\n }\n\n // If it's an array and length changed implicitly, we might need to handle it.\n // But usually 'length' is set explicitly or handled by the runtime.\n if (Array.isArray(target) && prop === 'length') {\n triggerIteration(target)\n }\n\n return result\n },\n\n deleteProperty(target, prop) {\n const hadKey = Object.prototype.hasOwnProperty.call(target, prop)\n const result = Reflect.deleteProperty(target, prop)\n\n if (result && hadKey) {\n const signals = SIGNAL_CACHE.get(target)\n if (signals && signals[prop]) {\n signals[prop](undefined)\n }\n\n // Clear bound method cache\n const boundMethods = BOUND_METHOD_CACHE.get(target)\n if (boundMethods && boundMethods.has(prop)) {\n boundMethods.delete(prop)\n }\n\n triggerIteration(target)\n }\n\n return result\n },\n\n ownKeys(target) {\n getSignal(target, ITERATE_KEY)()\n return Reflect.ownKeys(target)\n },\n\n has(target, prop) {\n getSignal(target, prop)()\n return Reflect.has(target, prop)\n },\n })\n\n PROXY_CACHE.set(initialValue, proxy)\n return proxy\n}\n"]}