@solidjs/signals 2.0.0-rc.3 → 2.0.0-rc.5

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 (71) hide show
  1. package/dist/dev.js +2494 -271
  2. package/dist/node.cjs +3691 -1595
  3. package/dist/prod/affects.js +13 -12
  4. package/dist/prod/boundaries.js +43 -35
  5. package/dist/prod/core/action.js +3 -3
  6. package/dist/prod/core/async.js +137 -106
  7. package/dist/prod/core/constants.js +55 -1
  8. package/dist/prod/core/core.js +354 -247
  9. package/dist/prod/core/effect.js +47 -50
  10. package/dist/prod/core/error.js +13 -1
  11. package/dist/prod/core/external.js +4 -4
  12. package/dist/prod/core/graph.js +88 -52
  13. package/dist/prod/core/heap.js +38 -38
  14. package/dist/prod/core/lanes.js +34 -34
  15. package/dist/prod/core/optimistic.js +61 -58
  16. package/dist/prod/core/owner.js +38 -38
  17. package/dist/prod/core/scheduler.js +401 -174
  18. package/dist/prod/core/verdict.js +132 -65
  19. package/dist/prod/index.js +7 -3
  20. package/dist/prod/map.js +106 -106
  21. package/dist/prod/signals.js +253 -25
  22. package/dist/prod/store/index.js +2 -0
  23. package/dist/prod/store/next/optimistic.js +314 -121
  24. package/dist/prod/store/next/patch-hooks.js +13 -0
  25. package/dist/prod/store/next/patch.js +614 -0
  26. package/dist/prod/store/next/projection.js +23 -19
  27. package/dist/prod/store/next/reconcile.js +307 -120
  28. package/dist/prod/store/next/store.js +440 -117
  29. package/dist/prod/store/next/target.js +13 -4
  30. package/dist/prod/store/store.js +5 -5
  31. package/dist/types/core/async.d.ts +2 -0
  32. package/dist/types/core/attribution.d.ts +9 -4
  33. package/dist/types/core/constants.d.ts +54 -0
  34. package/dist/types/core/core.d.ts +34 -21
  35. package/dist/types/core/dev.d.ts +8 -0
  36. package/dist/types/core/error.d.ts +9 -0
  37. package/dist/types/core/graph.d.ts +22 -0
  38. package/dist/types/core/index.d.ts +2 -2
  39. package/dist/types/core/scheduler.d.ts +46 -0
  40. package/dist/types/core/types.d.ts +12 -0
  41. package/dist/types/index.d.ts +3 -3
  42. package/dist/types/signals.d.ts +108 -0
  43. package/dist/types/store/index.d.ts +2 -0
  44. package/dist/types/store/next/optimistic.d.ts +13 -10
  45. package/dist/types/store/next/patch-hooks.d.ts +41 -0
  46. package/dist/types/store/next/patch.d.ts +91 -0
  47. package/dist/types/store/next/projection.d.ts +1 -1
  48. package/dist/types/store/next/reconcile.d.ts +14 -0
  49. package/dist/types/store/next/store.d.ts +52 -2
  50. package/dist/types/store/next/target.d.ts +73 -8
  51. package/dist/types-cjs/core/async.d.cts +2 -0
  52. package/dist/types-cjs/core/attribution.d.cts +9 -4
  53. package/dist/types-cjs/core/constants.d.cts +54 -0
  54. package/dist/types-cjs/core/core.d.cts +34 -21
  55. package/dist/types-cjs/core/dev.d.cts +8 -0
  56. package/dist/types-cjs/core/error.d.cts +9 -0
  57. package/dist/types-cjs/core/graph.d.cts +22 -0
  58. package/dist/types-cjs/core/index.d.cts +2 -2
  59. package/dist/types-cjs/core/scheduler.d.cts +46 -0
  60. package/dist/types-cjs/core/types.d.cts +12 -0
  61. package/dist/types-cjs/index.d.cts +3 -3
  62. package/dist/types-cjs/signals.d.cts +108 -0
  63. package/dist/types-cjs/store/index.d.cts +2 -0
  64. package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
  65. package/dist/types-cjs/store/next/patch-hooks.d.cts +41 -0
  66. package/dist/types-cjs/store/next/patch.d.cts +91 -0
  67. package/dist/types-cjs/store/next/projection.d.cts +1 -1
  68. package/dist/types-cjs/store/next/reconcile.d.cts +14 -0
  69. package/dist/types-cjs/store/next/store.d.cts +52 -2
  70. package/dist/types-cjs/store/next/target.d.cts +73 -8
  71. package/package.json +2 -2
