@solidjs/signals 2.0.0-rc.1 → 2.0.0-rc.11

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 (150) hide show
  1. package/dist/dev-shared.js +7155 -0
  2. package/dist/dev.attribution.js +3307 -0
  3. package/dist/dev.js +5765 -4496
  4. package/dist/observe/affects.js +129 -0
  5. package/dist/observe/attribution.js +7 -0
  6. package/dist/observe/boundaries.js +793 -0
  7. package/dist/observe/core/action.js +177 -0
  8. package/dist/observe/core/async.js +849 -0
  9. package/dist/observe/core/attribution-costs.js +66 -0
  10. package/dist/observe/core/attribution-feedback.js +280 -0
  11. package/dist/observe/core/attribution-hooks.js +83 -0
  12. package/dist/observe/core/attribution-queries.js +43 -0
  13. package/dist/observe/core/attribution.js +2904 -0
  14. package/dist/observe/core/constants.js +283 -0
  15. package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
  16. package/dist/observe/core/core.js +1999 -0
  17. package/dist/observe/core/dev.js +343 -0
  18. package/dist/observe/core/effect.js +200 -0
  19. package/dist/observe/core/error-hooks.js +71 -0
  20. package/dist/observe/core/error.js +80 -0
  21. package/dist/observe/core/external.js +98 -0
  22. package/dist/observe/core/graph.js +155 -0
  23. package/dist/observe/core/heap.js +147 -0
  24. package/dist/observe/core/invariants.js +45 -0
  25. package/dist/observe/core/lanes.js +205 -0
  26. package/dist/observe/core/optimistic.js +669 -0
  27. package/dist/observe/core/owner.js +353 -0
  28. package/dist/observe/core/scheduler.js +1561 -0
  29. package/dist/observe/core/verdict.js +674 -0
  30. package/dist/observe/index.js +59 -0
  31. package/dist/observe/map.js +507 -0
  32. package/dist/observe/signals.js +731 -0
  33. package/dist/observe/store/index.js +44 -0
  34. package/dist/observe/store/next/optimistic.js +717 -0
  35. package/dist/observe/store/next/projection.js +266 -0
  36. package/dist/observe/store/next/reconcile.js +375 -0
  37. package/dist/observe/store/next/store.js +2480 -0
  38. package/dist/observe/store/next/target.js +51 -0
  39. package/dist/observe/store/store.js +331 -0
  40. package/dist/observe/store/storePath.js +103 -0
  41. package/dist/observe/store/utils.js +1070 -0
  42. package/dist/prod/affects.js +10 -9
  43. package/dist/prod/attribution.js +45 -0
  44. package/dist/prod/boundaries.js +388 -178
  45. package/dist/prod/core/action.js +65 -37
  46. package/dist/prod/core/async.js +378 -163
  47. package/dist/prod/core/attribution-hooks.js +3 -0
  48. package/dist/prod/core/constants.js +186 -1
  49. package/dist/prod/core/context.js +10 -16
  50. package/dist/prod/core/core.js +1546 -416
  51. package/dist/prod/core/dev.js +18 -1
  52. package/dist/prod/core/effect.js +119 -79
  53. package/dist/prod/core/error-hooks.js +71 -0
  54. package/dist/prod/core/error.js +13 -1
  55. package/dist/prod/core/external.js +4 -4
  56. package/dist/prod/core/graph.js +105 -54
  57. package/dist/prod/core/heap.js +72 -65
  58. package/dist/prod/core/invariants.js +3 -2
  59. package/dist/prod/core/lanes.js +117 -52
  60. package/dist/prod/core/optimistic.js +500 -104
  61. package/dist/prod/core/owner.js +131 -84
  62. package/dist/prod/core/scheduler.js +1012 -241
  63. package/dist/prod/core/verdict.js +417 -112
  64. package/dist/prod/index.js +20 -8
  65. package/dist/prod/map.js +355 -171
  66. package/dist/prod/signals.js +363 -32
  67. package/dist/prod/store/index.js +2 -0
  68. package/dist/prod/store/next/optimistic.js +530 -197
  69. package/dist/prod/store/next/projection.js +142 -57
  70. package/dist/prod/store/next/reconcile.js +149 -103
  71. package/dist/prod/store/next/store.js +1470 -346
  72. package/dist/prod/store/next/target.js +40 -9
  73. package/dist/prod/store/store.js +23 -19
  74. package/dist/prod/store/utils.js +997 -130
  75. package/dist/types/attribution.d.ts +19 -0
  76. package/dist/types/attribution.prod.d.ts +22 -0
  77. package/dist/types/boundaries.d.ts +78 -5
  78. package/dist/types/core/action.d.ts +12 -5
  79. package/dist/types/core/async.d.ts +3 -1
  80. package/dist/types/core/attribution-costs.d.ts +35 -0
  81. package/dist/types/core/attribution-feedback.d.ts +133 -0
  82. package/dist/types/core/attribution-hooks.d.ts +304 -0
  83. package/dist/types/core/attribution-queries.d.ts +24 -0
  84. package/dist/types/core/attribution.d.ts +892 -0
  85. package/dist/types/core/constants.d.ts +179 -0
  86. package/dist/types/core/core.d.ts +299 -25
  87. package/dist/types/core/dev.d.ts +421 -8
  88. package/dist/types/core/effect.d.ts +8 -1
  89. package/dist/types/core/error-hooks.d.ts +71 -0
  90. package/dist/types/core/error.d.ts +9 -0
  91. package/dist/types/core/graph.d.ts +23 -0
  92. package/dist/types/core/heap.d.ts +5 -3
  93. package/dist/types/core/index.d.ts +6 -3
  94. package/dist/types/core/invariants.d.ts +5 -1
  95. package/dist/types/core/lanes.d.ts +51 -19
  96. package/dist/types/core/owner.d.ts +15 -3
  97. package/dist/types/core/scheduler.d.ts +252 -1
  98. package/dist/types/core/types.d.ts +124 -41
  99. package/dist/types/index.d.ts +18 -5
  100. package/dist/types/map.d.ts +2 -0
  101. package/dist/types/signals.d.ts +145 -13
  102. package/dist/types/store/index.d.ts +7 -6
  103. package/dist/types/store/next/optimistic.d.ts +18 -13
  104. package/dist/types/store/next/projection.d.ts +5 -2
  105. package/dist/types/store/next/reconcile.d.ts +7 -0
  106. package/dist/types/store/next/store.d.ts +83 -11
  107. package/dist/types/store/next/target.d.ts +108 -11
  108. package/dist/types/store/store.d.ts +20 -11
  109. package/dist/types/store/utils.d.ts +188 -0
  110. package/package.json +31 -26
  111. package/dist/node.cjs +0 -8137
  112. package/dist/types/store/optimistic.d.ts +0 -45
  113. package/dist/types/store/projection.d.ts +0 -70
  114. package/dist/types/store/reconcile.d.ts +0 -46
  115. package/dist/types-cjs/affects.d.cts +0 -47
  116. package/dist/types-cjs/boundaries.d.cts +0 -175
  117. package/dist/types-cjs/core/action.d.cts +0 -64
  118. package/dist/types-cjs/core/async.d.cts +0 -21
  119. package/dist/types-cjs/core/constants.d.cts +0 -71
  120. package/dist/types-cjs/core/core.d.cts +0 -156
  121. package/dist/types-cjs/core/dev.d.cts +0 -64
  122. package/dist/types-cjs/core/effect.d.cts +0 -30
  123. package/dist/types-cjs/core/error.d.cts +0 -47
  124. package/dist/types-cjs/core/external.d.cts +0 -15
  125. package/dist/types-cjs/core/graph.d.cts +0 -5
  126. package/dist/types-cjs/core/heap.d.cts +0 -22
  127. package/dist/types-cjs/core/index.d.cts +0 -13
  128. package/dist/types-cjs/core/invariants.d.cts +0 -59
  129. package/dist/types-cjs/core/lanes.d.cts +0 -56
  130. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  131. package/dist/types-cjs/core/owner.d.cts +0 -124
  132. package/dist/types-cjs/core/scheduler.d.cts +0 -172
  133. package/dist/types-cjs/core/types.d.cts +0 -148
  134. package/dist/types-cjs/core/verdict.d.cts +0 -2
  135. package/dist/types-cjs/index.d.cts +0 -10
  136. package/dist/types-cjs/map.d.cts +0 -69
  137. package/dist/types-cjs/package.json +0 -3
  138. package/dist/types-cjs/signals.d.cts +0 -624
  139. package/dist/types-cjs/store/index.d.cts +0 -19
  140. package/dist/types-cjs/store/next/optimistic.d.cts +0 -20
  141. package/dist/types-cjs/store/next/projection.d.cts +0 -24
  142. package/dist/types-cjs/store/next/reconcile.d.cts +0 -3
  143. package/dist/types-cjs/store/next/store.d.cts +0 -71
  144. package/dist/types-cjs/store/next/target.d.cts +0 -99
  145. package/dist/types-cjs/store/optimistic.d.cts +0 -45
  146. package/dist/types-cjs/store/projection.d.cts +0 -70
  147. package/dist/types-cjs/store/reconcile.d.cts +0 -46
  148. package/dist/types-cjs/store/store.d.cts +0 -138
  149. package/dist/types-cjs/store/storePath.d.cts +0 -58
  150. package/dist/types-cjs/store/utils.d.cts +0 -74
