@pyreon/core 0.13.1 → 0.15.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.
Files changed (44) hide show
  1. package/README.md +10 -1
  2. package/lib/analysis/index.js.html +1 -1
  3. package/lib/analysis/jsx-dev-runtime.js.html +1 -1
  4. package/lib/analysis/jsx-runtime.js.html +1 -1
  5. package/lib/index.js +168 -34
  6. package/lib/jsx-dev-runtime.js +7 -0
  7. package/lib/jsx-runtime.js +7 -0
  8. package/lib/types/index.d.ts +112 -15
  9. package/lib/types/jsx-dev-runtime.d.ts +6 -3
  10. package/lib/types/jsx-runtime.d.ts +6 -3
  11. package/package.json +3 -2
  12. package/src/compat-marker.ts +79 -0
  13. package/src/component.ts +2 -1
  14. package/src/context.ts +38 -7
  15. package/src/dynamic.ts +16 -5
  16. package/src/error-boundary.ts +15 -2
  17. package/src/index.ts +2 -0
  18. package/src/jsx-runtime.ts +20 -2
  19. package/src/lifecycle.ts +17 -6
  20. package/src/manifest.ts +43 -3
  21. package/src/props.ts +19 -6
  22. package/src/show.ts +19 -6
  23. package/src/suspense.ts +1 -2
  24. package/src/telemetry.ts +30 -2
  25. package/src/tests/compat-marker.test.ts +96 -0
  26. package/src/tests/component.test.ts +5 -5
  27. package/src/tests/context.test.ts +3 -3
  28. package/src/tests/core.test.ts +11 -11
  29. package/src/tests/dynamic.test.ts +33 -1
  30. package/src/tests/error-boundary.test.ts +1 -1
  31. package/src/tests/lifecycle.test.ts +18 -18
  32. package/src/tests/manifest-snapshot.test.ts +6 -1
  33. package/src/tests/native-marker-error-boundary.test.ts +12 -0
  34. package/src/tests/reactive-context.test.ts +3 -3
  35. package/src/tests/reactive-props.test.ts +87 -0
  36. package/src/tests/show.test.ts +76 -0
  37. package/src/tests/telemetry.test.ts +61 -0
  38. package/src/types.ts +9 -5
  39. package/lib/index.js.map +0 -1
  40. package/lib/jsx-dev-runtime.js.map +0 -1
  41. package/lib/jsx-runtime.js.map +0 -1
  42. package/lib/types/index.d.ts.map +0 -1
  43. package/lib/types/jsx-dev-runtime.d.ts.map +0 -1
  44. package/lib/types/jsx-runtime.d.ts.map +0 -1
