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