@@ -1,14 +1,28 @@
1
- import type { Signal } from "../../core/types.js";
1
+ import { hasActiveOverride, visibleOverride } from "../../core/core.js";
2
+ import type { Computed, Signal } from "../../core/types.js";
2
3
  import { type StoreNextFamily, type StoreNextTarget } from "./target.js";
3
4
  export declare function wrapNext<T extends Record<PropertyKey, any>>(value: T, parent?: StoreNextTarget | null, parentKey?: PropertyKey | null, fam?: StoreNextFamily | null): T;
4
5
  /** Unwrap our own proxies to their current backing; leave everything else. */
5
6
  export declare function unwrapValue(v: any): any;
6
- export declare function getNode(target: StoreNextTarget, key: PropertyKey, current: any): Signal<any>;
7
+ export declare function getNode(target: StoreNextTarget, key: PropertyKey, current: any, accKnown?: -1 | 0 | 1): Signal<any>;
8
+ /** Record a store proxy's declared name for attribution labels (observe tiers). */
9
+ export declare function nameStore(proxy: any, name: string | undefined): void;
7
10
  export declare function getHasNode(target: StoreNextTarget, key: PropertyKey, present: boolean): Signal<boolean>;
8
11
  export declare function getKeySetNode(target: StoreNextTarget): Signal<number>;
9
12
  /** Deep-witness bump: any value/shape change on a record with a live deep()
10
13
  * subscriber notifies it. One null check when unused. */
11
14
  export declare function bumpDeep(t: StoreNextTarget): void;
