@pyreon/core 0.1.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.
package/lib/index.js ADDED
@@ -0,0 +1,475 @@
1
+ import { signal } from "@pyreon/reactivity";
2
+
3
+ //#region src/lifecycle.ts
4
+ let _current = null;
5
+ function setCurrentHooks(hooks) {
6
+ _current = hooks;
7
+ }
8
+ /**
9
+ * Register a callback to run after the component is mounted to the DOM.
10
+ * Optionally return a cleanup function — it will run on unmount.
11
+ */
12
+ function onMount(fn) {
13
+ _current?.mount.push(fn);
14
+ }
15
+ /**
16
+ * Register a callback to run when the component is removed from the DOM.
17
+ */
18
+ function onUnmount(fn) {
19
+ _current?.unmount.push(fn);
20
+ }
21
+ /**
22
+ * Register a callback to run after each reactive update.
23
+ */
24
+ function onUpdate(fn) {
25
+ _current?.update.push(fn);
26
+ }
27
+ /**
28
+ * Register an error handler for this component subtree.
29
+ *
30
+ * When an error is thrown during rendering or in a child component,
31
+ * the nearest `onErrorCaptured` handler is called with the error.
32
+ * Return `true` to mark the error as handled and stop propagation.
33
+ *
34
+ * @example
35
+ * onErrorCaptured((err) => {
36
+ * setError(String(err))
37
+ * return true // handled — don't propagate
38
+ * })
39
+ */
40
+ function onErrorCaptured(fn) {
41
+ _current?.error.push(fn);
42
+ }
43
+
44
+ //#endregion
45
+ //#region src/component.ts
46
+ /**
47
+ * Identity wrapper — marks a function as a Pyreon component and preserves its type.
48
+ * Useful for IDE tooling and future compiler optimisations.
49
+ */
50
+ function defineComponent(fn) {
51
+ return fn;
52
+ }
53
+ /**
54
+ * Run a component function in a tracked context so that lifecycle hooks
55
+ * registered inside it (onMount, onUnmount, onErrorCaptured, etc.) are captured.
56
+ *
57
+ * Called by the renderer — not intended for user code.
58
+ */
59
+ function runWithHooks(fn, props) {
60
+ const hooks = {
61
+ mount: [],
62
+ unmount: [],
63
+ update: [],
64
+ error: []
65
+ };
66
+ setCurrentHooks(hooks);
67
+ let vnode = null;
68
+ try {
69
+ vnode = fn(props);
70
+ } finally {
71
+ setCurrentHooks(null);
72
+ }
73
+ return {
74
+ vnode,
75
+ hooks
76
+ };
77
+ }
78
+ /**
79
+ * Walk up error handlers collected during component rendering.
80
+ * Returns true if any handler marked the error as handled.
81
+ */
82
+ function propagateError(err, hooks) {
83
+ for (const handler of hooks.error) if (handler(err) === true) return true;
84
+ return false;
85
+ }
86
+ const _errorBoundaryStack = [];
87
+ function pushErrorBoundary(handler) {
88
+ _errorBoundaryStack.push(handler);
89
+ }
90
+ function popErrorBoundary() {
91
+ _errorBoundaryStack.pop();
92
+ }
93
+ /**
94
+ * Dispatch an error to the nearest active ErrorBoundary.
95
+ * Returns true if the boundary handled it, false if none was registered.
96
+ */
97
+ function dispatchToErrorBoundary(err) {
98
+ const handler = _errorBoundaryStack[_errorBoundaryStack.length - 1];
99
+ return handler ? handler(err) : false;
100
+ }
101
+
102
+ //#endregion
103
+ //#region src/context.ts
104
+ function createContext(defaultValue) {
105
+ return {
106
+ id: Symbol("PyreonContext"),
107
+ defaultValue
108
+ };
109
+ }
110
+ const _defaultStack = [];
111
+ let _stackProvider = () => _defaultStack;
112
+ /**
113
+ * Override the context stack provider. Called by @pyreon/runtime-server to
114
+ * inject an AsyncLocalStorage-backed stack that isolates concurrent SSR requests.
115
+ * Has no effect in the browser (CSR always uses the default module-level stack).
116
+ */
117
+ function setContextStackProvider(fn) {
118
+ _stackProvider = fn;
119
+ }
120
+ function getStack() {
121
+ return _stackProvider();
122
+ }
123
+ const __DEV__ = typeof process !== "undefined" && process.env.NODE_ENV !== "production";
124
+ function pushContext(values) {
125
+ getStack().push(values);
126
+ }
127
+ function popContext() {
128
+ const stack = getStack();
129
+ if (__DEV__ && stack.length === 0) return;
130
+ stack.pop();
131
+ }
132
+ /**
133
+ * Read the nearest provided value for a context.
134
+ * Falls back to `context.defaultValue` if none found.
135
+ */
136
+ function useContext(context) {
137
+ const stack = getStack();
138
+ for (let i = stack.length - 1; i >= 0; i--) {
139
+ const frame = stack[i];
140
+ if (frame?.has(context.id)) return frame.get(context.id);
141
+ }
142
+ return context.defaultValue;
143
+ }
144
+ /**
145
+ * Provide a value for `context` during `fn()`.
146
+ * Used by the renderer when it encounters a `<Provider>` component.
147
+ */
148
+ function withContext(context, value, fn) {
149
+ pushContext(new Map([[context.id, value]]));
150
+ try {
151
+ fn();
152
+ } finally {
153
+ popContext();
154
+ }
155
+ }
156
+
157
+ //#endregion
158
+ //#region src/h.ts
159
+ /** Marker for fragment nodes — renders children without a wrapper element */
160
+ const Fragment = Symbol("Pyreon.Fragment");
161
+ /**
162
+ * Hyperscript function — the compiled output of JSX.
163
+ * `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
164
+ *
165
+ * Generic on P so TypeScript validates props match the component's signature
166
+ * at the call site, then stores the result in the loosely-typed VNode.
167
+ */
168
+ /** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */
169
+ const EMPTY_PROPS = {};
170
+ function h(type, props, ...children) {
171
+ return {
172
+ type,
173
+ props: props ?? EMPTY_PROPS,
174
+ children: normalizeChildren(children),
175
+ key: props?.key ?? null
176
+ };
177
+ }
178
+ function normalizeChildren(children) {
179
+ for (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);
180
+ return children;
181
+ }
182
+ function flattenChildren(children) {
183
+ const result = [];
184
+ for (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));
185
+ else result.push(child);
186
+ return result;
187
+ }
188
+
189
+ //#endregion
190
+ //#region src/dynamic.ts
191
+ function Dynamic(props) {
192
+ const { component, ...rest } = props;
193
+ if (!component) return null;
194
+ return h(component, rest);
195
+ }
196
+
197
+ //#endregion
198
+ //#region src/telemetry.ts
199
+ let _handlers = [];
200
+ /**
201
+ * Register a global error handler. Called whenever a component throws in any
202
+ * lifecycle phase. Returns an unregister function.
203
+ */
204
+ function registerErrorHandler(handler) {
205
+ _handlers.push(handler);
206
+ return () => {
207
+ _handlers = _handlers.filter((h) => h !== handler);
208
+ };
209
+ }
210
+ /**
211
+ * Internal — called by the runtime whenever a component error is caught.
212
+ * Existing console.error calls are preserved; this is additive.
213
+ */
214
+ function reportError(ctx) {
215
+ for (const h of _handlers) try {
216
+ h(ctx);
217
+ } catch {}
218
+ }
219
+
220
+ //#endregion
221
+ //#region src/error-boundary.ts
222
+ /**
223
+ * ErrorBoundary — catches errors thrown by child components and renders a
224
+ * fallback UI instead of crashing the whole tree.
225
+ *
226
+ * Also reports caught errors to any registered telemetry handlers.
227
+ *
228
+ * How error propagation works:
229
+ * ErrorBoundary pushes a handler onto the module-level boundary stack
230
+ * synchronously during its own setup (before children are mounted).
231
+ * When mountComponent catches a child error, it calls dispatchToErrorBoundary()
232
+ * which invokes the innermost boundary's handler.
233
+ *
234
+ * Usage:
235
+ * h(ErrorBoundary, {
236
+ * fallback: (err) => h("p", null, `Error: ${err}`),
237
+ * children: h(MyComponent, null),
238
+ * })
239
+ *
240
+ * // or with JSX:
241
+ * <ErrorBoundary fallback={(err) => <p>Error: {String(err)}</p>}>
242
+ * <MyComponent />
243
+ * </ErrorBoundary>
244
+ */
245
+ function ErrorBoundary(props) {
246
+ const error = signal(null);
247
+ const reset = () => error.set(null);
248
+ const handler = (err) => {
249
+ if (error.peek() !== null) return false;
250
+ error.set(err);
251
+ reportError({
252
+ component: "ErrorBoundary",
253
+ phase: "render",
254
+ error: err,
255
+ timestamp: Date.now()
256
+ });
257
+ return true;
258
+ };
259
+ pushErrorBoundary(handler);
260
+ onUnmount(() => popErrorBoundary());
261
+ return () => {
262
+ const err = error();
263
+ if (err != null) return props.fallback(err, reset);
264
+ const ch = props.children;
265
+ return typeof ch === "function" ? ch() : ch;
266
+ };
267
+ }
268
+
269
+ //#endregion
270
+ //#region src/for.ts
271
+ /**
272
+ * Symbol used as the VNode type for a For list — runtime-dom handles it
273
+ * via mountFor, bypassing the generic VNode reconciler.
274
+ */
275
+ const ForSymbol = Symbol("pyreon.For");
276
+ /**
277
+ * Efficient reactive list rendering.
278
+ *
279
+ * Unlike a plain `() => items().map(item => h(...))`, For never re-creates
280
+ * VNodes for existing keys — only new keys invoke `children()`. Structural
281
+ * mutations (swap, sort, filter) are O(n) key scan + O(k) DOM moves where k
282
+ * is the number of actually displaced entries.
283
+ *
284
+ * Usage:
285
+ * <For each={items} by={r => r.id}>{r => <li>...</li>}</For>
286
+ */
287
+ function For(props) {
288
+ return {
289
+ type: ForSymbol,
290
+ props,
291
+ children: [],
292
+ key: null
293
+ };
294
+ }
295
+
296
+ //#endregion
297
+ //#region src/lazy.ts
298
+ function lazy(load) {
299
+ const loaded = signal(null);
300
+ const error = signal(null);
301
+ load().then((m) => loaded.set(m.default)).catch((e) => error.set(e instanceof Error ? e : new Error(String(e))));
302
+ const wrapper = ((props) => {
303
+ const err = error();
304
+ if (err) throw err;
305
+ const comp = loaded();
306
+ return comp ? h(comp, props) : null;
307
+ });
308
+ wrapper.__loading = () => loaded() === null && error() === null;
309
+ return wrapper;
310
+ }
311
+
312
+ //#endregion
313
+ //#region src/map-array.ts
314
+ /**
315
+ * mapArray — keyed reactive list mapping.
316
+ *
317
+ * Creates each mapped item exactly once per key, then reuses it across
318
+ * updates. When the source array is reordered or partially changed, only
319
+ * new keys invoke `map()`; existing entries return the cached result.
320
+ *
321
+ * This makes structural list operations (swap, sort, filter) O(k) in
322
+ * allocations where k is the number of new/removed keys, not O(n).
323
+ *
324
+ * The returned accessor reads `source()` reactively, so it can be passed
325
+ * directly to the keyed-list reconciler.
326
+ */
327
+ function mapArray(source, getKey, map) {
328
+ const cache = /* @__PURE__ */ new Map();
329
+ return () => {
330
+ const items = source();
331
+ const result = [];
332
+ const newKeys = /* @__PURE__ */ new Set();
333
+ for (const item of items) {
334
+ const key = getKey(item);
335
+ newKeys.add(key);
336
+ if (!cache.has(key)) cache.set(key, map(item));
337
+ result.push(cache.get(key));
338
+ }
339
+ for (const key of cache.keys()) if (!newKeys.has(key)) cache.delete(key);
340
+ return result;
341
+ };
342
+ }
343
+
344
+ //#endregion
345
+ //#region src/portal.ts
346
+ /**
347
+ * Symbol used as the VNode type for a Portal — runtime-dom mounts the
348
+ * children into `target` instead of the normal parent.
349
+ */
350
+ const PortalSymbol = Symbol("pyreon.Portal");
351
+ /**
352
+ * Portal — renders `children` into a different DOM node than the
353
+ * current parent tree.
354
+ *
355
+ * Useful for modals, tooltips, dropdowns, and any overlay that needs to
356
+ * escape CSS overflow/stacking context restrictions.
357
+ *
358
+ * @example
359
+ * // Render a modal at document.body level regardless of where in the
360
+ * // component tree <Modal> is used:
361
+ * Portal({ target: document.body, children: h(Modal, { onClose }) })
362
+ *
363
+ * // JSX:
364
+ * <Portal target={document.body}>
365
+ * <Modal onClose={close} />
366
+ * </Portal>
367
+ */
368
+ function Portal(props) {
369
+ return {
370
+ type: PortalSymbol,
371
+ props,
372
+ children: [],
373
+ key: null
374
+ };
375
+ }
376
+
377
+ //#endregion
378
+ //#region src/ref.ts
379
+ function createRef() {
380
+ return { current: null };
381
+ }
382
+
383
+ //#endregion
384
+ //#region src/show.ts
385
+ /**
386
+ * Conditionally render children based on a reactive condition.
387
+ *
388
+ * @example
389
+ * h(Show, { when: () => isLoggedIn() },
390
+ * h(Dashboard, null)
391
+ * )
392
+ *
393
+ * // With fallback:
394
+ * h(Show, { when: () => user(), fallback: h(Login, null) },
395
+ * h(Dashboard, null)
396
+ * )
397
+ */
398
+ function Show(props) {
399
+ return (() => props.when() ? props.children ?? null : props.fallback ?? null);
400
+ }
401
+ /**
402
+ * A branch inside `<Switch>`. Renders when `when()` is truthy.
403
+ * Must be used as a direct child of `Switch`.
404
+ *
405
+ * `Match` acts as a pure type/identity marker — Switch identifies it by checking
406
+ * `vnode.type === Match` rather than by the runtime return value.
407
+ */
408
+ function Match(_props) {
409
+ return null;
410
+ }
411
+ /**
412
+ * Multi-branch conditional rendering. Evaluates each `Match` child in order,
413
+ * renders the first whose `when()` is truthy, or `fallback` if none match.
414
+ *
415
+ * @example
416
+ * h(Switch, { fallback: h("p", null, "404") },
417
+ * h(Match, { when: () => route() === "/" }, h(Home, null)),
418
+ * h(Match, { when: () => route() === "/about" }, h(About, null)),
419
+ * )
420
+ */
421
+ function isMatchVNode(branch) {
422
+ return branch !== null && typeof branch === "object" && !Array.isArray(branch) && branch.type === Match;
423
+ }
424
+ function resolveMatchChildren(matchVNode) {
425
+ if (matchVNode.children.length === 0) return matchVNode.props.children ?? null;
426
+ if (matchVNode.children.length === 1) return matchVNode.children[0];
427
+ return matchVNode.children;
428
+ }
429
+ function normalizeBranches(children) {
430
+ if (Array.isArray(children)) return children;
431
+ if (children != null) return [children];
432
+ return [];
433
+ }
434
+ function Switch(props) {
435
+ return (() => {
436
+ const branches = normalizeBranches(props.children);
437
+ for (const branch of branches) {
438
+ if (!isMatchVNode(branch)) continue;
439
+ if (branch.props.when()) return resolveMatchChildren(branch);
440
+ }
441
+ return props.fallback ?? null;
442
+ });
443
+ }
444
+ const MatchSymbol = Symbol("pyreon.Match");
445
+
446
+ //#endregion
447
+ //#region src/suspense.ts
448
+ /**
449
+ * Suspense — shows `fallback` while a lazy child component is still loading.
450
+ *
451
+ * Works in tandem with `lazy()` from `@pyreon/react-compat` (or `@pyreon/core/lazy`).
452
+ * The child VNode's `.type.__loading()` signal drives the switch.
453
+ *
454
+ * Usage:
455
+ * const Page = lazy(() => import("./Page"))
456
+ *
457
+ * h(Suspense, { fallback: h(Spinner, null) }, h(Page, null))
458
+ * // or with JSX:
459
+ * <Suspense fallback={<Spinner />}><Page /></Suspense>
460
+ */
461
+ function Suspense(props) {
462
+ return h(Fragment, null, () => {
463
+ const ch = props.children;
464
+ const childNode = typeof ch === "function" ? ch() : ch;
465
+ if (childNode != null && typeof childNode === "object" && !Array.isArray(childNode) && typeof childNode.type === "function" && childNode.type.__loading?.()) {
466
+ const fb = props.fallback;
467
+ return typeof fb === "function" ? fb() : fb;
468
+ }
469
+ return childNode;
470
+ });
471
+ }
472
+
473
+ //#endregion
474
+ export { Dynamic, EMPTY_PROPS, ErrorBoundary, For, ForSymbol, Fragment, Match, MatchSymbol, Portal, PortalSymbol, Show, Suspense, Switch, createContext, createRef, defineComponent, dispatchToErrorBoundary, h, lazy, mapArray, onErrorCaptured, onMount, onUnmount, onUpdate, popContext, propagateError, pushContext, registerErrorHandler, reportError, runWithHooks, setContextStackProvider, useContext, withContext };
475
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/lifecycle.ts","../src/component.ts","../src/context.ts","../src/h.ts","../src/dynamic.ts","../src/telemetry.ts","../src/error-boundary.ts","../src/for.ts","../src/lazy.ts","../src/map-array.ts","../src/portal.ts","../src/ref.ts","../src/show.ts","../src/suspense.ts"],"sourcesContent":["import type { CleanupFn, LifecycleHooks } from \"./types\"\n\n// The currently-executing component's hook storage, set by the renderer\n// before calling the component function, cleared immediately after.\nlet _current: LifecycleHooks | null = null\n\nexport function setCurrentHooks(hooks: LifecycleHooks | null) {\n _current = hooks\n}\n\nexport function getCurrentHooks(): LifecycleHooks | null {\n return _current\n}\n\n/**\n * Register a callback to run after the component is mounted to the DOM.\n * Optionally return a cleanup function — it will run on unmount.\n */\nexport function onMount(fn: () => CleanupFn | undefined) {\n _current?.mount.push(fn)\n}\n\n/**\n * Register a callback to run when the component is removed from the DOM.\n */\nexport function onUnmount(fn: () => void) {\n _current?.unmount.push(fn)\n}\n\n/**\n * Register a callback to run after each reactive update.\n */\nexport function onUpdate(fn: () => void) {\n _current?.update.push(fn)\n}\n\n/**\n * Register an error handler for this component subtree.\n *\n * When an error is thrown during rendering or in a child component,\n * the nearest `onErrorCaptured` handler is called with the error.\n * Return `true` to mark the error as handled and stop propagation.\n *\n * @example\n * onErrorCaptured((err) => {\n * setError(String(err))\n * return true // handled — don't propagate\n * })\n */\nexport function onErrorCaptured(fn: (err: unknown) => boolean | undefined) {\n _current?.error.push(fn)\n}\n","import { setCurrentHooks } from \"./lifecycle\"\nimport type { ComponentFn, LifecycleHooks, Props, VNode } from \"./types\"\n\n/**\n * Identity wrapper — marks a function as a Pyreon component and preserves its type.\n * Useful for IDE tooling and future compiler optimisations.\n */\nexport function defineComponent<P extends Props>(fn: ComponentFn<P>): ComponentFn<P> {\n return fn\n}\n\n/**\n * Run a component function in a tracked context so that lifecycle hooks\n * registered inside it (onMount, onUnmount, onErrorCaptured, etc.) are captured.\n *\n * Called by the renderer — not intended for user code.\n */\nexport function runWithHooks<P extends Props>(\n fn: ComponentFn<P>,\n props: P,\n): { vnode: VNode | null; hooks: LifecycleHooks } {\n const hooks: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }\n setCurrentHooks(hooks)\n let vnode: VNode | null = null\n try {\n vnode = fn(props)\n } finally {\n setCurrentHooks(null)\n }\n return { vnode, hooks }\n}\n\n/**\n * Walk up error handlers collected during component rendering.\n * Returns true if any handler marked the error as handled.\n */\nexport function propagateError(err: unknown, hooks: LifecycleHooks): boolean {\n for (const handler of hooks.error) {\n if (handler(err) === true) return true\n }\n return false\n}\n\n// ─── Error boundary stack ────────────────────────────────────────────────────\n// Module-level stack of active ErrorBoundary handlers (innermost last).\n// ErrorBoundary pushes during its own setup (before children mount) so that\n// any child mountComponent error can dispatch up to the nearest boundary.\n\nconst _errorBoundaryStack: ((err: unknown) => boolean)[] = []\n\nexport function pushErrorBoundary(handler: (err: unknown) => boolean): void {\n _errorBoundaryStack.push(handler)\n}\n\nexport function popErrorBoundary(): void {\n _errorBoundaryStack.pop()\n}\n\n/**\n * Dispatch an error to the nearest active ErrorBoundary.\n * Returns true if the boundary handled it, false if none was registered.\n */\nexport function dispatchToErrorBoundary(err: unknown): boolean {\n const handler = _errorBoundaryStack[_errorBoundaryStack.length - 1]\n return handler ? handler(err) : false\n}\n","/**\n * Provide / inject — like React context or Vue provide/inject.\n *\n * Values flow down the component tree without prop-drilling.\n * The renderer maintains the context stack as it walks the VNode tree.\n */\n\nexport interface Context<T> {\n readonly id: symbol\n readonly defaultValue: T\n}\n\nexport function createContext<T>(defaultValue: T): Context<T> {\n return { id: Symbol(\"PyreonContext\"), defaultValue }\n}\n\n// ─── Runtime context stack (managed by the renderer) ─────────────────────────\n\n// Default stack — used for CSR and single-threaded SSR.\n// On Node.js with concurrent requests, @pyreon/runtime-server replaces this with\n// an AsyncLocalStorage-backed provider via setContextStackProvider().\nconst _defaultStack: Map<symbol, unknown>[] = []\nlet _stackProvider: () => Map<symbol, unknown>[] = () => _defaultStack\n\n/**\n * Override the context stack provider. Called by @pyreon/runtime-server to\n * inject an AsyncLocalStorage-backed stack that isolates concurrent SSR requests.\n * Has no effect in the browser (CSR always uses the default module-level stack).\n */\nexport function setContextStackProvider(fn: () => Map<symbol, unknown>[]): void {\n _stackProvider = fn\n}\n\nfunction getStack(): Map<symbol, unknown>[] {\n return _stackProvider()\n}\n\nconst __DEV__ = typeof process !== \"undefined\" && process.env.NODE_ENV !== \"production\"\n\nexport function pushContext(values: Map<symbol, unknown>) {\n getStack().push(values)\n}\n\nexport function popContext() {\n const stack = getStack()\n if (__DEV__ && stack.length === 0) {\n return\n }\n stack.pop()\n}\n\n/**\n * Read the nearest provided value for a context.\n * Falls back to `context.defaultValue` if none found.\n */\nexport function useContext<T>(context: Context<T>): T {\n const stack = getStack()\n for (let i = stack.length - 1; i >= 0; i--) {\n const frame = stack[i]\n if (frame?.has(context.id)) {\n return frame.get(context.id) as T\n }\n }\n return context.defaultValue\n}\n\n/**\n * Provide a value for `context` during `fn()`.\n * Used by the renderer when it encounters a `<Provider>` component.\n */\nexport function withContext<T>(context: Context<T>, value: T, fn: () => void) {\n const frame = new Map<symbol, unknown>([[context.id, value]])\n pushContext(frame)\n try {\n fn()\n } finally {\n popContext()\n }\n}\n","import type { ComponentFn, Props, VNode, VNodeChild } from \"./types\"\n\n/** Marker for fragment nodes — renders children without a wrapper element */\nexport const Fragment: unique symbol = Symbol(\"Pyreon.Fragment\")\n\n/**\n * Hyperscript function — the compiled output of JSX.\n * `<div class=\"x\">hello</div>` → `h(\"div\", { class: \"x\" }, \"hello\")`\n *\n * Generic on P so TypeScript validates props match the component's signature\n * at the call site, then stores the result in the loosely-typed VNode.\n */\n/** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */\nexport const EMPTY_PROPS: Props = {} as Props\n\nexport function h<P extends Props>(\n type: string | ComponentFn<P> | symbol,\n props: P | null,\n ...children: VNodeChild[]\n): VNode {\n return {\n type: type as string | ComponentFn | symbol,\n props: (props ?? EMPTY_PROPS) as Props,\n children: normalizeChildren(children),\n key: (props?.key as string | number | null) ?? null,\n }\n}\n\nfunction normalizeChildren(children: VNodeChild[]): VNodeChild[] {\n // Fast path: no nested arrays — return as-is without allocating\n for (let i = 0; i < children.length; i++) {\n if (Array.isArray(children[i])) {\n return flattenChildren(children)\n }\n }\n return children\n}\n\nfunction flattenChildren(children: VNodeChild[]): VNodeChild[] {\n const result: VNodeChild[] = []\n for (const child of children) {\n if (Array.isArray(child)) {\n result.push(...flattenChildren(child as VNodeChild[]))\n } else {\n result.push(child)\n }\n }\n return result\n}\n","import { h } from \"./h\"\nimport type { ComponentFn, Props, VNode } from \"./types\"\n\nexport interface DynamicProps extends Props {\n component: ComponentFn | string\n}\n\nexport function Dynamic(props: DynamicProps): VNode | null {\n const { component, ...rest } = props\n if (!component) return null\n return h(component as string | ComponentFn, rest as Props)\n}\n","/**\n * Error telemetry — hook into Pyreon's error reporting for Sentry, Datadog, etc.\n *\n * @example\n * import { registerErrorHandler } from \"@pyreon/core\"\n * import * as Sentry from \"@sentry/browser\"\n *\n * registerErrorHandler(ctx => {\n * Sentry.captureException(ctx.error, {\n * extra: { component: ctx.component, phase: ctx.phase },\n * })\n * })\n */\n\nexport interface ErrorContext {\n /** Component function name, or \"Anonymous\" */\n component: string\n /** Lifecycle phase where the error occurred */\n phase: \"setup\" | \"render\" | \"mount\" | \"unmount\" | \"effect\"\n /** The thrown value */\n error: unknown\n /** Unix timestamp (ms) */\n timestamp: number\n /** Component props at the time of the error */\n props?: Record<string, unknown>\n}\n\nexport type ErrorHandler = (ctx: ErrorContext) => void\n\nlet _handlers: ErrorHandler[] = []\n\n/**\n * Register a global error handler. Called whenever a component throws in any\n * lifecycle phase. Returns an unregister function.\n */\nexport function registerErrorHandler(handler: ErrorHandler): () => void {\n _handlers.push(handler)\n return () => {\n _handlers = _handlers.filter((h) => h !== handler)\n }\n}\n\n/**\n * Internal — called by the runtime whenever a component error is caught.\n * Existing console.error calls are preserved; this is additive.\n */\nexport function reportError(ctx: ErrorContext): void {\n for (const h of _handlers) {\n try {\n h(ctx)\n } catch {\n // handler errors must never propagate back into the framework\n }\n }\n}\n","import { signal } from \"@pyreon/reactivity\"\nimport { popErrorBoundary, pushErrorBoundary } from \"./component\"\nimport { onUnmount } from \"./lifecycle\"\nimport { reportError } from \"./telemetry\"\nimport type { VNodeChild, VNodeChildAtom } from \"./types\"\n\n/**\n * ErrorBoundary — catches errors thrown by child components and renders a\n * fallback UI instead of crashing the whole tree.\n *\n * Also reports caught errors to any registered telemetry handlers.\n *\n * How error propagation works:\n * ErrorBoundary pushes a handler onto the module-level boundary stack\n * synchronously during its own setup (before children are mounted).\n * When mountComponent catches a child error, it calls dispatchToErrorBoundary()\n * which invokes the innermost boundary's handler.\n *\n * Usage:\n * h(ErrorBoundary, {\n * fallback: (err) => h(\"p\", null, `Error: ${err}`),\n * children: h(MyComponent, null),\n * })\n *\n * // or with JSX:\n * <ErrorBoundary fallback={(err) => <p>Error: {String(err)}</p>}>\n * <MyComponent />\n * </ErrorBoundary>\n */\nexport function ErrorBoundary(props: {\n /**\n * Rendered when a child throws. Receives the caught error and a `reset`\n * function — calling `reset()` clears the error and re-renders children.\n */\n fallback: (err: unknown, reset: () => void) => VNodeChild\n children?: VNodeChild\n}): VNodeChild {\n const error = signal<unknown>(null)\n const reset = () => error.set(null)\n\n const handler = (err: unknown): boolean => {\n if (error.peek() !== null) return false // already in error state — let outer boundary catch it\n error.set(err)\n reportError({ component: \"ErrorBoundary\", phase: \"render\", error: err, timestamp: Date.now() })\n return true\n }\n\n // Push synchronously — before children are mounted — so child errors see this boundary\n pushErrorBoundary(handler)\n onUnmount(() => popErrorBoundary())\n\n return (): VNodeChildAtom => {\n const err = error()\n if (err != null) return props.fallback(err, reset) as VNodeChildAtom\n const ch = props.children\n return (typeof ch === \"function\" ? ch() : ch) as VNodeChildAtom\n }\n}\n","import type { NativeItem, Props, VNode } from \"./types\"\n\n/**\n * Symbol used as the VNode type for a For list — runtime-dom handles it\n * via mountFor, bypassing the generic VNode reconciler.\n */\nexport const ForSymbol: unique symbol = Symbol(\"pyreon.For\")\n\nexport interface ForProps<T> {\n each: () => T[]\n by: (item: T) => string | number\n children: (item: T) => VNode | NativeItem\n}\n\n/**\n * Efficient reactive list rendering.\n *\n * Unlike a plain `() => items().map(item => h(...))`, For never re-creates\n * VNodes for existing keys — only new keys invoke `children()`. Structural\n * mutations (swap, sort, filter) are O(n) key scan + O(k) DOM moves where k\n * is the number of actually displaced entries.\n *\n * Usage:\n * <For each={items} by={r => r.id}>{r => <li>...</li>}</For>\n */\nexport function For<T>(props: ForProps<T>): VNode {\n return {\n type: ForSymbol as unknown as string,\n props: props as unknown as Props,\n children: [],\n key: null,\n }\n}\n","import { signal } from \"@pyreon/reactivity\"\nimport { h } from \"./h\"\nimport type { LazyComponent } from \"./suspense\"\nimport type { ComponentFn, Props } from \"./types\"\n\nexport function lazy<P extends Props>(\n load: () => Promise<{ default: ComponentFn<P> }>,\n): LazyComponent<P> {\n const loaded = signal<ComponentFn<P> | null>(null)\n const error = signal<Error | null>(null)\n\n load()\n .then((m) => loaded.set(m.default))\n .catch((e) => error.set(e instanceof Error ? e : new Error(String(e))))\n\n const wrapper = ((props: P) => {\n const err = error()\n if (err) throw err\n const comp = loaded()\n return comp ? h(comp as ComponentFn, props as Props) : null\n }) as LazyComponent<P>\n\n wrapper.__loading = () => loaded() === null && error() === null\n return wrapper\n}\n","/**\n * mapArray — keyed reactive list mapping.\n *\n * Creates each mapped item exactly once per key, then reuses it across\n * updates. When the source array is reordered or partially changed, only\n * new keys invoke `map()`; existing entries return the cached result.\n *\n * This makes structural list operations (swap, sort, filter) O(k) in\n * allocations where k is the number of new/removed keys, not O(n).\n *\n * The returned accessor reads `source()` reactively, so it can be passed\n * directly to the keyed-list reconciler.\n */\nexport function mapArray<T, U>(\n source: () => T[],\n getKey: (item: T) => string | number,\n map: (item: T) => U,\n): () => U[] {\n const cache = new Map<string | number, U>()\n\n return () => {\n const items = source()\n const result: U[] = []\n const newKeys = new Set<string | number>()\n\n for (const item of items) {\n const key = getKey(item)\n newKeys.add(key)\n if (!cache.has(key)) {\n cache.set(key, map(item))\n }\n result.push(cache.get(key) as U)\n }\n\n // Evict entries whose keys are no longer present\n for (const key of cache.keys()) {\n if (!newKeys.has(key)) cache.delete(key)\n }\n\n return result\n }\n}\n","import type { Props, VNode, VNodeChild } from \"./types\"\n\n/**\n * Symbol used as the VNode type for a Portal — runtime-dom mounts the\n * children into `target` instead of the normal parent.\n */\nexport const PortalSymbol: unique symbol = Symbol(\"pyreon.Portal\")\n\nexport interface PortalProps {\n /** DOM element to render children into (e.g. document.body). */\n target: Element\n children: VNodeChild\n}\n\n/**\n * Portal — renders `children` into a different DOM node than the\n * current parent tree.\n *\n * Useful for modals, tooltips, dropdowns, and any overlay that needs to\n * escape CSS overflow/stacking context restrictions.\n *\n * @example\n * // Render a modal at document.body level regardless of where in the\n * // component tree <Modal> is used:\n * Portal({ target: document.body, children: h(Modal, { onClose }) })\n *\n * // JSX:\n * <Portal target={document.body}>\n * <Modal onClose={close} />\n * </Portal>\n */\nexport function Portal(props: PortalProps): VNode {\n return {\n type: PortalSymbol as unknown as string,\n props: props as unknown as Props,\n children: [],\n key: null,\n }\n}\n","/**\n * createRef — mutable container for a DOM element or component value.\n *\n * Usage:\n * const inputRef = createRef<HTMLInputElement>()\n * onMount(() => { inputRef.current?.focus() })\n * return <input ref={inputRef} />\n *\n * The runtime sets `ref.current` after the element is inserted into the DOM\n * and clears it to `null` when the element is removed.\n */\n\nexport interface Ref<T = unknown> {\n current: T | null\n}\n\nexport function createRef<T = unknown>(): Ref<T> {\n return { current: null }\n}\n","import type { Props, VNode, VNodeChild, VNodeChildAtom } from \"./types\"\n\n// ─── Show ─────────────────────────────────────────────────────────────────────\n\nexport interface ShowProps extends Props {\n /** Accessor — children render when truthy, fallback when falsy. */\n when: () => unknown\n fallback?: VNodeChild\n children?: VNodeChild\n}\n\n/**\n * Conditionally render children based on a reactive condition.\n *\n * @example\n * h(Show, { when: () => isLoggedIn() },\n * h(Dashboard, null)\n * )\n *\n * // With fallback:\n * h(Show, { when: () => user(), fallback: h(Login, null) },\n * h(Dashboard, null)\n * )\n */\nexport function Show(props: ShowProps): VNode | null {\n // Returns a reactive accessor; the renderer unwraps it at mount time.\n return ((): VNodeChildAtom =>\n (props.when()\n ? (props.children ?? null)\n : (props.fallback ?? null)) as VNodeChildAtom) as unknown as VNode\n}\n\n// ─── Switch / Match ───────────────────────────────────────────────────────────\n\nexport interface MatchProps extends Props {\n /** Accessor — this branch renders when truthy. */\n when: () => unknown\n children?: VNodeChild\n}\n\n/**\n * A branch inside `<Switch>`. Renders when `when()` is truthy.\n * Must be used as a direct child of `Switch`.\n *\n * `Match` acts as a pure type/identity marker — Switch identifies it by checking\n * `vnode.type === Match` rather than by the runtime return value.\n */\nexport function Match(_props: MatchProps): VNode | null {\n // Match is never mounted directly — Switch inspects Match VNodes by type identity.\n return null\n}\n\nexport interface SwitchProps extends Props {\n /** Rendered when no Match branch is truthy. */\n fallback?: VNodeChild\n children?: VNodeChild | VNodeChild[]\n}\n\n/**\n * Multi-branch conditional rendering. Evaluates each `Match` child in order,\n * renders the first whose `when()` is truthy, or `fallback` if none match.\n *\n * @example\n * h(Switch, { fallback: h(\"p\", null, \"404\") },\n * h(Match, { when: () => route() === \"/\" }, h(Home, null)),\n * h(Match, { when: () => route() === \"/about\" }, h(About, null)),\n * )\n */\nfunction isMatchVNode(branch: VNodeChild): branch is VNode {\n return (\n branch !== null &&\n typeof branch === \"object\" &&\n !Array.isArray(branch) &&\n (branch as VNode).type === Match\n )\n}\n\nfunction resolveMatchChildren(matchVNode: VNode): VNodeChildAtom {\n if (matchVNode.children.length === 0) {\n return ((matchVNode.props as unknown as MatchProps).children ?? null) as VNodeChildAtom\n }\n if (matchVNode.children.length === 1) return matchVNode.children[0] as VNodeChildAtom\n return matchVNode.children as unknown as VNodeChildAtom\n}\n\nfunction normalizeBranches(children: SwitchProps[\"children\"]): VNodeChild[] {\n if (Array.isArray(children)) return children\n if (children != null) return [children]\n return []\n}\n\nexport function Switch(props: SwitchProps): VNode | null {\n // Returns a reactive accessor; the renderer unwraps it at mount time.\n return ((): VNodeChildAtom => {\n const branches = normalizeBranches(props.children)\n\n for (const branch of branches) {\n if (!isMatchVNode(branch)) continue\n const matchProps = branch.props as unknown as MatchProps\n if (matchProps.when()) return resolveMatchChildren(branch)\n }\n\n return (props.fallback ?? null) as VNodeChildAtom\n }) as unknown as VNode\n}\n\n// Keep MatchSymbol export for any code that was using it\nexport const MatchSymbol: unique symbol = Symbol(\"pyreon.Match\")\n","import { Fragment, h } from \"./h\"\nimport type { Props, VNode, VNodeChild } from \"./types\"\n\n/** Internal marker attached to lazy()-wrapped components */\nexport type LazyComponent<P extends Props = Props> = ((props: P) => VNode | null) & {\n __loading: () => boolean\n}\n\n/**\n * Suspense — shows `fallback` while a lazy child component is still loading.\n *\n * Works in tandem with `lazy()` from `@pyreon/react-compat` (or `@pyreon/core/lazy`).\n * The child VNode's `.type.__loading()` signal drives the switch.\n *\n * Usage:\n * const Page = lazy(() => import(\"./Page\"))\n *\n * h(Suspense, { fallback: h(Spinner, null) }, h(Page, null))\n * // or with JSX:\n * <Suspense fallback={<Spinner />}><Page /></Suspense>\n */\nexport function Suspense(props: { fallback: VNodeChild; children?: VNodeChild }): VNode {\n return h(Fragment, null, () => {\n const ch = props.children\n const childNode = typeof ch === \"function\" ? ch() : ch\n\n // Check if the child is a VNode whose type is a lazy component still loading\n const isLoading =\n childNode != null &&\n typeof childNode === \"object\" &&\n !Array.isArray(childNode) &&\n typeof (childNode as VNode).type === \"function\" &&\n ((childNode as VNode).type as unknown as LazyComponent).__loading?.()\n\n if (isLoading) {\n const fb = props.fallback\n return typeof fb === \"function\" ? fb() : fb\n }\n return childNode\n })\n}\n"],"mappings":"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"}