fict 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fict",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "Main package for the Fict framework",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -74,12 +74,12 @@
74
74
  "src"
75
75
  ],
76
76
  "dependencies": {
77
- "@fictjs/runtime": "0.19.0"
77
+ "@fictjs/runtime": "0.21.0"
78
78
  },
79
79
  "devDependencies": {
80
80
  "tsup": "^8.5.1",
81
81
  "vite": "^7.3.1",
82
- "@fictjs/vite-plugin": "0.19.0"
82
+ "@fictjs/vite-plugin": "0.21.0"
83
83
  },
84
84
  "author": "unadlib",
85
85
  "license": "MIT",
@@ -89,7 +89,7 @@
89
89
  "lint": "eslint src",
90
90
  "typecheck": "tsc --noEmit",
91
91
  "test": "vitest run",
92
- "test:e2e": "playwright test --reporter=list",
92
+ "test:e2e": "env -u FICT_STRICT_GUARANTEE playwright test --reporter=list",
93
93
  "clean": "rm -rf dist"
94
94
  }
95
95
  }
package/src/advanced.ts CHANGED
@@ -34,11 +34,17 @@ export {
34
34
 
35
35
  // Utilities
36
36
  isReactive,
37
+ nonReactive,
38
+ reactive,
37
39
  unwrap,
38
40
 
39
41
  // Debugging & DevTools
42
+ FICT_DEVTOOLS_MIN_PROTOCOL_VERSION,
43
+ FICT_DEVTOOLS_PROTOCOL_VERSION,
40
44
  getDevtoolsHook,
45
+ isDevtoolsHookCompatible,
41
46
  setCycleProtectionOptions,
47
+ type FictDevtoolsCompatibility,
42
48
  type FictDevtoolsHook,
43
49
 
44
50
  // Low-level Primitives
package/src/index.ts CHANGED
@@ -32,10 +32,13 @@
32
32
  * @packageDocumentation
33
33
  */
34
34
 
35
+ import { createUncompiledMacroError } from './macro-diagnostics'
36
+
35
37
  // Re-export everything from runtime
36
38
  export * from '@fictjs/runtime'
37
39
 
38
- // Re-export commonly used advanced APIs for convenience
40
+ // Re-export commonly used advanced APIs for convenience. Manual reactive getter
41
+ // markers stay in `fict/advanced` so the main entrypoint remains user-facing.
39
42
  export { createSelector, createScope, runInScope } from '@fictjs/runtime/advanced'
40
43
 
41
44
  // ============================================================================
@@ -92,7 +95,7 @@ export { $store } from './store'
92
95
  */
93
96
  export function $state<T>(_initialValue: T): T {
94
97
  // This function is never called at runtime - the compiler transforms it
95
- throw new Error('$state() is a compiler macro and should be transformed at compile time')
98
+ throw createUncompiledMacroError('$state')
96
99
  }
97
100
 
98
101
  /**
@@ -110,5 +113,5 @@ export function $state<T>(_initialValue: T): T {
110
113
  */
111
114
  export function $effect(_fn: () => void | (() => void)): void {
112
115
  // This function is never called at runtime - the compiler transforms it
113
- throw new Error('$effect() is a compiler macro and should be transformed at compile time')
116
+ throw createUncompiledMacroError('$effect')
114
117
  }
package/src/internal.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  /**
2
- * @fileoverview Internal compiler/runtime bridge for the main `fict` package.
2
+ * @fileoverview Compiler ABI bridge for the main `fict` package.
3
3
  *
4
- * This subpath is intentionally not documented as a public user API. It exists
5
- * so compiler-generated code can resolve helpers through `fict` without forcing
6
- * applications to install `@fictjs/runtime` separately.
4
+ * This subpath is exported so compiler-generated code can resolve helpers
5
+ * through `fict` without forcing applications to install `@fictjs/runtime`
6
+ * separately. It is not a public user API and should not be imported by
7
+ * application or library source by hand.
8
+ *
9
+ * @internal
10
+ * @packageDocumentation
7
11
  */
8
12
 
9
13
  export * from '@fictjs/runtime/internal'
@@ -0,0 +1,7 @@
1
+ export function createUncompiledMacroError(macroName: '$state' | '$effect'): Error {
2
+ const isDev =
3
+ typeof __DEV__ !== 'undefined'
4
+ ? __DEV__
5
+ : typeof process === 'undefined' || process.env?.NODE_ENV !== 'production'
6
+ return new Error(isDev ? `${macroName}() compile-only.` : 'FICT_E_UNCOMPILED')
7
+ }
package/src/resource.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import { createEffect, onCleanup, createSuspenseToken } from '@fictjs/runtime'
12
- import { createSignal } from '@fictjs/runtime/advanced'
12
+ import { createSignal, isReactive } from '@fictjs/runtime/advanced'
13
13
 
14
14
  /**
15
15
  * The result of reading a resource.
@@ -93,7 +93,7 @@ export interface ResourceOptions<T, Args> {
93
93
  cache?: ResourceCacheOptions
94
94
 
95
95
  /**
96
- * A value or reactive getter that, when changed, resets the resource.
96
+ * A value or explicitly marked reactive getter that, when changed, resets the resource.
97
97
  * Useful for clearing cache when certain conditions change.
98
98
  */
99
99
  reset?: unknown | (() => unknown)
@@ -108,9 +108,9 @@ export interface ResourceOptions<T, Args> {
108
108
  export interface Resource<T, Args> {
109
109
  /**
110
110
  * Read the resource data, triggering a fetch if needed.
111
- * Can accept static args or a reactive getter.
111
+ * Can accept static args or an explicitly marked reactive getter.
112
112
  *
113
- * @param argsAccessor - Arguments or a getter returning arguments
113
+ * @param argsAccessor - Arguments or an explicitly marked getter returning arguments
114
114
  */
115
115
  read(argsAccessor: (() => Args) | Args): ResourceResult<T>
116
116
 
@@ -132,7 +132,7 @@ export interface Resource<T, Args> {
132
132
  /**
133
133
  * Optimistically update cached data for a given args/key.
134
134
  *
135
- * @param argsAccessor - Arguments or a getter returning arguments
135
+ * @param argsAccessor - Arguments or an explicitly marked getter returning arguments
136
136
  * @param value - New value or updater function
137
137
  * @param options - Optional settings (key override, revalidate)
138
138
  */
@@ -208,7 +208,8 @@ const defaultCacheOptions: Required<ResourceCacheOptions> = {
208
208
  *
209
209
  * @example
210
210
  * ```tsx
211
- * import { resource } from 'fict'
211
+ * import { reactive } from 'fict/advanced'
212
+ * import { resource } from 'fict/plus'
212
213
  *
213
214
  * // Simple fetcher
214
215
  * const userResource = resource(
@@ -229,7 +230,7 @@ const defaultCacheOptions: Required<ResourceCacheOptions> = {
229
230
  *
230
231
  * // Usage in component
231
232
  * function UserProfile({ userId }: { userId: string }) {
232
- * const { data, loading, error, refresh } = userResource.read(() => userId)
233
+ * const { data, loading, error, refresh } = userResource.read(reactive(() => userId))
233
234
  *
234
235
  * if (loading) return <Spinner />
235
236
  * if (error) return <ErrorMessage error={error} />
@@ -252,7 +253,7 @@ export function resource<T, Args = void>(
252
253
  const cache = new Map<unknown, ResourceEntry<T, Args>>()
253
254
 
254
255
  const readArgs = (argsAccessor: (() => Args) | Args): Args =>
255
- typeof argsAccessor === 'function' ? (argsAccessor as () => Args)() : argsAccessor
256
+ isReactive(argsAccessor) ? (argsAccessor as () => Args)() : (argsAccessor as Args)
256
257
 
257
258
  const computeKey = (argsAccessor: (() => Args) | Args): unknown => {
258
259
  const argsValue = readArgs(argsAccessor)
@@ -266,7 +267,7 @@ export function resource<T, Args = void>(
266
267
  const readResetToken = (): unknown => {
267
268
  if (typeof optionsOrFetcher !== 'object') return undefined
268
269
  const reset = optionsOrFetcher.reset
269
- if (typeof reset === 'function' && (reset as () => unknown).length === 0) {
270
+ if (isReactive(reset)) {
270
271
  return (reset as () => unknown)()
271
272
  }
272
273
  return reset
package/src/slim.ts CHANGED
@@ -8,12 +8,14 @@
8
8
  * @packageDocumentation
9
9
  */
10
10
 
11
+ import { createUncompiledMacroError } from './macro-diagnostics'
12
+
11
13
  /**
12
14
  * Compiler macro for reactive state.
13
15
  * This is transformed at compile time and should never be called at runtime.
14
16
  */
15
17
  export function $state<T>(_initialValue: T): T {
16
- throw new Error('$state() is a compiler macro and should be transformed at compile time')
18
+ throw createUncompiledMacroError('$state')
17
19
  }
18
20
 
19
21
  /**
@@ -21,5 +23,5 @@ export function $state<T>(_initialValue: T): T {
21
23
  * This is transformed at compile time and should never be called at runtime.
22
24
  */
23
25
  export function $effect(_fn: () => void | (() => void)): void {
24
- throw new Error('$effect() is a compiler macro and should be transformed at compile time')
26
+ throw createUncompiledMacroError('$effect')
25
27
  }
package/src/store.ts CHANGED
@@ -40,11 +40,11 @@ const isDev =
40
40
  ? __DEV__
41
41
  : typeof process === 'undefined' || process.env?.NODE_ENV !== 'production'
42
42
 
43
- /** Track if we've warned about direct mutation for a specific target+property */
44
- const MUTATION_WARNED = new WeakMap<object, Set<string | symbol>>()
43
+ /** Track if we've warned about direct mutation for a target object */
44
+ const MUTATION_WARNED = new WeakSet<object>()
45
45
 
46
46
  /** Properties to skip for direct mutation warning (built-in/internal properties) */
47
- const SKIP_MUTATION_WARNING_PROPS = new Set<string | symbol>([
47
+ const SKIP_MUTATION_WARNING_PROPS: (string | symbol)[] = [
48
48
  'constructor',
49
49
  'prototype',
50
50
  '__proto__',
@@ -57,7 +57,7 @@ const SKIP_MUTATION_WARNING_PROPS = new Set<string | symbol>([
57
57
  Symbol.toStringTag,
58
58
  Symbol.iterator,
59
59
  Symbol.toPrimitive,
60
- ])
60
+ ]
61
61
 
62
62
  /** Cache of signals per object property */
63
63
  const SIGNAL_CACHE = new WeakMap<object, Record<string | symbol, Signal<unknown>>>()
@@ -160,19 +160,15 @@ export function $store<T extends object>(initialValue: T): T {
160
160
  const currentValue = Reflect.get(target, prop, receiver ?? proxy)
161
161
 
162
162
  // Remove "read-time write" - direct mutation is now undefined behavior
163
- // In dev mode, warn once per property if we detect the underlying object was mutated directly
164
- if (isDev && currentValue !== trackedValue && !SKIP_MUTATION_WARNING_PROPS.has(prop)) {
165
- let warnedProps = MUTATION_WARNED.get(target)
166
- if (!warnedProps) {
167
- warnedProps = new Set()
168
- MUTATION_WARNED.set(target, warnedProps)
169
- }
170
- if (!warnedProps.has(prop)) {
171
- warnedProps.add(prop)
172
- console.warn(
173
- `[fict] Direct mutation detected for "${String(prop)}"; mutate via $store proxy.`,
174
- )
175
- }
163
+ // In dev mode, warn once per object if we detect the underlying object was mutated directly
164
+ if (
165
+ isDev &&
166
+ currentValue !== trackedValue &&
167
+ !SKIP_MUTATION_WARNING_PROPS.includes(prop) &&
168
+ !MUTATION_WARNED.has(target)
169
+ ) {
170
+ MUTATION_WARNED.add(target)
171
+ console.warn(`[fict] Use $store for ${String(prop)}.`)
176
172
  }
177
173
 
178
174
  if (typeof currentValue === 'function') {
@@ -244,7 +240,6 @@ export function $store<T extends object>(initialValue: T): T {
244
240
  // Ensure array length subscribers are notified even if the native push/pop
245
241
  // doesn't trigger a separate set trap for "length" (defensive).
246
242
  if (Array.isArray(target) && prop !== 'length') {
247
- const signals = SIGNAL_CACHE.get(target)
248
243
  if (signals && signals.length) {
249
244
  signals.length(target.length)
250
245
  }
@@ -254,7 +249,6 @@ export function $store<T extends object>(initialValue: T): T {
254
249
  if (Array.isArray(target) && prop === 'length') {
255
250
  const nextLength = target.length
256
251
  if (typeof oldLength === 'number' && nextLength < oldLength) {
257
- const signals = SIGNAL_CACHE.get(target)
258
252
  if (signals) {
259
253
  for (let i = nextLength; i < oldLength; i += 1) {
260
254
  const key = String(i)
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/store.ts"],"names":["signals"],"mappings":"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","file":"chunk-43IPAXWW.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/** Symbol to mark proxies and prevent double-wrapping */\nconst IS_STORE_PROXY = Symbol('fict-store-proxy')\n\n/** WeakSet to track raw objects that have been proxied (for reverse lookup) */\nconst RAW_TO_PROXY = new WeakMap<object, object>()\n\n/** Cache of proxied objects to avoid duplicate proxies */\nconst PROXY_CACHE = new WeakMap<object, unknown>()\n\n/** Dev mode detection */\nconst isDev =\n typeof __DEV__ !== 'undefined'\n ? __DEV__\n : typeof process === 'undefined' || process.env?.NODE_ENV !== 'production'\n\n/** Track if we've warned about direct mutation for a specific target+property */\nconst MUTATION_WARNED = new WeakMap<object, Set<string | symbol>>()\n\n/** Properties to skip for direct mutation warning (built-in/internal properties) */\nconst SKIP_MUTATION_WARNING_PROPS = new Set<string | symbol>([\n 'constructor',\n 'prototype',\n '__proto__',\n 'toString',\n 'valueOf',\n 'toLocaleString',\n 'hasOwnProperty',\n 'isPrototypeOf',\n 'propertyIsEnumerable',\n Symbol.toStringTag,\n Symbol.iterator,\n Symbol.toPrimitive,\n])\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 // Prevent double-wrapping - if already a store proxy, return as-is\n if ((initialValue as IndexableObject)[IS_STORE_PROXY]) {\n return initialValue\n }\n\n // Check if this object was already wrapped (reverse lookup)\n if (RAW_TO_PROXY.has(initialValue)) {\n return RAW_TO_PROXY.get(initialValue) as T\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 // Return true for IS_STORE_PROXY to identify this as a store proxy\n if (prop === IS_STORE_PROXY) {\n return true\n }\n\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\n // Remove \"read-time write\" - direct mutation is now undefined behavior\n // In dev mode, warn once per property if we detect the underlying object was mutated directly\n if (isDev && currentValue !== trackedValue && !SKIP_MUTATION_WARNING_PROPS.has(prop)) {\n let warnedProps = MUTATION_WARNED.get(target)\n if (!warnedProps) {\n warnedProps = new Set()\n MUTATION_WARNED.set(target, warnedProps)\n }\n if (!warnedProps.has(prop)) {\n warnedProps.add(prop)\n console.warn(\n `[fict] Direct mutation detected for \"${String(prop)}\"; mutate via $store proxy.`,\n )\n }\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 // Register reverse lookup for double-wrap prevention\n RAW_TO_PROXY.set(initialValue, proxy)\n return proxy\n}\n"]}