@pyreon/core 0.11.5 → 0.11.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.
Files changed (47) hide show
  1. package/README.md +2 -2
  2. package/lib/analysis/index.js.html +1 -1
  3. package/lib/index.js +33 -5
  4. package/lib/index.js.map +1 -1
  5. package/lib/jsx-dev-runtime.js.map +1 -1
  6. package/lib/jsx-runtime.js.map +1 -1
  7. package/lib/types/index.d.ts +145 -98
  8. package/lib/types/index.d.ts.map +1 -1
  9. package/lib/types/jsx-dev-runtime.d.ts +94 -94
  10. package/lib/types/jsx-runtime.d.ts +94 -94
  11. package/package.json +11 -11
  12. package/src/component.ts +2 -2
  13. package/src/context.ts +75 -4
  14. package/src/dynamic.ts +4 -4
  15. package/src/error-boundary.ts +10 -10
  16. package/src/for.ts +8 -2
  17. package/src/h.ts +4 -4
  18. package/src/index.ts +30 -27
  19. package/src/jsx-dev-runtime.ts +1 -1
  20. package/src/jsx-runtime.ts +108 -108
  21. package/src/lazy.ts +4 -4
  22. package/src/lifecycle.ts +6 -6
  23. package/src/portal.ts +2 -2
  24. package/src/show.ts +4 -4
  25. package/src/style.ts +51 -51
  26. package/src/suspense.ts +8 -8
  27. package/src/telemetry.ts +1 -1
  28. package/src/tests/component.test.ts +60 -60
  29. package/src/tests/context.test.ts +102 -102
  30. package/src/tests/core.test.ts +376 -376
  31. package/src/tests/cx.test.ts +34 -34
  32. package/src/tests/dynamic.test.ts +28 -28
  33. package/src/tests/error-boundary.test.ts +51 -51
  34. package/src/tests/for.test.ts +26 -26
  35. package/src/tests/h.test.ts +100 -100
  36. package/src/tests/jsx-compat.test.tsx +41 -41
  37. package/src/tests/lazy.test.ts +28 -28
  38. package/src/tests/lifecycle.test.ts +35 -35
  39. package/src/tests/map-array.test.ts +36 -36
  40. package/src/tests/portal.test.ts +21 -21
  41. package/src/tests/props-extended.test.ts +51 -51
  42. package/src/tests/props.test.ts +62 -62
  43. package/src/tests/ref.test.ts +20 -20
  44. package/src/tests/show.test.ts +94 -94
  45. package/src/tests/style.test.ts +101 -101
  46. package/src/tests/suspense.test.ts +44 -44
  47. package/src/tests/telemetry.test.ts +35 -35
@@ -48,7 +48,7 @@ declare function jsx(type: string | ComponentFn | symbol, props: Props & {
48
48
  children?: VNodeChild | VNodeChild[];
49
49
  }, key?: string | number | null): VNode;
50
50
  declare const jsxs: typeof jsx;
51
- type Booleanish = boolean | "true" | "false";
51
+ type Booleanish = boolean | 'true' | 'false';
52
52
  type CSSProperties = { [K in keyof CSSStyleDeclaration]?: string | number };
53
53
  type StyleValue = string | CSSProperties;
54
54
  /** Event with typed currentTarget — used in element-specific event handlers. */
