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/README.md +8 -0
- package/dist/advanced.cjs +20 -0
- package/dist/advanced.d.cts +1 -1
- package/dist/advanced.d.ts +1 -1
- package/dist/advanced.js +1 -1
- package/dist/{chunk-43IPAXWW.js → chunk-7CM2UZ6M.js} +13 -24
- package/dist/chunk-7CM2UZ6M.js.map +1 -0
- package/dist/chunk-TKWIPOL2.js +9 -0
- package/dist/chunk-TKWIPOL2.js.map +1 -0
- package/dist/index.cjs +18 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/plus.cjs +13 -24
- package/dist/plus.cjs.map +1 -1
- package/dist/plus.d.cts +7 -6
- package/dist/plus.d.ts +7 -6
- package/dist/plus.js +4 -4
- package/dist/plus.js.map +1 -1
- package/dist/slim.cjs +8 -2
- package/dist/slim.cjs.map +1 -1
- package/dist/slim.js +4 -2
- package/dist/slim.js.map +1 -1
- package/package.json +4 -4
- package/src/advanced.ts +6 -0
- package/src/index.ts +6 -3
- package/src/internal.ts +8 -4
- package/src/macro-diagnostics.ts +7 -0
- package/src/resource.ts +10 -9
- package/src/slim.ts +4 -2
- package/src/store.ts +13 -19
- package/dist/chunk-43IPAXWW.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fict",
|
|
3
|
-
"version": "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.
|
|
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.
|
|
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
|
|
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
|
|
116
|
+
throw createUncompiledMacroError('$effect')
|
|
114
117
|
}
|
package/src/internal.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Compiler ABI bridge for the main `fict` package.
|
|
3
3
|
*
|
|
4
|
-
* This subpath is
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
|
111
|
+
* Can accept static args or an explicitly marked reactive getter.
|
|
112
112
|
*
|
|
113
|
-
* @param argsAccessor - Arguments or
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
44
|
-
const MUTATION_WARNED = new
|
|
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
|
|
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
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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"]}
|