fict 0.2.1 → 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,6 +53,15 @@ 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
  }
@@ -69,6 +78,9 @@ function $store(initialValue) {
69
78
  const boundMethods = BOUND_METHOD_CACHE.get(target);
70
79
  if (boundMethods && boundMethods.has(prop)) {
71
80
  boundMethods.delete(prop);
81
+ if (boundMethods.size === 0) {
82
+ BOUND_METHOD_CACHE.delete(target);
83
+ }
72
84
  }
73
85
  const signals = SIGNAL_CACHE.get(target);
74
86
  if (signals && signals[prop]) {
@@ -111,6 +123,9 @@ function $store(initialValue) {
111
123
  const boundMethods = BOUND_METHOD_CACHE.get(target);
112
124
  if (boundMethods && boundMethods.has(prop)) {
113
125
  boundMethods.delete(prop);
126
+ if (boundMethods.size === 0) {
127
+ BOUND_METHOD_CACHE.delete(target);
128
+ }
114
129
  }
115
130
  triggerIteration(target);
116
131
  }
@@ -130,5 +145,5 @@ function $store(initialValue) {
130
145
  }
131
146
 
132
147
  export { $store };
133
- //# sourceMappingURL=chunk-T5BGEI6S.js.map
134
- //# sourceMappingURL=chunk-T5BGEI6S.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":"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","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,6 +56,15 @@ 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
  }
@@ -72,6 +81,9 @@ function $store(initialValue) {
72
81
  const boundMethods = BOUND_METHOD_CACHE.get(target);
73
82
  if (boundMethods && boundMethods.has(prop)) {
74
83
  boundMethods.delete(prop);
84
+ if (boundMethods.size === 0) {
85
+ BOUND_METHOD_CACHE.delete(target);
86
+ }
75
87
  }
76
88
  const signals = SIGNAL_CACHE.get(target);
77
89
  if (signals && signals[prop]) {
@@ -114,6 +126,9 @@ function $store(initialValue) {
114
126
  const boundMethods = BOUND_METHOD_CACHE.get(target);
115
127
  if (boundMethods && boundMethods.has(prop)) {
116
128
  boundMethods.delete(prop);
129
+ if (boundMethods.size === 0) {
130
+ BOUND_METHOD_CACHE.delete(target);
131
+ }
117
132
  }
118
133
  triggerIteration(target);
119
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 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 }\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 }\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-T5BGEI6S.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,6 +56,15 @@ 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
  }
@@ -72,6 +81,9 @@ function $store(initialValue) {
72
81
  const boundMethods = BOUND_METHOD_CACHE.get(target);
73
82
  if (boundMethods && boundMethods.has(prop)) {
74
83
  boundMethods.delete(prop);
84
+ if (boundMethods.size === 0) {
85
+ BOUND_METHOD_CACHE.delete(target);
86
+ }
75
87
  }
76
88
  const signals = SIGNAL_CACHE.get(target);
77
89
  if (signals && signals[prop]) {
@@ -114,6 +126,9 @@ function $store(initialValue) {
114
126
  const boundMethods = BOUND_METHOD_CACHE.get(target);
115
127
  if (boundMethods && boundMethods.has(prop)) {
116
128
  boundMethods.delete(prop);
129
+ if (boundMethods.size === 0) {
130
+ BOUND_METHOD_CACHE.delete(target);
131
+ }
117
132
  }
118
133
  triggerIteration(target);
119
134
  }
@@ -177,6 +192,7 @@ function resource(optionsOrFetcher) {
177
192
  generation: 0,
178
193
  expiresAt: void 0,
179
194
  inFlight: void 0,
195
+ inFlightArgs: void 0,
180
196
  controller: void 0
181
197
  };
182
198
  cache.set(key, state);
@@ -197,6 +213,9 @@ function resource(optionsOrFetcher) {
197
213
  entry.expiresAt = Number.isFinite(resolvedCacheOptions.ttlMs) ? Date.now() + resolvedCacheOptions.ttlMs : void 0;
198
214
  };
199
215
  const startFetch = (entry, key, args, isRevalidating = false) => {
216
+ if (entry.inFlight && entry.inFlightArgs === args) {
217
+ return;
218
+ }
200
219
  entry.controller?.abort();
201
220
  entry.inFlight = void 0;
202
221
  const controller = new AbortController();
@@ -238,9 +257,11 @@ function resource(optionsOrFetcher) {
238
257
  }
239
258
  }).finally(() => {
240
259
  entry.inFlight = void 0;
260
+ entry.inFlightArgs = void 0;
241
261
  entry.controller = void 0;
242
262
  });
243
263
  entry.inFlight = fetchPromise;
264
+ entry.inFlightArgs = args;
244
265
  runtime.onCleanup(() => {
245
266
  if (resolvedCacheOptions.mode === "none") {
246
267
  controller.abort();
@@ -276,6 +297,32 @@ function resource(optionsOrFetcher) {
276
297
  startFetch(entry, key, args);
277
298
  }
278
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
+ };
279
326
  return {
280
327
  read(argsAccessor) {
281
328
  const entryRef = advanced.createSignal(null);
@@ -333,7 +380,8 @@ function resource(optionsOrFetcher) {
333
380
  };
334
381
  },
335
382
  invalidate,
336
- prefetch
383
+ prefetch,
384
+ mutate
337
385
  };
338
386
  }
339
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 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 }\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 }\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-T5BGEI6S.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fict",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Main package for the Fict framework",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -61,12 +61,12 @@
61
61
  "src"
62
62
  ],
63
63
  "dependencies": {
64
- "@fictjs/runtime": "0.2.1"
64
+ "@fictjs/runtime": "0.2.2"
65
65
  },
66
66
  "devDependencies": {
67
67
  "tsup": "^8.5.1",
68
68
  "vite": "^7.3.0",
69
- "@fictjs/vite-plugin": "0.2.1"
69
+ "@fictjs/vite-plugin": "0.2.2"
70
70
  },
71
71
  "scripts": {
72
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/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) {
@@ -160,6 +169,9 @@ export function $store<T extends object>(initialValue: T): T {
160
169
  const boundMethods = BOUND_METHOD_CACHE.get(target)
161
170
  if (boundMethods && boundMethods.has(prop)) {
162
171
  boundMethods.delete(prop)
172
+ if (boundMethods.size === 0) {
173
+ BOUND_METHOD_CACHE.delete(target)
174
+ }
163
175
  }
164
176
 
165
177
  // Update the signal if it exists
@@ -216,6 +228,9 @@ export function $store<T extends object>(initialValue: T): T {
216
228
  const boundMethods = BOUND_METHOD_CACHE.get(target)
217
229
  if (boundMethods && boundMethods.has(prop)) {
218
230
  boundMethods.delete(prop)
231
+ if (boundMethods.size === 0) {
232
+ BOUND_METHOD_CACHE.delete(target)
233
+ }
219
234
  }
220
235
 
221
236
  triggerIteration(target)
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/store.ts"],"names":["signals"],"mappings":"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","file":"chunk-T5BGEI6S.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 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 }\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 }\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"]}