@solidjs/signals 2.0.0-rc.6 → 2.0.0-rc.7

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.
Files changed (65) hide show
  1. package/dist/dev.js +2033 -1291
  2. package/dist/node.cjs +1821 -2401
  3. package/dist/node.dev.cjs +13724 -0
  4. package/dist/prod/boundaries.js +3 -1
  5. package/dist/prod/core/async.js +6 -1
  6. package/dist/prod/core/attribution-hooks.js +3 -0
  7. package/dist/prod/core/constants.js +9 -1
  8. package/dist/prod/core/context.js +10 -16
  9. package/dist/prod/core/core.js +215 -139
  10. package/dist/prod/core/dev.js +2 -0
  11. package/dist/prod/core/effect.js +37 -28
  12. package/dist/prod/core/external.js +2 -2
  13. package/dist/prod/core/graph.js +35 -31
  14. package/dist/prod/core/heap.js +34 -40
  15. package/dist/prod/core/lanes.js +41 -27
  16. package/dist/prod/core/optimistic.js +42 -32
  17. package/dist/prod/core/owner.js +32 -32
  18. package/dist/prod/core/scheduler.js +140 -154
  19. package/dist/prod/core/verdict.js +31 -34
  20. package/dist/prod/index.js +0 -2
  21. package/dist/prod/map.js +104 -94
  22. package/dist/prod/signals.js +51 -34
  23. package/dist/prod/store/next/optimistic.js +153 -172
  24. package/dist/prod/store/next/reconcile.js +140 -288
  25. package/dist/prod/store/next/store.js +307 -237
  26. package/dist/prod/store/store.js +2 -2
  27. package/dist/types/core/attribution-hooks.d.ts +64 -0
  28. package/dist/types/core/attribution.d.ts +265 -9
  29. package/dist/types/core/constants.d.ts +8 -0
  30. package/dist/types/core/core.d.ts +12 -3
  31. package/dist/types/core/dev.d.ts +49 -8
  32. package/dist/types/core/heap.d.ts +5 -3
  33. package/dist/types/core/invariants.d.ts +1 -1
  34. package/dist/types/core/lanes.d.ts +10 -0
  35. package/dist/types/core/scheduler.d.ts +9 -4
  36. package/dist/types/signals.d.ts +10 -0
  37. package/dist/types/store/index.d.ts +3 -6
  38. package/dist/types/store/next/optimistic.d.ts +4 -2
  39. package/dist/types/store/next/reconcile.d.ts +5 -12
  40. package/dist/types/store/next/store.d.ts +3 -9
  41. package/dist/types/store/next/target.d.ts +20 -46
  42. package/dist/types/store/store.d.ts +14 -9
  43. package/dist/types-cjs/core/attribution-hooks.d.cts +64 -0
  44. package/dist/types-cjs/core/attribution.d.cts +265 -9
  45. package/dist/types-cjs/core/constants.d.cts +8 -0
  46. package/dist/types-cjs/core/core.d.cts +12 -3
  47. package/dist/types-cjs/core/dev.d.cts +49 -8
  48. package/dist/types-cjs/core/heap.d.cts +5 -3
  49. package/dist/types-cjs/core/invariants.d.cts +1 -1
  50. package/dist/types-cjs/core/lanes.d.cts +10 -0
  51. package/dist/types-cjs/core/scheduler.d.cts +9 -4
  52. package/dist/types-cjs/signals.d.cts +10 -0
  53. package/dist/types-cjs/store/index.d.cts +3 -6
  54. package/dist/types-cjs/store/next/optimistic.d.cts +4 -2
  55. package/dist/types-cjs/store/next/reconcile.d.cts +5 -12
  56. package/dist/types-cjs/store/next/store.d.cts +3 -9
  57. package/dist/types-cjs/store/next/target.d.cts +20 -46
  58. package/dist/types-cjs/store/store.d.cts +14 -9
  59. package/package.json +3 -2
  60. package/dist/prod/store/next/patch-hooks.js +0 -13
  61. package/dist/prod/store/next/patch.js +0 -614
  62. package/dist/types/store/next/patch-hooks.d.ts +0 -41
  63. package/dist/types/store/next/patch.d.ts +0 -91
  64. package/dist/types-cjs/store/next/patch-hooks.d.cts +0 -41
  65. package/dist/types-cjs/store/next/patch.d.cts +0 -91
@@ -105,6 +105,14 @@ export declare const CONFIG_FRESH_READ: number;
105
105
  * (A17). Cleared at commit (the commit IS the reveal); subscribers masked
106
106
  * during the hold are woken by finalizePureQueue's post-revert pass. */
107
107
  export declare const CONFIG_HELD_TRUTH: number;
108
+ /** SLOT node (store leaf): created through `slotSignal` with `_host`/`_key`
109
+ * backrefs baked into the literal. The unobserved sweep dispatches these to
110
+ * the ONE shared hook (`setSlotUnobserved`) instead of a per-node closure
111
+ * held in a per-node extension — store mounts materialize one signal per
112
+ * touched leaf, so per-node allocations (options object, equals closure,
113
+ * unobserved closure, NodeExtension) were the measured create-floor bytes
114
+ * (warm dbmon profile: store node machinery ~36% + GC ~29%). */
115
+ export declare const CONFIG_SLOT_NODE: number;
108
116
  export declare const STATUS_NONE = 0;