@@ -65,58 +65,58 @@ interface PyreonHTMLAttributes<E extends Element = HTMLElement> {
65
65
  tabIndex?: number | (() => number) | undefined;
66
66
  title?: string | (() => string) | undefined;
67
67
  lang?: string | undefined;
68
- dir?: "ltr" | "rtl" | "auto" | undefined;
68
+ dir?: 'ltr' | 'rtl' | 'auto' | undefined;
69
69
  hidden?: boolean | (() => boolean) | undefined;
70
70
  draggable?: Booleanish | undefined;
71
- contentEditable?: Booleanish | "inherit" | "plaintext-only" | undefined;
71
+ contentEditable?: Booleanish | 'inherit' | 'plaintext-only' | undefined;
72
72
  spellCheck?: Booleanish | undefined;
73
- autoCapitalize?: "off" | "on" | "sentences" | "words" | "characters" | undefined;
74
- translate?: "yes" | "no" | undefined;
75
- enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined;
76
- inputMode?: "none" | "text" | "decimal" | "numeric" | "tel" | "search" | "email" | "url" | undefined;
73
+ autoCapitalize?: 'off' | 'on' | 'sentences' | 'words' | 'characters' | undefined;
74
+ translate?: 'yes' | 'no' | undefined;
75
+ enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send' | undefined;
76
+ inputMode?: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url' | undefined;
77
77
  is?: string | undefined;
78
78
  slot?: string | undefined;
79
79
  part?: string | undefined;
80
- popover?: "auto" | "manual" | undefined;
80
+ popover?: 'auto' | 'manual' | undefined;
81
81
  popoverTarget?: string | undefined;
82
- popoverTargetAction?: "toggle" | "show" | "hide" | undefined;
82
+ popoverTargetAction?: 'toggle' | 'show' | 'hide' | undefined;
83
83
  inert?: boolean | undefined;
84
- "aria-label"?: string | (() => string) | undefined;
85
- "aria-hidden"?: Booleanish | (() => Booleanish) | undefined;
86
- "aria-disabled"?: Booleanish | (() => Booleanish) | undefined;
87
- "aria-expanded"?: Booleanish | (() => Booleanish) | undefined;
88
- "aria-selected"?: Booleanish | (() => Booleanish) | undefined;
89
- "aria-checked"?: Booleanish | "mixed" | (() => Booleanish | "mixed") | undefined;
90
- "aria-current"?: Booleanish | "page" | "step" | "location" | "date" | "time" | undefined;
91
- "aria-live"?: "off" | "assertive" | "polite" | undefined;
92
- "aria-atomic"?: Booleanish | undefined;
93
- "aria-busy"?: Booleanish | undefined;
94
- "aria-controls"?: string | undefined;
95
- "aria-describedby"?: string | undefined;
96
- "aria-labelledby"?: string | undefined;
97
- "aria-placeholder"?: string | undefined;
98
- "aria-required"?: Booleanish | (() => Booleanish) | undefined;
99
- "aria-invalid"?: Booleanish | "grammar" | "spelling" | undefined;
100
- "aria-valuemin"?: number | undefined;
101
- "aria-valuemax"?: number | undefined;
102
- "aria-valuenow"?: number | undefined;
103
- "aria-valuetext"?: string | undefined;
104
- "aria-haspopup"?: Booleanish | "menu" | "listbox" | "tree" | "grid" | "dialog" | undefined;
105
- "aria-posinset"?: number | undefined;
106
- "aria-setsize"?: number | undefined;
107
- "aria-level"?: number | undefined;
108
- "aria-multiline"?: Booleanish | undefined;
109
- "aria-multiselectable"?: Booleanish | undefined;
110
- "aria-orientation"?: "horizontal" | "vertical" | undefined;
111
- "aria-readonly"?: Booleanish | (() => Booleanish) | undefined;
112
- "aria-sort"?: "none" | "ascending" | "descending" | "other" | undefined;
113
- "aria-autocomplete"?: "none" | "inline" | "list" | "both" | undefined;
114
- "aria-colcount"?: number | undefined;
115
- "aria-colindex"?: number | undefined;
116
- "aria-colspan"?: number | undefined;
117
- "aria-rowcount"?: number | undefined;
118
- "aria-rowindex"?: number | undefined;
119
- "aria-rowspan"?: number | undefined;
84
+ 'aria-label'?: string | (() => string) | undefined;
85
+ 'aria-hidden'?: Booleanish | (() => Booleanish) | undefined;
86
+ 'aria-disabled'?: Booleanish | (() => Booleanish) | undefined;
87
+ 'aria-expanded'?: Booleanish | (() => Booleanish) | undefined;
88
+ 'aria-selected'?: Booleanish | (() => Booleanish) | undefined;
89
+ 'aria-checked'?: Booleanish | 'mixed' | (() => Booleanish | 'mixed') | undefined;
90
+ 'aria-current'?: Booleanish | 'page' | 'step' | 'location' | 'date' | 'time' | undefined;
91
+ 'aria-live'?: 'off' | 'assertive' | 'polite' | undefined;
92
+ 'aria-atomic'?: Booleanish | undefined;
93
+ 'aria-busy'?: Booleanish | undefined;
94
+ 'aria-controls'?: string | undefined;
95
+ 'aria-describedby'?: string | undefined;
96
+ 'aria-labelledby'?: string | undefined;
97
+ 'aria-placeholder'?: string | undefined;
98
+ 'aria-required'?: Booleanish | (() => Booleanish) | undefined;
99
+ 'aria-invalid'?: Booleanish | 'grammar' | 'spelling' | undefined;
100
+ 'aria-valuemin'?: number | undefined;
101
+ 'aria-valuemax'?: number | undefined;
102
+ 'aria-valuenow'?: number | undefined;
103
+ 'aria-valuetext'?: string | undefined;
104
+ 'aria-haspopup'?: Booleanish | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | undefined;
105
+ 'aria-posinset'?: number | undefined;
106
+ 'aria-setsize'?: number | undefined;
107
+ 'aria-level'?: number | undefined;
108
+ 'aria-multiline'?: Booleanish | undefined;
109
+ 'aria-multiselectable'?: Booleanish | undefined;
110
+ 'aria-orientation'?: 'horizontal' | 'vertical' | undefined;
111
+ 'aria-readonly'?: Booleanish | (() => Booleanish) | undefined;
112
+ 'aria-sort'?: 'none' | 'ascending' | 'descending' | 'other' | undefined;
113
+ 'aria-autocomplete'?: 'none' | 'inline' | 'list' | 'both' | undefined;
114
+ 'aria-colcount'?: number | undefined;
115
+ 'aria-colindex'?: number | undefined;
116
+ 'aria-colspan'?: number | undefined;
117
+ 'aria-rowcount'?: number | undefined;
118
+ 'aria-rowindex'?: number | undefined;
119
+ 'aria-rowspan'?: number | undefined;
120
120
  ref?: RefProp<E> | undefined;
121
121
  key?: string | number | undefined;
122
122
  children?: VNodeChild | VNodeChild[];
@@ -202,7 +202,7 @@ interface InputAttributes extends PyreonHTMLAttributes<HTMLInputElement> {
202
202
  accept?: string | undefined;
203
203
  autoComplete?: string | undefined;
204
204
  autoFocus?: boolean | undefined;
205
- capture?: "user" | "environment" | string | undefined;
205
+ capture?: 'user' | 'environment' | string | undefined;
206
206
  form?: string | undefined;
207
207
  formNoValidate?: boolean | undefined;
208
208
  list?: string | undefined;
@@ -217,12 +217,12 @@ interface AnchorAttributes extends PyreonHTMLAttributes<HTMLAnchorElement> {
217
217
  hreflang?: string | undefined;
218
218
  ping?: string | undefined;
219
219
  referrerPolicy?: string | undefined;
220
- target?: "_blank" | "_self" | "_parent" | "_top" | string | undefined;
220
+ target?: '_blank' | '_self' | '_parent' | '_top' | string | undefined;
221
221
  rel?: string | undefined;
222
222
  download?: string | boolean | undefined;
223
223
  }
224
224
  interface ButtonAttributes extends PyreonHTMLAttributes<HTMLButtonElement> {
225
- type?: "button" | "submit" | "reset" | undefined;
225
+ type?: 'button' | 'submit' | 'reset' | undefined;
226
226
  disabled?: boolean | (() => boolean) | undefined;
227
227
  name?: string | undefined;
228
228
  value?: string | undefined;
@@ -247,7 +247,7 @@ interface TextareaAttributes extends PyreonHTMLAttributes<HTMLTextAreaElement> {
247
247
  name?: string | undefined;
248
248
  autoFocus?: boolean | undefined;
249
249
  form?: string | undefined;
250
- wrap?: "hard" | "soft" | undefined;
250
+ wrap?: 'hard' | 'soft' | undefined;
251
251
  }
252
252
  interface SelectAttributes extends PyreonHTMLAttributes<HTMLSelectElement> {
253
253
  value?: string | string[] | (() => string | string[]) | undefined;
@@ -268,7 +268,7 @@ interface OptionAttributes extends PyreonHTMLAttributes<HTMLOptionElement> {
268
268
  }
269
269
  interface FormAttributes extends PyreonHTMLAttributes<HTMLFormElement> {
270
270
  action?: string | undefined;
271
- method?: "get" | "post" | undefined;
271
+ method?: 'get' | 'post' | undefined;
272
272
  encType?: string | undefined;
273
273
  noValidate?: boolean | undefined;
274
274
  target?: string | undefined;
@@ -282,13 +282,13 @@ interface ImgAttributes extends PyreonHTMLAttributes<HTMLImageElement> {
282
282
  alt?: string | (() => string) | undefined;
283
283
  width?: number | string | (() => number | string) | undefined;
284
284
  height?: number | string | (() => number | string) | undefined;
285
- loading?: "lazy" | "eager" | undefined;
286
- decoding?: "auto" | "async" | "sync" | undefined;
287
- crossOrigin?: "anonymous" | "use-credentials" | undefined;
285
+ loading?: 'lazy' | 'eager' | undefined;
286
+ decoding?: 'auto' | 'async' | 'sync' | undefined;
287
+ crossOrigin?: 'anonymous' | 'use-credentials' | undefined;
288
288
  referrerPolicy?: string | undefined;
289
289
  srcSet?: string | (() => string) | undefined;
290
290
  sizes?: string | (() => string) | undefined;
291
- fetchPriority?: "high" | "low" | "auto" | undefined;
291
+ fetchPriority?: 'high' | 'low' | 'auto' | undefined;
292
292
  }
293
293
  interface VideoAttributes extends PyreonHTMLAttributes<HTMLVideoElement> {
294
294
  src?: string | (() => string) | undefined;
@@ -299,9 +299,9 @@ interface VideoAttributes extends PyreonHTMLAttributes<HTMLVideoElement> {
299
299
  muted?: boolean | undefined;
300
300
  loop?: boolean | undefined;
301
301
  poster?: string | (() => string) | undefined;
302
- preload?: "none" | "metadata" | "auto" | undefined;
302
+ preload?: 'none' | 'metadata' | 'auto' | undefined;
303
303
  playsInline?: boolean | undefined;
304
- crossOrigin?: "anonymous" | "use-credentials" | undefined;
304
+ crossOrigin?: 'anonymous' | 'use-credentials' | undefined;
305
305
  disablePictureInPicture?: boolean | undefined;
306
306
  disableRemotePlayback?: boolean | undefined;
307
307
  }
@@ -311,8 +311,8 @@ interface AudioAttributes extends PyreonHTMLAttributes<HTMLAudioElement> {
311
311
  autoPlay?: boolean | undefined;
312
312
  muted?: boolean | undefined;
313
313
  loop?: boolean | undefined;
314
- preload?: "none" | "metadata" | "auto" | undefined;
315
- crossOrigin?: "anonymous" | "use-credentials" | undefined;
314
+ preload?: 'none' | 'metadata' | 'auto' | undefined;
315
+ crossOrigin?: 'anonymous' | 'use-credentials' | undefined;
316
316
  }
317
317
  interface LabelAttributes extends PyreonHTMLAttributes<HTMLLabelElement> {
318
318
  htmlFor?: string | undefined;
@@ -322,7 +322,7 @@ interface LabelAttributes extends PyreonHTMLAttributes<HTMLLabelElement> {
322
322
  interface ThAttributes extends PyreonHTMLAttributes<HTMLTableCellElement> {
323
323
  colSpan?: number | undefined;
324
324
  rowSpan?: number | undefined;
325
- scope?: "col" | "row" | "colgroup" | "rowgroup" | undefined;
325
+ scope?: 'col' | 'row' | 'colgroup' | 'rowgroup' | undefined;
326
326
  abbr?: string | undefined;
327
327
  headers?: string | undefined;
328
328
  }
@@ -340,7 +340,7 @@ interface IframeAttributes extends PyreonHTMLAttributes<HTMLIFrameElement> {
340
340
  height?: number | string | undefined;
341
341
  allow?: string | undefined;
342
342
  allowFullScreen?: boolean | undefined;
343
- loading?: "lazy" | "eager" | undefined;
343
+ loading?: 'lazy' | 'eager' | undefined;
344
344
  name?: string | undefined;
345
345
  sandbox?: string | undefined;
346
346
  referrerPolicy?: string | undefined;
@@ -352,7 +352,7 @@ interface LinkAttributes extends PyreonHTMLAttributes<HTMLLinkElement> {
352
352
  type?: string | undefined;
353
353
  as?: string | undefined;
354
354
  media?: string | undefined;
355
- crossOrigin?: "anonymous" | "use-credentials" | undefined;
355
+ crossOrigin?: 'anonymous' | 'use-credentials' | undefined;
356
356
  integrity?: string | undefined;
357
357
  referrerPolicy?: string | undefined;
358
358
  }
@@ -368,7 +368,7 @@ interface ScriptAttributes extends PyreonHTMLAttributes<HTMLScriptElement> {
368
368
  type?: string | undefined;
369
369
  async?: boolean | undefined;
370
370
  defer?: boolean | undefined;
371
- crossOrigin?: "anonymous" | "use-credentials" | undefined;
371
+ crossOrigin?: 'anonymous' | 'use-credentials' | undefined;
372
372
  integrity?: string | undefined;
373
373
  noModule?: boolean | undefined;
374
374
  referrerPolicy?: string | undefined;
@@ -401,7 +401,7 @@ interface DialogAttributes extends PyreonHTMLAttributes<HTMLDialogElement> {
401
401
  interface OlAttributes extends PyreonHTMLAttributes<HTMLOListElement> {
402
402
  start?: number | undefined;
403
403
  reversed?: boolean | undefined;
404
- type?: "1" | "a" | "A" | "i" | "I" | undefined;
404
+ type?: '1' | 'a' | 'A' | 'i' | 'I' | undefined;
405
405
  }
406
406
  interface CanvasAttributes extends PyreonHTMLAttributes<HTMLCanvasElement> {
407
407
  width?: number | string | undefined;
@@ -412,12 +412,12 @@ interface SvgAttributes extends PyreonHTMLAttributes<SVGElement> {
412
412
  xmlns?: string | undefined;
413
413
  fill?: string | (() => string) | undefined;
414
414
  stroke?: string | (() => string) | undefined;
415
- "stroke-width"?: string | number | undefined;
416
- "stroke-linecap"?: "butt" | "round" | "square" | undefined;
417
- "stroke-linejoin"?: "miter" | "round" | "bevel" | undefined;
418
- "fill-rule"?: "nonzero" | "evenodd" | undefined;
419
- "clip-rule"?: "nonzero" | "evenodd" | undefined;
420
- "clip-path"?: string | undefined;
415
+ 'stroke-width'?: string | number | undefined;
416
+ 'stroke-linecap'?: 'butt' | 'round' | 'square' | undefined;
417
+ 'stroke-linejoin'?: 'miter' | 'round' | 'bevel' | undefined;
418
+ 'fill-rule'?: 'nonzero' | 'evenodd' | undefined;
419
+ 'clip-rule'?: 'nonzero' | 'evenodd' | undefined;
420
+ 'clip-path'?: string | undefined;
421
421
  d?: string | undefined;
422
422
  cx?: string | number | undefined;
423
423
  cy?: string | number | undefined;
@@ -435,33 +435,33 @@ interface SvgAttributes extends PyreonHTMLAttributes<SVGElement> {
435
435
  transform?: string | (() => string) | undefined;
436
436
  opacity?: string | number | (() => string | number) | undefined;
437
437
  points?: string | undefined;
438
- "font-size"?: string | number | undefined;
439
- "text-anchor"?: "start" | "middle" | "end" | undefined;
440
- "dominant-baseline"?: string | undefined;
441
- gradientUnits?: "userSpaceOnUse" | "objectBoundingBox" | undefined;
438
+ 'font-size'?: string | number | undefined;
439
+ 'text-anchor'?: 'start' | 'middle' | 'end' | undefined;
440
+ 'dominant-baseline'?: string | undefined;
441
+ gradientUnits?: 'userSpaceOnUse' | 'objectBoundingBox' | undefined;
442
442
  gradientTransform?: string | undefined;
443
- patternUnits?: "userSpaceOnUse" | "objectBoundingBox" | undefined;
444
- patternContentUnits?: "userSpaceOnUse" | "objectBoundingBox" | undefined;
443
+ patternUnits?: 'userSpaceOnUse' | 'objectBoundingBox' | undefined;
444
+ patternContentUnits?: 'userSpaceOnUse' | 'objectBoundingBox' | undefined;
445
445
  patternTransform?: string | undefined;
446
- spreadMethod?: "pad" | "reflect" | "repeat" | undefined;
446
+ spreadMethod?: 'pad' | 'reflect' | 'repeat' | undefined;
447
447
  markerWidth?: string | number | undefined;
448
448
  markerHeight?: string | number | undefined;
449
- markerUnits?: "strokeWidth" | "userSpaceOnUse" | undefined;
449
+ markerUnits?: 'strokeWidth' | 'userSpaceOnUse' | undefined;
450
450
  orient?: string | number | undefined;
451
451
  refX?: string | number | undefined;
452
452
  refY?: string | number | undefined;
453
- maskUnits?: "userSpaceOnUse" | "objectBoundingBox" | undefined;
454
- maskContentUnits?: "userSpaceOnUse" | "objectBoundingBox" | undefined;
455
- clipPathUnits?: "userSpaceOnUse" | "objectBoundingBox" | undefined;
456
- filterUnits?: "userSpaceOnUse" | "objectBoundingBox" | undefined;
457
- primitiveUnits?: "userSpaceOnUse" | "objectBoundingBox" | undefined;
453
+ maskUnits?: 'userSpaceOnUse' | 'objectBoundingBox' | undefined;
454
+ maskContentUnits?: 'userSpaceOnUse' | 'objectBoundingBox' | undefined;
455
+ clipPathUnits?: 'userSpaceOnUse' | 'objectBoundingBox' | undefined;
456
+ filterUnits?: 'userSpaceOnUse' | 'objectBoundingBox' | undefined;
457
+ primitiveUnits?: 'userSpaceOnUse' | 'objectBoundingBox' | undefined;
458
458
  preserveAspectRatio?: string | undefined;
459
- "color-interpolation"?: string | undefined;
460
- "color-interpolation-filters"?: string | undefined;
461
- "shape-rendering"?: string | undefined;
462
- "image-rendering"?: string | undefined;
463
- "text-rendering"?: string | undefined;
464
- "pointer-events"?: string | undefined;
459
+ 'color-interpolation'?: string | undefined;
460
+ 'color-interpolation-filters'?: string | undefined;
461
+ 'shape-rendering'?: string | undefined;
462
+ 'image-rendering'?: string | undefined;
463
+ 'text-rendering'?: string | undefined;
464
+ 'pointer-events'?: string | undefined;
465
465
  visibility?: string | undefined;
466
466
  display?: string | undefined;
467
467
  overflow?: string | undefined;
@@ -469,10 +469,10 @@ interface SvgAttributes extends PyreonHTMLAttributes<SVGElement> {
469
469
  dx?: string | number | undefined;
470
470
  dy?: string | number | undefined;
471
471
  textLength?: string | number | undefined;
472
- lengthAdjust?: "spacing" | "spacingAndGlyphs" | undefined;
473
- "writing-mode"?: string | undefined;
474
- "letter-spacing"?: string | number | undefined;
475
- "word-spacing"?: string | number | undefined;
472
+ lengthAdjust?: 'spacing' | 'spacingAndGlyphs' | undefined;
473
+ 'writing-mode'?: string | undefined;
474
+ 'letter-spacing'?: string | number | undefined;
475
+ 'word-spacing'?: string | number | undefined;
476
476
  pathLength?: number | undefined;
477
477
  href?: string | undefined;
478
478
  }
@@ -615,8 +615,8 @@ declare global {
615
615
  radialGradient: SvgAttributes;
616
616
  stop: SvgAttributes & {
617
617
  offset?: string | number;
618
- "stop-color"?: string;
619
- "stop-opacity"?: string | number;
618
+ 'stop-color'?: string;
619
+ 'stop-opacity'?: string | number;
620
620
  };
621
621
  details: DetailsAttributes;
622
622
  summary: PyreonHTMLAttributes;
package/package.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "name": "@pyreon/core",
3
- "version": "0.11.5",
3
+ "version": "0.11.6",
4
4
  "description": "Core component model and lifecycle for Pyreon",
5
+ "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/core#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/pyreon/pyreon/issues"
8
+ },
5
9
  "license": "MIT",
6
10
  "repository": {
7
11
  "type": "git",
8
12
  "url": "https://github.com/pyreon/pyreon.git",
9
13
  "directory": "packages/core/core"
10
14
  },
11
- "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/core#readme",
12
- "bugs": {
13
- "url": "https://github.com/pyreon/pyreon/issues"
14
- },
15
15
  "files": [
16
16
  "lib",
17
17
  "src",
18
18
  "README.md",
19
19
  "LICENSE"
20
20
  ],
21
- "sideEffects": false,
22
21
  "type": "module",
22
+ "sideEffects": false,
23
23
  "main": "./lib/index.js",
24
24
  "module": "./lib/index.js",
25
25
  "types": "./lib/types/index.d.ts",
@@ -40,18 +40,18 @@
40
40
  "types": "./lib/types/jsx-dev-runtime.d.ts"
41
41
  }
42
42
  },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
43
46
  "scripts": {
44
47
  "build": "vl_rolldown_build",
45
48
  "dev": "vl_rolldown_build-watch",
46
49
  "test": "vitest run",
47
50
  "typecheck": "tsc --noEmit",
48
- "lint": "biome check .",
51
+ "lint": "oxlint .",
49
52
  "prepublishOnly": "bun run build"
50
53
  },
51
54
  "dependencies": {
52
- "@pyreon/reactivity": "^0.11.5"
53
- },
54
- "publishConfig": {
55
- "access": "public"
55
+ "@pyreon/reactivity": "^0.11.6"
56
56
  }
57
57
  }
package/src/component.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { setCurrentHooks } from "./lifecycle"
2
- import type { ComponentFn, LifecycleHooks, Props, VNodeChild } from "./types"
1
+ import { setCurrentHooks } from './lifecycle'
2
+ import type { ComponentFn, LifecycleHooks, Props, VNodeChild } from './types'
3
3
 
4
4
  /**
5
5
  * Identity wrapper — marks a function as a Pyreon component and preserves its type.
package/src/context.ts CHANGED
@@ -5,15 +5,42 @@
5
5
  * The renderer maintains the context stack as it walks the VNode tree.
6
6
  */
7
7
 
8
- import { onUnmount } from "./lifecycle"
8
+ import { onUnmount } from './lifecycle'
9
9
 
10
10
  export interface Context<T> {
11
11
  readonly id: symbol
12
12
  readonly defaultValue: T
13
13
  }
14
14
 
15
+ /** Branded marker for reactive contexts — distinguishes from regular Context at type level. */
16
+ declare const REACTIVE_BRAND: unique symbol
17
+
18
+ /**
19
+ * A context whose value is a reactive accessor `() => T`.
20
+ *
21
+ * When you `useContext(reactiveCtx)`, TypeScript returns `() => T` —
22
+ * you MUST call the accessor to read the value. This prevents the
23
+ * destructuring trap that breaks reactivity with getter-based objects.
24
+ *
25
+ * @example
26
+ * const ModeCtx = createReactiveContext<'light' | 'dark'>('light')
27
+ * // Provider: provide(ModeCtx, () => modeSignal())
28
+ * // Consumer: const getMode = useContext(ModeCtx); getMode() // 'light'
29
+ */
30
+ export interface ReactiveContext<T> extends Context<() => T> {
31
+ readonly [REACTIVE_BRAND]: T
32
+ }
33
+
15
34
  export function createContext<T>(defaultValue: T): Context<T> {
16
- return { id: Symbol("PyreonContext"), defaultValue }
35
+ return { id: Symbol('PyreonContext'), defaultValue }
36
+ }
37
+
38
+ /**
39
+ * Create a reactive context. Consumers get `() => T` and must call it.
40
+ * This is the safe pattern for values that change over time (mode, locale, etc.).
41
+ */
42
+ export function createReactiveContext<T>(defaultValue: T): ReactiveContext<T> {
43
+ return createContext<() => T>(() => defaultValue) as ReactiveContext<T>
17
44
  }
18
45
 
19
46
  // ─── Runtime context stack (managed by the renderer) ─────────────────────────
@@ -37,7 +64,7 @@ function getStack(): Map<symbol, unknown>[] {
37
64
  return _stackProvider()
38
65
  }
39
66
 
40
- const __DEV__ = typeof process !== "undefined" && process.env.NODE_ENV !== "production"
67
+ const __DEV__ = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'
41
68
 
42
69
  export function pushContext(values: Map<symbol, unknown>) {
43
70
  getStack().push(values)
@@ -48,7 +75,7 @@ export function popContext() {
48
75
  if (__DEV__ && stack.length === 0) {
49
76
  // biome-ignore lint/suspicious/noConsole: dev-only warning
50
77
  console.warn(
51
- "[Pyreon] popContext() called on an empty context stack. This likely indicates a missing Provider.",
78
+ '[Pyreon] popContext() called on an empty context stack. This likely indicates a missing Provider.',
52
79
  )
53
80
  return
54
81
  }
@@ -58,7 +85,12 @@ export function popContext() {
58
85
  /**
59
86
  * Read the nearest provided value for a context.
60
87
  * Falls back to `context.defaultValue` if none found.
88
+ *
89
+ * For ReactiveContext<T>, returns `() => T` — you MUST call the accessor.
90
+ * For regular Context<T>, returns `T` directly.
61
91
  */
92
+ export function useContext<T>(context: ReactiveContext<T>): () => T
93
+ export function useContext<T>(context: Context<T>): T
62
94
  export function useContext<T>(context: Context<T>): T {
63
95
  const stack = getStack()
64
96
  for (let i = stack.length - 1; i >= 0; i--) {
@@ -99,3 +131,42 @@ export function withContext<T>(context: Context<T>, value: T, fn: () => void) {
99
131
  popContext()
100
132
  }
101
133
  }
134
+
135
+ // ─── Context snapshot for deferred mounting ─────────────────────────────────
136
+
137
+ export type ContextSnapshot = Map<symbol, unknown>[]
138
+
139
+ /**
140
+ * Capture a snapshot of the current context stack.
141
+ *
142
+ * Used by `mountReactive` to preserve the context that was active when a
143
+ * reactive boundary (e.g. `<Show>`, `<For>`) was set up. When the boundary
144
+ * later mounts new children inside an effect, the snapshot is restored so
145
+ * those children can see ancestor providers via `useContext()`.
146
+ */
147
+ export function captureContextStack(): ContextSnapshot {
148
+ // Shallow copy — each frame (Map) is shared by reference, which is
149
+ // correct because providers don't mutate frames after creation.
150
+ return [...getStack()]
151
+ }
152
+
153
+ /**
154
+ * Execute `fn()` with a previously captured context stack active.
155
+ * Restores the original stack after `fn()` completes (even on throw).
156
+ */
157
+ export function restoreContextStack<T>(snapshot: ContextSnapshot, fn: () => T): T {
158
+ const stack = getStack()
159
+ const savedLength = stack.length
160
+
161
+ // Push all captured frames onto the current stack
162
+ for (const frame of snapshot) {
163
+ stack.push(frame)
164
+ }
165
+
166
+ try {
167
+ return fn()
168
+ } finally {
169
+ // Remove only the frames we pushed (preserve anything added by fn)
170
+ stack.length = savedLength
171
+ }
172
+ }
package/src/dynamic.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { h } from "./h"
2
- import type { ComponentFn, Props, VNode } from "./types"
1
+ import { h } from './h'
2
+ import type { ComponentFn, Props, VNode } from './types'
3
3
 
4
- const __DEV__ = typeof process !== "undefined" && process.env.NODE_ENV !== "production"
4
+ const __DEV__ = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'
5
5
 
6
6
  export interface DynamicProps extends Props {
7
7
  component: ComponentFn | string
@@ -11,7 +11,7 @@ export function Dynamic(props: DynamicProps): VNode | null {
11
11
  const { component, ...rest } = props
12
12
  if (__DEV__ && !component) {
13
13
  // biome-ignore lint/suspicious/noConsole: dev-only warning
14
- console.warn("[Pyreon] <Dynamic> received a falsy `component` prop. Nothing will be rendered.")
14
+ console.warn('[Pyreon] <Dynamic> received a falsy `component` prop. Nothing will be rendered.')
15
15
  }
16
16
  if (!component) return null
17
17
  return h(component as string | ComponentFn, rest as Props)
@@ -1,10 +1,10 @@
1
- import { signal } from "@pyreon/reactivity"
2
- import { popErrorBoundary, pushErrorBoundary } from "./component"
3
- import { onUnmount } from "./lifecycle"
4
- import { reportError } from "./telemetry"
5
- import type { VNodeChild, VNodeChildAtom } from "./types"
1
+ import { signal } from '@pyreon/reactivity'
2
+ import { popErrorBoundary, pushErrorBoundary } from './component'
3
+ import { onUnmount } from './lifecycle'
4
+ import { reportError } from './telemetry'
5
+ import type { VNodeChild, VNodeChildAtom } from './types'
6
6
 
7
- const __DEV__ = typeof process !== "undefined" && process.env.NODE_ENV !== "production"
7
+ const __DEV__ = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'
8
8
 
9
9
  /**
10
10
  * ErrorBoundary — catches errors thrown by child components and renders a
@@ -37,10 +37,10 @@ export function ErrorBoundary(props: {
37
37
  fallback: (err: unknown, reset: () => void) => VNodeChild
38
38
  children?: VNodeChild
39
39
  }): VNodeChild {
40
- if (__DEV__ && typeof props.fallback !== "function") {
40
+ if (__DEV__ && typeof props.fallback !== 'function') {
41
41
  // biome-ignore lint/suspicious/noConsole: dev-only warning
42
42
  console.warn(
43
- "[Pyreon] <ErrorBoundary> expects `fallback` to be a function: (err, reset) => VNode. " +
43
+ '[Pyreon] <ErrorBoundary> expects `fallback` to be a function: (err, reset) => VNode. ' +
44
44
  `Received ${typeof props.fallback}.`,
45
45
  )
46
46
  }
@@ -51,7 +51,7 @@ export function ErrorBoundary(props: {
51
51
  const handler = (err: unknown): boolean => {
52
52
  if (error.peek() !== null) return false // already in error state — let outer boundary catch it
53
53
  error.set(err)
54
- reportError({ component: "ErrorBoundary", phase: "render", error: err, timestamp: Date.now() })
54
+ reportError({ component: 'ErrorBoundary', phase: 'render', error: err, timestamp: Date.now() })
55
55
  return true
56
56
  }
57
57
 
@@ -63,6 +63,6 @@ export function ErrorBoundary(props: {
63
63
  const err = error()
64
64
  if (err != null) return props.fallback(err, reset) as VNodeChildAtom
65
65
  const ch = props.children
66
- return (typeof ch === "function" ? ch() : ch) as VNodeChildAtom
66
+ return (typeof ch === 'function' ? ch() : ch) as VNodeChildAtom
67
67
  }
68
68
  }
package/src/for.ts CHANGED
@@ -1,15 +1,21 @@
1
- import type { NativeItem, Props, VNode } from "./types"
1
+ import type { NativeItem, Props, VNode } from './types'
2
2
 
3
3
  /**
4
4
  * Symbol used as the VNode type for a For list — runtime-dom handles it
5
5
  * via mountFor, bypassing the generic VNode reconciler.
6
6
  */
7
- export const ForSymbol: unique symbol = Symbol("pyreon.For")
7
+ export const ForSymbol: unique symbol = Symbol('pyreon.For')
8
8
 
9
9
  export interface ForProps<T> {
10
10
  each: () => T[]
11
+ /** Keying function — use `by` not `key` (JSX extracts `key` for VNode reconciliation). */
11
12
  by: (item: T) => string | number
12
13
  children: (item: T) => VNode | NativeItem
14
+ /**
15
+ * @deprecated Use `by` instead of `key`. In Pyreon, `<For>` uses `by` for keying.
16
+ * JSX reserves `key` for VNode reconciliation — it won't reach the component.
17
+ */
18
+ key?: never
13
19
  }
14
20
 
15
21
  /**
package/src/h.ts CHANGED
@@ -1,7 +1,7 @@
1
- import type { ComponentFn, Props, VNode, VNodeChild } from "./types"
1
+ import type { ComponentFn, Props, VNode, VNodeChild } from './types'
2
2
 
3
3
  /** Marker for fragment nodes — renders children without a wrapper element */
4
- export const Fragment: unique symbol = Symbol("Pyreon.Fragment")
4
+ export const Fragment: unique symbol = Symbol('Pyreon.Fragment')
5
5
 
6
6
  /**
7
7
  * Hyperscript function — the compiled output of JSX.
@@ -14,8 +14,8 @@ export const Fragment: unique symbol = Symbol("Pyreon.Fragment")
14
14
  export const EMPTY_PROPS: Props = {} as Props
15
15
 
16
16
  /** Makes `children` optional in P (if present) so it can be passed as rest args to h(). */
17
- type PropsWithOptionalChildren<P extends Props> = Omit<P, "children"> &
18
- ("children" extends keyof P ? { children?: P["children"] } : unknown)
17
+ type PropsWithOptionalChildren<P extends Props> = Omit<P, 'children'> &
18
+ ('children' extends keyof P ? { children?: P['children'] } : unknown)
19
19
 
20
20
  // Overload: component with typed props — children is optional in the props object
21
21
  // because it can be passed as rest args. Extra keys are allowed via `& Props`.