@pyreon/core 0.24.4 → 0.24.6
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.
- package/lib/analysis/index.js.html +1 -1
- package/lib/index.js +53 -31
- package/package.json +2 -6
- package/src/compat-marker.ts +0 -79
- package/src/compat-shared.ts +0 -80
- package/src/component.ts +0 -98
- package/src/context.ts +0 -349
- package/src/defer.ts +0 -279
- package/src/dynamic.ts +0 -32
- package/src/env.d.ts +0 -6
- package/src/error-boundary.ts +0 -90
- package/src/for.ts +0 -51
- package/src/h.ts +0 -80
- package/src/index.ts +0 -80
- package/src/jsx-dev-runtime.ts +0 -2
- package/src/jsx-runtime.ts +0 -747
- package/src/lazy.ts +0 -25
- package/src/lifecycle.ts +0 -152
- package/src/manifest.ts +0 -579
- package/src/map-array.ts +0 -42
- package/src/portal.ts +0 -39
- package/src/props.ts +0 -269
- package/src/ref.ts +0 -32
- package/src/show.ts +0 -121
- package/src/style.ts +0 -102
- package/src/suspense.ts +0 -52
- package/src/telemetry.ts +0 -120
- package/src/tests/compat-marker.test.ts +0 -96
- package/src/tests/compat-shared.test.ts +0 -99
- package/src/tests/component.test.ts +0 -281
- package/src/tests/context.test.ts +0 -629
- package/src/tests/core.test.ts +0 -1290
- package/src/tests/cx.test.ts +0 -70
- package/src/tests/defer.test.ts +0 -359
- package/src/tests/dynamic.test.ts +0 -87
- package/src/tests/error-boundary.test.ts +0 -181
- package/src/tests/extract-props-overloads.types.test.ts +0 -135
- package/src/tests/for.test.ts +0 -117
- package/src/tests/h.test.ts +0 -221
- package/src/tests/jsx-compat.test.tsx +0 -86
- package/src/tests/lazy.test.ts +0 -100
- package/src/tests/lifecycle.test.ts +0 -350
- package/src/tests/manifest-snapshot.test.ts +0 -100
- package/src/tests/map-array.test.ts +0 -313
- package/src/tests/native-marker-error-boundary.test.ts +0 -12
- package/src/tests/portal.test.ts +0 -48
- package/src/tests/props-extended.test.ts +0 -157
- package/src/tests/props.test.ts +0 -250
- package/src/tests/reactive-context.test.ts +0 -69
- package/src/tests/reactive-props.test.ts +0 -157
- package/src/tests/ref.test.ts +0 -70
- package/src/tests/show.test.ts +0 -314
- package/src/tests/style.test.ts +0 -157
- package/src/tests/suspense.test.ts +0 -139
- package/src/tests/telemetry.test.ts +0 -297
- package/src/types.ts +0 -116
|
@@ -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":"
|
|
5389
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"4dae059e-1","name":"lifecycle.ts"},{"uid":"4dae059e-3","name":"component.ts"},{"uid":"4dae059e-5","name":"compat-marker.ts"},{"uid":"4dae059e-7","name":"compat-shared.ts"},{"uid":"4dae059e-9","name":"context.ts"},{"uid":"4dae059e-11","name":"dynamic.ts"},{"uid":"4dae059e-13","name":"telemetry.ts"},{"uid":"4dae059e-15","name":"error-boundary.ts"},{"uid":"4dae059e-17","name":"for.ts"},{"uid":"4dae059e-19","name":"ref.ts"},{"uid":"4dae059e-21","name":"defer.ts"},{"uid":"4dae059e-23","name":"lazy.ts"},{"uid":"4dae059e-25","name":"map-array.ts"},{"uid":"4dae059e-27","name":"portal.ts"},{"uid":"4dae059e-29","name":"props.ts"},{"uid":"4dae059e-31","name":"show.ts"},{"uid":"4dae059e-33","name":"style.ts"},{"uid":"4dae059e-35","name":"suspense.ts"},{"uid":"4dae059e-37","name":"index.ts"}]}]},{"name":"jsx-dev-runtime.js","children":[{"name":"src/jsx-dev-runtime.ts","uid":"4dae059e-39"}]},{"name":"jsx-runtime.js","children":[{"name":"src/jsx-runtime.ts","uid":"4dae059e-41"}]},{"name":"_chunks/h-CYSD6aBx.js","children":[{"name":"src/h.ts","uid":"4dae059e-43"}]}],"isRoot":true},"nodeParts":{"4dae059e-1":{"renderedLength":3580,"gzipLength":1505,"brotliLength":0,"metaUid":"4dae059e-0"},"4dae059e-3":{"renderedLength":2046,"gzipLength":964,"brotliLength":0,"metaUid":"4dae059e-2"},"4dae059e-5":{"renderedLength":3173,"gzipLength":1409,"brotliLength":0,"metaUid":"4dae059e-4"},"4dae059e-7":{"renderedLength":2346,"gzipLength":1033,"brotliLength":0,"metaUid":"4dae059e-6"},"4dae059e-9":{"renderedLength":9272,"gzipLength":3697,"brotliLength":0,"metaUid":"4dae059e-8"},"4dae059e-11":{"renderedLength":490,"gzipLength":292,"brotliLength":0,"metaUid":"4dae059e-10"},"4dae059e-13":{"renderedLength":2219,"gzipLength":1028,"brotliLength":0,"metaUid":"4dae059e-12"},"4dae059e-15":{"renderedLength":1666,"gzipLength":843,"brotliLength":0,"metaUid":"4dae059e-14"},"4dae059e-17":{"renderedLength":700,"gzipLength":478,"brotliLength":0,"metaUid":"4dae059e-16"},"4dae059e-19":{"renderedLength":86,"gzipLength":98,"brotliLength":0,"metaUid":"4dae059e-18"},"4dae059e-21":{"renderedLength":4387,"gzipLength":1891,"brotliLength":0,"metaUid":"4dae059e-20"},"4dae059e-23":{"renderedLength":461,"gzipLength":273,"brotliLength":0,"metaUid":"4dae059e-22"},"4dae059e-25":{"renderedLength":1018,"gzipLength":571,"brotliLength":0,"metaUid":"4dae059e-24"},"4dae059e-27":{"renderedLength":818,"gzipLength":491,"brotliLength":0,"metaUid":"4dae059e-26"},"4dae059e-29":{"renderedLength":6493,"gzipLength":2414,"brotliLength":0,"metaUid":"4dae059e-28"},"4dae059e-31":{"renderedLength":2022,"gzipLength":854,"brotliLength":0,"metaUid":"4dae059e-30"},"4dae059e-33":{"renderedLength":1858,"gzipLength":825,"brotliLength":0,"metaUid":"4dae059e-32"},"4dae059e-35":{"renderedLength":1104,"gzipLength":614,"brotliLength":0,"metaUid":"4dae059e-34"},"4dae059e-37":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"4dae059e-36"},"4dae059e-39":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"4dae059e-38"},"4dae059e-41":{"renderedLength":1789,"gzipLength":834,"brotliLength":0,"metaUid":"4dae059e-40"},"4dae059e-43":{"renderedLength":1813,"gzipLength":957,"brotliLength":0,"metaUid":"4dae059e-42"}},"nodeMetas":{"4dae059e-0":{"id":"/src/lifecycle.ts","moduleParts":{"index.js":"4dae059e-1"},"imported":[],"importedBy":[{"uid":"4dae059e-36"},{"uid":"4dae059e-2"},{"uid":"4dae059e-8"},{"uid":"4dae059e-14"},{"uid":"4dae059e-20"}]},"4dae059e-2":{"id":"/src/component.ts","moduleParts":{"index.js":"4dae059e-3"},"imported":[{"uid":"4dae059e-0"}],"importedBy":[{"uid":"4dae059e-36"},{"uid":"4dae059e-14"}]},"4dae059e-4":{"id":"/src/compat-marker.ts","moduleParts":{"index.js":"4dae059e-5"},"imported":[],"importedBy":[{"uid":"4dae059e-36"},{"uid":"4dae059e-14"}]},"4dae059e-6":{"id":"/src/compat-shared.ts","moduleParts":{"index.js":"4dae059e-7"},"imported":[],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-8":{"id":"/src/context.ts","moduleParts":{"index.js":"4dae059e-9"},"imported":[{"uid":"4dae059e-44"},{"uid":"4dae059e-0"}],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-10":{"id":"/src/dynamic.ts","moduleParts":{"index.js":"4dae059e-11"},"imported":[{"uid":"4dae059e-42"}],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-12":{"id":"/src/telemetry.ts","moduleParts":{"index.js":"4dae059e-13"},"imported":[{"uid":"4dae059e-44"}],"importedBy":[{"uid":"4dae059e-36"},{"uid":"4dae059e-14"}]},"4dae059e-14":{"id":"/src/error-boundary.ts","moduleParts":{"index.js":"4dae059e-15"},"imported":[{"uid":"4dae059e-44"},{"uid":"4dae059e-4"},{"uid":"4dae059e-2"},{"uid":"4dae059e-0"},{"uid":"4dae059e-12"}],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-16":{"id":"/src/for.ts","moduleParts":{"index.js":"4dae059e-17"},"imported":[],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-18":{"id":"/src/ref.ts","moduleParts":{"index.js":"4dae059e-19"},"imported":[],"importedBy":[{"uid":"4dae059e-36"},{"uid":"4dae059e-20"}]},"4dae059e-20":{"id":"/src/defer.ts","moduleParts":{"index.js":"4dae059e-21"},"imported":[{"uid":"4dae059e-44"},{"uid":"4dae059e-42"},{"uid":"4dae059e-0"},{"uid":"4dae059e-18"}],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-22":{"id":"/src/lazy.ts","moduleParts":{"index.js":"4dae059e-23"},"imported":[{"uid":"4dae059e-44"},{"uid":"4dae059e-42"}],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-24":{"id":"/src/map-array.ts","moduleParts":{"index.js":"4dae059e-25"},"imported":[],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-26":{"id":"/src/portal.ts","moduleParts":{"index.js":"4dae059e-27"},"imported":[],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-28":{"id":"/src/props.ts","moduleParts":{"index.js":"4dae059e-29"},"imported":[],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-30":{"id":"/src/show.ts","moduleParts":{"index.js":"4dae059e-31"},"imported":[],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-32":{"id":"/src/style.ts","moduleParts":{"index.js":"4dae059e-33"},"imported":[],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-34":{"id":"/src/suspense.ts","moduleParts":{"index.js":"4dae059e-35"},"imported":[{"uid":"4dae059e-42"}],"importedBy":[{"uid":"4dae059e-36"}]},"4dae059e-36":{"id":"/src/index.ts","moduleParts":{"index.js":"4dae059e-37"},"imported":[{"uid":"4dae059e-2"},{"uid":"4dae059e-4"},{"uid":"4dae059e-6"},{"uid":"4dae059e-8"},{"uid":"4dae059e-10"},{"uid":"4dae059e-14"},{"uid":"4dae059e-16"},{"uid":"4dae059e-42"},{"uid":"4dae059e-20"},{"uid":"4dae059e-22"},{"uid":"4dae059e-0"},{"uid":"4dae059e-24"},{"uid":"4dae059e-26"},{"uid":"4dae059e-28"},{"uid":"4dae059e-18"},{"uid":"4dae059e-30"},{"uid":"4dae059e-32"},{"uid":"4dae059e-34"},{"uid":"4dae059e-12"}],"importedBy":[],"isEntry":true},"4dae059e-38":{"id":"/src/jsx-dev-runtime.ts","moduleParts":{"jsx-dev-runtime.js":"4dae059e-39"},"imported":[{"uid":"4dae059e-40"}],"importedBy":[],"isEntry":true},"4dae059e-40":{"id":"/src/jsx-runtime.ts","moduleParts":{"jsx-runtime.js":"4dae059e-41"},"imported":[{"uid":"4dae059e-42"}],"importedBy":[{"uid":"4dae059e-38"}],"isEntry":true},"4dae059e-42":{"id":"/src/h.ts","moduleParts":{"_chunks/h-CYSD6aBx.js":"4dae059e-43"},"imported":[],"importedBy":[{"uid":"4dae059e-36"},{"uid":"4dae059e-10"},{"uid":"4dae059e-20"},{"uid":"4dae059e-22"},{"uid":"4dae059e-34"},{"uid":"4dae059e-40"}]},"4dae059e-44":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"4dae059e-8"},{"uid":"4dae059e-14"},{"uid":"4dae059e-20"},{"uid":"4dae059e-22"},{"uid":"4dae059e-12"}]}},"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
|
@@ -3,9 +3,12 @@ import { effect, getReactiveTrace, setSnapshotCapture, signal } from "@pyreon/re
|
|
|
3
3
|
|
|
4
4
|
//#region src/lifecycle.ts
|
|
5
5
|
const __DEV__$5 = process.env.NODE_ENV !== "production";
|
|
6
|
-
|
|
6
|
+
const _LIFECYCLE_KEY = Symbol.for("pyreon-core/lifecycle-state");
|
|
7
|
+
const _gHost = globalThis;
|
|
8
|
+
const _state = _gHost[_LIFECYCLE_KEY] ?? { current: null };
|
|
9
|
+
if (!_gHost[_LIFECYCLE_KEY]) _gHost[_LIFECYCLE_KEY] = _state;
|
|
7
10
|
function setCurrentHooks(hooks) {
|
|
8
|
-
|
|
11
|
+
_state.current = hooks;
|
|
9
12
|
}
|
|
10
13
|
/**
|
|
11
14
|
* Extract the first stack frame that's NOT inside the framework itself.
|
|
@@ -43,7 +46,7 @@ function captureCallSite() {
|
|
|
43
46
|
return "";
|
|
44
47
|
}
|
|
45
48
|
function warnOutsideSetup(hookName) {
|
|
46
|
-
if (__DEV__$5 && !
|
|
49
|
+
if (__DEV__$5 && !_state.current) {
|
|
47
50
|
const callSite = captureCallSite();
|
|
48
51
|
const callSiteSuffix = callSite ? `\n Called from: ${callSite}` : "";
|
|
49
52
|
console.warn(`[Pyreon] ${hookName}() called outside component setup. Lifecycle hooks must be called synchronously during a component's setup function.` + callSiteSuffix + (hookName === "onUnmount" ? "\n Hint: `provide()` internally calls onUnmount(). If you use provide(), ensure it runs during synchronous component setup — not inside effects, callbacks, or after awaits." : ""));
|
|
@@ -55,9 +58,9 @@ function warnOutsideSetup(hookName) {
|
|
|
55
58
|
*/
|
|
56
59
|
function onMount(fn) {
|
|
57
60
|
warnOutsideSetup("onMount");
|
|
58
|
-
if (
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
+
if (_state.current) {
|
|
62
|
+
if (_state.current.mount === null) _state.current.mount = [];
|
|
63
|
+
_state.current.mount.push(fn);
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
/**
|
|
@@ -65,9 +68,9 @@ function onMount(fn) {
|
|
|
65
68
|
*/
|
|
66
69
|
function onUnmount(fn) {
|
|
67
70
|
warnOutsideSetup("onUnmount");
|
|
68
|
-
if (
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
+
if (_state.current) {
|
|
72
|
+
if (_state.current.unmount === null) _state.current.unmount = [];
|
|
73
|
+
_state.current.unmount.push(fn);
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
/**
|
|
@@ -75,9 +78,9 @@ function onUnmount(fn) {
|
|
|
75
78
|
*/
|
|
76
79
|
function onUpdate(fn) {
|
|
77
80
|
warnOutsideSetup("onUpdate");
|
|
78
|
-
if (
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
+
if (_state.current) {
|
|
82
|
+
if (_state.current.update === null) _state.current.update = [];
|
|
83
|
+
_state.current.update.push(fn);
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
/**
|
|
@@ -95,9 +98,9 @@ function onUpdate(fn) {
|
|
|
95
98
|
*/
|
|
96
99
|
function onErrorCaptured(fn) {
|
|
97
100
|
warnOutsideSetup("onErrorCaptured");
|
|
98
|
-
if (
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
+
if (_state.current) {
|
|
102
|
+
if (_state.current.error === null) _state.current.error = [];
|
|
103
|
+
_state.current.error.push(fn);
|
|
101
104
|
}
|
|
102
105
|
}
|
|
103
106
|
|
|
@@ -144,9 +147,12 @@ function propagateError(err, hooks) {
|
|
|
144
147
|
for (const handler of hooks.error) if (handler(err) === true) return true;
|
|
145
148
|
return false;
|
|
146
149
|
}
|
|
147
|
-
const
|
|
150
|
+
const _EB_KEY = Symbol.for("pyreon-core/error-boundary-state");
|
|
151
|
+
const _gEbHost = globalThis;
|
|
152
|
+
const _ebState = _gEbHost[_EB_KEY] ?? { stack: [] };
|
|
153
|
+
if (!_gEbHost[_EB_KEY]) _gEbHost[_EB_KEY] = _ebState;
|
|
148
154
|
function pushErrorBoundary(handler) {
|
|
149
|
-
|
|
155
|
+
_ebState.stack.push(handler);
|
|
150
156
|
}
|
|
151
157
|
/**
|
|
152
158
|
* Remove a SPECIFIC handler from the error-boundary stack by reference
|
|
@@ -156,18 +162,18 @@ function pushErrorBoundary(handler) {
|
|
|
156
162
|
*/
|
|
157
163
|
function popErrorBoundary(handler) {
|
|
158
164
|
if (handler === void 0) {
|
|
159
|
-
|
|
165
|
+
_ebState.stack.pop();
|
|
160
166
|
return;
|
|
161
167
|
}
|
|
162
|
-
const idx =
|
|
163
|
-
if (idx !== -1)
|
|
168
|
+
const idx = _ebState.stack.lastIndexOf(handler);
|
|
169
|
+
if (idx !== -1) _ebState.stack.splice(idx, 1);
|
|
164
170
|
}
|
|
165
171
|
/**
|
|
166
172
|
* Dispatch an error to the nearest active ErrorBoundary.
|
|
167
173
|
* Returns true if the boundary handled it, false if none was registered.
|
|
168
174
|
*/
|
|
169
175
|
function dispatchToErrorBoundary(err) {
|
|
170
|
-
const handler =
|
|
176
|
+
const handler = _ebState.stack[_ebState.stack.length - 1];
|
|
171
177
|
return handler ? handler(err) : false;
|
|
172
178
|
}
|
|
173
179
|
|
|
@@ -331,18 +337,28 @@ function createContext(defaultValue) {
|
|
|
331
337
|
function createReactiveContext(defaultValue) {
|
|
332
338
|
return createContext(() => defaultValue);
|
|
333
339
|
}
|
|
334
|
-
const
|
|
335
|
-
|
|
340
|
+
const _CTX_KEY = Symbol.for("pyreon-core/context-stack-state");
|
|
341
|
+
const _gCtxHost = globalThis;
|
|
342
|
+
let _ctxState = _gCtxHost[_CTX_KEY];
|
|
343
|
+
if (!_ctxState) {
|
|
344
|
+
const defaultStack = [];
|
|
345
|
+
_ctxState = {
|
|
346
|
+
defaultStack,
|
|
347
|
+
provider: () => defaultStack
|
|
348
|
+
};
|
|
349
|
+
_gCtxHost[_CTX_KEY] = _ctxState;
|
|
350
|
+
}
|
|
351
|
+
const _ctx = _ctxState;
|
|
336
352
|
/**
|
|
337
353
|
* Override the context stack provider. Called by @pyreon/runtime-server to
|
|
338
354
|
* inject an AsyncLocalStorage-backed stack that isolates concurrent SSR requests.
|
|
339
355
|
* Has no effect in the browser (CSR always uses the default module-level stack).
|
|
340
356
|
*/
|
|
341
357
|
function setContextStackProvider(fn) {
|
|
342
|
-
|
|
358
|
+
_ctx.provider = fn;
|
|
343
359
|
}
|
|
344
360
|
function getStack() {
|
|
345
|
-
return
|
|
361
|
+
return _ctx.provider();
|
|
346
362
|
}
|
|
347
363
|
process.env.NODE_ENV;
|
|
348
364
|
function pushContext(values) {
|
|
@@ -575,7 +591,10 @@ function Dynamic(props) {
|
|
|
575
591
|
* })
|
|
576
592
|
*/
|
|
577
593
|
const __DEV__$3 = process.env.NODE_ENV !== "production";
|
|
578
|
-
|
|
594
|
+
const _ERR_KEY = Symbol.for("pyreon-core/error-handlers-state");
|
|
595
|
+
const _gErrHost = globalThis;
|
|
596
|
+
const _errState = _gErrHost[_ERR_KEY] ?? { handlers: [] };
|
|
597
|
+
if (!_gErrHost[_ERR_KEY]) _gErrHost[_ERR_KEY] = _errState;
|
|
579
598
|
/**
|
|
580
599
|
* Register a global error handler. Called whenever a component throws in any
|
|
581
600
|
* lifecycle phase, OR an effect throws in `@pyreon/reactivity`. Returns an
|
|
@@ -587,10 +606,10 @@ let _handlers = [];
|
|
|
587
606
|
* disconnected — Sentry/Datadog wiring missed effect-thrown errors.
|
|
588
607
|
*/
|
|
589
608
|
function registerErrorHandler(handler) {
|
|
590
|
-
|
|
609
|
+
_errState.handlers.push(handler);
|
|
591
610
|
_installReactivityBridge();
|
|
592
611
|
return () => {
|
|
593
|
-
|
|
612
|
+
_errState.handlers = _errState.handlers.filter((h) => h !== handler);
|
|
594
613
|
};
|
|
595
614
|
}
|
|
596
615
|
/**
|
|
@@ -602,7 +621,7 @@ function reportError(ctx) {
|
|
|
602
621
|
const trace = getReactiveTrace();
|
|
603
622
|
if (trace.length > 0) ctx.reactiveTrace = trace;
|
|
604
623
|
} catch {}
|
|
605
|
-
for (const h of
|
|
624
|
+
for (const h of _errState.handlers) try {
|
|
606
625
|
h(ctx);
|
|
607
626
|
} catch {}
|
|
608
627
|
}
|
|
@@ -1088,7 +1107,10 @@ function makeReactiveProps(raw) {
|
|
|
1088
1107
|
}
|
|
1089
1108
|
return result;
|
|
1090
1109
|
}
|
|
1091
|
-
|
|
1110
|
+
const _ID_KEY = Symbol.for("pyreon-core/id-counter-state");
|
|
1111
|
+
const _gIdHost = globalThis;
|
|
1112
|
+
const _idState = _gIdHost[_ID_KEY] ?? { counter: 0 };
|
|
1113
|
+
if (!_gIdHost[_ID_KEY]) _gIdHost[_ID_KEY] = _idState;
|
|
1092
1114
|
/**
|
|
1093
1115
|
* Generate a unique ID string for accessibility attributes (htmlFor, aria-describedby, etc.).
|
|
1094
1116
|
* SSR-safe: uses a deterministic counter that resets per request context.
|
|
@@ -1101,7 +1123,7 @@ let _idCounter = 0;
|
|
|
1101
1123
|
* </>
|
|
1102
1124
|
*/
|
|
1103
1125
|
function createUniqueId() {
|
|
1104
|
-
return `pyreon-${++
|
|
1126
|
+
return `pyreon-${++_idState.counter}`;
|
|
1105
1127
|
}
|
|
1106
1128
|
|
|
1107
1129
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/core",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.6",
|
|
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": {
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"lib",
|
|
17
17
|
"!lib/**/*.map",
|
|
18
|
-
"src",
|
|
19
18
|
"README.md",
|
|
20
19
|
"LICENSE"
|
|
21
20
|
],
|
|
@@ -26,17 +25,14 @@
|
|
|
26
25
|
"types": "./lib/types/index.d.ts",
|
|
27
26
|
"exports": {
|
|
28
27
|
".": {
|
|
29
|
-
"bun": "./src/index.ts",
|
|
30
28
|
"import": "./lib/index.js",
|
|
31
29
|
"types": "./lib/types/index.d.ts"
|
|
32
30
|
},
|
|
33
31
|
"./jsx-runtime": {
|
|
34
|
-
"bun": "./src/jsx-runtime.ts",
|
|
35
32
|
"import": "./lib/jsx-runtime.js",
|
|
36
33
|
"types": "./lib/types/jsx-runtime.d.ts"
|
|
37
34
|
},
|
|
38
35
|
"./jsx-dev-runtime": {
|
|
39
|
-
"bun": "./src/jsx-dev-runtime.ts",
|
|
40
36
|
"import": "./lib/jsx-dev-runtime.js",
|
|
41
37
|
"types": "./lib/types/jsx-dev-runtime.d.ts"
|
|
42
38
|
}
|
|
@@ -53,7 +49,7 @@
|
|
|
53
49
|
"prepublishOnly": "bun run build"
|
|
54
50
|
},
|
|
55
51
|
"dependencies": {
|
|
56
|
-
"@pyreon/reactivity": "^0.24.
|
|
52
|
+
"@pyreon/reactivity": "^0.24.6"
|
|
57
53
|
},
|
|
58
54
|
"devDependencies": {
|
|
59
55
|
"@pyreon/manifest": "0.13.1"
|
package/src/compat-marker.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Compat-mode native-component marker.
|
|
3
|
-
*
|
|
4
|
-
* Pyreon ships compat layers (`@pyreon/{react,preact,vue,solid}-compat`) that
|
|
5
|
-
* wrap every JSX-called component function to emulate that source framework's
|
|
6
|
-
* render-on-state-change semantics. That wrapping is correct for user code
|
|
7
|
-
* (the whole point of compat mode) but corrupts Pyreon framework components
|
|
8
|
-
* — those manage their own reactivity via `provide()` / signals / lifecycle
|
|
9
|
-
* hooks, and wrapping them runs their setup body inside the compat layer's
|
|
10
|
-
* render context instead of Pyreon's, breaking `provide()` and
|
|
11
|
-
* `onMount()` / `onUnmount()` calls.
|
|
12
|
-
*
|
|
13
|
-
* Framework components opt out of compat wrapping by setting a well-known
|
|
14
|
-
* registry symbol (`Symbol.for('pyreon:native-compat')`) on the function.
|
|
15
|
-
* The compat layer reads that symbol and routes marked components straight
|
|
16
|
-
* through Pyreon's `h()` mount path. The symbol is registry-shared, so no
|
|
17
|
-
* import direction between framework and compat is implied — both sides
|
|
18
|
-
* reference the same global symbol via the helpers exported here.
|
|
19
|
-
*
|
|
20
|
-
* Audience: framework-package authors writing JSX components in `@pyreon/*`
|
|
21
|
-
* packages whose setup body uses `provide()` / lifecycle hooks / signal
|
|
22
|
-
* subscriptions. Wrap exported components with `nativeCompat()`. One line
|
|
23
|
-
* per export site; zero runtime cost beyond a single property write at
|
|
24
|
-
* module load.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* The well-known registry symbol that marks a component as a Pyreon native
|
|
29
|
-
* framework component. Compat layers check this symbol to decide whether to
|
|
30
|
-
* skip their `wrapCompatComponent` call.
|
|
31
|
-
*
|
|
32
|
-
* Exported for advanced cases where a caller needs to test the marker
|
|
33
|
-
* directly (most callers should use `isNativeCompat()`).
|
|
34
|
-
*/
|
|
35
|
-
export const NATIVE_COMPAT_MARKER: symbol = Symbol.for('pyreon:native-compat')
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Mark a Pyreon framework component as "self-managing" — compat layers will
|
|
39
|
-
* skip their React/Vue/Solid/Preact-style wrapping and route the component
|
|
40
|
-
* directly through Pyreon's mount path. Use on every `@pyreon/*` JSX
|
|
41
|
-
* component whose setup body uses `provide()`, lifecycle hooks
|
|
42
|
-
* (`onMount` / `onUnmount`), signal-driven reactivity, or any other Pyreon
|
|
43
|
-
* native pattern that depends on the active component-setup frame.
|
|
44
|
-
*
|
|
45
|
-
* Idempotent: re-applying the marker is a no-op. Non-function inputs pass
|
|
46
|
-
* through unchanged so callers don't have to typecheck before wrapping.
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* import { nativeCompat, provide } from '@pyreon/core'
|
|
50
|
-
*
|
|
51
|
-
* export const RouterView = nativeCompat(function RouterView(props) {
|
|
52
|
-
* provide(RouterContext, ...)
|
|
53
|
-
* return <div data-pyreon-router-view>{children}</div>
|
|
54
|
-
* })
|
|
55
|
-
*/
|
|
56
|
-
export function nativeCompat<T>(fn: T): T {
|
|
57
|
-
if (typeof fn === 'function') {
|
|
58
|
-
;(fn as unknown as Record<symbol, boolean>)[NATIVE_COMPAT_MARKER] = true
|
|
59
|
-
}
|
|
60
|
-
return fn
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Read whether a component has been marked as a Pyreon native framework
|
|
65
|
-
* component. Compat-layer code calls this from its `jsx()` to decide whether
|
|
66
|
-
* to wrap or pass through.
|
|
67
|
-
*
|
|
68
|
-
* @example
|
|
69
|
-
* import { isNativeCompat } from '@pyreon/core'
|
|
70
|
-
*
|
|
71
|
-
* if (isNativeCompat(type)) return h(type, props)
|
|
72
|
-
* return wrapCompatComponent(type)(props)
|
|
73
|
-
*/
|
|
74
|
-
export function isNativeCompat(fn: unknown): boolean {
|
|
75
|
-
return (
|
|
76
|
-
typeof fn === 'function' &&
|
|
77
|
-
(fn as unknown as Record<symbol, boolean>)[NATIVE_COMPAT_MARKER] === true
|
|
78
|
-
)
|
|
79
|
-
}
|
package/src/compat-shared.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Code shared by the framework-compat JSX runtimes
|
|
3
|
-
* (`@pyreon/react-compat`, `@pyreon/preact-compat`).
|
|
4
|
-
*
|
|
5
|
-
* These helpers were previously copy-pasted byte-for-byte into both
|
|
6
|
-
* packages. `@pyreon/core` is the correct single home — it's already a
|
|
7
|
-
* dependency of every compat package and already hosts the sibling
|
|
8
|
-
* cross-compat module `compat-marker.ts` (`nativeCompat` / `isNativeCompat`).
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Shallow props comparison used by compat `memo()` / `useState` bailout.
|
|
13
|
-
* Same-length key sets with `Object.is`-equal values → equal.
|
|
14
|
-
*/
|
|
15
|
-
export function shallowEqualProps<P extends Record<string, unknown>>(a: P, b: P): boolean {
|
|
16
|
-
const keysA = Object.keys(a)
|
|
17
|
-
const keysB = Object.keys(b)
|
|
18
|
-
if (keysA.length !== keysB.length) return false
|
|
19
|
-
for (const k of keysA) {
|
|
20
|
-
if (!Object.is(a[k], b[k])) return false
|
|
21
|
-
}
|
|
22
|
-
return true
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Map React/Preact-style DOM attributes to standard HTML attributes,
|
|
27
|
-
* mutating `props` in place. No-op when `type` is not a host string
|
|
28
|
-
* (component vnodes keep their props untouched).
|
|
29
|
-
*
|
|
30
|
-
* The React and Preact variants were identical apart from React also
|
|
31
|
-
* stripping `suppressContentEditableWarning`. Both keys are React/Preact
|
|
32
|
-
* authoring-only and never valid DOM attributes, so always stripping
|
|
33
|
-
* both is behavior-preserving for Preact (the key is never set there;
|
|
34
|
-
* `delete` of an absent key is a no-op) and removes the only divergence.
|
|
35
|
-
*/
|
|
36
|
-
export function mapCompatDomProps(props: Record<string, unknown>, type: unknown): void {
|
|
37
|
-
if (typeof type !== 'string') return
|
|
38
|
-
|
|
39
|
-
if (props.className !== undefined) {
|
|
40
|
-
props.class = props.className
|
|
41
|
-
delete props.className
|
|
42
|
-
}
|
|
43
|
-
if (props.htmlFor !== undefined) {
|
|
44
|
-
props.for = props.htmlFor
|
|
45
|
-
delete props.htmlFor
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// React/Preact onChange fires on every keystroke for form elements (like onInput)
|
|
49
|
-
if (
|
|
50
|
-
(type === 'input' || type === 'textarea' || type === 'select') &&
|
|
51
|
-
props.onChange !== undefined
|
|
52
|
-
) {
|
|
53
|
-
if (props.onInput === undefined) {
|
|
54
|
-
props.onInput = props.onChange
|
|
55
|
-
}
|
|
56
|
-
delete props.onChange
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// autoFocus → autofocus
|
|
60
|
-
if (props.autoFocus !== undefined) {
|
|
61
|
-
props.autofocus = props.autoFocus
|
|
62
|
-
delete props.autoFocus
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// defaultValue / defaultChecked → value / checked when no controlled value
|
|
66
|
-
if (type === 'input' || type === 'textarea') {
|
|
67
|
-
if (props.defaultValue !== undefined && props.value === undefined) {
|
|
68
|
-
props.value = props.defaultValue
|
|
69
|
-
delete props.defaultValue
|
|
70
|
-
}
|
|
71
|
-
if (props.defaultChecked !== undefined && props.checked === undefined) {
|
|
72
|
-
props.checked = props.defaultChecked
|
|
73
|
-
delete props.defaultChecked
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Strip authoring-only props that have no DOM equivalent
|
|
78
|
-
delete props.suppressHydrationWarning
|
|
79
|
-
delete props.suppressContentEditableWarning
|
|
80
|
-
}
|
package/src/component.ts
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { setCurrentHooks } from './lifecycle'
|
|
2
|
-
import type { ComponentFn, LifecycleHooks, Props, VNodeChild } from './types'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Identity wrapper — marks a function as a Pyreon component and preserves its type.
|
|
6
|
-
* Useful for IDE tooling and future compiler optimisations.
|
|
7
|
-
*/
|
|
8
|
-
export function defineComponent<P extends Props>(fn: ComponentFn<P>): ComponentFn<P> {
|
|
9
|
-
return fn
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Run a component function in a tracked context so that lifecycle hooks
|
|
14
|
-
* registered inside it (onMount, onUnmount, onErrorCaptured, etc.) are captured.
|
|
15
|
-
*
|
|
16
|
-
* Called by the renderer — not intended for user code.
|
|
17
|
-
*/
|
|
18
|
-
export function runWithHooks<P extends Props>(
|
|
19
|
-
fn: ComponentFn<P>,
|
|
20
|
-
props: P,
|
|
21
|
-
): { vnode: VNodeChild; hooks: LifecycleHooks } {
|
|
22
|
-
const hooks: LifecycleHooks = { mount: null, unmount: null, update: null, error: null }
|
|
23
|
-
setCurrentHooks(hooks)
|
|
24
|
-
let vnode: VNodeChild = null
|
|
25
|
-
try {
|
|
26
|
-
vnode = fn(props)
|
|
27
|
-
} finally {
|
|
28
|
-
setCurrentHooks(null)
|
|
29
|
-
}
|
|
30
|
-
return { vnode, hooks }
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Walk up error handlers collected during component rendering.
|
|
35
|
-
* Returns true if any handler marked the error as handled.
|
|
36
|
-
*/
|
|
37
|
-
export function propagateError(err: unknown, hooks: LifecycleHooks): boolean {
|
|
38
|
-
if (!hooks.error) return false
|
|
39
|
-
for (const handler of hooks.error) {
|
|
40
|
-
if (handler(err) === true) return true
|
|
41
|
-
}
|
|
42
|
-
return false
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// ─── Error boundary stack ────────────────────────────────────────────────────
|
|
46
|
-
// Module-level stack of active ErrorBoundary handlers (innermost last).
|
|
47
|
-
// ErrorBoundary pushes during its own setup (before children mount) so that
|
|
48
|
-
// any child mountComponent error can dispatch up to the nearest boundary.
|
|
49
|
-
//
|
|
50
|
-
// Mutation contract: removal is IDENTITY-based (`lastIndexOf + splice`), not
|
|
51
|
-
// position-based (`pop`). Sibling boundaries unmount in an order that's
|
|
52
|
-
// driven by the renderer (keyed `<For>` reconciliation, `<Show>` flips,
|
|
53
|
-
// route nav), NOT in strict LIFO push order. A position-based `pop()` would
|
|
54
|
-
// remove the wrong frame whenever the unmount order diverges from the push
|
|
55
|
-
// order — the first boundary's `onUnmount` would pop the last boundary's
|
|
56
|
-
// handler, orphaning the first boundary's handler on the stack and removing
|
|
57
|
-
// the surviving boundary's handler from it. Subsequent errors would then
|
|
58
|
-
// route to the orphan (whose owning boundary's signal is already disposed,
|
|
59
|
-
// so the error vanishes silently) and the surviving boundary's children's
|
|
60
|
-
// errors would fall through to whichever boundary happens to sit at
|
|
61
|
-
// `stack[length-1]`. Same root-cause shape as the `popContext()` bug
|
|
62
|
-
// fixed in #725 for `provide()` — see
|
|
63
|
-
// `.claude/rules/anti-patterns.md` "Position-based pop for stack frames
|
|
64
|
-
// that may be pushed by reactive boundaries".
|
|
65
|
-
|
|
66
|
-
const _errorBoundaryStack: ((err: unknown) => boolean)[] = []
|
|
67
|
-
|
|
68
|
-
export function pushErrorBoundary(handler: (err: unknown) => boolean): void {
|
|
69
|
-
_errorBoundaryStack.push(handler)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Remove a SPECIFIC handler from the error-boundary stack by reference
|
|
74
|
-
* identity. Each `ErrorBoundary` registers `onUnmount(() => popErrorBoundary(handler))`
|
|
75
|
-
* with its OWN handler — so unmount in any order (LIFO, FIFO, middle-out)
|
|
76
|
-
* correctly removes the right handler.
|
|
77
|
-
*/
|
|
78
|
-
export function popErrorBoundary(handler?: (err: unknown) => boolean): void {
|
|
79
|
-
if (handler === undefined) {
|
|
80
|
-
// Back-compat: legacy callers that don't pass a handler get the old
|
|
81
|
-
// pop-last behaviour. Internal `ErrorBoundary` setup always passes
|
|
82
|
-
// its handler now; any external direct callers (tests, advanced
|
|
83
|
-
// consumers) keep working with no-arg form.
|
|
84
|
-
_errorBoundaryStack.pop()
|
|
85
|
-
return
|
|
86
|
-
}
|
|
87
|
-
const idx = _errorBoundaryStack.lastIndexOf(handler)
|
|
88
|
-
if (idx !== -1) _errorBoundaryStack.splice(idx, 1)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Dispatch an error to the nearest active ErrorBoundary.
|
|
93
|
-
* Returns true if the boundary handled it, false if none was registered.
|
|
94
|
-
*/
|
|
95
|
-
export function dispatchToErrorBoundary(err: unknown): boolean {
|
|
96
|
-
const handler = _errorBoundaryStack[_errorBoundaryStack.length - 1]
|
|
97
|
-
return handler ? handler(err) : false
|
|
98
|
-
}
|