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

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 +7138 -0
  2. package/dist/dev.attribution.js +3307 -0
  3. package/dist/dev.js +5691 -4491
  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 +627 -0
  27. package/dist/observe/core/owner.js +353 -0
  28. package/dist/observe/core/scheduler.js +1547 -0
  29. package/dist/observe/core/verdict.js +674 -0
  30. package/dist/observe/index.js +59 -0
  31. package/dist/observe/map.js +484 -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 +458 -104
  61. package/dist/prod/core/owner.js +131 -84
  62. package/dist/prod/core/scheduler.js +998 -241
  63. package/dist/prod/core/verdict.js +417 -112
  64. package/dist/prod/index.js +20 -8
  65. package/dist/prod/map.js +312 -151
  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 +248 -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
@@ -12,44 +12,873 @@ import "../core/effect.js";
12
12
 
13
13
  import { createMemo } from "../signals.js";
14
14
 
15
- import { ownEnumerableKeys, $PROXY } from "./store.js";
15
+ import { $PROXY, $RECORD, $TARGET, ownEnumerableKeys } from "./store.js";
16
16
 
17
17
  function trueFn() {
18
18
  return true;
19
19
  }
20
20
 
21
- const propTraps = {
22
- get(e, r, t) {
23
- if (r === $PROXY) return t;
24
- return e.get(r);
21
+ /** @internal What a source ENTRY is, decided once when the view is built
22
+ * (`merge()` learns it while flattening; `omit()` from its argument) and
23
+ * carried beside the entry — `MergeView.kinds[i]`, `OmitView.kind` — so no
24
+ * read has to ask. Asking is the cost: any brand check on a Proxy is a trap
25
+ * (`instanceof` is a `getPrototypeOf` trap, ~20 ns on a store — as much as
26
+ * the read itself), and a merge over a store did two per read. */ const SOURCE_PLAIN = 0;
27
+
28
+ // a plain object: own keys fixed, data is data
29
+ const SOURCE_OMIT = 1;
30
+
31
+ // an `OmitView` record (only as a merge entry)
32
+ const SOURCE_PROXY = 2;
33
+
34
+ // a store or foreign proxy: everything is a trap
35
+ const SOURCE_MEMO = 3;
36
+
37
+ // a function source (merge's memo): swaps objects
38
+ const SOURCE_MERGE = 4;
39
+
40
+ // a `MergeView` record (only as an omit's source)
41
+ const EMPTY = Object.freeze({});
42
+
43
+ // The object behind a LEAF entry (any kind but OMIT): a memo is read
44
+ // (tracked, as merge's own reads are) and a nullish result has no keys.
45
+ function leafOf(e, n) {
46
+ return n === SOURCE_MEMO ? (e = e()) == null ? EMPTY : e : e;
47
+ }
48
+
49
+ /** @internal The record behind an `omit()` proxy: `source` with `hidden`
50
+ * keys removed. It is the proxy's TARGET, so the shared handler reads it as
51
+ * plain fields — no per-instance closures — and it is what props consumers
52
+ * walk directly (`merge`, `spread`, `ssrElement`): a view never materializes
53
+ * a copy, and a consumer that knows the record never goes through its traps
54
+ * (a `getOwnPropertyDescriptor` trap per key allocates a descriptor and a
55
+ * getter, so enumerating a proxy costs more than the copy it was avoiding).
56
+ * `hidden` is a key list or a predicate (`omit(props, k => k[0] === "$")`).
57
+ *
58
+ * An omit over a `merge()` holds the merge's RECORD (`MergeView`, kind
59
+ * `SOURCE_MERGE`) — never its proxy, so no read hops through a trap — and is
60
+ * one record however many leaves the merge has. A consumer walks it as ONE
61
+ * filtered entry (`sourceKeys` / `sourceGet` recurse into the merge's
62
+ * sources by function call), and a later `merge()` over it carries the
63
+ * record as one entry instead of copying its leaves: on a component chain of
64
+ * defaults + omit + spread (`merge(omit(merge(omit(props))))`) the layers
65
+ * nest as records, each a few fields, where a flatten to leaf views built a
66
+ * view and a combined key list per leaf per layer — the largest allocation
67
+ * of a Kobalte-shaped render. A merge leaf may be merge's memo for a
68
+ * function source; it is resolved on access. */ class OmitView {
69
+ source;
70
+ kind;
71
+ hidden;
72
+ /** see `MergeView.table` */
73
+ table=0;
74
+ /** see `tableOwnKeys` / `tableDescriptor` */
75
+ keys=undefined;
76
+ descs=undefined;
77
+ constructor(e,
78
+ /** of `source` — PLAIN, PROXY (a store or a foreign proxy), MEMO, or
79
+ * MERGE (a `MergeView` record); never OMIT, a view over a view folds
80
+ * into one. */
81
+ n, t) {
82
+ this.source = e;
83
+ this.kind = n;
84
+ this.hidden = t;
85
+ }
86
+ }
87
+
88
+ function isHidden(e, n) {
89
+ const t = e.hidden;
90
+ return typeof t === "function" ? t(n) : t.includes(n);
91
+ }
92
+
93
+ // Both filters as one. Two key lists stay a key list (one `includes`, no
94
+ // closure); a predicate on either side needs a closure. An omit over a
95
+ // merge builds one combined list per leaf, per component layer, so the
96
+ // copy's form matters in every tier: `concat` runs the species/spreadable
97
+ // protocol (2–3× the cost of a copy once optimized), a hand loop is 2–4×
98
+ // `concat` in the interpreter and baseline tiers (a bytecode per element
99
+ // against one builtin), and a presized `new Array(n)` is holey, which takes
100
+ // `includes` off its fast path. `slice` + `push` of the (short) second list
101
+ // is within a third of the best form in every tier, and packed.
102
+ function combineHidden(e, n) {
103
+ if (typeof e !== "function" && typeof n !== "function") {
104
+ const t = e.slice();
105
+ for (let e = 0; e < n.length; e++) t.push(n[e]);
106
+ return t;
107
+ }
108
+ return t => (typeof e === "function" ? e(t) : e.includes(t)) || (typeof n === "function" ? n(t) : n.includes(t));
109
+ }
110
+
111
+ // The object a view filters (see `leafOf`).
112
+ function viewSource(e) {
113
+ return leafOf(e.source, e.kind);
114
+ }
115
+
116
+ // The record behind a `$PROXY`-marked object that is one of OUR views, else
117
+ // undefined for a store or a foreign proxy. ONE read: the view traps answer
118
+ // `$RECORD` first thing and a store's `get` trap answers it `undefined` on
119
+ // its symbol fast path, so the question never reaches a store's generic
120
+ // read path (firewall gate, tracked key read), which is what an unknown
121
+ // symbol would take — and a foreign proxy forwards it to a target that has
122
+ // no such key. Asking `$TARGET` first and then each view kind was two to
123
+ // four trap hops per source at every `merge()`, `omit()` and `ssrElement`.
124
+ // Call only after `$PROXY in o` is known true.
125
+ function recordOf(e) {
126
+ return e[$RECORD];
127
+ }
128
+
129
+ /** @internal The `OmitView` behind an `omit()` proxy, or undefined. */ function omitView(e) {
130
+ if (e == null || !($PROXY in e)) return undefined;
131
+ const n = e[$RECORD];
132
+ return n instanceof OmitView ? n : undefined;
133
+ }
134
+
135
+ // A props SOURCE ENTRY is a plain object, a proxy (store, merge, omit — the
136
+ // last two are normally unwrapped first: `mergeView` / `omitView`), a memo,
137
+ // or an `OmitView` record, and travels with its kind. These three answer for
138
+ // an entry what `Object.keys` / `in` / `[]` answer for an object, so every
139
+ // consumer walks entries with one code path, an `OmitView` is filtered
140
+ // rather than materialized, and nothing is asked of a proxy but the read.
141
+ // Own keys of a leaf: a proxy answers through ONE `ownKeys` trap (a store's
142
+ // keeps the key set tracked; `Object.keys` on a proxy would add a descriptor
143
+ // trap per key); a plain object its enumerable string keys. What a memo
144
+ // holds is only known once read.
145
+ function leafKeys(e, n) {
146
+ if (n === SOURCE_PLAIN) return Object.keys(e);
147
+ if (n === SOURCE_PROXY || e[$PROXY] === e) return Reflect.ownKeys(e);
148
+ return Object.keys(e);
149
+ }
150
+
151
+ /** @internal Own string keys of a source entry — every consumer skips
152
+ * symbols itself. */ function sourceKeys(e, n) {
153
+ if (n === SOURCE_OMIT) {
154
+ if (e.kind === SOURCE_MERGE) return mergeKeysOf(e.source, false, e);
155
+ const n = leafKeys(viewSource(e), e.kind);
156
+ const t = [];
157
+ for (let r = 0; r < n.length; r++) if (!isHidden(e, n[r])) t.push(n[r]);
158
+ return t;
159
+ }
160
+ return leafKeys(leafOf(e, n), n);
161
+ }
162
+
163
+ /** @internal `key in entry`. */ function sourceHas(e, n, t) {
164
+ if (n === SOURCE_OMIT) {
165
+ if (isHidden(e, t)) return false;
166
+ return e.kind === SOURCE_MERGE ? mergeHas(e.source, t) : t in viewSource(e);
167
+ }
168
+ return t in leafOf(e, n);
169
+ }
170
+
171
+ /** @internal `entry[key]` — the source's getter runs once, here. */ function sourceGet(e, n, t) {
172
+ if (n === SOURCE_OMIT) {
173
+ if (isHidden(e, t)) return undefined;
174
+ return e.kind === SOURCE_MERGE ? mergeGet(e.source, t) : viewSource(e)[t];
175
+ }
176
+ return leafOf(e, n)[t];
177
+ }
178
+
179
+ // An entry whose own key set is fixed: a plain object, or a view over one —
180
+ // an omit of a plain object, or of a merge whose entries all are. Not a
181
+ // store (its key set is a tracked signal), not a merge memo source (it
182
+ // swaps whole objects), and not any proxy that declares itself with
183
+ // `$PROXY in s` — a frames slot proxy answers `has` for every key and lists
184
+ // none, so only the `in` walk is right for it.
185
+ function entryHasStaticKeys(e, n) {
186
+ if (n === SOURCE_PLAIN) return true;
187
+ if (n !== SOURCE_OMIT) return false;
188
+ if (e.kind === SOURCE_PLAIN) return true;
189
+ return e.kind === SOURCE_MERGE && mergeHasStaticKeys(e.source);
190
+ }
191
+
192
+ function mergeHasStaticKeys(e) {
193
+ const n = e.sources, t = e.kinds;
194
+ for (let e = 0; e < n.length; e++) if (!entryHasStaticKeys(n[e], t[e])) return false;
195
+ return true;
196
+ }
197
+
198
+ /** @internal Whether the own key set of a props object cannot change
199
+ * reactively: a plain object, or a merge/omit view over plain objects only.
200
+ * A consumer may then decide from `Object.getOwnPropertyDescriptor` once —
201
+ * "no `children` key" or "a data `children`" holds for the object's lifetime,
202
+ * so no tracking scope is needed for it (#3388). For a store, or a view with
203
+ * a store or memo leaf, keys can appear later and the reactive path is the
204
+ * only correct one. */ function hasStaticKeys(e) {
205
+ if (!($PROXY in e)) return true;
206
+ const n = recordOf(e);
207
+ if (n instanceof MergeView) return mergeHasStaticKeys(n);
208
+ return n !== undefined && entryHasStaticKeys(n, SOURCE_OMIT);
209
+ }
210
+
211
+ /**
212
+ * Whether `o[key]` can never change for the lifetime of `o`: the key is a
213
+ * data property of a plain object, or is absent from an object whose key set
214
+ * is fixed. A getter, a key on a store, a memo-backed `merge()` source, or
215
+ * any key of an object whose keys can appear later (a store) is not static.
216
+ *
217
+ * Looks through `merge()`/`omit()` views to the leaf that owns the key. Any
218
+ * object will do, but props are the case it exists for: the compiler encodes
219
+ * a literal at the call site (`as="button"`) as a data property and an
220
+ * expression (`as={isLink() ? "a" : "button"}`) as a getter, so a component
221
+ * library reads the caller's own static/dynamic classification of a prop at
222
+ * runtime — identically on server and client, the compiler emitting the same
223
+ * own DESCRIPTORS on both (the object behind them may differ: the server
224
+ * builds props as a plain-prototype instance with shared getters) — and can
225
+ * take a no-computation path for the literal:
226
+ *
227
+ * ```tsx
228
+ * const Tag = dynamic(() => props.as, { static: isStatic(props, "as") });
229
+ * ```
230
+ *
231
+ * One descriptor lookup; no read of the value, nothing tracked.
232
+ */ function isStatic(e, n) {
233
+ if ($PROXY in e) {
234
+ // A store answers its descriptor trap with a value; through a view the
235
+ // descriptor is truthful (see `sourceDescriptor`). A foreign proxy is
236
+ // opaque: nothing about it is known to be fixed.
237
+ if (viewOf(e) === undefined) return false;
238
+ const t = Reflect.getOwnPropertyDescriptor(e, n);
239
+ return t === undefined ? hasStaticKeys(e) : t.get === undefined;
240
+ }
241
+ const t = Reflect.getOwnPropertyDescriptor(e, n);
242
+ return t === undefined || t.get === undefined && t.set === undefined;
243
+ }
244
+
245
+ function accessorDescriptor(e, n = true) {
246
+ return {
247
+ configurable: true,
248
+ enumerable: n,
249
+ get: e,
250
+ set: trueFn
251
+ };
252
+ }
253
+
254
+ /** The descriptor a consumer should see for `key` on an entry —
255
+ * the view proxies answer `getOwnPropertyDescriptor` with it, so it tells the
256
+ * truth through any depth of merge/omit layers.
257
+ *
258
+ * A DATA descriptor means "nothing reactive can hide behind this value": the
259
+ * key is a data property of a plain-object leaf — the compiler's own
260
+ * encoding of a static attribute. Everything else is an accessor: a getter
261
+ * on a leaf, a key on a store proxy (its "data" is a signal), or a key on a
262
+ * merge memo source (the whole object is reactive). That is what lets a
263
+ * consumer skip a reactive node for a static prop at the bottom of a
264
+ * component chain, and it is why the store case must NOT forward the store's
265
+ * own descriptor, which reports a value.
266
+ *
267
+ * `configurable: true` always — the target has no such property, and the
268
+ * Proxy invariants forbid reporting a non-configurable one. */
269
+ // `present` says the caller has already established `key in s` (a trap's
270
+ // shadowing walk did), so a store is not asked a second time.
271
+ function sourceDescriptor(e, n, t, r = false) {
272
+ if (n === SOURCE_OMIT) {
273
+ if (isHidden(e, t)) return undefined;
274
+ return sourceDescriptor(e.source, e.kind, t, r);
275
+ }
276
+ // An omit's merge record: the descriptor of the last entry that has the
277
+ // key, as the merge proxy's own trap answers (see `mergeDescriptor`).
278
+ if (n === SOURCE_MERGE) return mergeDescriptor(e, t);
279
+ // A memo source (`merge(() => …)`) is reactive wholesale: whatever shape
280
+ // the memo's current object has, the key is an accessor here.
281
+ if (n === SOURCE_MEMO) {
282
+ return r || t in leafOf(e, n) ? accessorDescriptor(() => leafOf(e, n)[t]) : undefined;
283
+ }
284
+ if (n === SOURCE_PROXY) {
285
+ // Another view (an omit's source may be a merge proxy) already answers
286
+ // truthfully. A store's reported "data" is a signal, and a foreign proxy
287
+ // (frames slot props) has no own descriptors: for both, existence is
288
+ // `in` and the kind is accessor — one trap, and never the store's
289
+ // descriptor path.
290
+ if (recordOf(e) !== undefined) return Reflect.getOwnPropertyDescriptor(e, t);
291
+ return r || t in e ? accessorDescriptor(() => e[t]) : undefined;
292
+ }
293
+ const i = Reflect.getOwnPropertyDescriptor(e, t);
294
+ if (i === undefined) return undefined;
295
+ if (i.get !== undefined || i.set !== undefined) return accessorDescriptor(() => e[t], i.enumerable);
296
+ // The proxy target has no such key, so the descriptor must be configurable;
297
+ // Reflect's is a fresh object, so a configurable one is handed out as is.
298
+ if (i.configurable) return i;
299
+ return {
300
+ configurable: true,
301
+ enumerable: i.enumerable,
302
+ writable: true,
303
+ value: i.value
304
+ };
305
+ }
306
+
307
+ // Own ENUMERABLE keys, symbols included, of an entry — the user-facing key
308
+ // set (`Object.keys(merged)`), where enumerability matters (#2769).
309
+ function sourceEnumerableKeys(e, n) {
310
+ {
311
+ if (e.kind === SOURCE_MERGE) return mergeEnumerableKeys(e.source, e);
312
+ const n = ownEnumerableKeys(viewSource(e));
313
+ const t = [];
314
+ for (let r = 0; r < n.length; r++) if (!isHidden(e, n[r])) t.push(n[r]);
315
+ return t;
316
+ }
317
+ }
318
+
319
+ // The target of a merge() proxy: the flattened sources, read by one shared
320
+ // handler — like OmitView, no per-instance closures. `sources` is what
321
+ // `mergeSources` answers.
322
+ /** @internal */ class MergeView {
323
+ sources;
324
+ kinds;
325
+ /** key → the plain leaf that owns it (later sources win), built by an
326
+ * enumeration or once the reads have paid for it (see `resolvedTable`)
327
+ * when every leaf has static keys; `null` when one doesn't. Until then
328
+ * the slot counts the per-key trap reads so far. One slot rather than a
329
+ * counter field of its own: a view is built per source per component
330
+ * layer, and each field initializer is a measurable share of a
331
+ * constructor that small in the lower JIT tiers. */
332
+ table=0;
333
+ /** see `tableOwnKeys` / `tableDescriptor` */
334
+ keys=undefined;
335
+ descs=undefined;
336
+ constructor(e,
337
+ /** `kinds[i]` is what `sources[i]` is (see `SourceKind`). */
338
+ n) {
339
+ this.sources = e;
340
+ this.kinds = n;
341
+ }
342
+ }
343
+
344
+ /** @internal The `MergeView` behind a `merge()` proxy — its flattened
345
+ * `sources` with their `kinds` — or undefined. */ function mergeView(e) {
346
+ if (e == null || !($PROXY in e)) return undefined;
347
+ const n = e[$RECORD];
348
+ return n instanceof MergeView ? n : undefined;
349
+ }
350
+
351
+ /** @internal The record behind a merge() or omit() proxy — a `MergeView`
352
+ * (flattened `sources` with their `kinds`) or an `OmitView` — or undefined
353
+ * for anything else (a plain object, a store, a foreign proxy). Two fast
354
+ * traps on a proxy (`has`, then `$RECORD`), none on a plain object. */ function viewOf(e) {
355
+ return e != null && $PROXY in e ? e[$RECORD] : undefined;
356
+ }
357
+
358
+ /** @internal The resolved key table of a merge/omit view — every own key of
359
+ * the view mapped to the plain object that owns it, in merged order (a key
360
+ * at the position of the last source that carries it, see `tableSet`) — or
361
+ * undefined when it has none: a leaf is a store or a memo source, whose
362
+ * keys can change, or the object is not a view at all.
363
+ *
364
+ * This is the flat object the eager copy used to build, made lazily and
365
+ * without copying: one pass over the leaves' own keys, then every
366
+ * `get`/`has`/descriptor is one lookup plus one read of the owning leaf, and
367
+ * a consumer (`spread` rerunning its effect) walks the table instead of
368
+ * re-deriving shadowing from the leaves each time. Own keys only, as the
369
+ * copy's were: a plain source's key set is fixed once merged (keys added to
370
+ * it later are not seen — the copy didn't see them either).
371
+ *
372
+ * It is built by an ENUMERATION — the `ownKeys` trap, or a consumer asking
373
+ * for it here — or once per-key reads have paid for it (`READS_FOR_TABLE`),
374
+ * not on the first read. A per-key read has a direct answer (a walk of the
375
+ * sources, last to first, one `in` each) whose cost is the source count,
376
+ * while the table's is every key of every leaf, so the walk wins until a
377
+ * view has been read about as many times as it has keys. On the server it
378
+ * never is: a component reads its props a few times, the element enumerates
379
+ * them once through its own source walk, and the view is gone — building on
380
+ * first read there cost a component chain a table per layer (profiled on
381
+ * the Kobalte-shaped chain: a third of SSR time in the table code and its
382
+ * garbage). On the
383
+ * client a view read on every reactive rerun crosses the threshold in its
384
+ * first few updates and is one lookup per read from then on, as before.
385
+ * Once built — by a `spread`, `Object.keys`, `{...props}`, or the count —
386
+ * every trap uses it. */ function resolvedTable(e) {
387
+ if (e == null || !($PROXY in e)) return undefined;
388
+ const n = recordOf(e);
389
+ if (n instanceof OmitView) return omitTable(n);
390
+ return n === undefined ? undefined : mergeTable(n);
391
+ }
392
+
393
+ function mergeTable(e) {
394
+ let n = e.table;
395
+ if (typeof n !== "object") {
396
+ // a read count: not decided yet
397
+ const t = e.sources, r = e.kinds;
398
+ for (let n = 0; n < t.length; n++) {
399
+ if (!entryHasStaticKeys(t[n], r[n])) {
400
+ e.table = null;
401
+ return undefined;
402
+ }
403
+ }
404
+ n = new Map;
405
+ collectTable(n, e, undefined);
406
+ e.table = n;
407
+ }
408
+ return n === null ? undefined : n;
409
+ }
410
+
411
+ // One pass over a merge record's leaves into `table`, through the nested
412
+ // omit-over-merge entries — the filters enclosing the current leaf are the
413
+ // `filters` stack — so a component chain builds ONE table at the view that
414
+ // asked, not one per layer. A key an entry hides that an EARLIER entry owned
415
+ // must stay: a filter applies to its own entry's contribution, not to the
416
+ // merge, which is exactly what the stack expresses. Every entry has static
417
+ // keys (the caller checked), so a leaf's own keys are the truth.
418
+ function collectTable(e, n, t) {
419
+ const r = n.sources, i = n.kinds;
420
+ for (let n = 0; n < r.length; n++) {
421
+ const f = r[n];
422
+ if (i[n] === SOURCE_OMIT) {
423
+ if (f.kind === SOURCE_MERGE) {
424
+ if (t === undefined) t = [ f ]; else t.push(f);
425
+ collectTable(e, f.source, t);
426
+ t.pop();
427
+ continue;
428
+ }
429
+ const n = f.source;
430
+ const r = Reflect.ownKeys(n);
431
+ for (let i = 0; i < r.length; i++) {
432
+ const u = r[i];
433
+ if (!isHidden(f, u) && !hiddenByAny(t, u)) tableSet(e, u, n);
434
+ }
435
+ } else {
436
+ const n = Reflect.ownKeys(f);
437
+ for (let r = 0; r < n.length; r++) {
438
+ const i = n[r];
439
+ if (!hiddenByAny(t, i)) tableSet(e, i, f);
440
+ }
441
+ }
442
+ }
443
+ }
444
+
445
+ function hiddenByAny(e, n) {
446
+ if (e !== undefined) for (let t = e.length - 1; t >= 0; t--) if (isHidden(e[t], n)) return true;
447
+ return false;
448
+ }
449
+
450
+ // Key order is the merged one — every key at the position of the LAST source
451
+ // that carries it — the order `ssrElement`'s array form serializes in and the
452
+ // eager copy enumerated in, so a spread through a view and a spread over the
453
+ // sources emit the same attribute order.
454
+ function tableSet(e, n, t) {
455
+ if (e.has(n)) e.delete(n);
456
+ e.set(n, t);
457
+ }
458
+
459
+ // An omit view's table: its source's (a merge's table, or a plain object's
460
+ // own keys) minus the hidden keys. Cached on the record.
461
+ function omitTable(e) {
462
+ let n = e.table;
463
+ if (typeof n !== "object") {
464
+ const t = e.source;
465
+ if (e.kind === SOURCE_MERGE) {
466
+ // One pass over the merge's leaves with this filter on the stack —
467
+ // the merge record builds no table of its own for it.
468
+ if (!mergeHasStaticKeys(t)) {
469
+ e.table = null;
470
+ return undefined;
471
+ }
472
+ n = new Map;
473
+ collectTable(n, t, [ e ]);
474
+ } else if (e.kind === SOURCE_PLAIN) {
475
+ n = new Map;
476
+ const r = Reflect.ownKeys(t);
477
+ for (let i = 0; i < r.length; i++) {
478
+ const f = r[i];
479
+ if (!isHidden(e, f)) n.set(f, t);
480
+ }
481
+ } else {
482
+ // a store or a memo: keys can change, no table
483
+ e.table = null;
484
+ return undefined;
485
+ }
486
+ e.table = n;
487
+ }
488
+ return n === null ? undefined : n;
489
+ }
490
+
491
+ // The user-facing key set of a resolved table: its keys that are enumerable
492
+ // on the leaf that owns them (`Object.keys(merged)`, #2769). Fixed, like the
493
+ // table, so it is built once per view: an `ownKeys` trap may hand back the
494
+ // same array every time (the engine copies it).
495
+ const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
496
+
497
+ function tableOwnKeys(e, n) {
498
+ let t = e.keys;
499
+ if (t === undefined) {
500
+ t = e.keys = [];
501
+ for (const [e, r] of n) if (propertyIsEnumerable.call(r, e)) t.push(e);
502
+ }
503
+ return t;
504
+ }
505
+
506
+ // The descriptor for a table key — its owning leaf's, truthful (see
507
+ // `sourceDescriptor`) — with the shape cached per key so an enumeration
508
+ // (`for…in`, `Object.keys`, `{...props}`: a descriptor trap per key, on
509
+ // every pass) does not re-read the leaf's descriptor and re-allocate a
510
+ // getter each time. An accessor reads live, so its descriptor is reused as
511
+ // is; a data descriptor is rebuilt with the current value.
512
+ function tableDescriptor(e, n, t) {
513
+ const r = n.get(t);
514
+ if (r === undefined) return undefined;
515
+ let i = e.descs;
516
+ if (i === undefined) i = e.descs = new Map;
517
+ let f = i.get(t);
518
+ if (f === undefined) {
519
+ f = sourceDescriptor(r, SOURCE_PLAIN, t);
520
+ if (f === undefined) return undefined;
521
+ i.set(t, f);
522
+ return f;
523
+ }
524
+ if (f.get !== undefined) return f;
525
+ return {
526
+ configurable: true,
527
+ enumerable: f.enumerable,
528
+ writable: f.writable,
529
+ value: r[t]
530
+ };
531
+ }
532
+
533
+ // Per-key trap reads a view takes before building its table, from the
534
+ // break-even: a build is ~60 ns per key (an `ownKeys` share, a `has`, a
535
+ // `set`), a walk ~20 ns per source (an `in`, a hidden-list check), and a
536
+ // leaf carries about five keys — so the table has paid for itself after
537
+ // ~15 reads. Measured on the Kobalte-shaped chain: a walk is 2× a lookup at
538
+ // depth 1 and up to 10× for a first-source key at depth 7 (a walk through
539
+ // seven nested layers, a hidden-list check at each), so a view read on
540
+ // every update wants the table; a view read a handful of times (every
541
+ // server-side view) never wants it.
542
+ const READS_FOR_TABLE = 16;
543
+
544
+ // The table for a per-key trap: the one a view HAS (built by an enumeration
545
+ // or an earlier read, see `resolvedTable`), or the one this read pays for,
546
+ // or none — a walk answers. One per view type, so a trap pays no type check.
547
+ // A settled slot is an object (the Map, or `null`); a number is the count.
548
+ function mergeReadTable(e) {
549
+ const n = e.table;
550
+ if (typeof n === "object") return n === null ? undefined : n;
551
+ if (n + 1 < READS_FOR_TABLE) {
552
+ e.table = n + 1;
553
+ return undefined;
554
+ }
555
+ return mergeTable(e);
556
+ }
557
+
558
+ // Only for an omit over a merge (`kind === SOURCE_MERGE`; the caller checks,
559
+ // inline — a call is not free in every tier): an omit over one object reads
560
+ // it directly — a list check and a property read, nothing a table would
561
+ // shorten.
562
+ function omitReadTable(e) {
563
+ const n = e.table;
564
+ if (typeof n === "object") return n === null ? undefined : n;
565
+ if (n + 1 < READS_FOR_TABLE) {
566
+ e.table = n + 1;
567
+ return undefined;
568
+ }
569
+ return omitTable(e);
570
+ }
571
+
572
+ // The table a record HAS — built already by an enumeration or a trap's read
573
+ // count — or undefined. What a nested walk asks: a record reached through an
574
+ // outer view's entry counts no reads of its own (the outer view decides for
575
+ // the whole tree, and its table build then builds the inner ones), so the
576
+ // inner merges of a component chain build nothing on the server where the
577
+ // leaves are read a few times each.
578
+ function tableOf(e) {
579
+ const n = e.table;
580
+ return typeof n === "object" && n !== null ? n : undefined;
581
+ }
582
+
583
+ // "no entry has the key" — distinct from an entry that holds `undefined`.
584
+ const MISSING = Symbol();
585
+
586
+ // The read: the value of the last entry that has the key, or MISSING. ONE
587
+ // walk — a nested omit-over-merge entry answers presence and value together,
588
+ // so a chain of layers is walked once per read, not once per layer per
589
+ // level.
590
+ function mergeLookup(e, n) {
591
+ const t = tableOf(e);
592
+ if (t !== undefined) {
593
+ const e = t.get(n);
594
+ return e === undefined ? MISSING : e[n];
595
+ }
596
+ const r = e.sources, i = e.kinds;
597
+ for (let e = r.length - 1; e >= 0; e--) {
598
+ const t = i[e];
599
+ // The common leaf first, read in place: a component's props view is a
600
+ // few plain objects, read once per key on the server.
601
+ if (t === SOURCE_PLAIN) {
602
+ const t = r[e];
603
+ if (n in t) return t[n];
604
+ continue;
605
+ }
606
+ if (t === SOURCE_OMIT) {
607
+ const t = r[e];
608
+ if (isHidden(t, n)) continue;
609
+ if (t.kind === SOURCE_MERGE) {
610
+ const e = mergeLookup(t.source, n);
611
+ if (e !== MISSING) return e;
612
+ continue;
613
+ }
614
+ const i = viewSource(t);
615
+ if (n in i) return i[n];
616
+ } else {
617
+ const i = leafOf(r[e], t);
618
+ if (n in i) return i[n];
619
+ }
620
+ }
621
+ return MISSING;
622
+ }
623
+
624
+ function mergeGet(e, n) {
625
+ const t = mergeLookup(e, n);
626
+ return t === MISSING ? undefined : t;
627
+ }
628
+
629
+ // `key in merge`, on the record.
630
+ function mergeHas(e, n) {
631
+ const t = tableOf(e);
632
+ if (t !== undefined) return t.has(n);
633
+ const r = e.sources, i = e.kinds;
634
+ for (let e = r.length - 1; e >= 0; e--) if (sourceHas(r[e], i[e], n)) return true;
635
+ return false;
636
+ }
637
+
638
+ // The proxy's `getOwnPropertyDescriptor`, on the record.
639
+ function mergeDescriptor(e, n) {
640
+ const t = tableOf(e);
641
+ if (t !== undefined) return tableDescriptor(e, t, n);
642
+ const r = e.sources, i = e.kinds;
643
+ for (let t = r.length - 1; t >= 0; t--) {
644
+ if (!sourceHas(r[t], i[t], n)) continue;
645
+ // `in` also answers for inherited keys, which have no own descriptor.
646
+ return sourceDescriptor(r[t], i[t], n, true) ?? accessorDescriptor(() => mergeGet(e, n));
647
+ }
648
+ return undefined;
649
+ }
650
+
651
+ // Own keys of a merge record in merged order — every key at the position
652
+ // of the LAST entry that carries it, the order the table keeps and
653
+ // `ssrElement` serializes in. `enumerable` selects the user-facing set
654
+ // (`Object.keys`, #2769) over every own string key (a consumer's walk);
655
+ // `filter` is the omit this record is read through, applied as the keys
656
+ // are gathered so an omit over a merge builds ONE list per layer. A list
657
+ // with `indexOf` rather than a Set: a props object has a dozen keys, and a
658
+ // Set's hash store was 2 KB per row on the Kobalte-shaped chain.
659
+ function mergeKeysOf(e, n, t) {
660
+ const r = [];
661
+ collectKeys(e, t === undefined ? undefined : [ t ], n, r, null);
662
+ return r;
663
+ }
664
+
665
+ // One pass over a merge record's leaves — through its nested omit-over-merge
666
+ // entries, `filters` the omits enclosing the current leaf (see
667
+ // `collectTable`) — appending each leaf's keys to `keys` in merged order
668
+ // (a key already listed moves to the end: later wins) and, when `owners` is
669
+ // given, the object that owns the key at the same index. A consumer that
670
+ // reads every key once (`ssrElement`) then reads `owners[i][keys[i]]`: no
671
+ // `in` walk per key, no table. A memo leaf is resolved once here.
672
+ function collectKeys(e, n, t, r, i) {
673
+ const f = e.sources, u = e.kinds;
674
+ for (let e = 0; e < f.length; e++) {
675
+ let o = f[e], c = u[e];
676
+ let s;
677
+ if (c === SOURCE_OMIT) {
678
+ if (o.kind === SOURCE_MERGE) {
679
+ if (n === undefined) n = [ o ]; else n.push(o);
680
+ collectKeys(o.source, n, t, r, i);
681
+ n.pop();
682
+ continue;
683
+ }
684
+ s = o;
685
+ c = o.kind;
686
+ o = o.source;
687
+ }
688
+ o = leafOf(o, c);
689
+ const d = t ? ownEnumerableKeys(o) : leafKeys(o, c);
690
+ for (let e = 0; e < d.length; e++) {
691
+ const t = d[e];
692
+ if (s !== undefined && isHidden(s, t)) continue;
693
+ if (hiddenByAny(n, t)) continue;
694
+ addKey(r, i, t, o);
695
+ }
696
+ }
697
+ }
698
+
699
+ // Append `key` owned by `owner`, moving an earlier listing to the end: later
700
+ // wins, and the position is the last owner's (the merged order).
701
+ function addKey(e, n, t, r) {
702
+ const i = e.indexOf(t);
703
+ if (i !== -1) {
704
+ e.splice(i, 1);
705
+ if (n !== null) n.splice(i, 1);
706
+ }
707
+ e.push(t);
708
+ if (n !== null) n.push(r);
709
+ }
710
+
711
+ /** @internal Every own string key of a props SOURCE — a plain object, a
712
+ * store or foreign proxy, or a merge/omit view — appended to `keys` in
713
+ * merged order with the object that owns each at the same index of
714
+ * `owners`: a key already listed (by this source or an earlier one) moves
715
+ * to the end, so several sources collected in turn give the order and the
716
+ * winners a merge of them would. A consumer that reads each key once
717
+ * (`ssrElement`) then reads `owners[i][keys[i]]` — the owner's getter runs
718
+ * there, once — and asks nothing else of a view: no table, no `in` walk per
719
+ * key through the merge/omit layers, no key list per leaf. One pass,
720
+ * however deep the layers nest. Symbols are listed; the consumer skips
721
+ * them. */ function sourceOwners(e, n, t) {
722
+ if ($PROXY in e) {
723
+ const r = viewOf(e);
724
+ if (r === undefined) {
725
+ // a store: one `ownKeys` trap, reads through `[]`
726
+ const r = Reflect.ownKeys(e);
727
+ for (let i = 0; i < r.length; i++) addKey(n, t, r[i], e);
728
+ return;
729
+ }
730
+ if (r instanceof OmitView) {
731
+ if (r.kind === SOURCE_MERGE) return collectKeys(r.source, [ r ], false, n, t);
732
+ const e = viewSource(r);
733
+ const i = leafKeys(e, r.kind);
734
+ for (let f = 0; f < i.length; f++) if (!isHidden(r, i[f])) addKey(n, t, i[f], e);
735
+ return;
736
+ }
737
+ return collectKeys(r, undefined, false, n, t);
738
+ }
739
+ const r = Object.keys(e);
740
+ for (let i = 0; i < r.length; i++) addKey(n, t, r[i], e);
741
+ }
742
+
743
+ // The user-facing key set of a merge record, read through `filter` if given.
744
+ function mergeEnumerableKeys(e, n) {
745
+ const t = mergeTable(e);
746
+ if (t === undefined) return mergeKeysOf(e, true, n);
747
+ const r = tableOwnKeys(e, t);
748
+ if (n === undefined) return r;
749
+ const i = [];
750
+ for (let e = 0; e < r.length; e++) if (!isHidden(n, r[e])) i.push(r[e]);
751
+ return i;
752
+ }
753
+
754
+ const mergeTraps = {
755
+ get(e, n, t) {
756
+ // The private keys are symbols; a string read (every prop) skips the
757
+ // compares. `$TARGET` is answered so a store check never walks the
758
+ // sources (a leaf that is a store would answer it).
759
+ if (typeof n === "symbol") {
760
+ if (n === $PROXY) return t;
761
+ if (n === $RECORD) return e;
762
+ if (n === $TARGET) return undefined;
763
+ }
764
+ // A trap read counts toward the table (see `mergeReadTable`); the walk
765
+ // itself is the record's. `mergeReadTable` and the plain walk of
766
+ // `mergeLookup` are inlined here: a trap is entered from the runtime, so
767
+ // nothing below it is inlined for it, and a component's props view is a
768
+ // few plain objects read once per key on the server — the walk is the
769
+ // whole read. A leaf that is not plain hands the walk to `mergeGet`,
770
+ // which starts over (a plain leaf walked twice is two `in` checks).
771
+ const r = e.table;
772
+ let i;
773
+ if (typeof r !== "object") {
774
+ if (r + 1 < READS_FOR_TABLE) {
775
+ e.table = r + 1;
776
+ const t = e.sources, i = e.kinds;
777
+ for (let r = t.length - 1; r >= 0; r--) {
778
+ if (i[r] !== SOURCE_PLAIN) return mergeGet(e, n);
779
+ // Read first, `in` only to tell a missing key from one holding
780
+ // undefined: the last source is the one that usually has the key.
781
+ const f = t[r][n];
782
+ if (f !== undefined || n in t[r]) return f;
783
+ }
784
+ return undefined;
785
+ }
786
+ i = mergeTable(e);
787
+ if (i === undefined) return mergeGet(e, n);
788
+ } else if (r === null) return mergeGet(e, n); else i = r;
789
+ const f = i.get(n);
790
+ return f === undefined ? undefined : f[n];
25
791
  },
26
- has(e, r) {
27
- if (r === $PROXY) return true;
28
- return e.has(r);
792
+ has(e, n) {
793
+ if (n === $PROXY) return true;
794
+ if (n === $TARGET || n === $RECORD) return false;
795
+ const t = mergeReadTable(e);
796
+ if (t !== undefined) return t.has(n);
797
+ return mergeHas(e, n);
29
798
  },
30
799
  set: trueFn,
31
800
  deleteProperty: trueFn,
32
- getOwnPropertyDescriptor(e, r) {
33
- return {
34
- configurable: true,
35
- enumerable: true,
36
- get() {
37
- return e.get(r);
38
- },
39
- set: trueFn,
40
- deleteProperty: trueFn
41
- };
801
+ getOwnPropertyDescriptor(e, n) {
802
+ if (n === $PROXY || n === $TARGET || n === $RECORD) return undefined;
803
+ const t = mergeReadTable(e);
804
+ if (t !== undefined) return tableDescriptor(e, t, n);
805
+ return mergeDescriptor(e, n);
42
806
  },
43
807
  ownKeys(e) {
44
- return e.keys();
808
+ return mergeEnumerableKeys(e);
45
809
  }
46
810
  };
47
811
 
48
- function resolveSource(e) {
49
- return !(e = typeof e === "function" ? e() : e) ? {} : e;
50
- }
812
+ // An omit view reads its source directly — a hidden-key check and one
813
+ // property read; over a merge record, the merge's own walk by function call,
814
+ // never a trap. Once an enumeration or the read count has built its table
815
+ // (over a merge with plain leaves only: the merge's, filtered) every trap
816
+ // answers from that instead.
817
+ const omitTraps = {
818
+ get(e, n, t) {
819
+ if (n === $PROXY) return t;
820
+ // `$RECORD` is THIS view — never the underlying merge's record, which is
821
+ // unfiltered: handing a re-merge the merge's own sources would leak the
822
+ // omitted keys (#3014). A consumer walks the omit record as ONE filtered
823
+ // entry (`recordOf` tells the two records apart by class).
824
+ if (n === $RECORD) return e;
825
+ if (n === $TARGET) return undefined;
826
+ if (e.kind === SOURCE_MERGE) {
827
+ const t = omitReadTable(e);
828
+ if (t !== undefined) {
829
+ const e = t.get(n);
830
+ return e === undefined ? undefined : e[n];
831
+ }
832
+ if (isHidden(e, n)) return undefined;
833
+ return mergeGet(e.source, n);
834
+ }
835
+ if (isHidden(e, n)) return undefined;
836
+ return viewSource(e)[n];
837
+ },
838
+ has(e, n) {
839
+ if (n === $PROXY) return true;
840
+ if (n === $TARGET || n === $RECORD) return false;
841
+ if (e.kind === SOURCE_MERGE) {
842
+ const t = omitReadTable(e);
843
+ if (t !== undefined) return t.has(n);
844
+ if (isHidden(e, n)) return false;
845
+ return mergeHas(e.source, n);
846
+ }
847
+ if (isHidden(e, n)) return false;
848
+ return n in viewSource(e);
849
+ },
850
+ set: trueFn,
851
+ deleteProperty: trueFn,
852
+ getOwnPropertyDescriptor(e, n) {
853
+ if (n === $PROXY || n === $TARGET || n === $RECORD) return undefined;
854
+ if (e.kind === SOURCE_MERGE) {
855
+ const t = omitReadTable(e);
856
+ if (t !== undefined) return tableDescriptor(e, t, n);
857
+ }
858
+ return sourceDescriptor(e, SOURCE_OMIT, n);
859
+ },
860
+ ownKeys(e) {
861
+ if (e.kind === SOURCE_MERGE) {
862
+ const n = omitTable(e);
863
+ if (n !== undefined) return tableOwnKeys(e, n);
864
+ // No table (a store or memo leaf): the merge's own key set, filtered.
865
+ return sourceEnumerableKeys(e);
866
+ }
867
+ const n = Reflect.ownKeys(viewSource(e));
868
+ const t = [];
869
+ for (let r = 0; r < n.length; r++) if (!isHidden(e, n[r])) t.push(n[r]);
870
+ return t;
871
+ }
872
+ };
51
873
 
52
- const $SOURCES = Symbol(0);
874
+ /** @internal The flattened sources behind a `merge()` proxy, or undefined.
875
+ * A merge's writes are no-ops, so its sources are the whole truth. A COPY of
876
+ * a merge (`{...merged}`, a descriptor copy) is a plain object that carries
877
+ * no sources — `ownKeys` never lists $RECORD — so what is on the copy is
878
+ * the truth there and every consumer reads it directly (#3384). */ function mergeSources(e) {
879
+ const n = mergeView(e);
880
+ return n === undefined ? undefined : n.sources;
881
+ }
53
882
 
54
883
  /**
55
884
  * Merges multiple props-like objects into a single proxy that *preserves
@@ -59,9 +888,23 @@ const $SOURCES = Symbol(0);
59
888
  * Function arguments are treated as memo-backed sources — useful for passing
60
889
  * derived defaults whose computation should track reactively.
61
890
  *
891
+ * The result is a live VIEW of its sources, never a copy: creating it costs
892
+ * nothing per key, every read goes to the source that owns the key (a getter
893
+ * runs there, a data property is read live), and writing to it is a no-op.
894
+ * A single non-function source is returned as is. To own a mutable object,
895
+ * copy it: `{ ...merged }` snapshots the current values.
896
+ *
62
897
  * Use this in component bodies to merge defaults / overrides without losing
63
898
  * Solid's per-property tracking.
64
899
  *
900
+ * Reading props, here and anywhere: a prop's getter is defined only for a
901
+ * read through its own object (`props.x`, `{ ...props }`, `Reflect.get`,
902
+ * these views). Forwarding its descriptor onto another object and reading it
903
+ * there is not supported — the compiler's server-side props keep their state
904
+ * on the instance, so the getter needs its object as receiver. A copy that
905
+ * must stay live defines its own getter that reads through the source, as
906
+ * the no-Proxy paths of merge() and omit() do.
907
+ *
65
908
  * @example
66
909
  * ```tsx
67
910
  * function Button(_props: { label: string; type?: string; disabled?: boolean }) {
@@ -72,130 +915,154 @@ const $SOURCES = Symbol(0);
72
915
  * ```
73
916
  */ function merge(...e) {
74
917
  if (e.length === 1 && typeof e[0] !== "function") return e[0];
75
- let r = false;
76
- const t = [];
77
- for (let n = 0; n < e.length; n++) {
78
- const o = e[n];
79
- r = r || !!o && $PROXY in o;
80
- const s = !!o && o[$SOURCES];
81
- if (s) {
82
- for (let e = 0; e < s.length; e++) t.push(s[e]);
83
- } else t.push(typeof o === "function" ? (r = true, createMemo(o)) : o);
84
- }
85
- if (SUPPORTS_PROXY && r) {
86
- return new Proxy({
87
- get(e) {
88
- if (e === $SOURCES) return t;
89
- for (let r = t.length - 1; r >= 0; r--) {
90
- const n = resolveSource(t[r]);
91
- if (e in n) return n[e];
92
- }
93
- },
94
- has(e) {
95
- for (let r = t.length - 1; r >= 0; r--) {
96
- if (e in resolveSource(t[r])) return true;
97
- }
98
- return false;
99
- },
100
- keys() {
101
- const e = new Set;
102
- for (let r = 0; r < t.length; r++) {
103
- const n = ownEnumerableKeys(resolveSource(t[r]));
104
- for (let r = 0; r < n.length; r++) e.add(n[r]);
918
+ // Sized to the argument count up front: a view is built per source per
919
+ // component layer, and growing two empty arrays by `push` was a third of
920
+ // its construction. A nested view's sources may run past the count (the
921
+ // array grows), a falsy source leaves it short (trimmed below).
922
+ const n = new Array(e.length);
923
+ const t = new Array(e.length);
924
+ let r = 0;
925
+ // The one non-falsy source, if there is exactly one: it IS the merge.
926
+ let i = undefined;
927
+ let f = 0;
928
+ for (let u = 0; u < e.length; u++) {
929
+ const o = e[u];
930
+ if (!o) continue;
931
+ f++;
932
+ i = o;
933
+ if (typeof o === "function") {
934
+ n[r] = createMemo(o);
935
+ t[r++] = SOURCE_MEMO;
936
+ continue;
937
+ }
938
+ if ($PROXY in o) {
939
+ // A store (or a foreign proxy) is a leaf as it is. A merge() proxy is
940
+ // flattened through: its writes are no-ops, so its sources are exactly
941
+ // what it reads. An omit() proxy joins as its view record — ONE entry,
942
+ // its filter travelling with it, whether it is over a plain object or
943
+ // a whole merge (never the merge's own sources, which would leak the
944
+ // omitted keys, #3014). A consumer's walk recurses into the record.
945
+ const e = recordOf(o);
946
+ if (e instanceof MergeView) {
947
+ for (let i = 0; i < e.sources.length; i++) {
948
+ n[r] = e.sources[i];
949
+ t[r++] = e.kinds[i];
105
950
  }
106
- return [ ...e ];
951
+ } else if (e !== undefined) {
952
+ n[r] = e;
953
+ t[r++] = SOURCE_OMIT;
954
+ } else {
955
+ n[r] = o;
956
+ t[r++] = SOURCE_PROXY;
107
957
  }
108
- }, propTraps);
958
+ continue;
959
+ }
960
+ n[r] = o;
961
+ t[r++] = SOURCE_PLAIN;
962
+ }
963
+ if (r !== n.length) {
964
+ n.length = r;
965
+ t.length = r;
966
+ }
967
+ if (SUPPORTS_PROXY) {
968
+ if (f === 1 && typeof i !== "function") return i;
969
+ // Always a view, never a copy. Building a plain object here costs a
970
+ // descriptor read, a bound getter and a defineProperty per key per
971
+ // layer, and component libraries stack several layers per element
972
+ // (defaults → omit → call-site statics → …), so the copies dominated
973
+ // their render cost while every consumer that matters — `spread`,
974
+ // `ssrElement`, a nested merge — reads the flattened sources directly
975
+ // anyway (#3448). The view is O(1) to create and reads through to the
976
+ // sources, so a data property on a source is read live, like a getter.
977
+ // Writes to the result are no-ops (a consumer that needs its own object
978
+ // copies: `{...merged}`, which the traps answer truthfully). Copies of
979
+ // the result never carry $RECORD (#3384): `ownKeys` answers only the
980
+ // sources' keys.
981
+ return new Proxy(new MergeView(n, t), mergeTraps);
109
982
  }
110
- const n = Object.create(null);
983
+ // No Proxy: an eager descriptor copy, semantics as close to the view as a
984
+ // plain object allows (getters stay live; data properties are snapshots).
985
+ const u = Object.create(null);
111
986
  let o = false;
112
- let s = t.length - 1;
113
- for (let e = s; e >= 0; e--) {
114
- const r = t[e];
115
- if (!r) {
116
- e === s && s--;
987
+ let c = n.length - 1;
988
+ for (let e = c; e >= 0; e--) {
989
+ const t = n[e];
990
+ if (!t) {
991
+ e === c && c--;
117
992
  continue;
118
993
  }
119
- const u = Object.getOwnPropertyNames(r);
120
- for (let t = u.length - 1; t >= 0; t--) {
121
- const c = u[t];
122
- if (c === "__proto__" || c === "constructor") continue;
123
- if (!n[c]) {
124
- o = o || e !== s;
125
- const t = Object.getOwnPropertyDescriptor(r, c);
126
- n[c] = t.get ? {
994
+ const r = Object.getOwnPropertyNames(t);
995
+ for (let n = r.length - 1; n >= 0; n--) {
996
+ const i = r[n];
997
+ if (i === "__proto__" || i === "constructor") continue;
998
+ if (!u[i]) {
999
+ o = o || e !== c;
1000
+ const n = Object.getOwnPropertyDescriptor(t, i);
1001
+ u[i] = n.get ? {
127
1002
  enumerable: true,
128
1003
  configurable: true,
129
- get: t.get.bind(r)
130
- } : t;
1004
+ get: n.get.bind(t)
1005
+ } : n;
131
1006
  }
132
1007
  }
133
1008
  }
134
- if (!o) return t[s];
135
- const u = {};
136
- const c = Object.keys(n);
137
- for (let e = c.length - 1; e >= 0; e--) {
138
- const r = c[e], t = n[r];
139
- if (t.get) Object.defineProperty(u, r, t); else u[r] = t.value;
1009
+ if (!o) return n[c];
1010
+ const s = {};
1011
+ const d = Object.keys(u);
1012
+ for (let e = d.length - 1; e >= 0; e--) {
1013
+ const n = d[e], t = u[n];
1014
+ if (t.get) Object.defineProperty(s, n, t); else s[n] = t.value;
140
1015
  }
141
- u[$SOURCES] = t;
142
- return u;
1016
+ return s;
143
1017
  }
144
1018
 
145
- /**
146
- * Returns a reactive proxy of `props` with the listed keys hidden. Tracking
147
- * on the remaining keys is preserved.
148
- *
149
- * Use it to forward "rest" props to a child element while pulling out the
150
- * keys your component handles itself — the equivalent of `splitProps(p, ["a","b"])[1]`.
151
- *
152
- * @example
153
- * ```tsx
154
- * function Input(props: { label: string; value: string; onInput: (v: string) => void } & JSX.HTMLAttributes<HTMLInputElement>) {
155
- * const rest = omit(props, "label", "value", "onInput");
156
- *
157
- * return (
158
- * <label>
159
- * {props.label}
160
- * <input
161
- * {...rest}
162
- * value={props.value}
163
- * onInput={e => props.onInput(e.currentTarget.value)}
164
- * />
165
- * </label>
166
- * );
167
- * }
168
- * ```
169
- */ function omit(e, ...r) {
170
- if (SUPPORTS_PROXY && $PROXY in e) {
171
- return new Proxy({
172
- get(t) {
173
- // $SOURCES must not tunnel through the filter: merge() flattens
174
- // whatever answers it, so forwarding would hand a re-merge the
175
- // UNFILTERED sources of an underlying merge proxy and the omitted
176
- // keys leak back in (#3014 — the SSR element-spread path re-merges
177
- // static attributes with the rest object). Opaque here: merge
178
- // composes omit proxies through their traps instead.
179
- return t === $SOURCES || r.includes(t) ? undefined : e[t];
180
- },
181
- has(t) {
182
- return t !== $SOURCES && !r.includes(t) && t in e;
183
- },
184
- keys() {
185
- return ownEnumerableKeys(e).filter(e => !r.includes(e));
1019
+ function omit(e, ...n) {
1020
+ let t = n.length === 1 && typeof n[0] === "function" ? n[0] : n;
1021
+ if (SUPPORTS_PROXY) {
1022
+ // A view over a view folds: one record, both filters, the original
1023
+ // source — so a consumer walks the real object however deep the omits go.
1024
+ // Over a merge() proxy the source is the merge's RECORD (see OmitView):
1025
+ // one record whatever the leaf count, read by function call.
1026
+ let n = e;
1027
+ let r = SOURCE_PLAIN;
1028
+ if (typeof e === "function") r = SOURCE_MEMO; else if ($PROXY in e) {
1029
+ r = SOURCE_PROXY;
1030
+ const i = recordOf(e);
1031
+ if (i instanceof OmitView) {
1032
+ n = i.source;
1033
+ r = i.kind;
1034
+ t = combineHidden(i.hidden, t);
1035
+ } else if (i !== undefined) {
1036
+ n = i;
1037
+ r = SOURCE_MERGE;
186
1038
  }
187
- }, propTraps);
1039
+ }
1040
+ return new Proxy(new OmitView(n, r, t), omitTraps);
188
1041
  }
189
- const t = {};
190
- const n = Object.getOwnPropertyNames(e);
191
- const o = r.length > 4 && n.length > r.length ? new Set(r) : undefined;
192
- for (const s of n) {
193
- if (o ? !o.has(s) : !r.includes(s)) {
194
- const r = Object.getOwnPropertyDescriptor(e, s);
195
- !r.get && !r.set && r.enumerable && r.writable && r.configurable ? t[s] = r.value : Object.defineProperty(t, s, r);
1042
+ const r = {};
1043
+ const i = Object.getOwnPropertyNames(e);
1044
+ const f = typeof t === "function" ? t : t.length > 4 && i.length > t.length ? (e => n => e.has(n))(new Set(t)) : e => t.includes(e);
1045
+ for (const n of i) {
1046
+ if (!f(n)) {
1047
+ const t = Object.getOwnPropertyDescriptor(e, n);
1048
+ if (!t.get && !t.set && t.enumerable && t.writable && t.configurable) {
1049
+ r[n] = t.value;
1050
+ } else if (t.get || t.set) {
1051
+ // An accessor is re-homed with its source as receiver, never copied
1052
+ // as-is: a props getter is only defined for a read THROUGH its own
1053
+ // object (the compiler's server props keep their state on the
1054
+ // instance, so a forwarded descriptor read on the copy throws). Same
1055
+ // rule as merge()'s copy path above.
1056
+ Object.defineProperty(r, n, {
1057
+ enumerable: t.enumerable,
1058
+ configurable: true,
1059
+ get: t.get && t.get.bind(e),
1060
+ set: t.set && t.set.bind(e)
1061
+ });
1062
+ } else Object.defineProperty(r, n, t);
196
1063
  }
197
1064
  }
198
- return t;
1065
+ return r;
199
1066
  }
200
1067
 
201
- export { merge, omit };
1068
+ export { MergeView, OmitView, SOURCE_MEMO, SOURCE_MERGE, SOURCE_OMIT, SOURCE_PLAIN, SOURCE_PROXY, hasStaticKeys, isStatic, merge, mergeSources, mergeView, omit, omitView, resolvedTable, sourceGet, sourceHas, sourceKeys, sourceOwners, viewOf };