package/README.md CHANGED
@@ -44,6 +44,13 @@ function Counter() {
44
44
  - **`onUpdate(fn)`** -- Runs after each reactive update.
45
45
  - **`onErrorCaptured(fn)`** -- Captures errors thrown by descendant components.
46
46
 
47
+ Lifecycle hook arrays are lazy-allocated -- `LifecycleHooks.mount`/`.unmount`/`.update`/`.error` start as `null` and are only allocated on first hook registration. Components with no hooks (the majority) pay zero allocation cost.
48
+
49
+ ### Props Reactivity
50
+
51
+ - **`makeReactiveProps(raw)`** -- Converts compiler-emitted `_rp()` wrappers into getter properties. Uses a scan-first strategy: checks for any branded reactive prop before allocating the result object. Static-only components return `raw` immediately with no allocation.
52
+ - **`_rp(fn)`** -- Brands a function as a reactive prop wrapper (compiler-emitted, not user-facing).
53
+
47
54
  ### Context
48
55
 
49
56
  - **`createContext<T>(defaultValue?): Context<T>`** -- Creates a context with an optional default.
@@ -82,7 +89,9 @@ function Counter() {
82
89
 
83
90
  ### Types
84
91
 
85
- `VNode`, `VNodeChild`, `VNodeChildAtom`, `Props`, `ComponentFn`, `ExtractProps`, `HigherOrderComponent`, `ComponentInstance`, `LifecycleHooks`, `CleanupFn`, `NativeItem`, `Ref`, `Context`, `LazyComponent`, `ShowProps`, `SwitchProps`, `MatchProps`, `ForProps`, `PortalProps`, `ErrorContext`, `ErrorHandler`, `ClassValue`, `TargetedEvent`, `PyreonHTMLAttributes`, `CSSProperties`, `StyleValue`, `CanvasAttributes`
92
+ `VNode`, `VNodeChild`, `VNodeChildAtom`, `VNodeChildAccessor`, `Props`, `ComponentFn`, `ExtractProps`, `HigherOrderComponent`, `ComponentInstance`, `LifecycleHooks`, `CleanupFn`, `NativeItem`, `Ref`, `Context`, `LazyComponent`, `ShowProps`, `SwitchProps`, `MatchProps`, `ForProps`, `PortalProps`, `ErrorContext`, `ErrorHandler`, `ClassValue`, `TargetedEvent`, `PyreonHTMLAttributes`, `CSSProperties`, `StyleValue`, `CanvasAttributes`
93
+
94
+ **VNodeChild union ordering**: `VNodeChild = VNodeChildAccessor | VNodeChildAtom | VNodeChildAtom[]` — the accessor type is FIRST so TypeScript matches `{() => cond && <X />}` against the function arm without falling through to `VNodeChildAtom` and erroring on `false | VNode`.
86
95
 
87
96
  ## License
88
97
 
@@ -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":"3de2ab69-1","name":"lifecycle.ts"},{"uid":"3de2ab69-3","name":"component.ts"},{"uid":"3de2ab69-5","name":"context.ts"},{"uid":"3de2ab69-7","name":"h.ts"},{"uid":"3de2ab69-9","name":"dynamic.ts"},{"uid":"3de2ab69-11","name":"telemetry.ts"},{"uid":"3de2ab69-13","name":"error-boundary.ts"},{"uid":"3de2ab69-15","name":"for.ts"},{"uid":"3de2ab69-17","name":"lazy.ts"},{"uid":"3de2ab69-19","name":"map-array.ts"},{"uid":"3de2ab69-21","name":"portal.ts"},{"uid":"3de2ab69-23","name":"props.ts"},{"uid":"3de2ab69-25","name":"ref.ts"},{"uid":"3de2ab69-27","name":"show.ts"},{"uid":"3de2ab69-29","name":"style.ts"},{"uid":"3de2ab69-31","name":"suspense.ts"},{"uid":"3de2ab69-33","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"3de2ab69-1":{"renderedLength":2776,"gzipLength":1260,"brotliLength":0,"metaUid":"3de2ab69-0"},"3de2ab69-3":{"renderedLength":1430,"gzipLength":679,"brotliLength":0,"metaUid":"3de2ab69-2"},"3de2ab69-5":{"renderedLength":2925,"gzipLength":1260,"brotliLength":0,"metaUid":"3de2ab69-4"},"3de2ab69-7":{"renderedLength":1082,"gzipLength":597,"brotliLength":0,"metaUid":"3de2ab69-6"},"3de2ab69-9":{"renderedLength":339,"gzipLength":245,"brotliLength":0,"metaUid":"3de2ab69-8"},"3de2ab69-11":{"renderedLength":570,"gzipLength":346,"brotliLength":0,"metaUid":"3de2ab69-10"},"3de2ab69-13":{"renderedLength":1622,"gzipLength":822,"brotliLength":0,"metaUid":"3de2ab69-12"},"3de2ab69-15":{"renderedLength":700,"gzipLength":478,"brotliLength":0,"metaUid":"3de2ab69-14"},"3de2ab69-17":{"renderedLength":461,"gzipLength":273,"brotliLength":0,"metaUid":"3de2ab69-16"},"3de2ab69-19":{"renderedLength":1018,"gzipLength":571,"brotliLength":0,"metaUid":"3de2ab69-18"},"3de2ab69-21":{"renderedLength":818,"gzipLength":491,"brotliLength":0,"metaUid":"3de2ab69-20"},"3de2ab69-23":{"renderedLength":4151,"gzipLength":1595,"brotliLength":0,"metaUid":"3de2ab69-22"},"3de2ab69-25":{"renderedLength":86,"gzipLength":98,"brotliLength":0,"metaUid":"3de2ab69-24"},"3de2ab69-27":{"renderedLength":1892,"gzipLength":810,"brotliLength":0,"metaUid":"3de2ab69-26"},"3de2ab69-29":{"renderedLength":1858,"gzipLength":825,"brotliLength":0,"metaUid":"3de2ab69-28"},"3de2ab69-31":{"renderedLength":1096,"gzipLength":603,"brotliLength":0,"metaUid":"3de2ab69-30"},"3de2ab69-33":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"3de2ab69-32"}},"nodeMetas":{"3de2ab69-0":{"id":"/src/lifecycle.ts","moduleParts":{"index.js":"3de2ab69-1"},"imported":[],"importedBy":[{"uid":"3de2ab69-32"},{"uid":"3de2ab69-2"},{"uid":"3de2ab69-4"},{"uid":"3de2ab69-12"}]},"3de2ab69-2":{"id":"/src/component.ts","moduleParts":{"index.js":"3de2ab69-3"},"imported":[{"uid":"3de2ab69-0"}],"importedBy":[{"uid":"3de2ab69-32"},{"uid":"3de2ab69-12"}]},"3de2ab69-4":{"id":"/src/context.ts","moduleParts":{"index.js":"3de2ab69-5"},"imported":[{"uid":"3de2ab69-0"}],"importedBy":[{"uid":"3de2ab69-32"}]},"3de2ab69-6":{"id":"/src/h.ts","moduleParts":{"index.js":"3de2ab69-7"},"imported":[],"importedBy":[{"uid":"3de2ab69-32"},{"uid":"3de2ab69-8"},{"uid":"3de2ab69-16"},{"uid":"3de2ab69-30"}]},"3de2ab69-8":{"id":"/src/dynamic.ts","moduleParts":{"index.js":"3de2ab69-9"},"imported":[{"uid":"3de2ab69-6"}],"importedBy":[{"uid":"3de2ab69-32"}]},"3de2ab69-10":{"id":"/src/telemetry.ts","moduleParts":{"index.js":"3de2ab69-11"},"imported":[],"importedBy":[{"uid":"3de2ab69-32"},{"uid":"3de2ab69-12"}]},"3de2ab69-12":{"id":"/src/error-boundary.ts","moduleParts":{"index.js":"3de2ab69-13"},"imported":[{"uid":"3de2ab69-34"},{"uid":"3de2ab69-2"},{"uid":"3de2ab69-0"},{"uid":"3de2ab69-10"}],"importedBy":[{"uid":"3de2ab69-32"}]},"3de2ab69-14":{"id":"/src/for.ts","moduleParts":{"index.js":"3de2ab69-15"},"imported":[],"importedBy":[{"uid":"3de2ab69-32"}]},"3de2ab69-16":{"id":"/src/lazy.ts","moduleParts":{"index.js":"3de2ab69-17"},"imported":[{"uid":"3de2ab69-34"},{"uid":"3de2ab69-6"}],"importedBy":[{"uid":"3de2ab69-32"}]},"3de2ab69-18":{"id":"/src/map-array.ts","moduleParts":{"index.js":"3de2ab69-19"},"imported":[],"importedBy":[{"uid":"3de2ab69-32"}]},"3de2ab69-20":{"id":"/src/portal.ts","moduleParts":{"index.js":"3de2ab69-21"},"imported":[],"importedBy":[{"uid":"3de2ab69-32"}]},"3de2ab69-22":{"id":"/src/props.ts","moduleParts":{"index.js":"3de2ab69-23"},"imported":[],"importedBy":[{"uid":"3de2ab69-32"}]},"3de2ab69-24":{"id":"/src/ref.ts","moduleParts":{"index.js":"3de2ab69-25"},"imported":[],"importedBy":[{"uid":"3de2ab69-32"}]},"3de2ab69-26":{"id":"/src/show.ts","moduleParts":{"index.js":"3de2ab69-27"},"imported":[],"importedBy":[{"uid":"3de2ab69-32"}]},"3de2ab69-28":{"id":"/src/style.ts","moduleParts":{"index.js":"3de2ab69-29"},"imported":[],"importedBy":[{"uid":"3de2ab69-32"}]},"3de2ab69-30":{"id":"/src/suspense.ts","moduleParts":{"index.js":"3de2ab69-31"},"imported":[{"uid":"3de2ab69-6"}],"importedBy":[{"uid":"3de2ab69-32"}]},"3de2ab69-32":{"id":"/src/index.ts","moduleParts":{"index.js":"3de2ab69-33"},"imported":[{"uid":"3de2ab69-2"},{"uid":"3de2ab69-4"},{"uid":"3de2ab69-8"},{"uid":"3de2ab69-12"},{"uid":"3de2ab69-14"},{"uid":"3de2ab69-6"},{"uid":"3de2ab69-16"},{"uid":"3de2ab69-0"},{"uid":"3de2ab69-18"},{"uid":"3de2ab69-20"},{"uid":"3de2ab69-22"},{"uid":"3de2ab69-24"},{"uid":"3de2ab69-26"},{"uid":"3de2ab69-28"},{"uid":"3de2ab69-30"},{"uid":"3de2ab69-10"}],"importedBy":[],"isEntry":true},"3de2ab69-34":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"3de2ab69-12"},{"uid":"3de2ab69-16"}]}},"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":"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}};
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":"457db80a-1","name":"h.ts"},{"uid":"457db80a-3","name":"jsx-runtime.ts"},{"uid":"457db80a-5","name":"jsx-dev-runtime.ts"}]}]}],"isRoot":true},"nodeParts":{"457db80a-1":{"renderedLength":1082,"gzipLength":597,"brotliLength":0,"metaUid":"457db80a-0"},"457db80a-3":{"renderedLength":657,"gzipLength":403,"brotliLength":0,"metaUid":"457db80a-2"},"457db80a-5":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"457db80a-4"}},"nodeMetas":{"457db80a-0":{"id":"/src/h.ts","moduleParts":{"jsx-dev-runtime.js":"457db80a-1"},"imported":[],"importedBy":[{"uid":"457db80a-2"}]},"457db80a-2":{"id":"/src/jsx-runtime.ts","moduleParts":{"jsx-dev-runtime.js":"457db80a-3"},"imported":[{"uid":"457db80a-0"}],"importedBy":[{"uid":"457db80a-4"}]},"457db80a-4":{"id":"/src/jsx-dev-runtime.ts","moduleParts":{"jsx-dev-runtime.js":"457db80a-5"},"imported":[{"uid":"457db80a-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":"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}};
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":"a8d14f92-1","name":"h.ts"},{"uid":"a8d14f92-3","name":"jsx-runtime.ts"}]}]}],"isRoot":true},"nodeParts":{"a8d14f92-1":{"renderedLength":1082,"gzipLength":597,"brotliLength":0,"metaUid":"a8d14f92-0"},"a8d14f92-3":{"renderedLength":657,"gzipLength":403,"brotliLength":0,"metaUid":"a8d14f92-2"}},"nodeMetas":{"a8d14f92-0":{"id":"/src/h.ts","moduleParts":{"jsx-runtime.js":"a8d14f92-1"},"imported":[],"importedBy":[{"uid":"a8d14f92-2"}]},"a8d14f92-2":{"id":"/src/jsx-runtime.ts","moduleParts":{"jsx-runtime.js":"a8d14f92-3"},"imported":[{"uid":"a8d14f92-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":"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}};
5390
5390
 