15
+ /** Downgrade a prototype-overlay pending backing to the clone path: builds
16
+ * the real container (committed + overlay writes − deletes) that fold will
17
+ * SWAP in as the committed backing, exactly as if the draft had started on
18
+ * the clone path. Consumers that need a complete container (reconcile's
19
+ * diff walks, drafts escaping into other storage) call this. */
20
+ export declare function materializePB(target: StoreNextTarget): void;
21
+ /** Resolve the held committed view (#3074): answers the masked old backing
22
+ * while the hold is live, and lazily clears a hold whose transition has
23
+ * committed (transitions merge — resolve through currentTransition, same as
24
+ * foldHeld's node stamps). */
25
+ export declare function heldMaskView(t: StoreNextTarget): Record<PropertyKey, any> | null;
12
26
  /**
13
27
  * Adoption (2026-08-16c): the incoming object becomes the committed backing
14
28
  * IMMEDIATELY — reconcile is eagerly visible to every reader (shipped
@@ -18,9 +32,16 @@ export declare function bumpDeep(t: StoreNextTarget): void;
18
32
  * authoritative base (R21/R32).
19
33
  */
20
34
  export declare function adoptPB(target: StoreNextTarget, incoming: Record<PropertyKey, any>, eager?: boolean): void;
35
+ /** Parked truth-staged pending backings (#3164 fold): a tentative draft that
36
+ * opens while a folded landing's backing is live moves the staged container
37
+ * here (see ensurePB); the tentative discard in notifyOptimisticWrites
38
+ * restores it in place of the usual null. */
39
+ export declare const stagedTruthPB: WeakMap<StoreNextTarget, Record<PropertyKey, any>>;
21
40
  /** Same logical slot: both values resolve to one (re-pointed) child target —
22
41
  * adoption preserved identity, so the slot did not change (R9). */
23
42
  export declare function targetsEqual(ov: any, nv: any): boolean;
43
+ export declare function arrayStructureChanged(old: any[], neu: any[]): boolean;
44
+ export declare function membershipChanged(old: Record<PropertyKey, any>, neu: Record<PropertyKey, any>): boolean;
24
45
  /**
25
46
  * The fold diff walks SUBSCRIPTION KEYS ONLY (legacy parity: `for key in
26
47
  * nodes`): nodes exist exactly where something tracked, so unobserved data
@@ -47,18 +68,69 @@ export declare function notifyFold(t: StoreNextTarget, old: Record<PropertyKey,
47
68
  * share the instance core reads — cross-module live-binding writes from other
48
69
  * store modules were observed not to propagate under the test transform). */
49
70
  export declare function runAuthoritative<T>(fn: () => T): T;
50
- /** Active optimistic override on an armed node (armed slot idles at
51
- * NOT_PENDING; undefined = unarmed plain node). */
52
- export declare function hasActiveOverride(node: Signal<any>): boolean;
71
+ export { hasActiveOverride, visibleOverride };
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;
87
+ /** The override a composed READER view (keys, descriptors, snapshot/deep,
88
+ * optimisticView) takes from an armed node whose override is active — the
89
+ * override itself, unless the node's own source superseded it (#3331,
90
+ * CONFIG_OVERRIDE_SUPERSEDED): then the reader-aware selection `serve`
91
+ * makes through nodeValue, so the composed view agrees with what `get`,
92
+ * `in` and `length` serve the same reader (a deriving pass: the staged
93
+ * truth; a lane pass or a context-free read: the override, A18). Composing
94
+ * the raw override left Object.keys / snapshot() / deep() one row behind the
95
+ * traps at a landing whose shape differed from the optimistic frame (F5
96
+ * parity cases). Draft and authoritative callers never reach here — the
97
+ * writer composes on hasActiveOverride, truth authors on the backing. */
98
+ export declare function readerOverride(node: Signal<any>, committed: any): any;
53
99
  export type SetStoreNextFunction<T> = (fn: (draft: T) => T | void) => void;
54
- /** Low-level setter primitive: opens write mode on a next proxy, runs `fn`,
55
- * emits write-time notifications at outermost exit, applies returned
56
- * replacements as adoptions. `guard=false` skips the owned-scope dev guard —
57
- * projection recomputes legitimately write from inside their computed. */
100
+ /** The derived store's setter (CS-R31): within a synchronous frame the manual
101
+ * write wins — the projection's recompute is masked for the tick. Across a
102
+ * hold the write is not a proposal: a leaf the fold staged under another
103
+ * transaction re-runs the fold under it, the write being the draft's prior
104
+ * state (#3612). Decided from the leaf notifications, so the mask lands
105
+ * after them (in the `finally`: a throwing setter's writes before the throw
106
+ * were notified, and are masked as before). */
107
+ export declare function derivedStoreWrite<T>(node: Computed<unknown>, proxy: T, fn: (draft: T) => T | void): void;
58
108
  export declare function storeSetterNext<T>(proxy: T, fn: (draft: T) => T | void, guard?: boolean): void;
