@pyreon/core 0.15.0 → 0.16.0

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.
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
5386
5386
  </script>
5387
5387
  <script>
5388
5388
  /*<!--*/
5389
- const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"824f9243-1","name":"lifecycle.ts"},{"uid":"824f9243-3","name":"component.ts"},{"uid":"824f9243-5","name":"compat-marker.ts"},{"uid":"824f9243-7","name":"context.ts"},{"uid":"824f9243-9","name":"h.ts"},{"uid":"824f9243-11","name":"dynamic.ts"},{"uid":"824f9243-13","name":"telemetry.ts"},{"uid":"824f9243-15","name":"error-boundary.ts"},{"uid":"824f9243-17","name":"for.ts"},{"uid":"824f9243-19","name":"lazy.ts"},{"uid":"824f9243-21","name":"map-array.ts"},{"uid":"824f9243-23","name":"portal.ts"},{"uid":"824f9243-25","name":"props.ts"},{"uid":"824f9243-27","name":"ref.ts"},{"uid":"824f9243-29","name":"show.ts"},{"uid":"824f9243-31","name":"style.ts"},{"uid":"824f9243-33","name":"suspense.ts"},{"uid":"824f9243-35","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"824f9243-1":{"renderedLength":3078,"gzipLength":1313,"brotliLength":0,"metaUid":"824f9243-0"},"824f9243-3":{"renderedLength":1471,"gzipLength":693,"brotliLength":0,"metaUid":"824f9243-2"},"824f9243-5":{"renderedLength":3173,"gzipLength":1409,"brotliLength":0,"metaUid":"824f9243-4"},"824f9243-7":{"renderedLength":3600,"gzipLength":1542,"brotliLength":0,"metaUid":"824f9243-6"},"824f9243-9":{"renderedLength":1082,"gzipLength":597,"brotliLength":0,"metaUid":"824f9243-8"},"824f9243-11":{"renderedLength":490,"gzipLength":291,"brotliLength":0,"metaUid":"824f9243-10"},"824f9243-13":{"renderedLength":1208,"gzipLength":633,"brotliLength":0,"metaUid":"824f9243-12"},"824f9243-15":{"renderedLength":1659,"gzipLength":842,"brotliLength":0,"metaUid":"824f9243-14"},"824f9243-17":{"renderedLength":700,"gzipLength":478,"brotliLength":0,"metaUid":"824f9243-16"},"824f9243-19":{"renderedLength":461,"gzipLength":273,"brotliLength":0,"metaUid":"824f9243-18"},"824f9243-21":{"renderedLength":1018,"gzipLength":571,"brotliLength":0,"metaUid":"824f9243-20"},"824f9243-23":{"renderedLength":818,"gzipLength":491,"brotliLength":0,"metaUid":"824f9243-22"},"824f9243-25":{"renderedLength":4339,"gzipLength":1641,"brotliLength":0,"metaUid":"824f9243-24"},"824f9243-27":{"renderedLength":86,"gzipLength":98,"brotliLength":0,"metaUid":"824f9243-26"},"824f9243-29":{"renderedLength":2022,"gzipLength":854,"brotliLength":0,"metaUid":"824f9243-28"},"824f9243-31":{"renderedLength":1858,"gzipLength":825,"brotliLength":0,"metaUid":"824f9243-30"},"824f9243-33":{"renderedLength":1104,"gzipLength":614,"brotliLength":0,"metaUid":"824f9243-32"},"824f9243-35":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"824f9243-34"}},"nodeMetas":{"824f9243-0":{"id":"/src/lifecycle.ts","moduleParts":{"index.js":"824f9243-1"},"imported":[],"importedBy":[{"uid":"824f9243-34"},{"uid":"824f9243-2"},{"uid":"824f9243-6"},{"uid":"824f9243-14"}]},"824f9243-2":{"id":"/src/component.ts","moduleParts":{"index.js":"824f9243-3"},"imported":[{"uid":"824f9243-0"}],"importedBy":[{"uid":"824f9243-34"},{"uid":"824f9243-14"}]},"824f9243-4":{"id":"/src/compat-marker.ts","moduleParts":{"index.js":"824f9243-5"},"imported":[],"importedBy":[{"uid":"824f9243-34"},{"uid":"824f9243-14"}]},"824f9243-6":{"id":"/src/context.ts","moduleParts":{"index.js":"824f9243-7"},"imported":[{"uid":"824f9243-36"},{"uid":"824f9243-0"}],"importedBy":[{"uid":"824f9243-34"}]},"824f9243-8":{"id":"/src/h.ts","moduleParts":{"index.js":"824f9243-9"},"imported":[],"importedBy":[{"uid":"824f9243-34"},{"uid":"824f9243-10"},{"uid":"824f9243-18"},{"uid":"824f9243-32"}]},"824f9243-10":{"id":"/src/dynamic.ts","moduleParts":{"index.js":"824f9243-11"},"imported":[{"uid":"824f9243-8"}],"importedBy":[{"uid":"824f9243-34"}]},"824f9243-12":{"id":"/src/telemetry.ts","moduleParts":{"index.js":"824f9243-13"},"imported":[],"importedBy":[{"uid":"824f9243-34"},{"uid":"824f9243-14"}]},"824f9243-14":{"id":"/src/error-boundary.ts","moduleParts":{"index.js":"824f9243-15"},"imported":[{"uid":"824f9243-36"},{"uid":"824f9243-4"},{"uid":"824f9243-2"},{"uid":"824f9243-0"},{"uid":"824f9243-12"}],"importedBy":[{"uid":"824f9243-34"}]},"824f9243-16":{"id":"/src/for.ts","moduleParts":{"index.js":"824f9243-17"},"imported":[],"importedBy":[{"uid":"824f9243-34"}]},"824f9243-18":{"id":"/src/lazy.ts","moduleParts":{"index.js":"824f9243-19"},"imported":[{"uid":"824f9243-36"},{"uid":"824f9243-8"}],"importedBy":[{"uid":"824f9243-34"}]},"824f9243-20":{"id":"/src/map-array.ts","moduleParts":{"index.js":"824f9243-21"},"imported":[],"importedBy":[{"uid":"824f9243-34"}]},"824f9243-22":{"id":"/src/portal.ts","moduleParts":{"index.js":"824f9243-23"},"imported":[],"importedBy":[{"uid":"824f9243-34"}]},"824f9243-24":{"id":"/src/props.ts","moduleParts":{"index.js":"824f9243-25"},"imported":[],"importedBy":[{"uid":"824f9243-34"}]},"824f9243-26":{"id":"/src/ref.ts","moduleParts":{"index.js":"824f9243-27"},"imported":[],"importedBy":[{"uid":"824f9243-34"}]},"824f9243-28":{"id":"/src/show.ts","moduleParts":{"index.js":"824f9243-29"},"imported":[],"importedBy":[{"uid":"824f9243-34"}]},"824f9243-30":{"id":"/src/style.ts","moduleParts":{"index.js":"824f9243-31"},"imported":[],"importedBy":[{"uid":"824f9243-34"}]},"824f9243-32":{"id":"/src/suspense.ts","moduleParts":{"index.js":"824f9243-33"},"imported":[{"uid":"824f9243-8"}],"importedBy":[{"uid":"824f9243-34"}]},"824f9243-34":{"id":"/src/index.ts","moduleParts":{"index.js":"824f9243-35"},"imported":[{"uid":"824f9243-2"},{"uid":"824f9243-4"},{"uid":"824f9243-6"},{"uid":"824f9243-10"},{"uid":"824f9243-14"},{"uid":"824f9243-16"},{"uid":"824f9243-8"},{"uid":"824f9243-18"},{"uid":"824f9243-0"},{"uid":"824f9243-20"},{"uid":"824f9243-22"},{"uid":"824f9243-24"},{"uid":"824f9243-26"},{"uid":"824f9243-28"},{"uid":"824f9243-30"},{"uid":"824f9243-32"},{"uid":"824f9243-12"}],"importedBy":[],"isEntry":true},"824f9243-36":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"824f9243-6"},{"uid":"824f9243-14"},{"uid":"824f9243-18"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5389
+ const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"46e89893-1","name":"lifecycle.ts"},{"uid":"46e89893-3","name":"component.ts"},{"uid":"46e89893-5","name":"compat-marker.ts"},{"uid":"46e89893-7","name":"context.ts"},{"uid":"46e89893-9","name":"h.ts"},{"uid":"46e89893-11","name":"dynamic.ts"},{"uid":"46e89893-13","name":"telemetry.ts"},{"uid":"46e89893-15","name":"error-boundary.ts"},{"uid":"46e89893-17","name":"for.ts"},{"uid":"46e89893-19","name":"lazy.ts"},{"uid":"46e89893-21","name":"map-array.ts"},{"uid":"46e89893-23","name":"portal.ts"},{"uid":"46e89893-25","name":"props.ts"},{"uid":"46e89893-27","name":"ref.ts"},{"uid":"46e89893-29","name":"show.ts"},{"uid":"46e89893-31","name":"style.ts"},{"uid":"46e89893-33","name":"suspense.ts"},{"uid":"46e89893-35","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"46e89893-1":{"renderedLength":3078,"gzipLength":1313,"brotliLength":0,"metaUid":"46e89893-0"},"46e89893-3":{"renderedLength":1471,"gzipLength":693,"brotliLength":0,"metaUid":"46e89893-2"},"46e89893-5":{"renderedLength":3173,"gzipLength":1409,"brotliLength":0,"metaUid":"46e89893-4"},"46e89893-7":{"renderedLength":3600,"gzipLength":1542,"brotliLength":0,"metaUid":"46e89893-6"},"46e89893-9":{"renderedLength":1813,"gzipLength":957,"brotliLength":0,"metaUid":"46e89893-8"},"46e89893-11":{"renderedLength":490,"gzipLength":291,"brotliLength":0,"metaUid":"46e89893-10"},"46e89893-13":{"renderedLength":1208,"gzipLength":633,"brotliLength":0,"metaUid":"46e89893-12"},"46e89893-15":{"renderedLength":1659,"gzipLength":842,"brotliLength":0,"metaUid":"46e89893-14"},"46e89893-17":{"renderedLength":700,"gzipLength":478,"brotliLength":0,"metaUid":"46e89893-16"},"46e89893-19":{"renderedLength":461,"gzipLength":273,"brotliLength":0,"metaUid":"46e89893-18"},"46e89893-21":{"renderedLength":1018,"gzipLength":571,"brotliLength":0,"metaUid":"46e89893-20"},"46e89893-23":{"renderedLength":818,"gzipLength":491,"brotliLength":0,"metaUid":"46e89893-22"},"46e89893-25":{"renderedLength":4339,"gzipLength":1641,"brotliLength":0,"metaUid":"46e89893-24"},"46e89893-27":{"renderedLength":86,"gzipLength":98,"brotliLength":0,"metaUid":"46e89893-26"},"46e89893-29":{"renderedLength":2022,"gzipLength":854,"brotliLength":0,"metaUid":"46e89893-28"},"46e89893-31":{"renderedLength":1858,"gzipLength":825,"brotliLength":0,"metaUid":"46e89893-30"},"46e89893-33":{"renderedLength":1104,"gzipLength":614,"brotliLength":0,"metaUid":"46e89893-32"},"46e89893-35":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"46e89893-34"}},"nodeMetas":{"46e89893-0":{"id":"/src/lifecycle.ts","moduleParts":{"index.js":"46e89893-1"},"imported":[],"importedBy":[{"uid":"46e89893-34"},{"uid":"46e89893-2"},{"uid":"46e89893-6"},{"uid":"46e89893-14"}]},"46e89893-2":{"id":"/src/component.ts","moduleParts":{"index.js":"46e89893-3"},"imported":[{"uid":"46e89893-0"}],"importedBy":[{"uid":"46e89893-34"},{"uid":"46e89893-14"}]},"46e89893-4":{"id":"/src/compat-marker.ts","moduleParts":{"index.js":"46e89893-5"},"imported":[],"importedBy":[{"uid":"46e89893-34"},{"uid":"46e89893-14"}]},"46e89893-6":{"id":"/src/context.ts","moduleParts":{"index.js":"46e89893-7"},"imported":[{"uid":"46e89893-36"},{"uid":"46e89893-0"}],"importedBy":[{"uid":"46e89893-34"}]},"46e89893-8":{"id":"/src/h.ts","moduleParts":{"index.js":"46e89893-9"},"imported":[],"importedBy":[{"uid":"46e89893-34"},{"uid":"46e89893-10"},{"uid":"46e89893-18"},{"uid":"46e89893-32"}]},"46e89893-10":{"id":"/src/dynamic.ts","moduleParts":{"index.js":"46e89893-11"},"imported":[{"uid":"46e89893-8"}],"importedBy":[{"uid":"46e89893-34"}]},"46e89893-12":{"id":"/src/telemetry.ts","moduleParts":{"index.js":"46e89893-13"},"imported":[],"importedBy":[{"uid":"46e89893-34"},{"uid":"46e89893-14"}]},"46e89893-14":{"id":"/src/error-boundary.ts","moduleParts":{"index.js":"46e89893-15"},"imported":[{"uid":"46e89893-36"},{"uid":"46e89893-4"},{"uid":"46e89893-2"},{"uid":"46e89893-0"},{"uid":"46e89893-12"}],"importedBy":[{"uid":"46e89893-34"}]},"46e89893-16":{"id":"/src/for.ts","moduleParts":{"index.js":"46e89893-17"},"imported":[],"importedBy":[{"uid":"46e89893-34"}]},"46e89893-18":{"id":"/src/lazy.ts","moduleParts":{"index.js":"46e89893-19"},"imported":[{"uid":"46e89893-36"},{"uid":"46e89893-8"}],"importedBy":[{"uid":"46e89893-34"}]},"46e89893-20":{"id":"/src/map-array.ts","moduleParts":{"index.js":"46e89893-21"},"imported":[],"importedBy":[{"uid":"46e89893-34"}]},"46e89893-22":{"id":"/src/portal.ts","moduleParts":{"index.js":"46e89893-23"},"imported":[],"importedBy":[{"uid":"46e89893-34"}]},"46e89893-24":{"id":"/src/props.ts","moduleParts":{"index.js":"46e89893-25"},"imported":[],"importedBy":[{"uid":"46e89893-34"}]},"46e89893-26":{"id":"/src/ref.ts","moduleParts":{"index.js":"46e89893-27"},"imported":[],"importedBy":[{"uid":"46e89893-34"}]},"46e89893-28":{"id":"/src/show.ts","moduleParts":{"index.js":"46e89893-29"},"imported":[],"importedBy":[{"uid":"46e89893-34"}]},"46e89893-30":{"id":"/src/style.ts","moduleParts":{"index.js":"46e89893-31"},"imported":[],"importedBy":[{"uid":"46e89893-34"}]},"46e89893-32":{"id":"/src/suspense.ts","moduleParts":{"index.js":"46e89893-33"},"imported":[{"uid":"46e89893-8"}],"importedBy":[{"uid":"46e89893-34"}]},"46e89893-34":{"id":"/src/index.ts","moduleParts":{"index.js":"46e89893-35"},"imported":[{"uid":"46e89893-2"},{"uid":"46e89893-4"},{"uid":"46e89893-6"},{"uid":"46e89893-10"},{"uid":"46e89893-14"},{"uid":"46e89893-16"},{"uid":"46e89893-8"},{"uid":"46e89893-18"},{"uid":"46e89893-0"},{"uid":"46e89893-20"},{"uid":"46e89893-22"},{"uid":"46e89893-24"},{"uid":"46e89893-26"},{"uid":"46e89893-28"},{"uid":"46e89893-30"},{"uid":"46e89893-32"},{"uid":"46e89893-12"}],"importedBy":[],"isEntry":true},"46e89893-36":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"46e89893-6"},{"uid":"46e89893-14"},{"uid":"46e89893-18"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5390
5390
 
5391
5391
  const run = () => {
5392
5392
  const width = window.innerWidth;
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
5386
5386
  </script>
5387
5387
  <script>
5388
5388
  /*<!--*/
5389
- const data = {"version":2,"tree":{"name":"root","children":[{"name":"jsx-dev-runtime.js","children":[{"name":"src","children":[{"uid":"3319ebff-1","name":"h.ts"},{"uid":"3319ebff-3","name":"jsx-runtime.ts"},{"uid":"3319ebff-5","name":"jsx-dev-runtime.ts"}]}]}],"isRoot":true},"nodeParts":{"3319ebff-1":{"renderedLength":1082,"gzipLength":597,"brotliLength":0,"metaUid":"3319ebff-0"},"3319ebff-3":{"renderedLength":1103,"gzipLength":640,"brotliLength":0,"metaUid":"3319ebff-2"},"3319ebff-5":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"3319ebff-4"}},"nodeMetas":{"3319ebff-0":{"id":"/src/h.ts","moduleParts":{"jsx-dev-runtime.js":"3319ebff-1"},"imported":[],"importedBy":[{"uid":"3319ebff-2"}]},"3319ebff-2":{"id":"/src/jsx-runtime.ts","moduleParts":{"jsx-dev-runtime.js":"3319ebff-3"},"imported":[{"uid":"3319ebff-0"}],"importedBy":[{"uid":"3319ebff-4"}]},"3319ebff-4":{"id":"/src/jsx-dev-runtime.ts","moduleParts":{"jsx-dev-runtime.js":"3319ebff-5"},"imported":[{"uid":"3319ebff-2"}],"importedBy":[],"isEntry":true}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5389
+ const data = {"version":2,"tree":{"name":"root","children":[{"name":"jsx-dev-runtime.js","children":[{"name":"src","children":[{"uid":"235a8e25-1","name":"h.ts"},{"uid":"235a8e25-3","name":"jsx-runtime.ts"},{"uid":"235a8e25-5","name":"jsx-dev-runtime.ts"}]}]}],"isRoot":true},"nodeParts":{"235a8e25-1":{"renderedLength":1813,"gzipLength":957,"brotliLength":0,"metaUid":"235a8e25-0"},"235a8e25-3":{"renderedLength":1103,"gzipLength":640,"brotliLength":0,"metaUid":"235a8e25-2"},"235a8e25-5":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"235a8e25-4"}},"nodeMetas":{"235a8e25-0":{"id":"/src/h.ts","moduleParts":{"jsx-dev-runtime.js":"235a8e25-1"},"imported":[],"importedBy":[{"uid":"235a8e25-2"}]},"235a8e25-2":{"id":"/src/jsx-runtime.ts","moduleParts":{"jsx-dev-runtime.js":"235a8e25-3"},"imported":[{"uid":"235a8e25-0"}],"importedBy":[{"uid":"235a8e25-4"}]},"235a8e25-4":{"id":"/src/jsx-dev-runtime.ts","moduleParts":{"jsx-dev-runtime.js":"235a8e25-5"},"imported":[{"uid":"235a8e25-2"}],"importedBy":[],"isEntry":true}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5390
5390
 
5391
5391
  const run = () => {
5392
5392
  const width = window.innerWidth;
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
5386
5386
  </script>
5387
5387
  <script>
5388
5388
  /*<!--*/
5389
- const data = {"version":2,"tree":{"name":"root","children":[{"name":"jsx-runtime.js","children":[{"name":"src","children":[{"uid":"843e1a91-1","name":"h.ts"},{"uid":"843e1a91-3","name":"jsx-runtime.ts"}]}]}],"isRoot":true},"nodeParts":{"843e1a91-1":{"renderedLength":1082,"gzipLength":597,"brotliLength":0,"metaUid":"843e1a91-0"},"843e1a91-3":{"renderedLength":1103,"gzipLength":640,"brotliLength":0,"metaUid":"843e1a91-2"}},"nodeMetas":{"843e1a91-0":{"id":"/src/h.ts","moduleParts":{"jsx-runtime.js":"843e1a91-1"},"imported":[],"importedBy":[{"uid":"843e1a91-2"}]},"843e1a91-2":{"id":"/src/jsx-runtime.ts","moduleParts":{"jsx-runtime.js":"843e1a91-3"},"imported":[{"uid":"843e1a91-0"}],"importedBy":[],"isEntry":true}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5389
+ const data = {"version":2,"tree":{"name":"root","children":[{"name":"jsx-runtime.js","children":[{"name":"src","children":[{"uid":"8c2807f7-1","name":"h.ts"},{"uid":"8c2807f7-3","name":"jsx-runtime.ts"}]}]}],"isRoot":true},"nodeParts":{"8c2807f7-1":{"renderedLength":1813,"gzipLength":957,"brotliLength":0,"metaUid":"8c2807f7-0"},"8c2807f7-3":{"renderedLength":1103,"gzipLength":640,"brotliLength":0,"metaUid":"8c2807f7-2"}},"nodeMetas":{"8c2807f7-0":{"id":"/src/h.ts","moduleParts":{"jsx-runtime.js":"8c2807f7-1"},"imported":[],"importedBy":[{"uid":"8c2807f7-2"}]},"8c2807f7-2":{"id":"/src/jsx-runtime.ts","moduleParts":{"jsx-runtime.js":"8c2807f7-3"},"imported":[{"uid":"8c2807f7-0"}],"importedBy":[],"isEntry":true}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5390
5390
 
5391
5391
  const run = () => {
5392
5392
  const width = window.innerWidth;
package/lib/index.js CHANGED
@@ -350,8 +350,22 @@ setSnapshotCapture({
350
350
 
351
351
  //#endregion
352
352
  //#region src/h.ts
353
- /** Marker for fragment nodes — renders children without a wrapper element */
354
- const Fragment = Symbol("Pyreon.Fragment");
353
+ /**
354
+ * Marker for fragment nodes — renders children without a wrapper element.
355
+ *
356
+ * MUST use `Symbol.for(...)` (global registry, keyed by string), NOT
357
+ * `Symbol(...)` (fresh per evaluation). `h.ts` is inlined into BOTH the
358
+ * main `lib/index.js` and the `lib/jsx-runtime.js` published bundles —
359
+ * each bundle's evaluation of a bare `Symbol(...)` would produce a
360
+ * DISTINCT Symbol identity. JSX `<>` compiles to `jsx(Fragment, ...)` and
361
+ * resolves to jsx-runtime's identity; `runtime-server` checks
362
+ * `vnode.type === Fragment` against the main-entry identity. Mismatch
363
+ * fell through to `renderElement` and crashed SSG with
364
+ * `TypeError: Cannot convert a Symbol value to a string`.
365
+ * `Symbol.for()` keys by string in a global registry shared across all
366
+ * bundle evaluations — same identity everywhere.
367
+ */
368
+ const Fragment = Symbol.for("Pyreon.Fragment");
355
369
  /**
356
370
  * Hyperscript function — the compiled output of JSX.
357
371
  * `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
@@ -1,6 +1,20 @@
1
1
  //#region src/h.ts
2
- /** Marker for fragment nodes — renders children without a wrapper element */
3
- const Fragment = Symbol("Pyreon.Fragment");
2
+ /**
3
+ * Marker for fragment nodes — renders children without a wrapper element.
4
+ *
5
+ * MUST use `Symbol.for(...)` (global registry, keyed by string), NOT
6
+ * `Symbol(...)` (fresh per evaluation). `h.ts` is inlined into BOTH the
7
+ * main `lib/index.js` and the `lib/jsx-runtime.js` published bundles —
8
+ * each bundle's evaluation of a bare `Symbol(...)` would produce a
9
+ * DISTINCT Symbol identity. JSX `<>` compiles to `jsx(Fragment, ...)` and
10
+ * resolves to jsx-runtime's identity; `runtime-server` checks
11
+ * `vnode.type === Fragment` against the main-entry identity. Mismatch
12
+ * fell through to `renderElement` and crashed SSG with
13
+ * `TypeError: Cannot convert a Symbol value to a string`.
14
+ * `Symbol.for()` keys by string in a global registry shared across all
15
+ * bundle evaluations — same identity everywhere.
16
+ */
17
+ const Fragment = Symbol.for("Pyreon.Fragment");
4
18
  /**
5
19
  * Hyperscript function — the compiled output of JSX.
6
20
  * `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
@@ -1,6 +1,20 @@
1
1
  //#region src/h.ts
2
- /** Marker for fragment nodes — renders children without a wrapper element */
3
- const Fragment = Symbol("Pyreon.Fragment");
2
+ /**
3
+ * Marker for fragment nodes — renders children without a wrapper element.
4
+ *
5
+ * MUST use `Symbol.for(...)` (global registry, keyed by string), NOT
6
+ * `Symbol(...)` (fresh per evaluation). `h.ts` is inlined into BOTH the
7
+ * main `lib/index.js` and the `lib/jsx-runtime.js` published bundles —
8
+ * each bundle's evaluation of a bare `Symbol(...)` would produce a
9
+ * DISTINCT Symbol identity. JSX `<>` compiles to `jsx(Fragment, ...)` and
10
+ * resolves to jsx-runtime's identity; `runtime-server` checks
11
+ * `vnode.type === Fragment` against the main-entry identity. Mismatch
12
+ * fell through to `renderElement` and crashed SSG with
13
+ * `TypeError: Cannot convert a Symbol value to a string`.
14
+ * `Symbol.for()` keys by string in a global registry shared across all
15
+ * bundle evaluations — same identity everywhere.
16
+ */
17
+ const Fragment = Symbol.for("Pyreon.Fragment");
4
18
  /**
5
19
  * Hyperscript function — the compiled output of JSX.
6
20
  * `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
@@ -16,8 +16,41 @@ type Props = Record<string, unknown>;
16
16
  * It returns any renderable content and may call lifecycle hooks during setup.
17
17
  */
18
18
  type ComponentFn<P extends Props = Props> = (props: P) => VNodeChild;
19
- /** Extract the props type from a component function, or pass through if already a props type. */
20
- type ExtractProps<T> = T extends ComponentFn<infer P> ? P : T;
19
+ /**
20
+ * Extract the props type from a component function, or pass through if already
21
+ * a props type. **Multi-overload aware** — matches up to 4 call signatures and
22
+ * produces the UNION of their first-argument types. A single-overload function
23
+ * still works (the union of 4 copies of the same props type dedupes back to
24
+ * the single shape).
25
+ *
26
+ * **Why this shape**. `T extends (props: infer P) => any ? P : never` only
27
+ * captures the LAST overload of a multi-overload function — TS's overload-
28
+ * resolution-against-conditional-types semantics. Multi-overload primitives
29
+ * (Iterator, List, Element, etc.) need the union of every overload's props
30
+ * to survive HOC wrapping (`rocketstyle()`, `attrs()`) without silently
31
+ * downgrading the public prop surface to the loosest overload. Mirrors
32
+ * vitus-labs PR #222.
33
+ *
34
+ * @example
35
+ * function Iterator<T extends SimpleValue>(p: { data: T[]; valueName?: string }): VNodeChild
36
+ * function Iterator<T extends ObjectValue>(p: { data: T[]; component: ComponentFn<T> }): VNodeChild
37
+ * type Props = ExtractProps<typeof Iterator>
38
+ * // → { data: SimpleValue[]; valueName?: string }
39
+ * // | { data: ObjectValue[]; component: ComponentFn<ObjectValue> }
40
+ */
41
+ type ExtractProps<T> = T extends {
42
+ (props: infer P1, ...args: any): any;
43
+ (props: infer P2, ...args: any): any;
44
+ (props: infer P3, ...args: any): any;
45
+ (props: infer P4, ...args: any): any;
46
+ } ? P1 | P2 | P3 | P4 : T extends {
47
+ (props: infer P1, ...args: any): any;
48
+ (props: infer P2, ...args: any): any;
49
+ (props: infer P3, ...args: any): any;
50
+ } ? P1 | P2 | P3 : T extends {
51
+ (props: infer P1, ...args: any): any;
52
+ (props: infer P2, ...args: any): any;
53
+ } ? P1 | P2 : T extends ComponentFn<infer P> ? P : T;
21
54
  /** A higher-order component that wraps a component, optionally transforming its props. */
22
55
  type HigherOrderComponent<HOP extends Props, P extends Props | undefined = undefined> = (Component: ComponentFn<HOP>) => ComponentFn<P extends undefined ? HOP : P>;
23
56
  /**
@@ -282,7 +315,19 @@ declare function ErrorBoundary(props: {
282
315
  */
283
316
  declare const ForSymbol: unique symbol;
284
317
  interface ForProps<T> {
285
- each: () => T[];
318
+ /**
319
+ * The list to iterate. Accepts EITHER a function returning the array
320
+ * (preferred — keeps reactivity intact when the array comes from a
321
+ * signal accessor) OR the array directly (convenient for static lists
322
+ * or already-resolved arrays). The runtime in `runtime-dom/src/mount.ts`
323
+ * normalizes both shapes; this type matches the runtime so users aren't
324
+ * forced to write `each={() => items}` for a plain array.
325
+ *
326
+ * @example
327
+ * <For each={items}>{r => <li>{r.label}</li>}</For> // static
328
+ * <For each={() => store.items()}>{r => <li>...</li>}</For> // reactive
329
+ */
330
+ each: T[] | (() => T[]);
286
331
  /** Keying function — use `by` not `key` (JSX extracts `key` for VNode reconciliation). */
287
332
  by: (item: T) => string | number;
288
333
  children: (item: T) => VNode | NativeItem;
@@ -306,8 +351,22 @@ interface ForProps<T> {
306
351
  declare function For<T>(props: ForProps<T>): VNode;
307
352
  //#endregion
308
353
  //#region src/h.d.ts
309
- /** Marker for fragment nodes — renders children without a wrapper element */
310
- declare const Fragment: unique symbol;
354
+ /**
355
+ * Marker for fragment nodes — renders children without a wrapper element.
356
+ *
357
+ * MUST use `Symbol.for(...)` (global registry, keyed by string), NOT
358
+ * `Symbol(...)` (fresh per evaluation). `h.ts` is inlined into BOTH the
359
+ * main `lib/index.js` and the `lib/jsx-runtime.js` published bundles —
360
+ * each bundle's evaluation of a bare `Symbol(...)` would produce a
361
+ * DISTINCT Symbol identity. JSX `<>` compiles to `jsx(Fragment, ...)` and
362
+ * resolves to jsx-runtime's identity; `runtime-server` checks
363
+ * `vnode.type === Fragment` against the main-entry identity. Mismatch
364
+ * fell through to `renderElement` and crashed SSG with
365
+ * `TypeError: Cannot convert a Symbol value to a string`.
366
+ * `Symbol.for()` keys by string in a global registry shared across all
367
+ * bundle evaluations — same identity everywhere.
368
+ */
369
+ declare const Fragment: symbol;
311
370
  /**
312
371
  * Hyperscript function — the compiled output of JSX.
313
372
  * `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
@@ -18,8 +18,22 @@ type Props = Record<string, unknown>;
18
18
  type ComponentFn<P extends Props = Props> = (props: P) => VNodeChild;
19
19
  //#endregion
20
20
  //#region src/h.d.ts
21
- /** Marker for fragment nodes — renders children without a wrapper element */
22
- declare const Fragment: unique symbol;
21
+ /**
22
+ * Marker for fragment nodes — renders children without a wrapper element.
23
+ *
24
+ * MUST use `Symbol.for(...)` (global registry, keyed by string), NOT
25
+ * `Symbol(...)` (fresh per evaluation). `h.ts` is inlined into BOTH the
26
+ * main `lib/index.js` and the `lib/jsx-runtime.js` published bundles —
27
+ * each bundle's evaluation of a bare `Symbol(...)` would produce a
28
+ * DISTINCT Symbol identity. JSX `<>` compiles to `jsx(Fragment, ...)` and
29
+ * resolves to jsx-runtime's identity; `runtime-server` checks
30
+ * `vnode.type === Fragment` against the main-entry identity. Mismatch
31
+ * fell through to `renderElement` and crashed SSG with
32
+ * `TypeError: Cannot convert a Symbol value to a string`.
33
+ * `Symbol.for()` keys by string in a global registry shared across all
34
+ * bundle evaluations — same identity everywhere.
35
+ */
36
+ declare const Fragment: symbol;
23
37
  //#endregion
24
38
  //#region src/ref.d.ts
25
39
  /**
@@ -18,8 +18,22 @@ type Props = Record<string, unknown>;
18
18
  type ComponentFn<P extends Props = Props> = (props: P) => VNodeChild;
19
19
  //#endregion
20
20
  //#region src/h.d.ts
21
- /** Marker for fragment nodes — renders children without a wrapper element */
22
- declare const Fragment: unique symbol;
21
+ /**
22
+ * Marker for fragment nodes — renders children without a wrapper element.
23
+ *
24
+ * MUST use `Symbol.for(...)` (global registry, keyed by string), NOT
25
+ * `Symbol(...)` (fresh per evaluation). `h.ts` is inlined into BOTH the
26
+ * main `lib/index.js` and the `lib/jsx-runtime.js` published bundles —
27
+ * each bundle's evaluation of a bare `Symbol(...)` would produce a
28
+ * DISTINCT Symbol identity. JSX `<>` compiles to `jsx(Fragment, ...)` and
29
+ * resolves to jsx-runtime's identity; `runtime-server` checks
30
+ * `vnode.type === Fragment` against the main-entry identity. Mismatch
31
+ * fell through to `renderElement` and crashed SSG with
32
+ * `TypeError: Cannot convert a Symbol value to a string`.
33
+ * `Symbol.for()` keys by string in a global registry shared across all
34
+ * bundle evaluations — same identity everywhere.
35
+ */
36
+ declare const Fragment: symbol;
23
37
  //#endregion
24
38
  //#region src/ref.d.ts
25
39
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/core",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Core component model and lifecycle for Pyreon",
5
5
  "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/core#readme",
6
6
  "bugs": {
@@ -53,7 +53,7 @@
53
53
  "prepublishOnly": "bun run build"
54
54
  },
55
55
  "dependencies": {
56
- "@pyreon/reactivity": "^0.15.0"
56
+ "@pyreon/reactivity": "^0.16.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@pyreon/manifest": "0.13.1"
package/src/for.ts CHANGED
@@ -7,7 +7,19 @@ import type { NativeItem, Props, VNode } from './types'
7
7
  export const ForSymbol: unique symbol = Symbol('pyreon.For')
8
8
 
9
9
  export interface ForProps<T> {
10
- each: () => T[]
10
+ /**
11
+ * The list to iterate. Accepts EITHER a function returning the array
12
+ * (preferred — keeps reactivity intact when the array comes from a
13
+ * signal accessor) OR the array directly (convenient for static lists
14
+ * or already-resolved arrays). The runtime in `runtime-dom/src/mount.ts`
15
+ * normalizes both shapes; this type matches the runtime so users aren't
16
+ * forced to write `each={() => items}` for a plain array.
17
+ *
18
+ * @example
19
+ * <For each={items}>{r => <li>{r.label}</li>}</For> // static
20
+ * <For each={() => store.items()}>{r => <li>...</li>}</For> // reactive
21
+ */
22
+ each: T[] | (() => T[])
11
23
  /** Keying function — use `by` not `key` (JSX extracts `key` for VNode reconciliation). */
12
24
  by: (item: T) => string | number
13
25
  children: (item: T) => VNode | NativeItem
package/src/h.ts CHANGED
@@ -1,7 +1,21 @@
1
1
  import type { ComponentFn, Props, VNode, VNodeChild } from './types'
2
2
 
3
- /** Marker for fragment nodes — renders children without a wrapper element */
4
- export const Fragment: unique symbol = Symbol('Pyreon.Fragment')
3
+ /**
4
+ * Marker for fragment nodes — renders children without a wrapper element.
5
+ *
6
+ * MUST use `Symbol.for(...)` (global registry, keyed by string), NOT
7
+ * `Symbol(...)` (fresh per evaluation). `h.ts` is inlined into BOTH the
8
+ * main `lib/index.js` and the `lib/jsx-runtime.js` published bundles —
9
+ * each bundle's evaluation of a bare `Symbol(...)` would produce a
10
+ * DISTINCT Symbol identity. JSX `<>` compiles to `jsx(Fragment, ...)` and
11
+ * resolves to jsx-runtime's identity; `runtime-server` checks
12
+ * `vnode.type === Fragment` against the main-entry identity. Mismatch
13
+ * fell through to `renderElement` and crashed SSG with
14
+ * `TypeError: Cannot convert a Symbol value to a string`.
15
+ * `Symbol.for()` keys by string in a global registry shared across all
16
+ * bundle evaluations — same identity everywhere.
17
+ */
18
+ export const Fragment: symbol = Symbol.for('Pyreon.Fragment')
5
19
 
6
20
  /**
7
21
  * Hyperscript function — the compiled output of JSX.
package/src/manifest.ts CHANGED
@@ -524,11 +524,19 @@ return wrapCompatComponent(type)(props)`,
524
524
  {
525
525
  name: 'ExtractProps',
526
526
  kind: 'type',
527
- signature: 'type ExtractProps<T> = T extends ComponentFn<infer P> ? P : T',
527
+ signature:
528
+ 'type ExtractProps<T> = /* matches up to 4 overloads, unions the props */ T extends ComponentFn<infer P> ? P : T',
528
529
  summary:
529
- 'Extracts the props type from a `ComponentFn`. Passes through unchanged if `T` is not a `ComponentFn`. Useful for HOC patterns and typed wrappers that need to infer the wrapped component\'s prop interface.',
530
- example: `const Greet: ComponentFn<{ name: string }> = ({ name }) => <h1>{name}</h1>
531
- type Props = ExtractProps<typeof Greet> // { name: string }`,
530
+ "Extracts the props type from a `ComponentFn`. Passes through unchanged if `T` is not a `ComponentFn`. **Multi-overload aware** — matches up to 4 call signatures and produces the UNION of their first-argument types. Critical for multi-overload primitives (Iterator, List, Element) whose loosest overload is last; without overload-aware extraction, HOC wrapping (`rocketstyle()`, `attrs()`) silently downgraded their public prop surface. Single-overload functions still work — the union of 4 copies of the same props type dedupes back to the single shape.",
531
+ example: `function Iterator<T extends SimpleValue>(p: { data: T[]; valueName?: string }): VNodeChild
532
+ function Iterator<T extends ObjectValue>(p: { data: T[]; component: ComponentFn<T> }): VNodeChild
533
+ type Props = ExtractProps<typeof Iterator>
534
+ // → { data: SimpleValue[]; valueName?: string }
535
+ // | { data: ObjectValue[]; component: ComponentFn<ObjectValue> }`,
536
+ mistakes: [
537
+ 'Assuming `ExtractProps<T>` returns only the LAST overload — pre-fix it did, post-fix it returns the UNION of up to 4 overloads. Functions with more than 4 overloads still drop the extras.',
538
+ 'Using `T extends (props: infer P) => any ? P : never` directly in user code — that pattern captures only the LAST overload of a multi-overload function. Use `ExtractProps<T>` to get the full union.',
539
+ ],
532
540
  seeAlso: ['HigherOrderComponent'],
533
541
  },
534
542
  {
@@ -982,7 +982,7 @@ describe('lifecycle hooks', () => {
982
982
 
983
983
  describe('edge cases', () => {
984
984
  test('h() with empty children array', () => {
985
- const node = h('div', null, ...[])
985
+ const node = h('div', null, )
986
986
  expect(node.children).toEqual([])
987
987
  })
988
988
 
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Compile-time type tests for `ExtractProps` multi-overload narrowing.
3
+ *
4
+ * Regression: pre-fix, `ExtractProps<T>` collapsed multi-overload functions
5
+ * to the LAST overload's props — TS's overload-resolution-against-conditional-
6
+ * types semantics. Multi-overload primitives (Iterator / List / Element in
7
+ * `@pyreon/elements`) silently downgraded their public prop surface to the
8
+ * loosest overload when wrapped through `rocketstyle()` / `attrs()`. The
9
+ * fix matches up to 4 call signatures via pattern matching and produces the
10
+ * UNION of every overload's first-argument type.
11
+ *
12
+ * Mirrors vitus-labs PR #222. Kept in sync across the 4 copies in
13
+ * `@pyreon/core`, `@pyreon/elements`, `@pyreon/attrs`, and `@pyreon/rocketstyle`
14
+ * — the canonical reference test lives here.
15
+ */
16
+
17
+ import { describe, expectTypeOf, it } from 'vitest'
18
+ import type { ComponentFn, ExtractProps, VNodeChild } from '../index'
19
+
20
+ describe('ExtractProps — single-overload functions still work', () => {
21
+ it('extracts props from a ComponentFn<P>', () => {
22
+ type Greet = ComponentFn<{ name: string }>
23
+ expectTypeOf<ExtractProps<Greet>>().toEqualTypeOf<{ name: string }>()
24
+ })
25
+
26
+ it('extracts props from a bare (props: P) => any signature', () => {
27
+ type Fn = (props: { count: number }) => string
28
+ expectTypeOf<ExtractProps<Fn>>().toEqualTypeOf<{ count: number }>()
29
+ })
30
+
31
+ it('passes through a non-function shape unchanged', () => {
32
+ type Props = { id: string; value: number }
33
+ expectTypeOf<ExtractProps<Props>>().toEqualTypeOf<Props>()
34
+ })
35
+ })
36
+
37
+ describe('ExtractProps — multi-overload narrowing (load-bearing assertions)', () => {
38
+ it('unions both arms of a 2-overload function', () => {
39
+ interface TwoOverloads {
40
+ (props: { kind: 'a'; value: number }): VNodeChild
41
+ (props: { kind: 'b'; value: string }): VNodeChild
42
+ }
43
+ type Props = ExtractProps<TwoOverloads>
44
+ // Both shapes appear in the extracted union.
45
+ expectTypeOf<Props>().toEqualTypeOf<
46
+ { kind: 'a'; value: number } | { kind: 'b'; value: string }
47
+ >()
48
+ })
49
+
50
+ it('unions all three arms of a 3-overload function (Iterator/List/Element shape)', () => {
51
+ interface ThreeOverloads {
52
+ (props: { mode: 'simple'; data: string[] }): VNodeChild
53
+ (props: { mode: 'object'; data: { id: number }[] }): VNodeChild
54
+ (props: { mode: 'children'; children: unknown }): VNodeChild
55
+ }
56
+ type Props = ExtractProps<ThreeOverloads>
57
+ expectTypeOf<Props>().toEqualTypeOf<
58
+ | { mode: 'simple'; data: string[] }
59
+ | { mode: 'object'; data: { id: number }[] }
60
+ | { mode: 'children'; children: unknown }
61
+ >()
62
+ })
63
+
64
+ it('unions all four arms of a 4-overload function', () => {
65
+ interface FourOverloads {
66
+ (props: { variant: 'a' }): VNodeChild
67
+ (props: { variant: 'b' }): VNodeChild
68
+ (props: { variant: 'c' }): VNodeChild
69
+ (props: { variant: 'd' }): VNodeChild
70
+ }
71
+ type Props = ExtractProps<FourOverloads>
72
+ expectTypeOf<Props>().toEqualTypeOf<
73
+ { variant: 'a' } | { variant: 'b' } | { variant: 'c' } | { variant: 'd' }
74
+ >()
75
+ })
76
+ })
77
+
78
+ describe('ExtractProps — bisect-load-bearing: pre-fix shape would FAIL these', () => {
79
+ /**
80
+ * If `ExtractProps<T>` were reverted to `T extends ComponentFn<infer P> ? P : T`,
81
+ * each of these would extract only the LAST overload's props and the
82
+ * `toEqualTypeOf<union>` check would fail at compile time. This is the
83
+ * structural anchor — the load-bearing regression guard.
84
+ */
85
+
86
+ it('a 2-overload function MUST extract BOTH arms (not just the last)', () => {
87
+ interface OverloadedComp {
88
+ (props: { mode: 'a'; valueA: number }): VNodeChild
89
+ (props: { mode: 'b'; valueB: string }): VNodeChild
90
+ }
91
+ // The first arm `{ mode: 'a'; valueA: number }` must be present in the
92
+ // union. Pre-fix, the conditional collapsed to just the LAST arm.
93
+ type Props = ExtractProps<OverloadedComp>
94
+ // Assignability check: both shapes must be assignable to the extracted type.
95
+ const a: Props = { mode: 'a', valueA: 1 }
96
+ const b: Props = { mode: 'b', valueB: 'x' }
97
+ void a
98
+ void b
99
+ })
100
+
101
+ it("a 3-overload Iterator-shaped surface MUST surface SimpleProps + ObjectProps + ChildrenProps", () => {
102
+ // Synthetic Iterator overload-shape — mirrors the real
103
+ // `@pyreon/elements` Iterator. The structural failure mode pre-fix:
104
+ // `ExtractProps<typeof Iterator>` returned just `ChildrenProps`, so any
105
+ // HOC wrapping (rocketstyle, attrs) lost the SimpleProps + ObjectProps
106
+ // surfaces from the public typed API.
107
+ type SimpleItem = ComponentFn<{ value: string }>
108
+ type ObjectItem = ComponentFn<{ id: number }>
109
+ interface IteratorLike {
110
+ <T extends string | number>(props: {
111
+ data: T[]
112
+ component: SimpleItem
113
+ valueName?: string
114
+ }): VNodeChild
115
+ <T extends { id: number }>(props: {
116
+ data: T[]
117
+ component: ObjectItem
118
+ }): VNodeChild
119
+ (props: { children: VNodeChild }): VNodeChild
120
+ }
121
+ type Props = ExtractProps<IteratorLike>
122
+
123
+ const noopSimple: SimpleItem = () => null
124
+ const noopObject: ObjectItem = () => null
125
+ // SimpleProps arm assignable:
126
+ const simple: Props = { data: ['a', 'b'], component: noopSimple, valueName: 'text' }
127
+ // ObjectProps arm assignable:
128
+ const obj: Props = { data: [{ id: 1 }], component: noopObject }
129
+ // ChildrenProps arm assignable:
130
+ const ch: Props = { children: null }
131
+ void simple
132
+ void obj
133
+ void ch
134
+ })
135
+ })
@@ -91,4 +91,27 @@ describe('For', () => {
91
91
  expect((result as VNode).type).toBe('li')
92
92
  expect((result as VNode).key).toBe(1)
93
93
  })
94
+
95
+ // Regression: `ForProps.each` previously typed as `() => T[]` only.
96
+ // Users writing `<For each={items}>` (with `items: T[]` directly) hit
97
+ // a confusing TS error: `Type 'T[]' is not assignable to type
98
+ // '() => T[]'`. The runtime in `runtime-dom/src/mount.ts:144-147`
99
+ // already accepted both shapes — only the type was forcing the
100
+ // accessor form. Type now accepts `T[] | (() => T[])` so users with
101
+ // already-resolved arrays don't need to wrap them in a thunk just to
102
+ // satisfy the type.
103
+ test('each accepts T[] directly (not just () => T[])', () => {
104
+ // TypeScript-level test: this would not compile pre-fix.
105
+ const items = [1, 2, 3]
106
+ const childFn = (n: number): VNode => h('li', { key: n }, String(n))
107
+ const node = For<number>({ each: items, by: (n) => n, children: childFn })
108
+ expect(node.type).toBe(ForSymbol as unknown as string)
109
+ // Both shapes still work — function form continues to typecheck.
110
+ const node2 = For<number>({
111
+ each: () => items,
112
+ by: (n) => n,
113
+ children: childFn,
114
+ })
115
+ expect(node2.type).toBe(ForSymbol as unknown as string)
116
+ })
94
117
  })
@@ -176,6 +176,27 @@ describe('h() — VNode creation', () => {
176
176
  expect(outer.children).toHaveLength(2)
177
177
  expect((outer.children[0] as VNode).type).toBe(Fragment)
178
178
  })
179
+
180
+ // Regression: pre-fix, `Fragment` was `Symbol('Pyreon.Fragment')` — a
181
+ // fresh symbol per module evaluation. When `h.ts` got bundled into BOTH
182
+ // `lib/index.js` AND `lib/jsx-runtime.js` (each a separate published
183
+ // entry point), each bundle created a DISTINCT Symbol identity. JSX
184
+ // `<>` compiles to `jsx(Fragment, ...)` referring to jsx-runtime's
185
+ // Fragment; `runtime-server` checks `vnode.type === Fragment` against
186
+ // `@pyreon/core`'s main-entry Fragment. The two never matched →
187
+ // fell through to `renderElement` → tried to stringify the Symbol →
188
+ // SSG crashed with `TypeError: Cannot convert a Symbol value to
189
+ // a string`.
190
+ //
191
+ // Fix: use `Symbol.for('Pyreon.Fragment')` — the global registry keys
192
+ // by string, so all bundles inlining h.ts share the same identity.
193
+ //
194
+ // This test asserts the global-registry contract: Fragment IS
195
+ // retrievable from the registry. Bisect-verifiable: reverting h.ts to
196
+ // `Symbol(...)` makes this fail.
197
+ test('Fragment uses Symbol.for() for cross-bundle identity stability', () => {
198
+ expect(Fragment).toBe(Symbol.for('Pyreon.Fragment'))
199
+ })
179
200
  })
180
201
  })
181
202
 
package/src/types.ts CHANGED
@@ -30,8 +30,49 @@ export type ComponentFn<P extends Props = Props> = (props: P) => VNodeChild
30
30
 
31
31
  // ─── Utility types ───────────────────────────────────────────────────────────
32
32
 
33
- /** Extract the props type from a component function, or pass through if already a props type. */
34
- export type ExtractProps<T> = T extends ComponentFn<infer P> ? P : T
33
+ /**
34
+ * Extract the props type from a component function, or pass through if already
35
+ * a props type. **Multi-overload aware** — matches up to 4 call signatures and
36
+ * produces the UNION of their first-argument types. A single-overload function
37
+ * still works (the union of 4 copies of the same props type dedupes back to
38
+ * the single shape).
39
+ *
40
+ * **Why this shape**. `T extends (props: infer P) => any ? P : never` only
41
+ * captures the LAST overload of a multi-overload function — TS's overload-
42
+ * resolution-against-conditional-types semantics. Multi-overload primitives
43
+ * (Iterator, List, Element, etc.) need the union of every overload's props
44
+ * to survive HOC wrapping (`rocketstyle()`, `attrs()`) without silently
45
+ * downgrading the public prop surface to the loosest overload. Mirrors
46
+ * vitus-labs PR #222.
47
+ *
48
+ * @example
49
+ * function Iterator<T extends SimpleValue>(p: { data: T[]; valueName?: string }): VNodeChild
50
+ * function Iterator<T extends ObjectValue>(p: { data: T[]; component: ComponentFn<T> }): VNodeChild
51
+ * type Props = ExtractProps<typeof Iterator>
52
+ * // → { data: SimpleValue[]; valueName?: string }
53
+ * // | { data: ObjectValue[]; component: ComponentFn<ObjectValue> }
54
+ */
55
+ export type ExtractProps<T> = T extends {
56
+ (props: infer P1, ...args: any): any
57
+ (props: infer P2, ...args: any): any
58
+ (props: infer P3, ...args: any): any
59
+ (props: infer P4, ...args: any): any
60
+ }
61
+ ? P1 | P2 | P3 | P4
62
+ : T extends {
63
+ (props: infer P1, ...args: any): any
64
+ (props: infer P2, ...args: any): any
65
+ (props: infer P3, ...args: any): any
66
+ }
67
+ ? P1 | P2 | P3
68
+ : T extends {
69
+ (props: infer P1, ...args: any): any
70
+ (props: infer P2, ...args: any): any
71
+ }
72
+ ? P1 | P2
73
+ : T extends ComponentFn<infer P>
74
+ ? P
75
+ : T
35
76
 
36
77
  /** A higher-order component that wraps a component, optionally transforming its props. */
37
78
  export type HigherOrderComponent<HOP extends Props, P extends Props | undefined = undefined> = (