@@ -0,0 +1,91 @@
1
+ import type { Owner } from "../../core/types.js";
2
+ import { type StoreNextTarget } from "./target.js";
3
+ export type PatchFn = (next: any, prev: any, force?: boolean) => void;
4
+ interface PatchEntry {
5
+ fn: PatchFn;
6
+ owner: Owner | null;
7
+ }
8
+ /**
9
+ * Emit a record's visibility transition. Callers gate on `hasPatches()` and
10
+ * `t.d` cheaply; this function re-checks and walks ancestors (§4b).
11
+ */
12
+ export declare function emitPatch(t: StoreNextTarget, next: any, prev: any): void;
13
+ /** Emission for sites that already stand at the record with both sides in
14
+ * hand and have already handled ancestors (the adoption walk descends —
15
+ * parents were visited first), so no bubbling walk. */
16
+ export declare function emitPatchLocal(t: StoreNextTarget, next: any, prev: any): void;
17
+ export declare function emitPatchOptimistic(t: StoreNextTarget, next: any, prev: any): void;
18
+ /** Row-ops emission at OPTIMISTIC (lane) timing: user drafts on an
19
+ * optimistic family must show structure IN FLIGHT — bypassing the
20
+ * transition stash exactly like emitPatchOptimistic. Two forms:
21
+ * - `ops` given (write site): `nextRows` is the draft's intended visible
22
+ * list, ops the identity diff against the pre-write optimistic view.
23
+ * - `ops === null` (revert site): RESYNC — the consumer rebuilds retention
24
+ * by row identity against the live post-revert view, resolved from the
25
+ * target at drain time (overrides are gone by then, so `pb ?? v` IS the
26
+ * committed truth). */
27
+ export declare function emitRowOpsOptimistic(t: StoreNextTarget, nextRows: any[] | null, ops: RowOps | null): void;
28
+ /** Test-only accounting probe: the live registration count must return to
29
+ * baseline across register/unbind/demote cycles. @internal */
30
+ export declare function patchCountForTests(): number;
31
+ export declare function hasPatches(): boolean;
32
+ export declare function registerPatch(record: any, fn: PatchFn): () => void;
33
+ /** Dual-driver bind probe (compiler runtime contract): when `record` is a
34
+ * patchable store record, returns its CURRENT raw backing (the driver's
35
+ * initial force-apply reads it directly — no proxy traffic, no tracking);
36
+ * returns undefined otherwise (driver falls back to the effect path).
37
+ * Not patchable: non-records, non-proxies, accessor-bearing records
38
+ * (patches read raw — getters need tracked evaluation), broken chains. */
39
+ export declare function patchableRaw(record: any): Record<PropertyKey, any> | undefined;
40
+ /** Accessor demotion (design §5): a record that acquires an accessor after
41
+ * registration stops being patchable — reads must go through tracked
42
+ * evaluation. Clears patches and repairs the global count; callers re-drive
43
+ * the pulled bodies (demoteToEffects). */
44
+ export declare function demotePatches(t: StoreNextTarget): PatchEntry[] | null;
45
+ /** The demotion re-drive (re-audit blocker 3): each pulled body becomes the
46
+ * SAME dual-driver effect fallback the web runtime would have chosen had the
47
+ * record carried the accessor at bind — a tracked compute pass (next === prev
48
+ * short-circuits every compare into a pure read THROUGH THE PROXY, so getter
49
+ * dependencies track) plus an untracked force-apply at effect timing.
50
+ *
51
+ * Creation is DEFERRED to the effect phase: the trap that discovers the
52
+ * accessor runs mid-draft, and an effect's initial pass must not read
53
+ * through the proxy inside the write window. The record's own transition
54
+ * for that draft is covered by the new effect's initial force-apply.
55
+ *
56
+ * Known edge (documented): a demoted LIST-ROW body re-drives under its
57
+ * registering owner (the list owner), so per-row severing on removal is
58
+ * lost for demoted rows — the effect lives until the LIST disposes. Rows
59
+ * only demote when user code defines an accessor on a row record at
60
+ * runtime. */
61
+ export declare function demoteToEffects(t: StoreNextTarget): void;
62
+ /** Structural ops for one keyed-array transition. `prefix` rows key-matched
63
+ * in place; for each later index i (absolute), `sources[i - prefix]` is the
64
+ * OLD index its row retained from, or -1 for a new row. `removed` holds the
65
+ * dropped old row values (unbind/teardown handles). Aligned value ticks emit
66
+ * NOTHING — ops exist only when structure changed. */
67
+ export interface RowOps {
68
+ prefix: number;
69
+ sources: number[];
70
+ removed: any[];
71
+ }
72
+ /** `ops === null` is the RESYNC form (optimistic revert): the consumer
73
+ * rebuilds retention by row identity against `next` (the live view). */
74
+ export type RowOpsFn = (next: any[], ops: RowOps | null) => void;
75
+ /** Register a structural-ops consumer on a keyed store array (the list
76
+ * container's channel — what `For` consumes through the seam). */
77
+ export declare function registerRowOps(array: any, fn: RowOpsFn): () => void;
78
+ /** Slot patches (shallow arrays) ride the same apply queue: the walk emits
79
+ * per aligned value-replaced slot; application happens at effect phase under
80
+ * the registration owner's lifetime. */
81
+ export declare function emitSlotPatch(t: StoreNextTarget, index: number, next: any, prev: any): void;
82
+ /** Slot patch for shallow arrays: the reconcile walk emits (index, next,
83
+ * prev) for KEY-ALIGNED value-replaced slots (structure rides row ops), and
84
+ * the emission queues through the patch apply queue — effect-phase timing,
85
+ * transition stamping, disposed-owner drop — like every other channel. */
86
+ export declare function registerSlotPatchNext(arr: any, fn: (index: number, next: any, prev: any) => void): () => void;
87
+ /** Row-ops ride the SAME apply queue/timing as record patches: transition-
88
+ * stamped, applied at effect phase, in emission order (structure before the
89
+ * new rows' own patches can exist; retained rows' value patches commute). */
90
+ export declare function emitRowOps(t: StoreNextTarget, next: any[], ops: RowOps): void;
91
+ export {};
@@ -5,4 +5,4 @@ export declare function createProjectionNext<T extends object = {}>(fn: (draft:
5
5
  * masks the recompute for the tick (core R31 — the manual write wins over a
6
6
  * same-flush dependency change). */
7
7
  export declare function createStoreDerivedNext<T extends object = {}>(fn: (draft: T) => void | T | Promise<void | T> | AsyncIterable<void | T>, seed: Partial<T> | Store<NoFn<T>>, options?: ProjectionOptions): [Refreshable<Store<T>>, (f: (draft: T) => T | void) => void];
8
- export declare function runProjectionComputedNext<T extends object>(wrappedStore: Store<T>, fn: (draft: T) => void | T | Promise<void | T> | AsyncIterable<void | T>, key: string | ((item: NonNullable<any>) => any) | null, wrapCommit?: (write: () => void) => void, onDraftWrite?: () => void): Computed<void | T>;
8
+ export declare function runProjectionComputedNext<T extends object>(wrappedStore: Store<T>, fn: (draft: T) => void | T | Promise<void | T> | AsyncIterable<void | T>, key: string | ((item: NonNullable<any>) => any) | null, wrapCommit?: (write: () => void, value: T) => void, aroundDraftWrite?: (op: () => void) => void): Computed<void | T>;
@@ -1,3 +1,17 @@
1
+ import type { RowOps } from "./patch.js";
2
+ import { type StoreNextTarget } from "./target.js";
1
3
  type KeyFn = (item: any) => any;
2
4
  export declare function reconcileNextState(value: any, state: any, key: string | KeyFn | null | undefined, replace?: boolean): void;
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. */
10
+ 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;
3
17
  export {};
@@ -1,5 +1,7 @@
1
1
  import type { Signal } from "../../core/types.js";
2
- import { type StoreNextFamily, type StoreNextTarget } from "./target.js";
2
+ import { type StoreNextFamily, type StoreNextTarget, type PatchChannel } from "./target.js";
3
+ /** Lazily allocate the patch-channel extension (one literal shape). */
4
+ export declare function pcOf(t: StoreNextTarget): PatchChannel;
3
5
  export declare function wrapNext<T extends Record<PropertyKey, any>>(value: T, parent?: StoreNextTarget | null, parentKey?: PropertyKey | null, fam?: StoreNextFamily | null): T;
4
6
  /** Unwrap our own proxies to their current backing; leave everything else. */
5
7
  export declare function unwrapValue(v: any): any;
@@ -9,6 +11,10 @@ export declare function getKeySetNode(target: StoreNextTarget): Signal<number>;
9
11
  /** Deep-witness bump: any value/shape change on a record with a live deep()
10
12
  * subscriber notifies it. One null check when unused. */
11
13
  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;
12
18
  /** Downgrade a prototype-overlay pending backing to the clone path: builds
13
19
  * the real container (committed + overlay writes − deletes) that fold will
14
20
  * SWAP in as the committed backing, exactly as if the draft had started on
@@ -24,9 +30,16 @@ export declare function materializePB(target: StoreNextTarget): void;
24
30
  * authoritative base (R21/R32).
25
31
  */
26
32
  export declare function adoptPB(target: StoreNextTarget, incoming: Record<PropertyKey, any>, eager?: boolean): void;
33
+ /** Parked truth-staged pending backings (#3164 fold): a tentative draft that
34
+ * opens while a folded landing's backing is live moves the staged container
35
+ * here (see ensurePB); the tentative discard in notifyOptimisticWrites
36
+ * restores it in place of the usual null. */
37
+ export declare const stagedTruthPB: WeakMap<StoreNextTarget, Record<PropertyKey, any>>;
27
38
  /** Same logical slot: both values resolve to one (re-pointed) child target —
28
39
  * adoption preserved identity, so the slot did not change (R9). */
29
40
  export declare function targetsEqual(ov: any, nv: any): boolean;
41
+ export declare function arrayStructureChanged(old: any[], neu: any[]): boolean;
42
+ export declare function membershipChanged(old: Record<PropertyKey, any>, neu: Record<PropertyKey, any>): boolean;
30
43
  /**
31
44
  * The fold diff walks SUBSCRIPTION KEYS ONLY (legacy parity: `for key in
32
45
  * nodes`): nodes exist exactly where something tracked, so unobserved data
@@ -56,6 +69,21 @@ export declare function runAuthoritative<T>(fn: () => T): T;
56
69
  /** Active optimistic override on an armed node (armed slot idles at
57
70
  * NOT_PENDING; undefined = unarmed plain node). */
58
71
  export declare function hasActiveOverride(node: Signal<any>): boolean;
72
+ /** The reading computation is until()'s authoritative-view predicate — same
73
+ * source of truth as core read()'s A17 carve-out (`context`, which persists
74
+ * under untrack). optimisticView()'s composition gate consults exactly this:
75
+ * write-side machinery (patch emission, tentative re-application) must keep
76
+ * composing even when it runs inside an authoritative-write bracket. */
77
+ export declare function authoritativeRead(): boolean;
78
+ /** Serve-side authoritative gate: until()'s predicate PLUS truth authors —
79
+ * the projection derive's draft (wrapDraft trap brackets, runAuthoritative;
80
+ * the same posture pair ensurePB classifies drafts by). A source computing
81
+ * the next truth must never read its callers' tentative overlays: a derive
82
+ * continuation's `store.push` computing its index from an action's
83
+ * optimistic row landed truth in the wrong slot and corrupted committed
84
+ * state (#3108). Trap-level overlay serves gate on this so values, length,
85
+ * membership, and keys leave the authoritative view together. */
86
+ export declare function authoritativeServe(): boolean;
59
87
  export type SetStoreNextFunction<T> = (fn: (draft: T) => T | void) => void;
60
88
  /** Low-level setter primitive: opens write mode on a next proxy, runs `fn`,
61
89
  * emits write-time notifications at outermost exit, applies returned
@@ -63,8 +91,30 @@ export type SetStoreNextFunction<T> = (fn: (draft: T) => T | void) => void;
63
91
  * projection recomputes legitimately write from inside their computed. */
64
92
  export declare function storeSetterNext<T>(proxy: T, fn: (draft: T) => T | void, guard?: boolean): void;
65
93
  export declare function createStoreNext<T extends Record<PropertyKey, any>>(init: T, shallow?: boolean): [T, SetStoreNextFunction<T>];
94
+ /** True when `proxy` is a SHALLOW store (children served verbatim, slots
95
+ * replaced by reference — #2932). The list driver uses this to choose the
96
+ * slot-patch channel (collected row bodies) over per-record registration. */
97
+ export declare function storeIsShallow(proxy: any): boolean;
98
+ /** True when `proxy` belongs to a projection/optimistic FAMILY. The list
99
+ * driver must DECLINE family arrays (external audit finding): family
100
+ * structural changes never emit row/slot ops (the setter channel is
101
+ * fam-gated; optimistic writes ride node overrides), and the proxy identity
102
+ * is stable so the each-watch cannot catch the change either — an engaged
103
+ * list would freeze on optimistic/projection structural updates. Record-
104
+ * level family patches are unaffected (they have their own emission). */
105
+ export declare function storeHasFamily(proxy: any): boolean;
106
+ /** True when `proxy` belongs to an OPTIMISTIC family specifically. The list
107
+ * driver declines these (audit finding, narrowed): optimistic user writes
108
+ * ride node-level overrides — they never enter the reconcile walk, so no
109
+ * row/slot ops are emitted and an engaged list would freeze on optimistic
110
+ * structural changes. PROJECTION (non-optimistic) families are drivable:
111
+ * their recomputes go through the reconcile walk, whose emissions are
112
+ * transition-stamped in the apply queue like any other (equivalence-matrix
113
+ * gated). Re-admitting optimistic families requires a lane-timed structural
114
+ * emission mirroring emitPatchOptimistic, plus revert resync. */
115
+ export declare function storeHasOptimisticFamily(proxy: any): boolean;
66
116
  /** Tracking deep snapshot (`deep()` for next targets): subscribes to the
67
- * key-set and every property node at every reachable level, then returns the
117
+ * key-set and deep-witness node at every reachable level, then returns the
68
118
  * plain view. Shared references and cycles handled via the visited set. */
69
119
  export declare function deepNext<T>(value: T): T;
70
120
  /**
@@ -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, Signal } from "../../core/types.js";
15
+ import type { Computed, Owner, Signal } from "../../core/types.js";
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. */
@@ -27,11 +27,56 @@ export interface StoreNextFamily {
27
27
  /** Targets currently carrying active node overrides (landing-consumption
28
28
  * walk, RUL-2: visible landed truth replaces optimism). */
29
29
  overlaid?: Set<any>;
30
+ /** Retaining transactions (#3164 fold ruling): every transaction that made
31
+ * an optimistic setter call on this family and may still be open. While
32
+ * any member is live, truth landings FOLD — they stage into the retaining
33
+ * transaction and reveal atomically at its settle, exactly like a signal
34
+ * landing under an active override. Dead members prune lazily at each
35
+ * landing (retainingTransition). */
36
+ rt?: Set<any>;
37
+ /** Normalized row-key fn (same resolution as the projection channels:
38
+ * `options.key`, "id" default, null = unkeyed). The staged-landing walk
39
+ * reads it: key-matched rows keep their proxy identity across a fold. */
40
+ key?: ((item: any) => any) | null;
30
41
  map: WeakMap<object, StoreNextTarget>;
31
42
  /** The projection computed — assigned after creation (accessor pattern). */
32
43
  node: Computed<any> | null;
33
44
  shallow?: boolean;
34
45
  }
46
+ /** Write-side patch-channel state (stage 2), grouped off the target's named
47
+ * fields — see the shape rule on `StoreNextTarget.pc`. One literal shape,
48
+ * allocated by `pcOf` on first use. */
49
+ export interface PatchChannel {
50
+ /** Slot-patch hooks for shallow arrays — the reconcile walk emits
51
+ * (i, next, prev) for key-aligned value-replaced slots through the patch
52
+ * apply queue (records are raw, no per-record targets exist).
53
+ * MULTI-CONSUMER (external audit): one array can drive several lists. */
54
+ sp: {
55
+ fn: (index: number, next: any, prev: any) => void;
56
+ owner: Owner | null;
57
+ }[] | null;
58
+ /** Patch-channel consumers (next/patch.ts): per-record compiled patch
59
+ * entries, multi-consumer. null when unpatched (the common case). */
60
+ p: object[] | null;
61
+ /** Same-batch coalescing stamp (re-audit 2/3): the container array this
62
+ * channel last pushed a non-forced SELF entry into, plus that entry. A
63
+ * later same-batch emission UPDATES the queued entry's `next` in place
64
+ * (latest state wins — adoption REPLACES the captured object, so dropping
65
+ * the later emission would apply stale state) while `prev` stays the
66
+ * batch's earliest. The drain clears both stamps so a quiet record
67
+ * retains nothing from its last batch. */
68
+ qa: unknown;
69
+ qe: unknown;
70
+ /** Row-ops consumers (next/patch.ts, PR-B): structural list ops —
71
+ * (nextRows, { prefix, sources, removed }) at apply timing. */
72
+ ro: object[] | null;
73
+ /** Keys written through the traps since the last fold commit. Bounds the
74
+ * setter notify/hold-check to O(written) instead of O(subscribed nodes) —
75
+ * a record with thousands of per-key subscriptions (selection maps) would
76
+ * otherwise pay a full node scan on every write. null = no trap writes
77
+ * this batch (bulk paths fall back to the full scan). */
78
+ wk: Set<PropertyKey> | null;
79
+ }
35
80
  export interface StoreNextTarget {
36
81
  /** Committed backing: source object (shared) or owned clone. */
37
82
  v: Record<PropertyKey, any>;
@@ -47,6 +92,15 @@ export interface StoreNextTarget {
47
92
  h: Record<PropertyKey, Signal<boolean>> | null;
48
93
  /** Lazy key-set node: membership/iteration/$TRACK subscriptions (§6). */
49
94
  k: Signal<number> | null;
95
+ /** Patch-channel extension (lazily allocated on first use): groups the
96
+ * write-side stage-2 fields so they never widen the TARGET's own named
97
+ * field count. LOAD-BEARING SHAPE RULE: array proxy targets carry their
98
+ * fields as named properties on a real array, and V8 normalizes an array
99
+ * to dictionary properties as the named count grows (empirically at
100
+ * counts ≡ 0 mod 3 from 18 up on V8 13.x) — every trap field read then
101
+ * becomes a hash lookup (~15% uibench, tree suites worst). New
102
+ * patch-channel state MUST go inside this object, not on the target. */
103
+ pc: PatchChannel | null;
50
104
  /** Lazy deep-witness node: `deep()` subscribes ONE node per record instead
51
105
  * of one per path; write paths bump it only when it exists. Separate from
52
106
  * `k` so $TRACK/mapArray never rerun on leaf value changes (R9). */
@@ -79,17 +133,21 @@ export interface StoreNextTarget {
79
133
  /** Keys deleted in the overlay window (a prototype overlay cannot shadow
80
134
  * a delete); null when none. */
81
135
  del: Set<PropertyKey> | null;
82
- /** Keys written through the traps since the last fold commit. Bounds the
83
- * setter notify/hold-check to O(written) instead of O(subscribed nodes) —
84
- * a record with thousands of per-key subscriptions (selection maps) would
85
- * otherwise pay a full node scan on every write. null = no trap writes
86
- * this batch (bulk paths fall back to the full scan); WK_ALL sentinel =
87
- * bound unusable this batch (array length write implies index deletes). */
88
- wk: Set<PropertyKey> | null;
89
136
  /** Projection family, null for plain stores (§7b). */
90
137
  fam: StoreNextFamily | null;
91
138
  /** Shallow store root (values served raw). */
92
139
  s: boolean;
140
+ /** Held committed view (#3074/#3075): the pre-hold committed backing,
141
+ * served to committed-visibility readers while `ht` is live. Adoption is
142
+ * eager by contract, but a projection recompute deriving from uncommitted
143
+ * inputs (a transition-held source, or a latest()-pull ahead of the flush)
144
+ * swaps the backing SPECULATIVELY — the old view must stay servable until
145
+ * the hold resolves. */
146
+ hv: Record<PropertyKey, any> | null;
147
+ /** The holder for `hv`: a live transition (cleared lazily when it is done)
148
+ * or the PLAIN_HOLD sentinel (a latest()-pull staging — cleared by the
149
+ * fold commit). null = no hold. */
150
+ ht: any;
93
151
  }
94
152
  /**
95
153
  * Ownership (first cut, decision 2026-08-16d): one WeakSet of store-owned
@@ -112,6 +170,13 @@ export interface OptStoreHooks {
112
170
  notifyOptimisticWrites(t: any, pb: Record<PropertyKey, any>): void;
113
171
  optimisticView(t: any, src: Record<PropertyKey, any>): Record<PropertyKey, any>;
114
172
  applyTentative(t: any, incoming: any, keyFn: ((item: any) => any) | null): void;
173
+ /** #3164 fold: does this live transaction still retain optimism (armed
174
+ * nodes or tracked stores)? Backs the held-truth masks in next/store.ts so
175
+ * plain-store bundles don't carry the transition-optimism probe. */
176
+ retainsOptimism(t: any): boolean;
115
177
  }
116
178
  export declare let optHooks: OptStoreHooks | null;
117
179
  export declare function setOptHooks(h: OptStoreHooks): void;
180
+ /** Sticky descendants flag walk (§6d): reconcile's keyed pruning descends
181
+ * only where subscriptions exist at/below. Nodes AND patches count. */
182
+ export declare function markDescendants(target: StoreNextTarget): void;
@@ -16,6 +16,8 @@ export declare function releaseSettledDependents(el: Computed<any>): void;
16
16
  export declare function settleErroredDependents(el: Computed<any>, error: any): void;
17
17
  export declare function settlePendingSource(el: Computed<any>): void;
18
18
  export declare function isThenable<T>(value: T | PromiseLike<T>): value is PromiseLike<T>;
19
+ /** Fire and clear a node's iterator-flight cancellation hook (#3122). */
20
+ export declare function releaseFlightTeardown(el: Computed<any>): void;
19
21
  export declare function handleAsync<T>(el: Computed<T>, result: T | PromiseLike<T> | AsyncIterable<T>, setter?: (value: T) => void): T;
20
22
  export declare function clearStatus(el: Computed<any>, clearUninitialized?: boolean): void;
21
23
  export declare function notifyStatus(el: Computed<any>, status: number, error: any, blockStatus?: boolean, lane?: OptimisticLane): void;
@@ -60,11 +60,16 @@ export interface RerunEvent {
60
60
  /** Wall time of this run including nested recomputes (ms). */
61
61
  totalMs: number;
62
62
  /**
63
- * Whether the run committed a changed value. A PLAIN memo run with
63
+ * Whether the run produced a changed value. A PLAIN memo run with
64
64
  * `changed: false` was pure waste — the equality cutoff stopped it from
65
- * notifying anyone; an effect run with `changed: false` computed without
66
- * firing its effect phase. Summed as `wastedMs` in costs() (plain,
67
- * non-held runs only see `phase`).
65
+ * notifying anyone. Effects run with `_equals: false` in core (their
66
+ * effect phase re-fires on every recompute), so the engine derives this
67
+ * fact itself: an effect run whose compute output is identical to the
68
+ * previous run's reports `changed: false` — the phase re-fired with the
69
+ * same input, pure waste. Side-effect-only computes (`undefined` output)
70
+ * are exempt: identity of `undefined` proves nothing about their work.
71
+ * Summed as `wastedMs` in costs() (plain, non-held runs only — see
72
+ * `phase`).
68
73
  */
69
74
  changed: boolean;
70
75
  /**
@@ -51,6 +51,60 @@ export declare const CONFIG_CHILD_COMPANIONS: number;
51
51
  * moved into the cold extension (§12), and an unconditional `_x` deref per
52
52
  * marked node measurably taxed the propagation hot path (diamond -22%). */
53
53
  export declare const CONFIG_FW_CHILDREN: number;
54
+ /** Authoritative-view reader (`until()`): while this node computes, reads
55
+ * dodge active optimistic OVERRIDES only — the predicate must observe
56
+ * arriving truth, never the caller's own tentative writes (which would
57
+ * trivially satisfy it). Everything else reads normally, INCLUDING
58
+ * transition-staged `_pendingValue`: staged data is authoritative (optimism
59
+ * lives only in override slots), and a hold that refused staged reads would
60
+ * deadlock on data the open transaction itself is holding (a refresh the
61
+ * action issued lands staged and cannot commit until the hold releases).
62
+ * read() checks the bit on the reading computation (`context`) directly — no
63
+ * ambient flag — so a shared computed the predicate pulls recomputes as
64
+ * itself (no bit) under the normal view, and its cache never forks. */
65
+ export declare const CONFIG_AUTHORITATIVE_READ: number;
66
+ /** Sticky mark: an authoritative-view reader read this node PAST an active
67
+ * override. The ack shape — an authoritative arrival EQUAL to the override —
68
+ * rides paths that are deliberately silent under A17 (every ordinary reader
69
+ * sees the override, so an equal landing changes nothing for them). A marked
70
+ * node notifies those readers on such paths anyway, so the landed truth is
71
+ * seen without re-firing ordinary subscribers. Never cleared — only nodes an
72
+ * until() predicate observed mid-override pay. */
73
+ export declare const CONFIG_AUTHORITATIVE_OBSERVED: number;
74
+ /** Promise-delivery effect (resolve()/until()): commits its computed value
75
+ * directly even when recomputing under its own held transition. These
76
+ * effects deliver applies on a microtask (#2930) instead of the stashed
77
+ * effect queues, so the value must ride the same immediate schedule — a
78
+ * staged value with an immediate apply delivers stale state (resolve) or
79
+ * deadlocks the hold (until). Safe because the node is a private leaf: no
80
+ * subscriber reads an effect's value, only its own apply does. */
81
+ export declare const CONFIG_DIRECT_COMMIT: number;
82
+ /** Fresh-pull reader (awaitable `refresh()`'s waiter effect): a read of a
83
+ * dirty source recomputes it inline even when the height gate defers to the
84
+ * flush. Closes the same-flush ordering race where a waiter created
85
+ * alongside a refresh() mark read the PRE-re-ask value as settled and
86
+ * delivered stale; with the pull, the waiter either parks on the re-ask's
87
+ * pending window (async — woken by the settle walk, which runs on every
88
+ * landing including equal-value ones) or serves its sync answer. resolve()
89
+ * deliberately keeps that race — its contract is "first settled value"
90
+ * (#2930), not "next quiescent state". */
91
+ export declare const CONFIG_FRESH_READ: number;
92
+ /** HELD truth (#3164): this node's staged `_pendingValue` is confirming
93
+ * truth riding a transaction that retains optimism, revealed only at that
94
+ * transaction's settle. Two arming sites, one meaning: the store fold
95
+ * (a landing staged into the retaining transaction) and until()'s
96
+ * flip-entanglement (a foreign carrier's staged write, stolen when it
97
+ * flipped the awaited predicate truthy). Until the reveal, ordinary
98
+ * readers — lane and speculative recomputes included — keep committed:
99
+ * the staging notified subscribers as a plain write, so without the mask
100
+ * a mid-hold recompute composes live optimism with the confirming truth,
101
+ * a frame no timeline contains (GabbeV's union tear). Authoritative
102
+ * readers (until()'s predicate) and latest() tunnel through — the
103
+ * exemption that keeps holds deadlock-free. Override-covered nodes never
104
+ * arm: the override is their display and its revert their notification
105
+ * (A17). Cleared at commit (the commit IS the reveal); subscribers masked
106
+ * during the hold are woken by finalizePureQueue's post-revert pass. */
107
+ export declare const CONFIG_HELD_TRUTH: number;
54
108
  export declare const STATUS_NONE = 0;
55
109
  export declare const STATUS_PENDING: number;
56
110
  export declare const STATUS_ERROR: number;
@@ -1,4 +1,3 @@
1
- import { type Refreshable } from "./constants.cjs";
2
1
  import { type OptimisticLane } from "./lanes.cjs";
3
2
  import type { Computed, FirewallSignal, NodeExtension, NodeOptions, Owner, Signal } from "./types.cjs";
4
3
  export declare const PRIMITIVE_IN_FORBIDDEN_SCOPE_MESSAGE = "[PRIMITIVE_IN_FORBIDDEN_SCOPE] Cannot create reactive primitives inside createTrackedEffect or owner-backed onSettled";
@@ -38,7 +37,21 @@ export declare function ext(el: {
38
37
  * mode (recompute is called explicitly by `effect()`), so we hardcode the lazy bits and skip
39
38
  * the auto-dispose CONFIG bit (effect() previously cleared it post-construction).
40
39
  */
41
- export declare function createEffectNode<T>(fn: (prev?: T) => T, effectFn: (val: T, prev: T | undefined) => void | (() => void), errorFn: ((err: unknown, cleanup: () => void) => void | (() => void)) | undefined, type: number, notifyStatus: ((status?: number, error?: any) => void) | undefined, options: NodeOptions<T> | undefined): any;
40
+ export declare function createEffectNode<T>(fn: (prev?: T) => T, effectFn: (val: T, prev: T | undefined) => void | (() => void), errorFn: ((err: unknown, cleanup: () => void) => void | (() => void)) | undefined, type: number, options: NodeOptions<T> | undefined): any;
41
+ /**
42
+ * The shared status notifier for effect nodes, installed once by effect.ts
43
+ * at module evaluation (`this`-dispatched — one function serves every
44
+ * effect, so nodes never store it). Boundary computeds keep their own
45
+ * per-node channel on `_x._notifyStatus`, which takes precedence.
46
+ */
47
+ export declare let effectStatusNotify: ((this: any, status?: number, error?: any) => void) | null;
48
+ export declare function setEffectStatusNotify(fn: NonNullable<typeof effectStatusNotify>): void;
49
+ /** Resolve a node's status notifier: an own `_x` channel (boundaries) wins;
50
+ * effect nodes (`_type` — EFFECT_PURE is 0, and only effect literals carry
51
+ * the field) fall back to the shared notifier. Presence doubles as the
52
+ * "display consumer" membership test in the status walks, exactly as the
53
+ * per-node field did when every effect carried one. */
54
+ export declare function statusNotifierOf(el: any): ((this: any, status?: number, error?: any) => void) | undefined;
42
55
  export declare function signal<T>(v: T, options?: NodeOptions<T>): Signal<T>;
43
56
  export declare function signal<T>(v: T, options?: NodeOptions<T>, firewall?: Computed<any>): FirewallSignal<T>;
44
57
  export declare function optimisticSignal<T>(v: T, options?: NodeOptions<T>): Signal<T>;
@@ -93,6 +106,19 @@ export declare const READ_SLOW: unique symbol;
93
106
  * snapshot / transition / lane / dev-strictRead state all take the full
94
107
  * resolution. Anything slow returns READ_SLOW; the caller then calls read().
95
108
  */
109
+ /**
110
+ * Wake only authoritative-view readers (until() predicates) subscribed to `el`.
111
+ * The A17-silent ack paths — an authoritative arrival equal to the active
112
+ * override — use this so the predicate re-evaluates without re-firing
113
+ * ordinary subscribers whose visible (override) value did not change.
114
+ * Pay-for-use: reached through GlobalQueue._notifyAuthoritativeObservers,
115
+ * installed at first until() call — apps that never use until() shake it.
116
+ */
117
+ 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). */
121
+ export declare function installAuthoritativeRead(): void;
96
122
  export declare function readNodeFast<T>(el: Signal<T>): T | typeof READ_SLOW;
97
123
  export declare function read<T>(el: Signal<T> | Computed<T>): T;
98
124
  /**
@@ -141,23 +167,10 @@ export declare function setMemo<T>(el: Computed<T>, v: T | ((prev: T) => T)): T;
141
167
  export declare function runWithOwner<T>(owner: Owner | null, fn: () => T): T;
142
168
  export declare function staleValues<T>(fn: () => T, set?: boolean): T;
143
169
  /**
144
- * Invalidates one reactive source, forcing it to re-execute even if its inputs
145
- * haven't changed.
146
- *
147
- * Pass either a Solid-created accessor or a projected store created from
148
- * `createStore(fn, ...)` / `createProjection(...)`. `refresh()` is a
149
- * write-like invalidation operation: it does not read the target's value, and
150
- * refreshing a plain signal accessor is a no-op.
151
- *
152
- * Use it to invalidate cached async values (e.g. force a re-fetch) without
153
- * tearing the consumer down.
154
- *
155
- * @example
156
- * ```ts
157
- * const user = createMemo(async () => fetch(`/users/${id()}`).then(r => r.json()));
158
- *
159
- * // Re-fetch on demand
160
- * <button onClick={() => refresh(user)}>Reload</button>
161
- * ```
170
+ * Core marking half of `refresh()` (the public wrapper lives in signals.ts
171
+ * it validates the target, marks through here, then builds the quiescence
172
+ * promise on the resolve()/until() effect machinery). Flags the node's next
173
+ * recompute as a quiet re-ask and schedules it; no-ops for non-derived or
174
+ * disposed targets and for same-tick manual writes.
162
175
  */
163
- export declare function refresh<T>(target: Refreshable<T>): void;
176
+ export declare function markRefresh(node: Computed<any>): void;
@@ -33,6 +33,14 @@ export interface DiagnosticCapture {
33
33
  export interface Diagnostics {
34
34
  subscribe(listener: DiagnosticListener): () => void;
35
35
  capture(): DiagnosticCapture;
36
+ /**
37
+ * Registers a console footer printed after the first console report of
38
+ * each diagnostic code — a discovery pointer to deeper guidance (e.g.
39
+ * solid-js registers its shipped repair skill). Returning undefined for
40
+ * an event suppresses the footer. Passing undefined unregisters and
41
+ * resets the once-per-code memory.
42
+ */
43
+ setConsoleFooter(footer: ((event: DiagnosticEvent) => string | undefined) | undefined): void;
36
44
  }
37
45
  export interface Dev {
38
46
  hooks: DevHooks;
@@ -39,6 +39,15 @@ export declare class StatusError extends Error {
39
39
  }
40
40
  /** Return the user's error from an internal status wrapper. */
41
41
  export declare function unwrapStatusError(error: unknown): unknown;
42
+ /**
43
+ * Rejection value of `until(fn, { timeout })` when the predicate does not turn
44
+ * truthy within the window. Inside an `action()`, the rejection is thrown back
45
+ * in at the `yield` point — catchable there, or the action fails and its
46
+ * optimistic state reverts.
47
+ */
48
+ export declare class TimeoutError extends Error {
49
+ constructor(message?: string);
50
+ }
42
51
  export declare class NoOwnerError extends Error {
43
52
  constructor();
44
53
  }
@@ -3,4 +3,26 @@ export declare function unlinkSubs(link: Link): Link | null;
3
3
  export declare function trimStaleDeps(el: Computed<any>): void;
4
4
  export declare function clearDeps(el: Computed<unknown>): void;
5
5
  export declare function unobserved(el: Computed<unknown>): void;
6
+ /**
7
+ * Deferred dormancy for never-observed auto-dispose computeds (#3078).
8
+ *
9
+ * An untracked top-level read of a subscriber-less observation-lifecycle memo
10
+ * used to call unobserved() inline at the end of read(). That kept the leak
11
+ * closed (the compute links the memo into its deps' sub lists — without a
12
+ * teardown point a never-observed memo is retained by its sources forever;
13
+ * upstream alien-signals has exactly this retention), but it made reads
14
+ * destructive: each read disposed the node, the next read revived it with a
15
+ * full recompute in whatever ambient transition/lane context happened to be
16
+ * current, so consecutive reads could return different answers with no write
17
+ * in between.
18
+ *
19
+ * Instead, reads queue the node here and the scheduler sweeps at the top of
20
+ * the next flush (before runHeap, so a same-tick dirtying is reclaimed
21
+ * instead of recomputed). Reads become idempotent within a tick (the node
22
+ * stays alive and serves its cache, uniform with observed memos) while
23
+ * reclamation still happens within one microtask — the enqueue site arms
24
+ * schedule(), so a flush is guaranteed even when no other work is queued.
25
+ */
26
+ export declare const dormantNodes: Set<Computed<unknown>>;
27
+ export declare function sweepDormant(): void;
6
28
  export declare function link(dep: Signal<any> | Computed<any>, sub: Computed<any>, pendingObserver?: boolean): void;
@@ -1,5 +1,5 @@
1
- export { ContextNotFoundError, NoOwnerError, NotReadyError } from "./error.cjs";
2
- export { isEqual, untrack, runWithOwner, computed, signal, read, setSignal, setMemo, suppressComputedRecompute, optimisticSignal, optimisticComputed, refresh, staleValues, setSnapshotCapture, markSnapshotScope, releaseSnapshotScope, clearSnapshots } from "./core.cjs";
1
+ export { ContextNotFoundError, NoOwnerError, NotReadyError, TimeoutError } from "./error.cjs";
2
+ export { isEqual, untrack, runWithOwner, computed, signal, read, setSignal, setMemo, suppressComputedRecompute, optimisticSignal, optimisticComputed, installAuthoritativeRead, markRefresh, staleValues, setSnapshotCapture, markSnapshotScope, releaseSnapshotScope, clearSnapshots } from "./core.cjs";
3
3
  export { enableExternalSource, _resetExternalSourceConfig, type ExternalSourceFactory, type ExternalSource, type ExternalSourceConfig } from "./external.cjs";
4
4
  export { createOwner, createRoot, dispose, getNextChildId, getObserver, getOwner, isDisposed, cleanup, peekNextChildId } from "./owner.cjs";
5
5
  export { createContext, getContext, setContext, type Context, type ContextRecord } from "./context.cjs";