@tschk/moonshine-svelte 0.0.0-stage → 0.4.4

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.
@@ -0,0 +1,153 @@
1
+ /** Compiler-facing prop layers; expression caching belongs to the compiler. */
2
+ export type PropLayer = object | null | undefined;
3
+ export type LivePropLayer = PropLayer | (() => PropLayer);
4
+
5
+ // Like object spread, broad index signatures do not erase statically named keys.
6
+ type Known<T> = {
7
+ [
8
+ K in keyof T as string extends K
9
+ ? never
10
+ : number extends K
11
+ ? never
12
+ : symbol extends K
13
+ ? never
14
+ : K
15
+ ]: T[K];
16
+ };
17
+ type IsAny<T> = 0 extends 1 & T ? true : false;
18
+ type LayerValue<T> = T extends () => infer V ? V : T;
19
+ type RequiredKeys<T> = {
20
+ [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
21
+ }[keyof T];
22
+ type SpreadValue<A, B, K extends keyof A | keyof B> = K extends keyof B
23
+ ? K extends RequiredKeys<B>
24
+ ? B[K]
25
+ : B[K] | (K extends keyof A ? A[K] : never)
26
+ : K extends keyof A
27
+ ? A[K]
28
+ : never;
29
+ type Merge<A, B> = A extends unknown
30
+ ? B extends unknown
31
+ ? B extends null | undefined
32
+ ? A
33
+ : {
34
+ [K in RequiredKeys<A> | RequiredKeys<Known<B>>]: SpreadValue<
35
+ A,
36
+ Known<B>,
37
+ K
38
+ >;
39
+ } & {
40
+ [
41
+ K in Exclude<
42
+ keyof A | keyof Known<B>,
43
+ RequiredKeys<A> | RequiredKeys<Known<B>>
44
+ >
45
+ ]?: SpreadValue<A, Known<B>, K>;
46
+ }
47
+ : never
48
+ : never;
49
+ type Layers<
50
+ Items extends readonly unknown[],
51
+ Acc = {},
52
+ > = Items extends readonly [infer Head, ...infer Tail]
53
+ ? IsAny<LayerValue<Head>> extends true
54
+ ? any
55
+ : Layers<Tail, Merge<Acc, LayerValue<Head>>>
56
+ : Acc;
57
+ export type SpreadProps<Items extends readonly LivePropLayer[]> =
58
+ number extends Items["length"] ? Record<PropertyKey, unknown> : Layers<Items>;
59
+
60
+ // Server snapshots enumerate Object.keys, unlike the live DOM proxy's symbols.
61
+ type WithoutSymbols<T> =
62
+ IsAny<T> extends true
63
+ ? any
64
+ : T extends unknown
65
+ ? { [K in keyof T as K extends symbol ? never : K]: T[K] }
66
+ : never;
67
+ export type SpreadServerProps<Items extends readonly PropLayer[]> =
68
+ number extends Items["length"]
69
+ ? Record<PropertyKey, unknown>
70
+ : WithoutSymbols<Layers<Items>>;
71
+
72
+ function resolve(layer: LivePropLayer): PropLayer {
73
+ return typeof layer === "function" ? layer() : layer;
74
+ }
75
+
76
+ export function spreadProps<const Items extends readonly LivePropLayer[]>(
77
+ ...layers: Items
78
+ ): SpreadProps<Items>;
79
+ export function spreadProps(
80
+ ...layers: LivePropLayer[]
81
+ ): Record<PropertyKey, unknown> {
82
+ function descriptor(key: PropertyKey): PropertyDescriptor | undefined {
83
+ for (let index = layers.length - 1; index >= 0; index--) {
84
+ const layer = resolve(layers[index]!);
85
+ if (layer !== null && typeof layer === "object" && key in layer) {
86
+ const found = Object.getOwnPropertyDescriptor(layer, key);
87
+ return found ? { ...found, configurable: true } : undefined;
88
+ }
89
+ }
90
+ }
91
+ return new Proxy(
92
+ {},
93
+ {
94
+ get(_target, key) {
95
+ for (let index = layers.length - 1; index >= 0; index--) {
96
+ const layer = resolve(layers[index]!);
97
+ if (layer !== null && typeof layer === "object" && key in layer)
98
+ return Reflect.get(layer, key);
99
+ }
100
+ },
101
+ set(_target, key, value) {
102
+ for (let index = layers.length - 1; index >= 0; index--) {
103
+ const layer = resolve(layers[index]!);
104
+ // Nullish layers are absent during writes too, avoiding the host null-layer trap.
105
+ if (layer == null) continue;
106
+ const found = Object.getOwnPropertyDescriptor(layer, key);
107
+ if (found?.set) {
108
+ // Svelte forwards through the descriptor, including its receiver.
109
+ found.set(value);
110
+ return true;
111
+ }
112
+ }
113
+ return false;
114
+ },
115
+ has(_target, key) {
116
+ return layers.some((layer) => {
117
+ const value = resolve(layer);
118
+ return value != null && key in value;
119
+ });
120
+ },
121
+ ownKeys() {
122
+ const keys: Array<string | symbol> = [];
123
+ for (const source of layers) {
124
+ const layer = resolve(source);
125
+ if (layer == null) continue;
126
+ for (const key in layer) if (!keys.includes(key)) keys.push(key);
127
+ for (const key of Object.getOwnPropertySymbols(layer))
128
+ if (!keys.includes(key)) keys.push(key);
129
+ }
130
+ return keys;
131
+ },
132
+ getOwnPropertyDescriptor: (_target, key) => descriptor(key),
133
+ },
134
+ );
135
+ }
136
+
137
+ export function spreadServerProps<const Items extends readonly PropLayer[]>(
138
+ layers: Items,
139
+ ): SpreadServerProps<Items>;
140
+ export function spreadServerProps(
141
+ layers: readonly PropLayer[],
142
+ ): Record<PropertyKey, unknown> {
143
+ const merged: Record<PropertyKey, unknown> = {};
144
+ for (const layer of layers) {
145
+ if (layer == null) continue;
146
+ for (const key of Object.keys(layer)) {
147
+ const descriptor = Object.getOwnPropertyDescriptor(layer, key);
148
+ if (descriptor) Object.defineProperty(merged, key, descriptor);
149
+ else merged[key] = Reflect.get(layer, key);
150
+ }
151
+ }
152
+ return merged;
153
+ }
package/src/ssr.ts ADDED
@@ -0,0 +1,50 @@
1
+ /** Compiler-facing server bindings; use explicit native SSR factories. */
2
+ export { createServerProp } from "./server-prop";
3
+ export { afterRender, settle } from "@tschk/moonshine-dom/ssr";
4
+ export { spreadServerProps } from "./spread-props";
5
+ export {
6
+ setStoreValue,
7
+ mutateServerStore,
8
+ updateStoreValue,
9
+ } from "./store-write";
10
+
11
+ import { isSSRRendering } from "@tschk/moonshine-dom/ssr-owner";
12
+ import { peekComponentContext } from "@tschk/moonshine-dom/component";
13
+
14
+ /** Server tick resolves without flushing another renderer's pending work. */
15
+ export async function tick(): Promise<void> {}
16
+
17
+ /** Server state is a plain value box; function values are never invoked. */
18
+ export function createServerState<T>(value: T): { value: T } {
19
+ return { value };
20
+ }
21
+
22
+ /** Capture render-time caching once. Component lowering passes true explicitly. */
23
+ export function createServerDerived<T>(
24
+ compute: () => T,
25
+ cache = isSSRRendering() || peekComponentContext()?.mode === "ssr",
26
+ ): { value: T } {
27
+ let ready = false;
28
+ let cached!: T, updated: T | undefined;
29
+ return {
30
+ get value() {
31
+ if (updated != null) return updated;
32
+ if (!cache) return compute();
33
+ if (!ready) {
34
+ cached = compute();
35
+ ready = true;
36
+ }
37
+ return cached;
38
+ },
39
+ set value(value: T) {
40
+ updated = value;
41
+ },
42
+ };
43
+ }
44
+
45
+ export { createServerStoreReferences } from "./server-store";
46
+
47
+ export type {
48
+ ServerStoreReference,
49
+ ServerStoreReferences,
50
+ } from "./server-store";
package/src/state.ts ADDED
@@ -0,0 +1,492 @@
1
+ /** Independent deep state for compiler lowering; no Svelte internal ABI. */
2
+ import {
3
+ batch,
4
+ createSignal,
5
+ untrack,
6
+ getOwner,
7
+ onCleanup,
8
+ } from "@tschk/moonshine-dom/reactive";
9
+
10
+ import { withHistoryMutation, recordHistory, readHistory } from "./history";
11
+
12
+ type Cell = ReturnType<typeof createSignal<number>>;
13
+ const stateProxies = new WeakSet<object>();
14
+ const origins = new WeakMap<object, object>();
15
+ /** Internal binding comparison: retain overlay values; unwrap only native state origins. */
16
+ export function stateEquals(left: unknown, right: unknown): boolean {
17
+ const original = (value: unknown) =>
18
+ value !== null && typeof value === "object"
19
+ ? (origins.get(value) ?? value)
20
+ : value;
21
+ return Object.is(original(left), original(right));
22
+ }
23
+
24
+ const actionChildren = /* @__PURE__ */ Symbol(
25
+ "native action materialized children",
26
+ );
27
+ type ActionInterest = { proxies: Set<object>; notify: () => void };
28
+ const actionInterests = new WeakMap<object, Set<ActionInterest>>();
29
+ function attachActionInterest(value: unknown, interest: ActionInterest): void {
30
+ if (
31
+ value === null ||
32
+ typeof value !== "object" ||
33
+ !stateProxies.has(value) ||
34
+ interest.proxies.has(value)
35
+ )
36
+ return;
37
+ interest.proxies.add(value);
38
+ let interests = actionInterests.get(value);
39
+ if (!interests) actionInterests.set(value, (interests = new Set()));
40
+ interests.add(interest);
41
+ for (const child of (value as any)[actionChildren])
42
+ attachActionInterest(child, interest);
43
+ }
44
+ function inheritActionInterests(parent: object, value: unknown): void {
45
+ const interests = actionInterests.get(parent);
46
+ if (interests)
47
+ for (const interest of interests) attachActionInterest(value, interest);
48
+ }
49
+ const modifiedProxies = new WeakSet<object>();
50
+ const mutations = new Set([
51
+ "copyWithin",
52
+ "fill",
53
+ "pop",
54
+ "push",
55
+ "reverse",
56
+ "shift",
57
+ "sort",
58
+ "splice",
59
+ "unshift",
60
+ ]);
61
+ function plain(value: unknown): value is Record<PropertyKey, any> {
62
+ if (value === null || typeof value !== "object") return false;
63
+ const proto = Object.getPrototypeOf(value);
64
+ return proto === Object.prototype || proto === Array.prototype;
65
+ }
66
+ function bump(cell: Cell | undefined): void {
67
+ cell?.[1]((value) => value + 1);
68
+ }
69
+ function observe(cells: Map<PropertyKey, Cell>, key: PropertyKey): void {
70
+ let cell = cells.get(key);
71
+ if (!cell) cells.set(key, (cell = createSignal(0)));
72
+ cell[0]();
73
+ }
74
+ export function graph() {
75
+ const deleted = Symbol("deleted");
76
+ function wrap<T>(value: T): T {
77
+ if (!plain(value) || stateProxies.has(value)) return value;
78
+ const raw: Record<PropertyKey, any> = value;
79
+ const array = Array.isArray(raw);
80
+ for (const key of Reflect.ownKeys(raw)) {
81
+ if (array && key === "length") continue;
82
+ const descriptor = Object.getOwnPropertyDescriptor(raw, key)!;
83
+ if (
84
+ !descriptor.configurable ||
85
+ !descriptor.enumerable ||
86
+ !descriptor.writable ||
87
+ !("value" in descriptor)
88
+ )
89
+ throw new TypeError(
90
+ "Native state supports only ordinary writable enumerable data properties",
91
+ );
92
+ }
93
+ const overlay = new Map<PropertyKey, unknown>();
94
+ if (array) overlay.set("length", raw.length);
95
+ const reads = new Map<PropertyKey, Cell>(),
96
+ presence = new Map<PropertyKey, Cell>();
97
+ const keys = createSignal(0),
98
+ methods = new Map<PropertyKey, Function>();
99
+ function ownKeys(): (string | symbol)[] {
100
+ const result = Reflect.ownKeys(raw).filter(
101
+ (key) => overlay.get(key) !== deleted,
102
+ );
103
+ // Own overlay data must shadow inherited names, unlike Svelte's proxy edge behavior.
104
+ for (const [key, next] of overlay) {
105
+ if (next !== deleted && !Object.hasOwn(raw, key))
106
+ result.push(key as string | symbol);
107
+ }
108
+ return result;
109
+ }
110
+ function read(key: PropertyKey, receiver: unknown): unknown {
111
+ if (overlay.has(key)) {
112
+ const next = overlay.get(key);
113
+ return next === deleted ? undefined : next;
114
+ }
115
+ const descriptor = Reflect.getOwnPropertyDescriptor(raw, key);
116
+ if (descriptor?.writable || !Reflect.has(raw, key)) {
117
+ const next = descriptor ? wrap(descriptor.value) : deleted;
118
+ overlay.set(key, next);
119
+ return next === deleted ? undefined : next;
120
+ }
121
+ return Reflect.get(raw, key, receiver);
122
+ }
123
+ function currentDescriptor(
124
+ key: PropertyKey,
125
+ ): PropertyDescriptor | undefined {
126
+ const original = Reflect.getOwnPropertyDescriptor(raw, key);
127
+ if (!overlay.has(key)) return original;
128
+ const next = readHistory(reads, key, () => overlay.get(key));
129
+ if (next === deleted) return undefined;
130
+ return original
131
+ ? { ...original, value: next }
132
+ : {
133
+ value: next,
134
+ enumerable: true,
135
+ configurable: true,
136
+ writable: true,
137
+ };
138
+ }
139
+ function mutate(key: PropertyKey, next: unknown, remove: boolean): boolean {
140
+ return withHistoryMutation(() =>
141
+ batch(() =>
142
+ untrack(() => {
143
+ const before = ownKeys();
144
+ const oldLength = array ? (overlay.get("length") as number) : 0;
145
+ if (array && key === "length" && remove) return false;
146
+ const affected = new Set<PropertyKey>([key]);
147
+ if (array) affected.add("length");
148
+ if (array && key === "length") {
149
+ const length = +(next as number);
150
+ if (length !== length >>> 0)
151
+ throw new RangeError("Invalid array length");
152
+ next = length;
153
+ for (const previous of new Set([
154
+ ...Reflect.ownKeys(raw),
155
+ ...overlay.keys(),
156
+ ]))
157
+ if (
158
+ typeof previous === "string" &&
159
+ /^(0|[1-9]\d*)$/.test(previous) &&
160
+ Number(previous) >= length &&
161
+ Number(previous) < oldLength
162
+ )
163
+ affected.add(previous);
164
+ }
165
+ const old = new Map(
166
+ [...affected].map((property) => [
167
+ property,
168
+ {
169
+ initialized: overlay.has(property),
170
+ value: overlay.has(property)
171
+ ? overlay.get(property)
172
+ : Reflect.get(raw, property),
173
+ has: overlay.has(property)
174
+ ? overlay.get(property) !== deleted
175
+ : Reflect.has(raw, property),
176
+ },
177
+ ]),
178
+ );
179
+ if (array && key === "length")
180
+ for (const property of affected)
181
+ if (property !== "length") overlay.set(property, deleted);
182
+ const resolved = remove ? deleted : wrap(next);
183
+ const previousKey = old.get(key)!;
184
+ overlay.set(
185
+ key,
186
+ previousKey.initialized && previousKey.value === resolved
187
+ ? previousKey.value
188
+ : resolved,
189
+ );
190
+ modifiedProxies.add(proxy);
191
+ if (
192
+ array &&
193
+ resolved !== deleted &&
194
+ typeof key === "string" &&
195
+ /^(0|[1-9]\d*)$/.test(key) &&
196
+ Number(key) < 4294967295 &&
197
+ Number(key) >= oldLength
198
+ )
199
+ overlay.set("length", Number(key) + 1);
200
+ for (const property of affected) {
201
+ const previous = old.get(property)!;
202
+ const current = overlay.has(property)
203
+ ? overlay.get(property)
204
+ : Reflect.get(raw, property);
205
+ const has = overlay.has(property)
206
+ ? overlay.get(property) !== deleted
207
+ : Reflect.has(raw, property);
208
+ if (
209
+ (!previous.initialized && !remove && property === key) ||
210
+ previous.has !== has ||
211
+ previous.value !== current
212
+ ) {
213
+ // First assignment creates an undefined source; deletion/truncation
214
+ // of an unread property creates an already-deleted source instead.
215
+ if (previous.initialized || (!remove && property === key))
216
+ recordHistory(
217
+ reads,
218
+ property,
219
+ previous.initialized ? previous.value : undefined,
220
+ current,
221
+ );
222
+ }
223
+ }
224
+ const after = ownKeys();
225
+ const keysChanged =
226
+ before.length !== after.length ||
227
+ before.some((item, i) => item !== after[i]);
228
+ for (const property of affected) {
229
+ const previous = old.get(property)!;
230
+ const current = overlay.has(property)
231
+ ? overlay.get(property)
232
+ : Reflect.get(raw, property);
233
+ const has = overlay.has(property)
234
+ ? overlay.get(property) !== deleted
235
+ : Reflect.has(raw, property);
236
+ if (previous.has !== has || previous.value !== current)
237
+ bump(reads.get(property));
238
+ if (previous.has !== has) bump(presence.get(property));
239
+ }
240
+ if (keysChanged) keys[1]((version) => version + 1);
241
+ if (
242
+ actionInterests.has(proxy) &&
243
+ (keysChanged ||
244
+ [...affected].some(
245
+ (property) =>
246
+ typeof property === "string" &&
247
+ old.get(property)!.value !==
248
+ (overlay.has(property)
249
+ ? overlay.get(property)
250
+ : Reflect.get(raw, property)),
251
+ ))
252
+ ) {
253
+ if (typeof key === "string")
254
+ inheritActionInterests(proxy, resolved);
255
+ const interests = actionInterests.get(proxy);
256
+ if (interests)
257
+ for (const interest of interests) interest.notify();
258
+ }
259
+ return true;
260
+ }),
261
+ ),
262
+ );
263
+ }
264
+ const proxy = new Proxy(raw, {
265
+ get(_object, key, receiver) {
266
+ if (key === actionChildren)
267
+ return [...overlay]
268
+ .filter(
269
+ ([property, next]) =>
270
+ typeof property === "string" && next !== deleted,
271
+ )
272
+ .map(([, next]) => next);
273
+ observe(reads, key);
274
+ read(key, receiver);
275
+ const stored = readHistory(reads, key, () =>
276
+ overlay.has(key) ? overlay.get(key) : Reflect.get(raw, key, receiver),
277
+ );
278
+ const next = stored === deleted ? undefined : stored;
279
+ if (typeof key === "string") inheritActionInterests(proxy, next);
280
+ if (
281
+ array &&
282
+ mutations.has(key as string) &&
283
+ next === Reflect.get(Array.prototype, key)
284
+ ) {
285
+ let method = methods.get(key);
286
+ if (!method) {
287
+ method = function (this: unknown, ...args: unknown[]) {
288
+ return batch(() =>
289
+ untrack(() => Reflect.apply(next as Function, this, args)),
290
+ );
291
+ };
292
+ methods.set(key, method);
293
+ }
294
+ return method;
295
+ }
296
+ return next;
297
+ },
298
+ set: (_object, key, next) => mutate(key, next, false),
299
+ deleteProperty: (_object, key) => mutate(key, undefined, true),
300
+ has(_object, key) {
301
+ observe(presence, key);
302
+ if (!overlay.has(key) && getOwner()) read(key, proxy);
303
+ return overlay.has(key)
304
+ ? readHistory(reads, key, () => overlay.get(key)) !== deleted
305
+ : Reflect.has(raw, key);
306
+ },
307
+ ownKeys() {
308
+ keys[0]();
309
+ return ownKeys();
310
+ },
311
+ getOwnPropertyDescriptor(_object, key) {
312
+ if (!overlay.has(key) && getOwner()) read(key, proxy);
313
+ return currentDescriptor(key);
314
+ },
315
+ defineProperty(_object, key, descriptor) {
316
+ if (
317
+ !("value" in descriptor) ||
318
+ descriptor.configurable === false ||
319
+ descriptor.enumerable === false ||
320
+ descriptor.writable === false
321
+ )
322
+ throw new TypeError(
323
+ "Native state requires configurable enumerable writable value descriptors",
324
+ );
325
+ return mutate(key, descriptor.value, false);
326
+ },
327
+ setPrototypeOf() {
328
+ throw new TypeError("Native state prototype is fixed");
329
+ },
330
+ preventExtensions() {
331
+ throw new TypeError("Native state must remain extensible");
332
+ },
333
+ });
334
+ origins.set(proxy, raw);
335
+ stateProxies.add(proxy);
336
+ return proxy as T;
337
+ }
338
+ return wrap;
339
+ }
340
+ export interface State<T> {
341
+ value: T;
342
+ }
343
+ export function createState<T>(initial: T): State<T> {
344
+ const wrap = graph();
345
+ const [read, write] = createSignal(wrap(initial));
346
+ return {
347
+ get value() {
348
+ return readHistory(read, "value", read);
349
+ },
350
+ set value(next: T) {
351
+ withHistoryMutation(() => {
352
+ const previous = untrack(read),
353
+ resolved = wrap(next);
354
+ if (previous !== resolved)
355
+ recordHistory(read, "value", previous, resolved);
356
+ write(() => resolved);
357
+ });
358
+ },
359
+ };
360
+ }
361
+ /** Detached snapshot; uncloneable values are retained, as in Svelte production. */
362
+ export function stateSnapshot<T>(value: T): T {
363
+ const seen = new WeakMap<object, object>();
364
+ const activeOrigins = new WeakMap<object, object>();
365
+ const serializing = new WeakSet<object>();
366
+ function copy(input: any, original?: object): any {
367
+ if (input !== null && typeof input === "object") {
368
+ if (seen.has(input)) return seen.get(input);
369
+ if (input instanceof Map) return new Map(input);
370
+ if (input instanceof Set) return new Set(input);
371
+ const array = Array.isArray(input);
372
+ if (array || Object.getPrototypeOf(input) === Object.prototype) {
373
+ const origin = origins.get(input) ?? input;
374
+ const active = activeOrigins.get(origin);
375
+ // Raw cycles create fresh lazy proxies; only collapse untouched implicit edges.
376
+ if (active && !modifiedProxies.has(input)) return active;
377
+ const output: Record<PropertyKey, any> = array ? [] : {};
378
+ seen.set(input, output);
379
+ if (original) seen.set(original, output);
380
+ const previous = activeOrigins.get(origin);
381
+ activeOrigins.set(origin, output);
382
+ try {
383
+ if (array) {
384
+ output.length = input.length;
385
+ for (let index = 0; index < input.length; index++) {
386
+ const next = input[index];
387
+ if (index in input) output[index] = copy(next);
388
+ }
389
+ } else {
390
+ for (const key of Object.keys(input))
391
+ Object.defineProperty(output, key, {
392
+ value: copy(input[key]),
393
+ configurable: true,
394
+ enumerable: true,
395
+ writable: true,
396
+ });
397
+ }
398
+ } finally {
399
+ if (previous) activeOrigins.set(origin, previous);
400
+ else activeOrigins.delete(origin);
401
+ }
402
+ return output;
403
+ }
404
+ if (input instanceof Date) return structuredClone(input);
405
+ if (typeof input.toJSON === "function") {
406
+ if (serializing.has(input)) return input;
407
+ serializing.add(input);
408
+ try {
409
+ return copy(input.toJSON(), input);
410
+ } finally {
411
+ serializing.delete(input);
412
+ }
413
+ }
414
+ if (typeof EventTarget !== "undefined" && input instanceof EventTarget)
415
+ return input;
416
+ }
417
+ try {
418
+ return structuredClone(input);
419
+ } catch {
420
+ return input;
421
+ }
422
+ }
423
+ return untrack(() => copy(value));
424
+ }
425
+
426
+ /** Private action dependency traversal: cycles retain independent overlay identities. */
427
+ export function trackActionState(value: unknown): void {
428
+ const eventTarget = (input: object) =>
429
+ typeof EventTarget !== "undefined" && input instanceof EventTarget;
430
+ if (value === null || typeof value !== "object" || eventTarget(value)) return;
431
+ const visited = new Set<object>(),
432
+ activeOrigins = new Set<object>();
433
+ const interest: ActionInterest = { proxies: new Set(), notify: () => {} };
434
+ // The signal tracks only mutation events; materializing a lazy alias is read-only.
435
+ // Register before traversal so later materialized cycle descendants inherit it.
436
+ const [version, bumpVersion] = createSignal(0);
437
+ interest.notify = () => bumpVersion((v) => v + 1);
438
+ version();
439
+ onCleanup(() => {
440
+ for (const proxy of interest.proxies)
441
+ actionInterests.get(proxy)?.delete(interest);
442
+ interest.proxies.clear();
443
+ });
444
+ function deep(input: unknown): void {
445
+ if (
446
+ input === null ||
447
+ typeof input !== "object" ||
448
+ eventTarget(input) ||
449
+ visited.has(input)
450
+ )
451
+ return;
452
+ visited.add(input);
453
+ attachActionInterest(input, interest);
454
+ const origin = origins.get(input);
455
+ if (origin && activeOrigins.has(origin)) return;
456
+ if (origin) activeOrigins.add(origin);
457
+ try {
458
+ if (input instanceof Date) input.getTime();
459
+ for (const key in input) {
460
+ try {
461
+ deep((input as any)[key]);
462
+ } catch {}
463
+ }
464
+ const proto = Object.getPrototypeOf(input);
465
+ if (
466
+ proto &&
467
+ proto !== Object.prototype &&
468
+ proto !== Array.prototype &&
469
+ proto !== Map.prototype &&
470
+ proto !== Set.prototype &&
471
+ proto !== Date.prototype
472
+ )
473
+ for (const descriptor of Object.values(
474
+ Object.getOwnPropertyDescriptors(proto),
475
+ ))
476
+ if (descriptor.get) {
477
+ try {
478
+ descriptor.get.call(input);
479
+ } catch {}
480
+ }
481
+ } finally {
482
+ if (origin) activeOrigins.delete(origin);
483
+ }
484
+ }
485
+ if (stateProxies.has(value)) deep(value);
486
+ else if (!Array.isArray(value))
487
+ for (const key in value) {
488
+ const next = (value as any)[key];
489
+ if (next !== null && typeof next === "object" && stateProxies.has(next))
490
+ deep(next);
491
+ }
492
+ }