59
- export declare function createStoreNext<T extends Record<PropertyKey, any>>(init: T, shallow?: boolean): [T, SetStoreNextFunction<T>];
109
+ export declare function createStoreNext<T extends Record<PropertyKey, any>>(initialValue: T, shallow?: boolean): [T, SetStoreNextFunction<T>];
110
+ /** True when `proxy` is a SHALLOW store (children served verbatim, slots
111
+ * replaced by reference — #2932). The list driver uses this to choose the
112
+ * slot-patch channel (collected row bodies) over per-record registration. */
113
+ export declare function storeIsShallow(proxy: any): boolean;
114
+ /** True when `proxy` belongs to a projection/optimistic FAMILY. The list
115
+ * driver must DECLINE family arrays (external audit finding): family
116
+ * structural changes never emit row/slot ops (the setter channel is
117
+ * fam-gated; optimistic writes ride node overrides), and the proxy identity
118
+ * is stable so the each-watch cannot catch the change either — an engaged
119
+ * list would freeze on optimistic/projection structural updates. Record-
120
+ * level family patches are unaffected (they have their own emission). */
121
+ export declare function storeHasFamily(proxy: any): boolean;
122
+ /** True when `proxy` belongs to an OPTIMISTIC family specifically. The list
123
+ * driver declines these (audit finding, narrowed): optimistic user writes
124
+ * ride node-level overrides — they never enter the reconcile walk, so no
125
+ * row/slot ops are emitted and an engaged list would freeze on optimistic
126
+ * structural changes. PROJECTION (non-optimistic) families are drivable:
127
+ * their recomputes go through the reconcile walk, whose emissions are
128
+ * transition-stamped in the apply queue like any other (equivalence-matrix
129
+ * gated). Re-admitting optimistic families requires a lane-timed structural
130
+ * emission mirroring emitPatchOptimistic, plus revert resync. */
131
+ export declare function storeHasOptimisticFamily(proxy: any): boolean;
60
132
  /** Tracking deep snapshot (`deep()` for next targets): subscribes to the
61
- * key-set and every property node at every reachable level, then returns the
133
+ * key-set and deep-witness node at every reachable level, then returns the
62
134
  * plain view. Shared references and cycles handled via the visited set. */
63
135
  export declare function deepNext<T>(value: T): T;
64
136
  /**
@@ -27,10 +27,34 @@ 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
+ /** Flight-owned transaction (#3146): declared when a truth-flight
38
+ * registers (the ask's transaction — created by the flight's own pending
39
+ * throw when none was ambient, the causing write's when one was), renewed
40
+ * per settle event once the previous reveal committed. Bare optimistic
41
+ * writes and landings route into it BY DECLARATION; the transitionBlocked
42
+ * store-half checks it for ownership instead of reconstructing it from
43
+ * `_optimisticStores` membership. null = no flight declared one (sync
44
+ * derive, or a loading-window flight — the loading rail is
45
+ * transaction-invisible, #2933). */
46
+ ft?: any;
47
+ /** Normalized row-key fn (same resolution as the projection channels:
48
+ * `options.key`, "id" default, null = unkeyed). The staged-landing walk
49
+ * reads it: key-matched rows keep their proxy identity across a fold. */
50
+ key?: ((item: any) => any) | null;
30
51
  map: WeakMap<object, StoreNextTarget>;
31
52
  /** The projection computed — assigned after creation (accessor pattern). */
32
53
  node: Computed<any> | null;
33
54
  shallow?: boolean;
55
+ /** Derive run counter (proj R37): the run whose draft is live. A draft
56
+ * compares its own run against this — the next run's start supersedes it. */
57
+ run?: number;
34
58
  }