5391
5391
  const run = () => {
5392
5392
  const width = window.innerWidth;
package/lib/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { signal } from "@pyreon/reactivity";
1
+ import { setSnapshotCapture, signal } from "@pyreon/reactivity";
2
2
 
3
3
  //#region src/lifecycle.ts
4
- const __DEV__$3 = import.meta.env?.DEV === true;
4
+ const __DEV__$3 = process.env.NODE_ENV !== "production";
5
5
  let _current = null;
6
6
  function setCurrentHooks(hooks) {
7
7
  _current = hooks;
@@ -52,21 +52,30 @@ function warnOutsideSetup(hookName) {
52
52
  */
53
53
  function onMount(fn) {
54
54
  warnOutsideSetup("onMount");
55
- _current?.mount.push(fn);
55
+ if (_current) {
56
+ if (_current.mount === null) _current.mount = [];
57
+ _current.mount.push(fn);
58
+ }
56
59
  }
57
60
  /**
58
61
  * Register a callback to run when the component is removed from the DOM.
59
62
  */
60
63
  function onUnmount(fn) {
61
64
  warnOutsideSetup("onUnmount");
62
- _current?.unmount.push(fn);
65
+ if (_current) {
66
+ if (_current.unmount === null) _current.unmount = [];
67
+ _current.unmount.push(fn);
68
+ }
63
69
  }
64
70
  /**
65
71
  * Register a callback to run after each reactive update.
66
72
  */
67
73
  function onUpdate(fn) {
68
74
  warnOutsideSetup("onUpdate");
69
- _current?.update.push(fn);
75
+ if (_current) {
76
+ if (_current.update === null) _current.update = [];
77
+ _current.update.push(fn);
78
+ }
70
79
  }
71
80
  /**
72
81
  * Register an error handler for this component subtree.
@@ -83,7 +92,10 @@ function onUpdate(fn) {
83
92
  */
84
93
  function onErrorCaptured(fn) {
85
94
  warnOutsideSetup("onErrorCaptured");
86
- _current?.error.push(fn);
95
+ if (_current) {
96
+ if (_current.error === null) _current.error = [];
97
+ _current.error.push(fn);
98
+ }
87
99
  }
88
100
 
89
101
  //#endregion
@@ -103,10 +115,10 @@ function defineComponent(fn) {
103
115
  */
104
116
  function runWithHooks(fn, props) {
105
117
  const hooks = {
106
- mount: [],
107
- unmount: [],
108
- update: [],
109
- error: []
118
+ mount: null,
119
+ unmount: null,
120
+ update: null,
121
+ error: null
110
122
  };
111
123
  setCurrentHooks(hooks);
112
124
  let vnode = null;
@@ -125,6 +137,7 @@ function runWithHooks(fn, props) {
125
137
  * Returns true if any handler marked the error as handled.
126
138
  */
127
139
  function propagateError(err, hooks) {
140
+ if (!hooks.error) return false;
128
141
  for (const handler of hooks.error) if (handler(err) === true) return true;
129
142
  return false;
130
143
  }
@@ -144,6 +157,80 @@ function dispatchToErrorBoundary(err) {
144
157
  return handler ? handler(err) : false;
145
158
  }
146
159
 
160
+ //#endregion
161
+ //#region src/compat-marker.ts
162
+ /**
163
+ * Compat-mode native-component marker.
164
+ *
165
+ * Pyreon ships compat layers (`@pyreon/{react,preact,vue,solid}-compat`) that
166
+ * wrap every JSX-called component function to emulate that source framework's
167
+ * render-on-state-change semantics. That wrapping is correct for user code
168
+ * (the whole point of compat mode) but corrupts Pyreon framework components
169
+ * — those manage their own reactivity via `provide()` / signals / lifecycle
170
+ * hooks, and wrapping them runs their setup body inside the compat layer's
171
+ * render context instead of Pyreon's, breaking `provide()` and
172
+ * `onMount()` / `onUnmount()` calls.
173
+ *
174
+ * Framework components opt out of compat wrapping by setting a well-known
175
+ * registry symbol (`Symbol.for('pyreon:native-compat')`) on the function.
176
+ * The compat layer reads that symbol and routes marked components straight
177
+ * through Pyreon's `h()` mount path. The symbol is registry-shared, so no
178
+ * import direction between framework and compat is implied — both sides
179
+ * reference the same global symbol via the helpers exported here.
180
+ *
181
+ * Audience: framework-package authors writing JSX components in `@pyreon/*`
182
+ * packages whose setup body uses `provide()` / lifecycle hooks / signal
183
+ * subscriptions. Wrap exported components with `nativeCompat()`. One line
184
+ * per export site; zero runtime cost beyond a single property write at
185
+ * module load.
186
+ */
187
+ /**
188
+ * The well-known registry symbol that marks a component as a Pyreon native
189
+ * framework component. Compat layers check this symbol to decide whether to
190
+ * skip their `wrapCompatComponent` call.
191
+ *
192
+ * Exported for advanced cases where a caller needs to test the marker
193
+ * directly (most callers should use `isNativeCompat()`).
194
+ */
195
+ const NATIVE_COMPAT_MARKER = Symbol.for("pyreon:native-compat");
196
+ /**
197
+ * Mark a Pyreon framework component as "self-managing" — compat layers will
198
+ * skip their React/Vue/Solid/Preact-style wrapping and route the component
199
+ * directly through Pyreon's mount path. Use on every `@pyreon/*` JSX
200
+ * component whose setup body uses `provide()`, lifecycle hooks
201
+ * (`onMount` / `onUnmount`), signal-driven reactivity, or any other Pyreon
202
+ * native pattern that depends on the active component-setup frame.
203
+ *
204
+ * Idempotent: re-applying the marker is a no-op. Non-function inputs pass
205
+ * through unchanged so callers don't have to typecheck before wrapping.
206
+ *
207
+ * @example
208
+ * import { nativeCompat, provide } from '@pyreon/core'
209
+ *
210
+ * export const RouterView = nativeCompat(function RouterView(props) {
211
+ * provide(RouterContext, ...)
212
+ * return <div data-pyreon-router-view>{children}</div>
213
+ * })
214
+ */
215
+ function nativeCompat(fn) {
216
+ if (typeof fn === "function") fn[NATIVE_COMPAT_MARKER] = true;
217
+ return fn;
218
+ }
219
+ /**
220
+ * Read whether a component has been marked as a Pyreon native framework
221
+ * component. Compat-layer code calls this from its `jsx()` to decide whether
222
+ * to wrap or pass through.
223
+ *
224
+ * @example
225
+ * import { isNativeCompat } from '@pyreon/core'
226
+ *
227
+ * if (isNativeCompat(type)) return h(type, props)
228
+ * return wrapCompatComponent(type)(props)
229
+ */
230
+ function isNativeCompat(fn) {
231
+ return typeof fn === "function" && fn[NATIVE_COMPAT_MARKER] === true;
232
+ }
233
+
147
234
  //#endregion
148
235
  //#region src/context.ts
149
236
  /**
@@ -178,7 +265,7 @@ function setContextStackProvider(fn) {
178
265
  function getStack() {
179
266
  return _stackProvider();
180
267
  }
181
- import.meta.env?.DEV;
268
+ process.env.NODE_ENV;
182
269
  function pushContext(values) {
183
270
  getStack().push(values);
184
271
  }
@@ -235,18 +322,31 @@ function captureContextStack() {
235
322
  }
236
323
  /**
237
324
  * Execute `fn()` with a previously captured context stack active.
238
- * Restores the original stack after `fn()` completes (even on throw).
325
+ *
326
+ * After `fn()` returns, removes ONLY the snapshot frames this call pushed
327
+ * — anything `fn()` itself pushed (typically provider frames from
328
+ * `provide()` calls during component mount) stays on the stack so
329
+ * subsequent reactive re-runs (e.g. `_bind` text bindings,
330
+ * `renderEffect` callbacks) can still find ancestor providers via
331
+ * `useContext`. Pre-fix this method was `stack.length = savedLength`,
332
+ * which destructively truncated provider frames pushed during mount —
333
+ * silently breaking `useMode()` / `useTheme()` / `useRouter()` etc. on
334
+ * every signal-driven update under a `mountReactive` boundary.
239
335
  */
240
336
  function restoreContextStack(snapshot, fn) {
241
337
  const stack = getStack();
242
- const savedLength = stack.length;
338
+ const insertIndex = stack.length;
243
339
  for (const frame of snapshot) stack.push(frame);
244
340
  try {
245
341
  return fn();
246
342
  } finally {
247
- stack.length = savedLength;
343
+ stack.splice(insertIndex, snapshot.length);
248
344
  }
249
345
  }
346
+ setSnapshotCapture({
347
+ capture: () => captureContextStack(),
348
+ restore: (snap, fn) => restoreContextStack(snap, fn)
349
+ });
250
350
 
251
351
  //#endregion
252
352
  //#region src/h.ts
@@ -282,12 +382,14 @@ function flattenChildren(children) {
282
382
 
283
383
  //#endregion
284
384
  //#region src/dynamic.ts
285
- const __DEV__$2 = import.meta.env?.DEV === true;
385
+ const __DEV__$2 = process.env.NODE_ENV !== "production";
286
386
  function Dynamic(props) {
287
- const { component, ...rest } = props;
387
+ const { component, children, ...rest } = props;
288
388
  if (__DEV__$2 && !component) console.warn("[Pyreon] <Dynamic> received a falsy `component` prop. Nothing will be rendered.");
289
389
  if (!component) return null;
290
- return h(component, rest);
390
+ if (children === void 0) return h(component, rest);
391
+ if (Array.isArray(children)) return h(component, rest, ...children);
392
+ return h(component, rest, children);
291
393
  }
292
394
 
293
395
  //#endregion
@@ -295,10 +397,17 @@ function Dynamic(props) {
295
397
  let _handlers = [];
296
398
  /**
297
399
  * Register a global error handler. Called whenever a component throws in any
298
- * lifecycle phase. Returns an unregister function.
400
+ * lifecycle phase, OR an effect throws in `@pyreon/reactivity`. Returns an
401
+ * unregister function.
402
+ *
403
+ * Also installs a `globalThis.__pyreon_report_error__` bridge so the
404
+ * reactivity package (which can't depend on core) can forward effect errors
405
+ * into the same telemetry pipeline. Pre-fix the two surfaces were
406
+ * disconnected — Sentry/Datadog wiring missed effect-thrown errors.
299
407
  */
300
408
  function registerErrorHandler(handler) {
301
409
  _handlers.push(handler);
410
+ _installReactivityBridge();
302
411
  return () => {
303
412
  _handlers = _handlers.filter((h) => h !== handler);
304
413
  };
@@ -312,10 +421,22 @@ function reportError(ctx) {
312
421
  h(ctx);
313
422
  } catch {}
314
423
  }
424
+ const _bridgeHost = globalThis;
425
+ function _installReactivityBridge() {
426
+ if (_bridgeHost.__pyreon_report_error__) return;
427
+ _bridgeHost.__pyreon_report_error__ = (err, phase) => {
428
+ reportError({
429
+ component: "Effect",
430
+ phase,
431
+ error: err,
432
+ timestamp: Date.now()
433
+ });
434
+ };
435
+ }
315
436
 
316
437
  //#endregion
317
438
  //#region src/error-boundary.ts
318
- const __DEV__$1 = import.meta.env?.DEV === true;
439
+ const __DEV__$1 = process.env.NODE_ENV !== "production";
319
440
  /**
320
441
  * ErrorBoundary — catches errors thrown by child components and renders a
321
442
  * fallback UI instead of crashing the whole tree.
@@ -363,6 +484,7 @@ function ErrorBoundary(props) {
363
484
  return typeof ch === "function" ? ch() : ch;
364
485
  };
365
486
  }
487
+ nativeCompat(ErrorBoundary);
366
488
 
367
489
  //#endregion
368
490
  //#region src/for.ts
@@ -577,20 +699,28 @@ function _rp(fn) {
577
699
  * Returns the same object if no reactive props found (fast path).
578
700
  */
579
701
  function makeReactiveProps(raw) {
702
+ const keys = Object.keys(raw);
703
+ let hasAny = false;
704
+ for (let i = 0; i < keys.length; i++) {
705
+ const val = raw[keys[i]];
706
+ if (typeof val === "function" && val[REACTIVE_PROP]) {
707
+ hasAny = true;
708
+ break;
709
+ }
710
+ }
711
+ if (!hasAny) return raw;
580
712
  const result = {};
581
- let hasGetters = false;
582
- for (const key of Object.keys(raw)) {
713
+ for (let i = 0; i < keys.length; i++) {
714
+ const key = keys[i];
583
715
  const val = raw[key];
584
- if (typeof val === "function" && val[REACTIVE_PROP]) {
585
- Object.defineProperty(result, key, {
586
- get: val,
587
- enumerable: true,
588
- configurable: true
589
- });
590
- hasGetters = true;
591
- } else result[key] = val;
716
+ if (typeof val === "function" && val[REACTIVE_PROP]) Object.defineProperty(result, key, {
717
+ get: val,
718
+ enumerable: true,
719
+ configurable: true
720
+ });
721
+ else result[key] = val;
592
722
  }
593
- return hasGetters ? result : raw;
723
+ return result;
594
724
  }
595
725
  let _idCounter = 0;
596
726
  /**
@@ -616,6 +746,9 @@ function createRef() {
616
746
 
617
747
  //#endregion
618
748
  //#region src/show.ts
749
+ function callWhen(when) {
750
+ return typeof when === "function" ? when() : when;
751
+ }
619
752
  /**
620
753
  * Conditionally render children based on a reactive condition.
621
754
  *
@@ -630,7 +763,7 @@ function createRef() {
630
763
  * )
631
764
  */
632
765
  function Show(props) {
633
- return (() => props.when() ? props.children ?? null : props.fallback ?? null);
766
+ return (() => callWhen(props.when) ? props.children ?? null : props.fallback ?? null);
634
767
  }
635
768
  /**
636
769
  * A branch inside `<Switch>`. Renders when `when()` is truthy.
@@ -670,7 +803,8 @@ function Switch(props) {
670
803
  const branches = normalizeBranches(props.children);
671
804
  for (const branch of branches) {
672
805
  if (!isMatchVNode(branch)) continue;
673
- if (branch.props.when()) return resolveMatchChildren(branch);
806
+ const matchProps = branch.props;
807
+ if (callWhen(matchProps.when)) return resolveMatchChildren(branch);
674
808
  }
675
809
  return props.fallback ?? null;
676
810
  });
@@ -760,7 +894,7 @@ function normalizeStyleValue(key, value) {
760
894
 
761
895
  //#endregion
762
896
  //#region src/suspense.ts
763
- const __DEV__ = import.meta.env?.DEV === true;
897
+ const __DEV__ = process.env.NODE_ENV !== "production";
764
898
  /**
765
899
  * Suspense — shows `fallback` while a lazy child component is still loading.
766
900
  *
@@ -788,5 +922,5 @@ function Suspense(props) {
788
922
  }
789
923
 
790
924
  //#endregion
791
- export { CSS_UNITLESS, Dynamic, EMPTY_PROPS, ErrorBoundary, For, ForSymbol, Fragment, Match, MatchSymbol, Portal, PortalSymbol, REACTIVE_PROP, Show, Suspense, Switch, _rp, captureContextStack, createContext, createReactiveContext, createRef, createUniqueId, cx, defineComponent, dispatchToErrorBoundary, h, lazy, makeReactiveProps, mapArray, mergeProps, normalizeStyleValue, onErrorCaptured, onMount, onUnmount, onUpdate, popContext, propagateError, provide, pushContext, registerErrorHandler, reportError, restoreContextStack, runWithHooks, setContextStackProvider, splitProps, toKebabCase, useContext, withContext };
925
+ export { CSS_UNITLESS, Dynamic, EMPTY_PROPS, ErrorBoundary, For, ForSymbol, Fragment, Match, MatchSymbol, NATIVE_COMPAT_MARKER, Portal, PortalSymbol, REACTIVE_PROP, Show, Suspense, Switch, _rp, captureContextStack, createContext, createReactiveContext, createRef, createUniqueId, cx, defineComponent, dispatchToErrorBoundary, h, isNativeCompat, lazy, makeReactiveProps, mapArray, mergeProps, nativeCompat, normalizeStyleValue, onErrorCaptured, onMount, onUnmount, onUpdate, popContext, propagateError, provide, pushContext, registerErrorHandler, reportError, restoreContextStack, runWithHooks, setContextStackProvider, splitProps, toKebabCase, useContext, withContext };
792
926
  //# sourceMappingURL=index.js.map
@@ -37,6 +37,13 @@ function flattenChildren(children) {
37
37
  * When tsconfig has `"jsxImportSource": "@pyreon/core"`, the TS/bundler compiler
38
38
  * rewrites JSX to imports from this file automatically:
39
39
  * <div class="x" /> → jsx("div", { class: "x" })
40
+ *
41
+ * The triple-slash reference above makes this file self-declare its DOM-lib
42
+ * dependency. Without it, any consumer whose tsconfig has `lib: ["ESNext"]`
43
+ * (no DOM) — e.g. backend-only packages like @pyreon/cli — fails to typecheck
44
+ * once `@pyreon/core` becomes resolvable from their dependency graph (e.g. via
45
+ * a transitive devDep), because tsc auto-resolves jsxImportSource and pulls
46
+ * jsx-runtime.ts into the consumer's compilation unit.
40
47
  */
41
48
  function jsx(type, props, key) {
42
49
  const { children, ...rest } = props;
@@ -37,6 +37,13 @@ function flattenChildren(children) {
37
37
  * When tsconfig has `"jsxImportSource": "@pyreon/core"`, the TS/bundler compiler
38
38
  * rewrites JSX to imports from this file automatically:
39
39
  * <div class="x" /> → jsx("div", { class: "x" })
40
+ *
41
+ * The triple-slash reference above makes this file self-declare its DOM-lib
42
+ * dependency. Without it, any consumer whose tsconfig has `lib: ["ESNext"]`
43
+ * (no DOM) — e.g. backend-only packages like @pyreon/cli — fails to typecheck
44
+ * once `@pyreon/core` becomes resolvable from their dependency graph (e.g. via
45
+ * a transitive devDep), because tsc auto-resolves jsxImportSource and pulls
46
+ * jsx-runtime.ts into the consumer's compilation unit.
40
47
  */
41
48
  function jsx(type, props, key) {
42
49
  const { children, ...rest } = props;