@solidjs/signals 2.0.0-rc.8 → 2.0.0-rc.9

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