109
117
  export declare const STATUS_PENDING: number;
110
118
  export declare const STATUS_ERROR: number;
@@ -54,6 +54,13 @@ export declare function setEffectStatusNotify(fn: NonNullable<typeof effectStatu
54
54
  export declare function statusNotifierOf(el: any): ((this: any, status?: number, error?: any) => void) | undefined;
55
55
  export declare function signal<T>(v: T, options?: NodeOptions<T>): Signal<T>;
56
56
  export declare function signal<T>(v: T, options?: NodeOptions<T>, firewall?: Computed<any>): FirewallSignal<T>;
57
+ /** The shared slot-node unobserved handler — a live binding read directly by
58
+ * the sweep sites (no wrapper frame, no null check: a CONFIG_SLOT_NODE node
59
+ * existing implies the store module loaded and registered the hook). */
60
+ export declare let slotUnobservedHook: (node: Signal<any>) => void;
61
+ /** Install the shared slot-node unobserved handler (store module, once). */
62
+ export declare function setSlotUnobserved(fn: (node: Signal<any>) => void): void;
63
+ export declare function slotSignal<T>(v: T, equals: (a: T, b: T) => boolean, host: object, key: PropertyKey, acc: boolean, firewall?: Computed<unknown> | null): Signal<T>;
57
64
  export declare function optimisticSignal<T>(v: T, options?: NodeOptions<T>): Signal<T>;
58
65
  export declare function optimisticComputed<T>(fn: (prev?: T) => T | PromiseLike<T> | AsyncIterable<T>, options?: NodeOptions<T>): Computed<T>;
59
66
  export declare function isEqual<T>(a: T, b: T): boolean;
@@ -115,9 +122,11 @@ export declare const READ_SLOW: unique symbol;
115
122
  * installed at first until() call — apps that never use until() shake it.
116
123
  */
117
124
  export declare function notifyAuthoritativeObservers(el: Signal<any> | Computed<any>): void;
118
- /** Installs the until() machinery hook. Idempotent; called by until() before
119
- * any authoritative-view read happens (same late-binding contract as the
120
- * optimistic engine). */
125
+ /** Installs the authoritative-reader wakeup hook. Idempotent; called by every
126
+ * creator of a CONFIG_AUTHORITATIVE_READ computation — until() and refresh()
127
+ * before its first read (same late-binding contract as the optimistic engine;
128
+ * the gating bit is only ever set by such a read, so the `!` call sites are
129
+ * safe once every setter installs, #3303). */
121
130
  export declare function installAuthoritativeRead(): void;
122
131
  export declare function readNodeFast<T>(el: Signal<T>): T | typeof READ_SLOW;
123
132
  export declare function read<T>(el: Signal<T> | Computed<T>): T;
@@ -13,8 +13,10 @@ export interface DevHooks {
13
13
  * `warn`/`error` as failures unless they opt in to `info`.
14
14
  */
15
15
  export type DiagnosticSeverity = "info" | "warn" | "error";
16
- export type DiagnosticCode = "STRICT_READ_UNTRACKED" | "PENDING_ASYNC_UNTRACKED_READ" | "PENDING_ASYNC_FORBIDDEN_SCOPE" | "REACTIVE_WRITE_IN_OWNED_SCOPE" | "ACTION_CALLED_IN_OWNED_SCOPE" | "RUN_WITH_DISPOSED_OWNER" | "NO_OWNER_CLEANUP" | "CLEANUP_IN_FORBIDDEN_SCOPE" | "SETTLED_CLEANUP_UNOWNED" | "SETTLE_WALK_UNINITIALIZED_SOURCE" | "FLUSH_IN_EFFECT_CALLBACK" | "PRIMITIVE_IN_FORBIDDEN_SCOPE" | "NO_OWNER_EFFECT" | "NO_OWNER_BOUNDARY" | "ASYNC_OUTSIDE_LOADING_BOUNDARY" | "INVALID_REFRESH_TARGET" | "INVALID_AFFECTS_TARGET" | "MISSING_EFFECT_FN" | "SYNC_NODE_RECEIVED_ASYNC" | "REACTIVITY_HALTED" | "INVARIANT_VIOLATION" | "HUGE_FAN_OUT" | "HUGE_FAN_IN" | "HOT_SCOPE_RERUNS" | "HOT_SCOPE_TIME" | "WIDE_SCOPE_DEPS" | "UNSTABLE_MEMO_OUTPUT" | "WIDE_WRITE" | "ASYNC_WATERFALL" | "HOT_SCOPE_FANOUT";
17
- export type DiagnosticKind = "strict-read" | "async" | "write" | "lifecycle" | "owner" | "error" | "perf" | "graph";
16
+ export type DiagnosticCode = "STRICT_READ_UNTRACKED" | "PENDING_ASYNC_UNTRACKED_READ" | "PENDING_ASYNC_FORBIDDEN_SCOPE" | "REACTIVE_WRITE_IN_OWNED_SCOPE" | "ACTION_CALLED_IN_OWNED_SCOPE" | "RUN_WITH_DISPOSED_OWNER" | "NO_OWNER_CLEANUP" | "CLEANUP_IN_FORBIDDEN_SCOPE" | "SETTLED_CLEANUP_UNOWNED" | "SETTLE_WALK_UNINITIALIZED_SOURCE" | "FLUSH_IN_EFFECT_CALLBACK" | "PRIMITIVE_IN_FORBIDDEN_SCOPE" | "NO_OWNER_EFFECT" | "NO_OWNER_BOUNDARY" | "ASYNC_OUTSIDE_LOADING_BOUNDARY" | "INVALID_REFRESH_TARGET" | "INVALID_AFFECTS_TARGET" | "MISSING_EFFECT_FN" | "SYNC_NODE_RECEIVED_ASYNC" | "REACTIVITY_HALTED" | "INVARIANT_VIOLATION" | "HUGE_FAN_OUT" | "HUGE_FAN_IN" | "HOT_SCOPE_RERUNS" | "HOT_SCOPE_TIME" | "WIDE_SCOPE_DEPS" | "UNSTABLE_MEMO_OUTPUT" | "WIDE_WRITE" | "ASYNC_WATERFALL" | "HOT_SCOPE_FANOUT" | "SILENT_HOLD" | "LONG_HOLD" | "EFFECT_WRITES_OWN_SOURCE" | "EFFECT_RELAY_TEAR" | "IMMUTABLE_UPDATE_IN_STORE" | "UNSTABLE_LIST_IDENTITY";
17
+ export type DiagnosticKind = "strict-read" | "async" | "write" | "lifecycle" | "owner" | "error" | "perf" | "graph"
18
+ /** Perceived responsiveness: the runtime behaved correctly but the user saw no feedback. */
19
+ | "responsiveness";
18
20
  /** First warning when a node's live edge count reaches this size. */
19
21
  export declare const GRAPH_SIZE_WARN_AT = 2000;
20
22
  /** Repeat the warning at this interval after the first. */
@@ -28,6 +30,15 @@ export interface DiagnosticEvent {
28
30
  ownerId?: string;
29
31
  ownerName?: string;
30
32
  nodeName?: string;
33
+ /**
34
+ * Root-first chain of named owners enclosing the subject of the event —
35
+ * component roots as `<Name>`, computations by their `name` option (or
36
+ * the `effect`/`computed` default) — e.g. `["<App>", "<TodoRow>", "effect"]`.
37
+ * Unnamed owners (plain roots) are skipped. Absent when the subject has no
38
+ * named owner at all (a top-level scope, or an unowned primitive — which
39
+ * is usually the finding itself).
40
+ */
41
+ ownerPath?: string[];
31
42
  data?: Record<string, unknown>;
32
43
  }
33
44
  export type DiagnosticListener = (event: DiagnosticEvent) => void;
@@ -40,10 +51,12 @@ export interface Diagnostics {
40
51
  subscribe(listener: DiagnosticListener): () => void;
41
52
  capture(): DiagnosticCapture;
42
53
  /**
43
- * Registers a console footer printed after the first console report of
54
+ * Registers a console footer appended to the first console report of
44
55
  * each diagnostic code — a discovery pointer to deeper guidance (e.g.
45
- * solid-js registers its shipped repair skill). Returning undefined for
46
- * an event suppresses the footer. Passing undefined unregisters and
56
+ * solid-js registers its shipped repair skill). Reported events carry
57
+ * it as trailing lines of the same console entry; events that surface as
58
+ * a thrown error instead get it as a follow-up line. Returning undefined
59
+ * for an event suppresses the footer. Passing undefined unregisters and
47
60
  * resets the once-per-code memory.
48
61
  */
49
62
  setConsoleFooter(footer: ((event: DiagnosticEvent) => string | undefined) | undefined): void;
@@ -68,14 +81,42 @@ export declare const DEV: Dev;
68
81
  * dev builds so user apps degrade instead of crashing.
69
82
  */
70
83
  export declare function assertInvariant(condition: boolean, name: string, message: string): void;
71
- export declare function emitDiagnostic(event: Omit<DiagnosticEvent, "sequence">): DiagnosticEvent;
84
+ /** Anything a diagnostic can be about: an owner (root, computed, effect) or a signal. */
85
+ export type DiagnosticSubject = Owner | Signal<any> | Computed<any>;
86
+ /**
87
+ * Root-first names of the owners enclosing `subject` (inclusive when the
88
+ * subject is itself a named owner). Signals hop to their registering owner
89
+ * (`_owner`, set by registerGraph). Unnamed owners are skipped so the path
90
+ * reads as the component tree plus the scope: `<App> › <TodoRow> › effect`.
91
+ */
92
+ export declare function ownerPath(subject: DiagnosticSubject | null | undefined): string[] | undefined;
93
+ /**
94
+ * Records a diagnostic on the structured channel (listeners, captures) and
95
+ * returns the entry. `subject` locates it: the current reactive `context` by
96
+ * default (right for the synchronous rule checks — they fire inside the
97
+ * scope that misbehaved); pass the node for scheduler-time findings whose
98
+ * ambient context is the flush, or `null` for events that have no location
99
+ * by nature. Console output is a separate step — see `reportDiagnostic`.
100
+ */
101
+ export declare function emitDiagnostic(event: Omit<DiagnosticEvent, "sequence" | "ownerPath">, subject?: DiagnosticSubject | null | undefined): DiagnosticEvent;
102
+ /**
103
+ * The console face of a diagnostic — ONE entry per finding: the message, the
104
+ * owner path (`in <App> › <TodoRow> › effect`) so a human can locate it, the
105
+ * once-per-code footer as trailing lines, and — when the subject is a
106
+ * binding effect the rendering runtime tagged — the element it writes, as a
107
+ * second console argument (hover highlights it, click jumps to Elements).
108
+ * Severity picks the console method. Call sites report the entry
109
+ * `emitDiagnostic` returned so the structured and console channels never
110
+ * disagree.
111
+ */
112
+ export declare function reportDiagnostic(entry: DiagnosticEvent): void;
72
113
  /**
73
114
  * Shared strict-read diagnostics for core read() and the store proxy traps.
74
115
  * Single source for the message text — the #2897 safeguard parity between
75
116
  * memos and stores is exactly these firing identically from both paths.
76
117
  */
77
- export declare function throwPendingUntrackedRead(strictReadLabel: string, fields?: Partial<Omit<DiagnosticEvent, "sequence" | "data">>): never;
78
- export declare function warnStrictReadUntracked(strictReadLabel: string, fields?: Partial<Omit<DiagnosticEvent, "sequence">>): void;
118
+ export declare function throwPendingUntrackedRead(strictReadLabel: string, fields?: Partial<Omit<DiagnosticEvent, "sequence" | "data" | "ownerPath">>): never;
119
+ export declare function warnStrictReadUntracked(strictReadLabel: string, fields?: Partial<Omit<DiagnosticEvent, "sequence" | "ownerPath">>): void;
79
120
  export declare function registerGraph(value: any, owner: Owner | null): void;
80
121
  export declare function clearSignals(node: Owner): void;
81
122
  export declare function getChildren(owner: Owner): Owner[];
@@ -2,9 +2,11 @@ import type { Computed } from "./types.cjs";
2
2
  /** The queue a node belongs to, picked from its own zombie flag. */
3
3
  export declare function queueFor(n: Computed<any>): Heap;
4
4
  /**
5
- * Schedule one subscriber to re-run on the next flush: tracked effects bypass
6
- * the heap and go directly to their effect queue; everything else is inserted
7
- * into its own (zombie-flag-routed) heap with the `_min` cursor pulled down.
5
+ * Schedule one subscriber to re-run on the next flush: inserted into its own
6
+ * (zombie-flag-routed) heap with the `_min` cursor pulled down. Tracked
7
+ * effects ride the heap too the heap visit is their (empty) compute phase,
8
+ * which hands the callback to the user queue once the pass has committed
9
+ * (see GlobalQueue._update, #3291).
8
10
  */
9
11
  export declare function enqueueSub(node: Computed<any>): void;
10
12
  export interface Heap {
@@ -2,7 +2,7 @@ import type { OptimisticLane } from "./lanes.cjs";
2
2
  import type { Computed, Signal } from "./types.cjs";
3
3
  /**
4
4
  * Test-mode invariant checks for the async/transition/lane machinery.
5
- * Catalog and rationale: packages/signals/INTERNALS-ASYNC-STATE.md.
5
+ * Catalog and rationale: packages/signals/docs/INTERNALS-ASYNC-STATE.md.
6
6
  *
7
7
  * These are implementation self-consistency checks, not semantic rules: a
8
8
  * violation means the reactive system contradicted itself.
@@ -24,6 +24,16 @@ export declare function getOrCreateLane(signal: Signal<any>): OptimisticLane;
24
24
  * Union-find: find the root lane.
25
25
  */
26
26
  export declare function findLane(lane: OptimisticLane): OptimisticLane;
27
+ /**
28
+ * Is the lane held? `_pendingAsync` records the async the lane OWNS (derived
29
+ * under it); the transaction's reporter map records the async a render effect
30
+ * OBSERVED pending with no boundary taking it (INV-3, the one registration
31
+ * site). A hold needs both — the same rule the transaction itself uses, so a
32
+ * memo nobody renders, or one a fallback-showing boundary caught, cannot tear
33
+ * a frame and holds nothing (#3289). An orphan lane has no observation record
34
+ * and never holds.
35
+ */
36
+ export declare function laneHeld(lane: OptimisticLane): boolean;
27
37
  /**
28
38
  * Merge two lanes when their dependency graphs overlap.
29
39
  */
@@ -9,7 +9,11 @@ export declare let clock: number;
9
9
  export declare let activeTransition: Transition | null;
10
10
  export declare let projectionWriteActive: boolean;
11
11
  export declare let _hitUnhandledAsync: boolean;
12
- export declare function resetUnhandledAsync(): void;
12
+ /**
13
+ * Consume the unhandled-async hit. Returns whether this is the first report
14
+ * of the current enforcement window — the caller warns only then.
15
+ */
16
+ export declare function resetUnhandledAsync(): boolean;
13
17
  /**
14
18
  * Toggles the dev-mode "must be inside a `<Loading>` boundary" enforcement
15
19
  * window. Only `render()` calls this — wrapping the initial mount so that a
@@ -130,9 +134,10 @@ export declare class GlobalQueue extends Queue {
130
134
  static _laneReadsCommitted: ((el: OptimisticNode, owner: OptimisticNode, c: Computed<any>) => boolean) | null;
131
135
  static _recomputeLane: ((el: Computed<any>, own: boolean) => OptimisticLane | null | false) | null;
132
136
  static _laneAsyncPending: ((el: Computed<any>) => void) | null;
133
- /** Authoritative-view reader wakeup (until()): installed at first until() call.
134
- * Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which only until()'s
135
- * carve-out read can set, so `!` invocations are safe once the gate holds. */
137
+ /** Authoritative-view reader wakeup: installed by until() and refresh() before
138
+ * their first read. Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which
139
+ * only such a reader's carve-out read can set, so `!` invocations are safe once
140
+ * the gate holds (#3303). */
136
141
  static _notifyAuthoritativeObservers: ((el: Signal<any> | Computed<any>) => void) | null;
137
142
  static _laneAsyncSettled: ((el: Computed<any>) => void) | null;
138
143
  static _trackOptimisticStore: ((store: any) => void) | null;
@@ -399,6 +399,16 @@ export declare function createRenderEffect<T>(compute: ComputeFunction<undefined
399
399
  * Creates a tracked reactive effect where dependency tracking and side effects happen
400
400
  * in the same scope.
401
401
  *
402
+ * @deprecated Do not use in new code. For a side effect that follows reactive
403
+ * state, use `createEffect(compute, effect)` — it separates tracking from the
404
+ * side effect, knows its dependencies before it runs, and participates in
405
+ * async and transitions. For one-time DOM work after render (measuring,
406
+ * attaching third-party widgets to a ref), use `onSettled`. Tracking from
407
+ * inside the effect phase — the only thing this primitive adds — is retained
408
+ * solely to ease 1.x migration: it runs beside user-effect callbacks after
409
+ * values commit, never holds a transition, and cannot observe a write staged
410
+ * earlier in the same flush by a signal it has not read yet.
411
+ *
402
412
  * WARNING: Because tracking and effects happen in the same scope, this primitive
403
413
  * may run multiple times for a single change or show tearing (reading inconsistent
404
414
  * state). Use only when dynamic subscription patterns require same-scope tracking.
@@ -4,15 +4,12 @@ export { isWrappable, $TRACK, $PROXY, $TARGET } from "./store.cjs";
4
4
  import type { NoFn, ProjectionOptions, Store, StoreOptions, StoreSetter } from "./store.cjs";
5
5
  import type { Refreshable } from "../core/index.cjs";
6
6
  export { createProjectionNext as createProjection } from "./next/projection.cjs";
7
- export { registerPatch, registerRowOps, registerSlotPatchNext as registerSlotPatch, patchableRaw } from "./next/patch.cjs";
8
7
  export { storeIsShallow, storeHasFamily, storeHasOptimisticFamily } from "./next/store.cjs";
9
8
  export { createOptimisticStoreNext as createOptimisticStore } from "./next/optimistic.cjs";
10
- /** Public createStore: plain form `(init, options?)` and derived writable
9
+ /** Public createStore: plain form `(initialValue, options?)` and derived writable
11
10
  * form `(fn, seed, options?)`. */
12
- export declare function createStore<T extends object = {}>(store: NoFn<T> | Store<NoFn<T>>, options?: StoreOptions & {
13
- shallow?: boolean;
14
- }): [get: Store<T>, set: StoreSetter<T>];
15
- export declare function createStore<T extends object = {}>(fn: (store: T) => void | T | Promise<void | T> | AsyncIterable<void | T>, store: Partial<T> | Store<NoFn<T>>, options?: ProjectionOptions): [get: Refreshable<Store<T>>, set: StoreSetter<T>];
11
+ export declare function createStore<T extends object = {}>(initialValue: NoFn<T> | Store<NoFn<T>>, options?: StoreOptions): [get: Store<T>, set: StoreSetter<T>];
12
+ export declare function createStore<T extends object = {}>(fn: (draft: T) => void | T | Promise<void | T> | AsyncIterable<void | T>, seed: Partial<T> | Store<NoFn<T>>, options?: ProjectionOptions): [get: Refreshable<Store<T>>, set: StoreSetter<T>];
16
13
  export declare function reconcile<T extends U, U>(value: T, key?: string | ((item: NonNullable<any>) => any) | null): (state: U) => T;
17
14
  export declare function snapshot<T>(value: T): T;
18
15
  export declare function deep<T>(value: T): T;
@@ -1,5 +1,6 @@
1
+ import { type Refreshable } from "../../core/index.cjs";
1
2
  import { type Transition } from "../../core/scheduler.cjs";
2
- import { type NoFn, type ProjectionOptions, type Store, type StoreSetter } from "../store.cjs";
3
+ import { type NoFn, type ProjectionOptions, type Store, type StoreOptions, type StoreSetter } from "../store.cjs";
3
4
  import type { StoreNextTarget } from "./target.cjs";
4
5
  /** #3164 fold: a stamped truth is HELD (masked from ordinary readers until
5
6
  * the reveal) only while its transition is live AND retaining optimism —
@@ -8,7 +9,8 @@ import type { StoreNextTarget } from "./target.cjs";
8
9
  * staged values to converge (normal speculation). Resolves merges first:
9
10
  * merge unions optimistic nodes/stores into the target. */
10
11
  export declare function transitionHoldsOptimism(transition: Transition): boolean;
11
- export declare function createOptimisticStoreNext<T extends object = {}>(first: T | ((store: T) => void | T | Promise<void | T> | AsyncIterable<void | T>), second?: NoFn<T> | Store<NoFn<T>>, options?: ProjectionOptions): [get: Store<T>, set: StoreSetter<T>];
12
+ export declare function createOptimisticStoreNext<T extends object = {}>(initialValue: NoFn<T> | Store<NoFn<T>>, options?: StoreOptions): [get: Store<T>, set: StoreSetter<T>];
13
+ export declare function createOptimisticStoreNext<T extends object = {}>(fn: (draft: T) => void | T | Promise<void | T> | AsyncIterable<void | T>, seed: Partial<T> | Store<NoFn<T>>, options?: ProjectionOptions): [get: Refreshable<Store<T>>, set: StoreSetter<T>];
12
14
  /** Diff the draft against the current OPTIMISTIC VIEW (committed + active
13
15
  * overrides — the same view the draft was seeded from) and emit engine writes
14
16
  * for exactly the changed keys. Visible-view diffing keeps no-op writes from
@@ -1,17 +1,10 @@
1
- import type { RowOps } from "./patch.cjs";
2
- import { type StoreNextTarget } from "./target.cjs";
3
1
  type KeyFn = (item: any) => any;
4
2
  export declare function reconcileNextState(value: any, state: any, key: string | KeyFn | null | undefined, replace?: boolean): void;
3
+ /** Setter-channel row ops (the fold site calls this for array targets with
4
+ * ops consumers): structural mutation through the setter — push/splice/index
5
5
  /** Key equality for EVERY key comparison in this module (re-audit 2, P1-5):
6
- * SameValueZero, matching the Map-based matchers (buildRowOps, the adoption
7
- * window) — NaN keys are equal to themselves, so aligned NaN rows stay
8
- * aligned in the prefix walk instead of forever misaligning. Adoption and
9
- * row ops MUST agree on key equality or retained DOM rows go stale. */
6
+ * SameValueZero, matching the adoption window's Map-based matcher NaN keys
7
+ * are equal to themselves, so aligned NaN rows stay aligned in the prefix
8
+ * walk instead of forever misaligning. */
10
9
  export declare function sameKey(a: any, b: any): boolean;
11
- export declare function emitSetterRowOps(t: StoreNextTarget, prevRows: any[], nextRows: any[]): void;
12
- /** Identity-keyed structural diff, returned rather than emitted: shared by
13
- * the setter channel (regular queue) and the OPTIMISTIC write channel (lane
14
- * queue) — same retention semantics, different dispatch timing. Returns
15
- * null when the lists are identity-aligned (no structure changed). */
16
- export declare function buildIdentityRowOps(prevRows: any[], nextRows: any[]): RowOps | null;
17
10
  export {};
@@ -1,20 +1,14 @@
1
1
  import type { Signal } from "../../core/types.cjs";
2
- import { type StoreNextFamily, type StoreNextTarget, type PatchChannel } from "./target.cjs";
3
- /** Lazily allocate the patch-channel extension (one literal shape). */
4
- export declare function pcOf(t: StoreNextTarget): PatchChannel;
2
+ import { type StoreNextFamily, type StoreNextTarget } from "./target.cjs";
5
3
  export declare function wrapNext<T extends Record<PropertyKey, any>>(value: T, parent?: StoreNextTarget | null, parentKey?: PropertyKey | null, fam?: StoreNextFamily | null): T;
6
4
  /** Unwrap our own proxies to their current backing; leave everything else. */
7
5
  export declare function unwrapValue(v: any): any;
8
- export declare function getNode(target: StoreNextTarget, key: PropertyKey, current: any): Signal<any>;
6
+ export declare function getNode(target: StoreNextTarget, key: PropertyKey, current: any, accKnown?: -1 | 0 | 1): Signal<any>;
9
7
  export declare function getHasNode(target: StoreNextTarget, key: PropertyKey, present: boolean): Signal<boolean>;
10
8
  export declare function getKeySetNode(target: StoreNextTarget): Signal<number>;
11
9
  /** Deep-witness bump: any value/shape change on a record with a live deep()
12
10
  * subscriber notifies it. One null check when unused. */
13
11
  export declare function bumpDeep(t: StoreNextTarget): void;
14
- /** Scanned plainness for patch admission (patchableRaw): runs the one-time
15
- * accessor scan if it hasn't happened yet — the sticky `a` flag alone is not
16
- * trustworthy before a scan (it starts false and is discovered lazily). */
17
- export declare function targetIsPlain(target: StoreNextTarget): boolean;
18
12
  /** Downgrade a prototype-overlay pending backing to the clone path: builds
19
13
  * the real container (committed + overlay writes − deletes) that fold will
20
14
  * SWAP in as the committed backing, exactly as if the draft had started on
@@ -90,7 +84,7 @@ export type SetStoreNextFunction<T> = (fn: (draft: T) => T | void) => void;
90
84
  * replacements as adoptions. `guard=false` skips the owned-scope dev guard —
91
85
  * projection recomputes legitimately write from inside their computed. */
92
86
  export declare function storeSetterNext<T>(proxy: T, fn: (draft: T) => T | void, guard?: boolean): void;
93
- export declare function createStoreNext<T extends Record<PropertyKey, any>>(init: T, shallow?: boolean): [T, SetStoreNextFunction<T>];
87
+ export declare function createStoreNext<T extends Record<PropertyKey, any>>(initialValue: T, shallow?: boolean): [T, SetStoreNextFunction<T>];
94
88
  /** True when `proxy` is a SHALLOW store (children served verbatim, slots
95
89
  * replaced by reference — #2932). The list driver uses this to choose the
96
90
  * slot-patch channel (collected row bodies) over per-record registration. */
@@ -12,7 +12,7 @@
12
12
  * entry per read-through object; zero layer slots; nodes, has-nodes, and the
13
13
  * key-set node are lazy, materialized only by subscription.
14
14
  */
15
- import type { Computed, Owner, Signal } from "../../core/types.cjs";
15
+ import type { Computed, Signal } from "../../core/types.cjs";
16
16
  /** Projection family (§7b): children wrap into the family's own map (writes
17
17
  * land in the projection, never the source family), and every node created
18
18
  * under the family carries the projection computed as its firewall. */
@@ -53,40 +53,6 @@ export interface StoreNextFamily {
53
53
  node: Computed<any> | null;
54
54
  shallow?: boolean;
55
55
  }
56
- /** Write-side patch-channel state (stage 2), grouped off the target's named
57
- * fields — see the shape rule on `StoreNextTarget.pc`. One literal shape,
58
- * allocated by `pcOf` on first use. */
59
- export interface PatchChannel {
60
- /** Slot-patch hooks for shallow arrays — the reconcile walk emits
61
- * (i, next, prev) for key-aligned value-replaced slots through the patch
62
- * apply queue (records are raw, no per-record targets exist).
63
- * MULTI-CONSUMER (external audit): one array can drive several lists. */
64
- sp: {
65
- fn: (index: number, next: any, prev: any) => void;
66
- owner: Owner | null;
67
- }[] | null;
68
- /** Patch-channel consumers (next/patch.ts): per-record compiled patch
69
- * entries, multi-consumer. null when unpatched (the common case). */
70
- p: object[] | null;
71
- /** Same-batch coalescing stamp (re-audit 2/3): the container array this
72
- * channel last pushed a non-forced SELF entry into, plus that entry. A
73
- * later same-batch emission UPDATES the queued entry's `next` in place
74
- * (latest state wins — adoption REPLACES the captured object, so dropping
75
- * the later emission would apply stale state) while `prev` stays the
76
- * batch's earliest. The drain clears both stamps so a quiet record
77
- * retains nothing from its last batch. */
78
- qa: unknown;
79
- qe: unknown;
80
- /** Row-ops consumers (next/patch.ts, PR-B): structural list ops —
81
- * (nextRows, { prefix, sources, removed }) at apply timing. */
82
- ro: object[] | null;
83
- /** Keys written through the traps since the last fold commit. Bounds the
84
- * setter notify/hold-check to O(written) instead of O(subscribed nodes) —
85
- * a record with thousands of per-key subscriptions (selection maps) would
86
- * otherwise pay a full node scan on every write. null = no trap writes
87
- * this batch (bulk paths fall back to the full scan). */
88
- wk: Set<PropertyKey> | null;
89
- }
90
56
  export interface StoreNextTarget {
91
57
  /** Committed backing: source object (shared) or owned clone. */
92
58
  v: Record<PropertyKey, any>;
@@ -102,15 +68,19 @@ export interface StoreNextTarget {
102
68
  h: Record<PropertyKey, Signal<boolean>> | null;
103
69
  /** Lazy key-set node: membership/iteration/$TRACK subscriptions (§6). */
104
70
  k: Signal<number> | null;
105
- /** Patch-channel extension (lazily allocated on first use): groups the
106
- * write-side stage-2 fields so they never widen the TARGET's own named
107
- * field count. LOAD-BEARING SHAPE RULE: array proxy targets carry their
108
- * fields as named properties on a real array, and V8 normalizes an array
109
- * to dictionary properties as the named count grows (empirically at
110
- * counts 0 mod 3 from 18 up on V8 13.x) every trap field read then
111
- * becomes a hash lookup (~15% uibench, tree suites worst). New
112
- * patch-channel state MUST go inside this object, not on the target. */
113
- pc: PatchChannel | null;
71
+ /** Keys written through the traps since the last fold commit. Bounds the
72
+ * setter notify/hold-check to O(written) instead of O(subscribed nodes)
73
+ * a record with thousands of per-key subscriptions (selection maps) would
74
+ * otherwise pay a full node scan on every write. null = no trap writes
75
+ * this batch (bulk paths fall back to the full scan); WK_ALL = bound
76
+ * unusable (array length write). LOAD-BEARING SHAPE RULE: array proxy
77
+ * targets carry their fields as named properties on a real array, and V8
78
+ * normalizes an array to dictionary properties as the named count grows
79
+ * (empirically at counts ≡ 0 mod 3 from 18 up on V8 13.x) — every trap
80
+ * field read then becomes a hash lookup (~15% uibench, tree suites
81
+ * worst). Future write-side state MUST ride an extension object, not new
82
+ * named fields. */
83
+ wk: Set<PropertyKey> | null;
114
84
  /** Lazy deep-witness node: `deep()` subscribes ONE node per record instead
115
85
  * of one per path; write paths bump it only when it exists. Separate from
116
86
  * `k` so $TRACK/mapArray never rerun on leaf value changes (R9). */
@@ -130,8 +100,12 @@ export interface StoreNextTarget {
130
100
  /** Accessor scan performed (scan-once on first trap read; adopted data is
131
101
  * not rescanned — legacy-parity behavior). */
132
102
  sc: boolean;
133
- /** Backing was swapped by adoption this batch (fold diff-notifies it). */
134
- adopted: boolean;
103
+ /** Adoption diff base, non-null when the backing was swapped by adoption
104
+ * this batch: the view the nodes were LAST TOLD — the pre-batch committed
105
+ * backing, or the draft's pending backing when a draft preceded the
106
+ * adoption (its setter-exit notifications already moved the nodes, #3296).
107
+ * The deferred fold diffs incoming against this, never against committed. */
108
+ ab: Record<PropertyKey, any> | null;
135
109
  /** Pending backing is a prototype-chain OVERLAY of the committed backing
136
110
  * (`Object.create(v)` — own keys are this batch's writes, everything else
137
111
  * reads through). O(written) per flush instead of O(container) clones
@@ -1,7 +1,7 @@
1
1
  import { type Signal } from "../core/index.cjs";
2
2
  import type { Refreshable } from "../core/index.cjs";
3
- /** A read-only view of a store's value as seen by consumers. Mutate it via the paired `StoreSetter`. */
4
- export type Store<T> = Readonly<T>;
3
+ /** A reactive view of a store's value. Update it through the paired `StoreSetter`. */
4
+ export type Store<T> = T;
5
5
  /**
6
6
  * A store setter. The callback receives a writable **draft** of the store.
7
7
  *
@@ -14,25 +14,30 @@ export type Store<T> = Readonly<T>;
14
14
  *
15
15
  * The setter does **not** perform keyed reconciliation. If you need surviving
16
16
  * items to keep their store identity across full-array replacement, use the
17
- * projection form — `createStore(fn, seed, { key })` or `createProjection` —
18
- * whose derive function reconciles its return by `options.key`.
17
+ * projection form — `createStore(fn, seed, { key })` or
18
+ * `createProjection(fn, seed, { key })` — whose derive function reconciles
19
+ * its return by `options.key`.
19
20
  */
20
21
  export type StoreSetter<T> = (fn: (state: T) => T | void) => void;
21
- /** Tuple returned by the plain `createStore(initialValue)` form. */
22
+ /** Tuple returned by the plain `createStore(initialValue, options?)` form. */
22
23
  export type StoreReturn<T> = [get: Store<T>, set: StoreSetter<T>];
23
24
  /** Tuple returned by the derived `createStore(fn, seed, options?)` form. */
24
25
  export type ProjectionStoreReturn<T> = [get: Refreshable<Store<T>>, set: StoreSetter<T>];
25
- /** Base options for store primitives. */
26
+ /** Options shared by all store primitives. */
26
27
  export interface StoreOptions {
27
28
  /** Debug name (dev mode only) */
28
29
  name?: string;
30
+ /** Single-layer store: root keys reactive, values raw records replaced by reference */
31
+ shallow?: boolean;
29
32
  }
30
- /** Options for derived/projected stores created with `createStore(fn)`, `createProjection`, or `createOptimisticStore(fn)`. */
33
+ /**
34
+ * Options for derived/projected stores created with
35
+ * `createStore(fn, seed, options?)`, `createProjection(fn, seed, options?)`,
36
+ * or `createOptimisticStore(fn, seed, options?)`.
37
+ */
31
38
  export interface ProjectionOptions extends StoreOptions {
32
39
  /** Key property name or function for reconciliation identity; `null` merges positionally */
33
40
  key?: string | ((item: NonNullable<any>) => any) | null;
34
- /** Single-layer store: root keys reactive, values raw records replaced by reference */
35
- shallow?: boolean;
36
41
  /**
37
42
  * Treat the seed as commit #0: the store is born committed with the seed's
38
43
  * contents, shown until the derive's first real answer lands. While that
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidjs/signals",
3
- "version": "2.0.0-rc.6",
3
+ "version": "2.0.0-rc.7",
4
4
  "description": "Solid's reactive primitives: signals, memos, effects, stores, and async-aware computations.",
5
5
  "author": "Ryan Carniato",
6
6
  "license": "MIT",
@@ -34,6 +34,7 @@
34
34
  },
35
35
  "require": {
36
36
  "types": "./dist/types-cjs/index.d.cts",
37
+ "development": "./dist/node.dev.cjs",
37
38
  "default": "./dist/node.cjs"
38
39
  }
39
40
  },
@@ -41,7 +42,7 @@
41
42
  },
42
43
  "scripts": {
43
44
  "build": "npm-run-all -nl build:* && pnpm types",
44
- "build:clean": "rimraf dist/dev dist/prod dist/node dist/dev.js dist/prod.js dist/node.cjs",
45
+ "build:clean": "rimraf dist/dev dist/prod dist/node dist/dev.js dist/prod.js dist/node.cjs dist/node.dev.cjs",
45
46
  "build:js": "rollup -c && node ./scripts/mangle-props.mjs dist/prod dist/node.cjs && node ./scripts/check-pure.mjs dist/prod",
46
47
  "types": "tsc -p tsconfig.build.json && node ../../scripts/sync-dual-types.mjs ./dist/types ./dist/types-cjs",
47
48
  "test": "vitest run",
@@ -1,13 +0,0 @@
1
- let patchHooks = null;
2
-
3
- let rowHooks = null;
4
-
5
- function installPatchHooks(o) {
6
- patchHooks = o;
7
- }
8
-
9
- function installRowHooks(o) {
10
- rowHooks = o;
11
- }
12
-
13
- export { installPatchHooks, installRowHooks, patchHooks, rowHooks };