35
59
  export interface StoreNextTarget {
36
60
  /** Committed backing: source object (shared) or owned clone. */
@@ -47,6 +71,19 @@ export interface StoreNextTarget {
47
71
  h: Record<PropertyKey, Signal<boolean>> | null;
48
72
  /** Lazy key-set node: membership/iteration/$TRACK subscriptions (§6). */
49
73
  k: Signal<number> | null;
74
+ /** Keys written through the traps since the last fold commit. Bounds the
75
+ * setter notify/hold-check to O(written) instead of O(subscribed nodes) —
76
+ * a record with thousands of per-key subscriptions (selection maps) would
77
+ * otherwise pay a full node scan on every write. null = no trap writes
78
+ * this batch (bulk paths fall back to the full scan); WK_ALL = bound
79
+ * unusable (array length write). LOAD-BEARING SHAPE RULE: array proxy
80
+ * targets carry their fields as named properties on a real array, and V8
81
+ * normalizes an array to dictionary properties as the named count grows
82
+ * (empirically at counts ≡ 0 mod 3 from 18 up on V8 13.x) — every trap
83
+ * field read then becomes a hash lookup (~15% uibench, tree suites
84
+ * worst). Future write-side state MUST ride an extension object, not new
85
+ * named fields. */
86
+ wk: Set<PropertyKey> | null;
50
87
  /** Lazy deep-witness node: `deep()` subscribes ONE node per record instead
51
88
  * of one per path; write paths bump it only when it exists. Separate from
52
89
  * `k` so $TRACK/mapArray never rerun on leaf value changes (R9). */
@@ -63,24 +100,77 @@ export interface StoreNextTarget {
63
100
  * this target (first-read scan, defineProperty, or clone scan). Gates the
64
101
  * fold diff's descriptor-safe path and the get trap's descriptor path. */
65
102
  a: boolean;
66
- /** Accessor scan performed (scan-once on first trap read; adopted data is
67
- * not rescanned — legacy-parity behavior). */
68
- sc: boolean;
69
- /** Backing was swapped by adoption this batch (fold diff-notifies it). */
70
- adopted: boolean;
103
+ /** Accessor scan grade: 0 = not yet scanned (adoption resets — adopted data
104
+ * is not rescanned until the next draft), 1 = scanned, 2 = scanned and
105
+ * PLAIN DATA — `Object.prototype` with every own key an enumerable data
106
+ * property. Grade 2 unlocks the spread clone (cloneRaw), bare-assignment
107
+ * overlay writes and flatten (#3360); a non-plain defineProperty through
108
+ * the draft downgrades it to 1. */
109
+ sc: 0 | 1 | 2;
110
+ /** Own-key count: exact at scan, then bumped by set-trap writes of keys new
111
+ * to the container (never decremented — an estimate for the overlay/clone
112
+ * choice only, #3360). */
113
+ kc: number;
114
+ /** Adoption diff base, non-null when the backing was swapped by adoption
115
+ * this batch: the view the nodes were LAST TOLD — the pre-batch committed
116
+ * backing, or the draft's pending backing when a draft preceded the
117
+ * adoption (its setter-exit notifications already moved the nodes, #3296).
118
+ * The deferred fold diffs incoming against this, never against committed. */
119
+ ab: Record<PropertyKey, any> | null;
120
+ /** Pending backing is a prototype-chain OVERLAY of the committed backing
121
+ * (`Object.create(v)` — own keys are this batch's writes, everything else
122
+ * reads through). O(written) per flush instead of O(container) clones
123
+ * (#3044); commit flattens own keys onto an owned committed backing in
124
+ * place. Plain-data non-array containers qualify, including projection
125
+ * and derived-store families (#3352); optimistic families, chained
126
+ * backings, and accessor containers keep the descriptor clone.
127
+ * `materializePB` downgrades to the clone path when a consumer needs a
128
+ * real container (reconcile, draft escape). */
129
+ ovl: boolean;
130
+ /** Keys deleted in the overlay window (a prototype overlay cannot shadow
131
+ * a delete); null when none. */
132
+ del: Set<PropertyKey> | null;
71
133
  /** Projection family, null for plain stores (§7b). */
72
134
  fam: StoreNextFamily | null;
73
135
  /** Shallow store root (values served raw). */
74
136
  s: boolean;
137
+ /** Held committed view (#3074/#3075): the pre-hold committed backing,
138
+ * served to committed-visibility readers while `ht` is live. Adoption is
139
+ * eager by contract, but a projection recompute deriving from uncommitted
140
+ * inputs (a transition-held source, or a latest()-pull ahead of the flush)
141
+ * swaps the backing SPECULATIVELY — the old view must stay servable until
142
+ * the hold resolves. */
143
+ hv: Record<PropertyKey, any> | null;
144
+ /** The holder for `hv`: a live transition (cleared lazily when it is done)
145
+ * or the PLAIN_HOLD sentinel (a latest()-pull staging — cleared by the
146
+ * fold commit). null = no hold. */
147
+ ht: any;
75
148
  }
76
149
  /**
77
- * Ownership (first cut, decision 2026-08-16d): one WeakSet of store-owned
78
- * backings serving both the production identity-skip guard and the __TEST__
79
- * no-mutation oracle.
150
+ * Ownership stamp (#3360): every backing the store ALLOCATES (CoW clones,
151
+ * privatized committed backings) carries its owning target under this
152
+ * enumerable symbol. One property write replaces the two weak-collection
153
+ * registrations (ownership set + raw→target map) a fresh object used to pay
154
+ * per draft — V8's identity-hash + ephemeron cost dominated the one-key
155
+ * write floor. Enumerable so a spread copy (the plain-data clone path) stays
156
+ * on the fast path and carries the stamp along.
157
+ *
158
+ * Owned backings are never user-reachable (`snapshot` copies them, the traps
159
+ * hide the key), so every raw key walk in the store must skip `$OWNER`, and
160
+ * ownership is answered by `isOwned` — a user object never carries it.
161
+ * Overlay drafts (`Object.create(v)` over an owned `v`) inherit the stamp.
80
162
  */
81
- export declare const ownedRaw: WeakSet<object>;
82
- /** raw → target. The only raw-keyed lookup; boundary mechanism (O8). */
163
+ export declare const $OWNER: unique symbol;
164
+ /** raw → target for UNOWNED backings (user-ingested, adopted); owned
165
+ * backings resolve through their `$OWNER` stamp. Boundary mechanism (O8). */
83
166
  export declare const storeNextLookup: WeakMap<object, StoreNextTarget>;
167
+ /** A backing the store allocated and may mutate in place. */
168
+ export declare function isOwned(raw: object): boolean;
169
+ /** raw → target within a family (`null` = plain stores / the global map).
170
+ * The stamp answers for backings owned by a target OF THAT FAMILY; anything
171
+ * else (user objects, adoptees, another family's backings the family
172
+ * re-registered for its own wrapper) resolves through the family's map. */
173
+ export declare function lookupTarget(raw: object, fam: StoreNextFamily | null): StoreNextTarget | undefined;
84
174
  /** __TEST__ oracle: every object ingested from a user (never mutate). */
85
175
  export declare const ingestedRaw: WeakSet<object> | null;
86
176
  export declare function devAssertNeverUserMutation(target: object): void;
@@ -92,8 +182,15 @@ export declare function devAssertNeverUserMutation(target: object): void;
92
182
  * the optimistic channel entirely. */
93
183
  export interface OptStoreHooks {
94
184
  notifyOptimisticWrites(t: any, pb: Record<PropertyKey, any>): void;
95
- optimisticView(t: any, src: Record<PropertyKey, any>): Record<PropertyKey, any>;
185
+ optimisticView(t: any, src: Record<PropertyKey, any>, draft?: boolean): Record<PropertyKey, any>;
96
186
  applyTentative(t: any, incoming: any, keyFn: ((item: any) => any) | null): void;
187
+ /** #3164 fold: does this live transaction still retain optimism (armed
188
+ * nodes or tracked stores)? Backs the held-truth masks in next/store.ts so
189
+ * plain-store bundles don't carry the transition-optimism probe. */
190
+ retainsOptimism(t: any): boolean;
97
191
  }
98
192
  export declare let optHooks: OptStoreHooks | null;
99
193
  export declare function setOptHooks(h: OptStoreHooks): void;
194
+ /** Sticky descendants flag walk (§6d): reconcile's keyed pruning descends
195
+ * only where subscriptions exist at/below. Nodes AND patches count. */
196
+ export declare function markDescendants(target: StoreNextTarget): void;
@@ -1,7 +1,7 @@
1
1
  import { type Signal } from "../core/index.js";
2
2
  import type { Refreshable } from "../core/index.js";
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,34 @@ 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
- /** Debug name (dev mode only) */
28
+ /**
29
+ * Debug name (dev and observe builds). Property nodes are labelled
30
+ * `<name>.<key>` in attribution output (`todos.title`); a derived store's
31
+ * projection node carries the name itself.
32
+ */
28
33
  name?: string;
34
+ /** Single-layer store: root keys reactive, values raw records replaced by reference */
35
+ shallow?: boolean;
29
36
  }
30
- /** Options for derived/projected stores created with `createStore(fn)`, `createProjection`, or `createOptimisticStore(fn)`. */
37
+ /**
38
+ * Options for derived/projected stores created with
39
+ * `createStore(fn, seed, options?)`, `createProjection(fn, seed, options?)`,
40
+ * or `createOptimisticStore(fn, seed, options?)`.
41
+ */
31
42
  export interface ProjectionOptions extends StoreOptions {
32
43
  /** Key property name or function for reconciliation identity; `null` merges positionally */
33
44
  key?: string | ((item: NonNullable<any>) => any) | null;
34
- /** Single-layer store: root keys reactive, values raw records replaced by reference */
35
- shallow?: boolean;
36
45
  /**
37
46
  * Treat the seed as commit #0: the store is born committed with the seed's
38
47
  * contents, shown until the derive's first real answer lands. While that
@@ -57,7 +66,7 @@ type DataNodes = Record<PropertyKey, DataNode>;
57
66
  *
58
67
  * @internal
59
68
  */
60
- export declare const $TRACK: unique symbol, $TARGET: unique symbol, $PROXY: unique symbol, $DELETED: unique symbol, $AFFECTS: unique symbol;
69
+ export declare const $TRACK: unique symbol, $TARGET: unique symbol, $PROXY: unique symbol, $RECORD: unique symbol, $DELETED: unique symbol, $AFFECTS: unique symbol;
61
70
  export declare const STORE_VALUE = "v", STORE_NODE = "n", STORE_HAS = "h", STORE_PARENT = "u", STORE_DESC = "d", STORE_SHALLOW = "s";
62
71
  /** Structural view of a store target as shared machinery sees it (the real
63
72
  * shape is `StoreNextTarget` in ./next/target.ts). */
@@ -21,6 +21,173 @@ type _Merge<T extends unknown[], Curr = {}> = T extends [
21
21
  ...infer Rest
22
22
  ] ? _Merge<Rest, Override<Curr, Next>> : T extends [...infer Rest, infer Next | (() => infer Next)] ? Override<_Merge<Rest, Curr>, Next> : T extends [] ? Curr : T extends (infer I | (() => infer I))[] ? OverrideSpread<Curr, I> : Curr;
23
23
  export type Merge<T extends unknown[]> = Simplify<_Merge<T>>;
24
+ /** @internal What a source ENTRY is, decided once when the view is built
25
+ * (`merge()` learns it while flattening; `omit()` from its argument) and
26
+ * carried beside the entry — `MergeView.kinds[i]`, `OmitView.kind` — so no
27
+ * read has to ask. Asking is the cost: any brand check on a Proxy is a trap
28
+ * (`instanceof` is a `getPrototypeOf` trap, ~20 ns on a store — as much as
29
+ * the read itself), and a merge over a store did two per read. */
30
+ export declare const SOURCE_PLAIN = 0;
31
+ export declare const SOURCE_OMIT = 1;
32
+ export declare const SOURCE_PROXY = 2;
33
+ export declare const SOURCE_MEMO = 3;
34
+ export declare const SOURCE_MERGE = 4;
35
+ export type SourceKind = 0 | 1 | 2 | 3 | 4;
36
+ /** @internal The record behind an `omit()` proxy: `source` with `hidden`
37
+ * keys removed. It is the proxy's TARGET, so the shared handler reads it as
38
+ * plain fields — no per-instance closures — and it is what props consumers
39
+ * walk directly (`merge`, `spread`, `ssrElement`): a view never materializes
40
+ * a copy, and a consumer that knows the record never goes through its traps
41
+ * (a `getOwnPropertyDescriptor` trap per key allocates a descriptor and a
42
+ * getter, so enumerating a proxy costs more than the copy it was avoiding).
43
+ * `hidden` is a key list or a predicate (`omit(props, k => k[0] === "$")`).
44
+ *
45
+ * An omit over a `merge()` holds the merge's RECORD (`MergeView`, kind
46
+ * `SOURCE_MERGE`) — never its proxy, so no read hops through a trap — and is
47
+ * one record however many leaves the merge has. A consumer walks it as ONE
48
+ * filtered entry (`sourceKeys` / `sourceGet` recurse into the merge's
49
+ * sources by function call), and a later `merge()` over it carries the
50
+ * record as one entry instead of copying its leaves: on a component chain of
51
+ * defaults + omit + spread (`merge(omit(merge(omit(props))))`) the layers
52
+ * nest as records, each a few fields, where a flatten to leaf views built a
53
+ * view and a combined key list per leaf per layer — the largest allocation
54
+ * of a Kobalte-shaped render. A merge leaf may be merge's memo for a
55
+ * function source; it is resolved on access. */
56
+ export declare class OmitView {
57
+ source: any;
58
+ /** of `source` — PLAIN, PROXY (a store or a foreign proxy), MEMO, or
59
+ * MERGE (a `MergeView` record); never OMIT, a view over a view folds
60
+ * into one. */
61
+ kind: SourceKind;
62
+ hidden: Hidden;
63
+ /** see `MergeView.table` */
64
+ table: Map<PropertyKey, any> | null | number;
65
+ /** see `tableOwnKeys` / `tableDescriptor` */
66
+ keys: (string | symbol)[] | undefined;
67
+ descs: Map<PropertyKey, PropertyDescriptor> | undefined;
68
+ constructor(source: any,
69
+ /** of `source` — PLAIN, PROXY (a store or a foreign proxy), MEMO, or
70
+ * MERGE (a `MergeView` record); never OMIT, a view over a view folds
71
+ * into one. */
72
+ kind: SourceKind, hidden: Hidden);
73
+ }
74
+ type Hidden = PropertyKey[] | ((key: PropertyKey) => boolean);
75
+ /** @internal The `OmitView` behind an `omit()` proxy, or undefined. */
76
+ export declare function omitView(o: any): OmitView | undefined;
77
+ /** @internal Own string keys of a source entry — every consumer skips
78
+ * symbols itself. */
79
+ export declare function sourceKeys(s: any, kind: SourceKind): (string | symbol)[];
80
+ /** @internal `key in entry`. */
81
+ export declare function sourceHas(s: any, kind: SourceKind, key: PropertyKey): boolean;
82
+ /** @internal `entry[key]` — the source's getter runs once, here. */
83
+ export declare function sourceGet(s: any, kind: SourceKind, key: PropertyKey): any;
84
+ /** @internal Whether the own key set of a props object cannot change
85
+ * reactively: a plain object, or a merge/omit view over plain objects only.
86
+ * A consumer may then decide from `Object.getOwnPropertyDescriptor` once —
87
+ * "no `children` key" or "a data `children`" holds for the object's lifetime,
88
+ * so no tracking scope is needed for it (#3388). For a store, or a view with
89
+ * a store or memo leaf, keys can appear later and the reactive path is the
90
+ * only correct one. */
91
+ export declare function hasStaticKeys(o: any): boolean;
92
+ /**
93
+ * Whether `o[key]` can never change for the lifetime of `o`: the key is a
94
+ * data property of a plain object, or is absent from an object whose key set
95
+ * is fixed. A getter, a key on a store, a memo-backed `merge()` source, or
96
+ * any key of an object whose keys can appear later (a store) is not static.
97
+ *
98
+ * Looks through `merge()`/`omit()` views to the leaf that owns the key. Any
99
+ * object will do, but props are the case it exists for: the compiler encodes
100
+ * a literal at the call site (`as="button"`) as a data property and an
101
+ * expression (`as={isLink() ? "a" : "button"}`) as a getter, so a component
102
+ * library reads the caller's own static/dynamic classification of a prop at
103
+ * runtime — identically on server and client, the compiler emitting the same
104
+ * own DESCRIPTORS on both (the object behind them may differ: the server
105
+ * builds props as a plain-prototype instance with shared getters) — and can
106
+ * take a no-computation path for the literal:
107
+ *
108
+ * ```tsx
109
+ * const Tag = dynamic(() => props.as, { static: isStatic(props, "as") });
110
+ * ```
111
+ *
112
+ * One descriptor lookup; no read of the value, nothing tracked.
113
+ */
114
+ export declare function isStatic(o: object, key: PropertyKey): boolean;
115
+ /** @internal */
116
+ export declare class MergeView {
117
+ sources: any[];
118
+ /** `kinds[i]` is what `sources[i]` is (see `SourceKind`). */
119
+ kinds: SourceKind[];
120
+ /** key → the plain leaf that owns it (later sources win), built by an
121
+ * enumeration or once the reads have paid for it (see `resolvedTable`)
122
+ * when every leaf has static keys; `null` when one doesn't. Until then
123
+ * the slot counts the per-key trap reads so far. One slot rather than a
124
+ * counter field of its own: a view is built per source per component
125
+ * layer, and each field initializer is a measurable share of a
126
+ * constructor that small in the lower JIT tiers. */
127
+ table: Map<PropertyKey, any> | null | number;
128
+ /** see `tableOwnKeys` / `tableDescriptor` */
129
+ keys: (string | symbol)[] | undefined;
130
+ descs: Map<PropertyKey, PropertyDescriptor> | undefined;
131
+ constructor(sources: any[],
132
+ /** `kinds[i]` is what `sources[i]` is (see `SourceKind`). */
133
+ kinds: SourceKind[]);
134
+ }
135
+ /** @internal The `MergeView` behind a `merge()` proxy — its flattened
136
+ * `sources` with their `kinds` — or undefined. */
137
+ export declare function mergeView(o: any): MergeView | undefined;
138
+ /** @internal The record behind a merge() or omit() proxy — a `MergeView`
139
+ * (flattened `sources` with their `kinds`) or an `OmitView` — or undefined
140
+ * for anything else (a plain object, a store, a foreign proxy). Two fast
141
+ * traps on a proxy (`has`, then `$RECORD`), none on a plain object. */
142
+ export declare function viewOf(o: any): MergeView | OmitView | undefined;
143
+ /** @internal The resolved key table of a merge/omit view — every own key of
144
+ * the view mapped to the plain object that owns it, in merged order (a key
145
+ * at the position of the last source that carries it, see `tableSet`) — or
146
+ * undefined when it has none: a leaf is a store or a memo source, whose
147
+ * keys can change, or the object is not a view at all.
148
+ *
149
+ * This is the flat object the eager copy used to build, made lazily and
150
+ * without copying: one pass over the leaves' own keys, then every
151
+ * `get`/`has`/descriptor is one lookup plus one read of the owning leaf, and
152
+ * a consumer (`spread` rerunning its effect) walks the table instead of
153
+ * re-deriving shadowing from the leaves each time. Own keys only, as the
154
+ * copy's were: a plain source's key set is fixed once merged (keys added to
155
+ * it later are not seen — the copy didn't see them either).
156
+ *
157
+ * It is built by an ENUMERATION — the `ownKeys` trap, or a consumer asking
158
+ * for it here — or once per-key reads have paid for it (`READS_FOR_TABLE`),
159
+ * not on the first read. A per-key read has a direct answer (a walk of the
160
+ * sources, last to first, one `in` each) whose cost is the source count,
161
+ * while the table's is every key of every leaf, so the walk wins until a
162
+ * view has been read about as many times as it has keys. On the server it
163
+ * never is: a component reads its props a few times, the element enumerates
164
+ * them once through its own source walk, and the view is gone — building on
165
+ * first read there cost a component chain a table per layer (profiled on
166
+ * the Kobalte-shaped chain: a third of SSR time in the table code and its
167
+ * garbage). On the
168
+ * client a view read on every reactive rerun crosses the threshold in its
169
+ * first few updates and is one lookup per read from then on, as before.
170
+ * Once built — by a `spread`, `Object.keys`, `{...props}`, or the count —
171
+ * every trap uses it. */
172
+ export declare function resolvedTable(o: any): Map<PropertyKey, any> | undefined;
173
+ /** @internal Every own string key of a props SOURCE — a plain object, a
174
+ * store or foreign proxy, or a merge/omit view — appended to `keys` in
175
+ * merged order with the object that owns each at the same index of
176
+ * `owners`: a key already listed (by this source or an earlier one) moves
177
+ * to the end, so several sources collected in turn give the order and the
178
+ * winners a merge of them would. A consumer that reads each key once
179
+ * (`ssrElement`) then reads `owners[i][keys[i]]` — the owner's getter runs
180
+ * there, once — and asks nothing else of a view: no table, no `in` walk per
181
+ * key through the merge/omit layers, no key list per leaf. One pass,
182
+ * however deep the layers nest. Symbols are listed; the consumer skips
183
+ * them. */
184
+ export declare function sourceOwners(s: any, keys: (string | symbol)[], owners: any[]): void;
185
+ /** @internal The flattened sources behind a `merge()` proxy, or undefined.
186
+ * A merge's writes are no-ops, so its sources are the whole truth. A COPY of
187
+ * a merge (`{...merged}`, a descriptor copy) is a plain object that carries
188
+ * no sources — `ownKeys` never lists $RECORD — so what is on the copy is
189
+ * the truth there and every consumer reads it directly (#3384). */
190
+ export declare function mergeSources(o: any): any[] | undefined;
24
191
  /**
25
192
  * Merges multiple props-like objects into a single proxy that *preserves
26
193
  * reactivity*. Reads are forwarded to the right-most source that defines the
@@ -29,9 +196,23 @@ export type Merge<T extends unknown[]> = Simplify<_Merge<T>>;
29
196
  * Function arguments are treated as memo-backed sources — useful for passing
30
197
  * derived defaults whose computation should track reactively.
31
198
  *
199
+ * The result is a live VIEW of its sources, never a copy: creating it costs
200
+ * nothing per key, every read goes to the source that owns the key (a getter
201
+ * runs there, a data property is read live), and writing to it is a no-op.
202
+ * A single non-function source is returned as is. To own a mutable object,
203
+ * copy it: `{ ...merged }` snapshots the current values.
204
+ *
32
205
  * Use this in component bodies to merge defaults / overrides without losing
33
206
  * Solid's per-property tracking.
34
207
  *
208
+ * Reading props, here and anywhere: a prop's getter is defined only for a
209
+ * read through its own object (`props.x`, `{ ...props }`, `Reflect.get`,
210
+ * these views). Forwarding its descriptor onto another object and reading it
211
+ * there is not supported — the compiler's server-side props keep their state
212
+ * on the instance, so the getter needs its object as receiver. A copy that
213
+ * must stay live defines its own getter that reads through the source, as
214
+ * the no-Proxy paths of merge() and omit() do.
215
+ *
35
216
  * @example
36
217
  * ```tsx
37
218
  * function Button(_props: { label: string; type?: string; disabled?: boolean }) {
@@ -52,6 +233,12 @@ export type Omit<T, K extends readonly (keyof T)[]> = {
52
233
  * Use it to forward "rest" props to a child element while pulling out the
53
234
  * keys your component handles itself — the equivalent of `splitProps(p, ["a","b"])[1]`.
54
235
  *
236
+ * The result is a live VIEW of `props`, not a copy: nothing is read or
237
+ * materialized until a key is used, and a spread (`{...rest}`) or a later
238
+ * `merge()` walks the underlying object directly. A predicate hides keys by
239
+ * rule instead of by name — `omit(props, k => k[0] === "$")` — without
240
+ * enumerating first.
241
+ *
55
242
  * @example
56
243
  * ```tsx
57
244
  * function Input(props: { label: string; value: string; onInput: (v: string) => void } & JSX.HTMLAttributes<HTMLInputElement>) {
@@ -71,4 +258,5 @@ export type Omit<T, K extends readonly (keyof T)[]> = {
71
258
  * ```
72
259
  */
73
260
  export declare function omit<T extends Record<any, any>, K extends readonly (keyof T)[]>(props: T, ...keys: K): Omit<T, K>;
261
+ export declare function omit<T extends Record<any, any>>(props: T, hidden: (key: keyof T & (string | symbol)) => boolean): Partial<T>;
74
262
  export {};