sygnal 5.1.0 → 5.1.2

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 (37) hide show
  1. package/README.md +23 -2
  2. package/dist/astro/client.cjs.js +29 -4
  3. package/dist/astro/client.mjs +29 -4
  4. package/dist/astro/cycle/dom/DocumentDOMSource.d.ts +7 -5
  5. package/dist/astro/cycle/dom/MainDOMSource.d.ts +2 -1
  6. package/dist/astro/cycle/dom/enrichEventStream.d.ts +24 -0
  7. package/dist/astro/server.cjs.js +6 -0
  8. package/dist/astro/server.mjs +6 -0
  9. package/dist/cycle/dom/DocumentDOMSource.d.ts +7 -5
  10. package/dist/cycle/dom/MainDOMSource.d.ts +2 -1
  11. package/dist/cycle/dom/enrichEventStream.d.ts +24 -0
  12. package/dist/index.cjs.js +36 -5
  13. package/dist/index.esm.js +36 -5
  14. package/dist/jsx-dev-runtime.cjs.js +17 -2
  15. package/dist/jsx-dev-runtime.esm.js +17 -2
  16. package/dist/jsx-runtime.cjs.js +17 -2
  17. package/dist/jsx-runtime.esm.js +17 -2
  18. package/dist/jsx.cjs.js +1 -1
  19. package/dist/jsx.esm.js +1 -1
  20. package/dist/sygnal.min.js +1 -1
  21. package/dist/vike/+config.cjs.js +58 -0
  22. package/dist/vike/+config.js +56 -0
  23. package/dist/vike/onRenderClient.cjs.js +66 -0
  24. package/dist/vike/onRenderClient.mjs +64 -0
  25. package/dist/vike/onRenderHtml.cjs.js +158 -0
  26. package/dist/vike/onRenderHtml.mjs +156 -0
  27. package/package.json +24 -1
  28. package/src/component.ts +14 -3
  29. package/src/cycle/dom/DocumentDOMSource.ts +5 -5
  30. package/src/cycle/dom/MainDOMSource.ts +5 -5
  31. package/src/cycle/dom/snabbdom.ts +5 -1
  32. package/src/extra/ssr.ts +7 -0
  33. package/src/pragma/index.ts +1 -1
  34. package/src/vike/+config.ts +58 -0
  35. package/src/vike/onRenderClient.ts +84 -0
  36. package/src/vike/onRenderHtml.ts +175 -0
  37. package/src/vike/types.ts +29 -0
package/README.md CHANGED
@@ -21,12 +21,13 @@ A reactive component framework with pure functions, zero side effects, and autom
21
21
  **Scaffold a new project:**
22
22
 
23
23
  ```bash
24
- npx degit tpresley/sygnal-template my-app
24
+ npm create sygnal-app my-app
25
25
  cd my-app
26
- npm install
27
26
  npm run dev
28
27
  ```
29
28
 
29
+ Choose from Vite (SPA), Vike (SSR), or Astro templates during setup.
30
+
30
31
  **Or add to an existing project:**
31
32
 
32
33
  ```bash
@@ -386,6 +387,25 @@ import Counter from '../components/Counter.jsx'
386
387
  <Counter client:load />
387
388
  ```
388
389
 
390
+ ### Vike Integration
391
+
392
+ File-based routing with SSR, client-side navigation, and automatic hydration:
393
+
394
+ ```javascript
395
+ // vite.config.js
396
+ import sygnal from 'sygnal/vite'
397
+ import vike from 'vike/plugin'
398
+ export default defineConfig({ plugins: [sygnal({ disableHmr: true }), vike()] })
399
+ ```
400
+
401
+ ```javascript
402
+ // pages/+config.js
403
+ import vikeSygnal from 'sygnal/config'
404
+ export default { extends: [vikeSygnal] }
405
+ ```
406
+
407
+ Pages are standard Sygnal components in `pages/*/+Page.jsx`. Supports layouts, data fetching, and SPA mode.
408
+
389
409
  ### TypeScript
390
410
 
391
411
  Full type definitions included:
@@ -443,6 +463,7 @@ h('div', [h('h1', 'Hello'), h('button.btn', 'Click')])
443
463
  |---------|-------------|
444
464
  | [Getting Started](./examples/getting-started) | Interactive guide with live demos (Astro) |
445
465
  | [Kanban Board](./examples/kanban) | Drag-and-drop with Collections and cross-component communication |
466
+ | [Vike SSR](./examples/vike) | File-based routing with SSR, layouts, and data fetching |
446
467
  | [Advanced Features](./examples/advanced-feature-tests) | Portals, slots, disposal, suspense, lazy loading |
447
468
  | [TypeScript 2048](./examples/ts-example-2048) | Full game in TypeScript |
448
469
  | [AI Discussion Panel](./examples/ai-panel-spa) | Complex SPA with custom drivers |
@@ -4,7 +4,7 @@ var h = require('snabbdom/build/h');
4
4
  var init = require('snabbdom/build/init');
5
5
  var tovnode = require('snabbdom/build/tovnode');
6
6
  var vnode = require('snabbdom/build/vnode');
7
- require('snabbdom/build/jsx');
7
+ var jsx = require('snabbdom/build/jsx');
8
8
  var _class = require('snabbdom/build/modules/class');
9
9
  var props = require('snabbdom/build/modules/props');
10
10
  var attributes = require('snabbdom/build/modules/attributes');
@@ -3570,6 +3570,20 @@ function withState(main, name = 'state') {
3570
3570
  };
3571
3571
  }
3572
3572
 
3573
+ /**
3574
+ * Local snabbdom re-export barrel.
3575
+ *
3576
+ * Imports from specific snabbdom subpaths instead of the main barrel to avoid
3577
+ * triggering the broken `styleModule` top-level `window` reference in
3578
+ * snabbdom 3.6.3 (which causes ReferenceError in Node.js environments).
3579
+ *
3580
+ * The styleModule is provided separately by ./styleModule.ts with a fixed
3581
+ * window guard.
3582
+ */
3583
+ // Core
3584
+ // Tag Fragment so we can identify it even after minification mangles Function.name
3585
+ jsx.Fragment.__sygnalFragment = true;
3586
+
3573
3587
  function fromEvent(element, eventName, useCapture = false, preventDefault = false, passive = false) {
3574
3588
  let next = null;
3575
3589
  return Stream_1.create({
@@ -5448,7 +5462,18 @@ function wrapDOMSource(domSource) {
5448
5462
  }
5449
5463
  });
5450
5464
  }
5451
- const ABORT = Symbol('ABORT');
5465
+ const ABORT = Symbol.for('sygnal.ABORT');
5466
+ /**
5467
+ * Check if a value is the ABORT sentinel.
5468
+ * Uses Symbol.for() identity first, then falls back to description check
5469
+ * in case bundlers (e.g. Vite) create duplicate module instances with
5470
+ * separate Symbol.for() registries.
5471
+ */
5472
+ function isAbort(value) {
5473
+ if (value === ABORT)
5474
+ return true;
5475
+ return typeof value === 'symbol' && value.description === 'sygnal.ABORT';
5476
+ }
5452
5477
  function normalizeCalculatedEntry(field, entry) {
5453
5478
  if (typeof entry === 'function') {
5454
5479
  return { fn: entry, deps: null };
@@ -6170,7 +6195,7 @@ class Component {
6170
6195
  const enhancedState = this.addCalculated(_state);
6171
6196
  props.state = enhancedState;
6172
6197
  const newState = reducer(enhancedState, data, next, props);
6173
- if (newState === ABORT)
6198
+ if (isAbort(newState))
6174
6199
  return _state;
6175
6200
  return this.cleanupCalculated(newState);
6176
6201
  }
@@ -6199,7 +6224,7 @@ class Component {
6199
6224
  return ABORT;
6200
6225
  }
6201
6226
  }
6202
- }).filter((result) => result !== ABORT);
6227
+ }).filter((result) => !isAbort(result));
6203
6228
  }
6204
6229
  else if (reducer === undefined || reducer === true) {
6205
6230
  returnStream$ = filtered$.map(({ data }) => data);
@@ -2,7 +2,7 @@ import { h } from 'snabbdom/build/h';
2
2
  import { init } from 'snabbdom/build/init';
3
3
  import { toVNode } from 'snabbdom/build/tovnode';
4
4
  import { vnode } from 'snabbdom/build/vnode';
5
- import 'snabbdom/build/jsx';
5
+ import { Fragment } from 'snabbdom/build/jsx';
6
6
  import { classModule } from 'snabbdom/build/modules/class';
7
7
  import { propsModule } from 'snabbdom/build/modules/props';
8
8
  import { attributesModule } from 'snabbdom/build/modules/attributes';
@@ -3568,6 +3568,20 @@ function withState(main, name = 'state') {
3568
3568
  };
3569
3569
  }
3570
3570
 
3571
+ /**
3572
+ * Local snabbdom re-export barrel.
3573
+ *
3574
+ * Imports from specific snabbdom subpaths instead of the main barrel to avoid
3575
+ * triggering the broken `styleModule` top-level `window` reference in
3576
+ * snabbdom 3.6.3 (which causes ReferenceError in Node.js environments).
3577
+ *
3578
+ * The styleModule is provided separately by ./styleModule.ts with a fixed
3579
+ * window guard.
3580
+ */
3581
+ // Core
3582
+ // Tag Fragment so we can identify it even after minification mangles Function.name
3583
+ Fragment.__sygnalFragment = true;
3584
+
3571
3585
  function fromEvent(element, eventName, useCapture = false, preventDefault = false, passive = false) {
3572
3586
  let next = null;
3573
3587
  return Stream_1.create({
@@ -5446,7 +5460,18 @@ function wrapDOMSource(domSource) {
5446
5460
  }
5447
5461
  });
5448
5462
  }
5449
- const ABORT = Symbol('ABORT');
5463
+ const ABORT = Symbol.for('sygnal.ABORT');
5464
+ /**
5465
+ * Check if a value is the ABORT sentinel.
5466
+ * Uses Symbol.for() identity first, then falls back to description check
5467
+ * in case bundlers (e.g. Vite) create duplicate module instances with
5468
+ * separate Symbol.for() registries.
5469
+ */
5470
+ function isAbort(value) {
5471
+ if (value === ABORT)
5472
+ return true;
5473
+ return typeof value === 'symbol' && value.description === 'sygnal.ABORT';
5474
+ }
5450
5475
  function normalizeCalculatedEntry(field, entry) {
5451
5476
  if (typeof entry === 'function') {
5452
5477
  return { fn: entry, deps: null };
@@ -6168,7 +6193,7 @@ class Component {
6168
6193
  const enhancedState = this.addCalculated(_state);
6169
6194
  props.state = enhancedState;
6170
6195
  const newState = reducer(enhancedState, data, next, props);
6171
- if (newState === ABORT)
6196
+ if (isAbort(newState))
6172
6197
  return _state;
6173
6198
  return this.cleanupCalculated(newState);
6174
6199
  }
@@ -6197,7 +6222,7 @@ class Component {
6197
6222
  return ABORT;
6198
6223
  }
6199
6224
  }
6200
- }).filter((result) => result !== ABORT);
6225
+ }).filter((result) => !isAbort(result));
6201
6226
  }
6202
6227
  else if (reducer === undefined || reducer === true) {
6203
6228
  returnStream$ = filtered$.map(({ data }) => data);
@@ -1,10 +1,12 @@
1
- import { Stream, MemoryStream } from 'xstream';
1
+ import { MemoryStream } from 'xstream';
2
2
  import { EventsFnOptions } from './DOMSource';
3
+ import { EnrichedEventStream } from './enrichEventStream';
3
4
  export declare class DocumentDOMSource {
4
5
  private _name;
5
- constructor(_name: string);
6
+ private _selector;
7
+ constructor(_name: string, selector?: string);
6
8
  select(selector: string): DocumentDOMSource;
7
- elements(): MemoryStream<Array<Document>>;
8
- element(): MemoryStream<Document>;
9
- events<K extends keyof DocumentEventMap>(eventType: K, options?: EventsFnOptions, bubbles?: boolean): Stream<DocumentEventMap[K]>;
9
+ elements(): MemoryStream<Array<Document | Element>>;
10
+ element(): MemoryStream<Document | Element | null>;
11
+ events<K extends keyof DocumentEventMap>(eventType: K, options?: EventsFnOptions, bubbles?: boolean): EnrichedEventStream<DocumentEventMap[K]>;
10
12
  }
@@ -3,6 +3,7 @@ import { EventsFnOptions } from './DOMSource';
3
3
  import { DocumentDOMSource } from './DocumentDOMSource';
4
4
  import { BodyDOMSource } from './BodyDOMSource';
5
5
  import { VNode } from './snabbdom';
6
+ import { EnrichedEventStream } from './enrichEventStream';
6
7
  import { Scope, IsolateSink } from './isolate';
7
8
  import { IsolateModule } from './IsolateModule';
8
9
  import { EventDelegator } from './EventDelegator';
@@ -24,7 +25,7 @@ export declare class MainDOMSource {
24
25
  get namespace(): Array<Scope>;
25
26
  select<T extends keyof SpecialSelector>(selector: T): SpecialSelector[T];
26
27
  select(selector: string): MainDOMSource;
27
- events<K extends keyof HTMLElementEventMap>(eventType: K, options?: EventsFnOptions, bubbles?: boolean): Stream<HTMLElementEventMap[K]>;
28
+ events<K extends keyof HTMLElementEventMap>(eventType: K, options?: EventsFnOptions, bubbles?: boolean): EnrichedEventStream<HTMLElementEventMap[K]>;
28
29
  dispose(): void;
29
30
  isolateSource: (source: MainDOMSource, scope: string) => MainDOMSource;
30
31
  isolateSink: IsolateSink<VNode>;
@@ -0,0 +1,24 @@
1
+ import { Stream } from 'xstream';
2
+ export interface EnrichedEventStream<T = Event> extends Stream<T> {
3
+ value(): EnrichedEventStream<string>;
4
+ value<R>(fn: (val: string) => R): EnrichedEventStream<R>;
5
+ checked(): EnrichedEventStream<boolean>;
6
+ checked<R>(fn: (val: boolean) => R): EnrichedEventStream<R>;
7
+ data(name: string): EnrichedEventStream<string | undefined>;
8
+ data<R>(name: string, fn: (val: string | undefined) => R): EnrichedEventStream<R>;
9
+ target(): EnrichedEventStream<EventTarget | null>;
10
+ target<R>(fn: (el: EventTarget | null) => R): EnrichedEventStream<R>;
11
+ key(): EnrichedEventStream<string>;
12
+ key<R>(fn: (key: string) => R): EnrichedEventStream<R>;
13
+ }
14
+ /**
15
+ * Adds chainable convenience methods to a DOM event stream.
16
+ *
17
+ * DOM.select('.input').events('input').value()
18
+ * DOM.input('.input').value()
19
+ * DOM.select('.item').events('click').data('id')
20
+ * DOM.click('.item').data('id', Number)
21
+ * DOM.change('.checkbox').checked()
22
+ * DOM.keydown('.field').key()
23
+ */
24
+ export declare function enrichEventStream(stream$: any): any;
@@ -514,6 +514,12 @@ function vnodeToHtml(vnode) {
514
514
  if (VOID_ELEMENTS.has(tag)) {
515
515
  return html;
516
516
  }
517
+ // If innerHTML is set via props, use it as raw content (no escaping)
518
+ if (vnode.data?.props?.innerHTML != null) {
519
+ html += String(vnode.data.props.innerHTML);
520
+ html += `</${tag}>`;
521
+ return html;
522
+ }
517
523
  // Children — snabbdom uses `text` for single text children (even when
518
524
  // `children` holds a text element object). Prioritize `text` when set.
519
525
  if (vnode.text != null) {
@@ -510,6 +510,12 @@ function vnodeToHtml(vnode) {
510
510
  if (VOID_ELEMENTS.has(tag)) {
511
511
  return html;
512
512
  }
513
+ // If innerHTML is set via props, use it as raw content (no escaping)
514
+ if (vnode.data?.props?.innerHTML != null) {
515
+ html += String(vnode.data.props.innerHTML);
516
+ html += `</${tag}>`;
517
+ return html;
518
+ }
513
519
  // Children — snabbdom uses `text` for single text children (even when
514
520
  // `children` holds a text element object). Prioritize `text` when set.
515
521
  if (vnode.text != null) {
@@ -1,10 +1,12 @@
1
- import { Stream, MemoryStream } from 'xstream';
1
+ import { MemoryStream } from 'xstream';
2
2
  import { EventsFnOptions } from './DOMSource';
3
+ import { EnrichedEventStream } from './enrichEventStream';
3
4
  export declare class DocumentDOMSource {
4
5
  private _name;
5
- constructor(_name: string);
6
+ private _selector;
7
+ constructor(_name: string, selector?: string);
6
8
  select(selector: string): DocumentDOMSource;
7
- elements(): MemoryStream<Array<Document>>;
8
- element(): MemoryStream<Document>;
9
- events<K extends keyof DocumentEventMap>(eventType: K, options?: EventsFnOptions, bubbles?: boolean): Stream<DocumentEventMap[K]>;
9
+ elements(): MemoryStream<Array<Document | Element>>;
10
+ element(): MemoryStream<Document | Element | null>;
11
+ events<K extends keyof DocumentEventMap>(eventType: K, options?: EventsFnOptions, bubbles?: boolean): EnrichedEventStream<DocumentEventMap[K]>;
10
12
  }
@@ -3,6 +3,7 @@ import { EventsFnOptions } from './DOMSource';
3
3
  import { DocumentDOMSource } from './DocumentDOMSource';
4
4
  import { BodyDOMSource } from './BodyDOMSource';
5
5
  import { VNode } from './snabbdom';
6
+ import { EnrichedEventStream } from './enrichEventStream';
6
7
  import { Scope, IsolateSink } from './isolate';
7
8
  import { IsolateModule } from './IsolateModule';
8
9
  import { EventDelegator } from './EventDelegator';
@@ -24,7 +25,7 @@ export declare class MainDOMSource {
24
25
  get namespace(): Array<Scope>;
25
26
  select<T extends keyof SpecialSelector>(selector: T): SpecialSelector[T];
26
27
  select(selector: string): MainDOMSource;
27
- events<K extends keyof HTMLElementEventMap>(eventType: K, options?: EventsFnOptions, bubbles?: boolean): Stream<HTMLElementEventMap[K]>;
28
+ events<K extends keyof HTMLElementEventMap>(eventType: K, options?: EventsFnOptions, bubbles?: boolean): EnrichedEventStream<HTMLElementEventMap[K]>;
28
29
  dispose(): void;
29
30
  isolateSource: (source: MainDOMSource, scope: string) => MainDOMSource;
30
31
  isolateSink: IsolateSink<VNode>;
@@ -0,0 +1,24 @@
1
+ import { Stream } from 'xstream';
2
+ export interface EnrichedEventStream<T = Event> extends Stream<T> {
3
+ value(): EnrichedEventStream<string>;
4
+ value<R>(fn: (val: string) => R): EnrichedEventStream<R>;
5
+ checked(): EnrichedEventStream<boolean>;
6
+ checked<R>(fn: (val: boolean) => R): EnrichedEventStream<R>;
7
+ data(name: string): EnrichedEventStream<string | undefined>;
8
+ data<R>(name: string, fn: (val: string | undefined) => R): EnrichedEventStream<R>;
9
+ target(): EnrichedEventStream<EventTarget | null>;
10
+ target<R>(fn: (el: EventTarget | null) => R): EnrichedEventStream<R>;
11
+ key(): EnrichedEventStream<string>;
12
+ key<R>(fn: (key: string) => R): EnrichedEventStream<R>;
13
+ }
14
+ /**
15
+ * Adds chainable convenience methods to a DOM event stream.
16
+ *
17
+ * DOM.select('.input').events('input').value()
18
+ * DOM.input('.input').value()
19
+ * DOM.select('.item').events('click').data('id')
20
+ * DOM.click('.item').data('id', Number)
21
+ * DOM.change('.checkbox').checked()
22
+ * DOM.keydown('.field').key()
23
+ */
24
+ export declare function enrichEventStream(stream$: any): any;
package/dist/index.cjs.js CHANGED
@@ -7,7 +7,7 @@ var h = require('snabbdom/build/h');
7
7
  var init$1 = require('snabbdom/build/init');
8
8
  var tovnode = require('snabbdom/build/tovnode');
9
9
  var vnode$1 = require('snabbdom/build/vnode');
10
- require('snabbdom/build/jsx');
10
+ var jsx = require('snabbdom/build/jsx');
11
11
  var _class = require('snabbdom/build/modules/class');
12
12
  var props = require('snabbdom/build/modules/props');
13
13
  var attributes = require('snabbdom/build/modules/attributes');
@@ -624,6 +624,20 @@ function withState(main, name = 'state') {
624
624
  };
625
625
  }
626
626
 
627
+ /**
628
+ * Local snabbdom re-export barrel.
629
+ *
630
+ * Imports from specific snabbdom subpaths instead of the main barrel to avoid
631
+ * triggering the broken `styleModule` top-level `window` reference in
632
+ * snabbdom 3.6.3 (which causes ReferenceError in Node.js environments).
633
+ *
634
+ * The styleModule is provided separately by ./styleModule.ts with a fixed
635
+ * window guard.
636
+ */
637
+ // Core
638
+ // Tag Fragment so we can identify it even after minification mangles Function.name
639
+ jsx.Fragment.__sygnalFragment = true;
640
+
627
641
  function copyToThunk(vnode, thunkVNode) {
628
642
  thunkVNode.elm = vnode.elm;
629
643
  vnode.data.fn = thunkVNode.data.fn;
@@ -2191,7 +2205,18 @@ function wrapDOMSource(domSource) {
2191
2205
  }
2192
2206
  });
2193
2207
  }
2194
- const ABORT = Symbol('ABORT');
2208
+ const ABORT = Symbol.for('sygnal.ABORT');
2209
+ /**
2210
+ * Check if a value is the ABORT sentinel.
2211
+ * Uses Symbol.for() identity first, then falls back to description check
2212
+ * in case bundlers (e.g. Vite) create duplicate module instances with
2213
+ * separate Symbol.for() registries.
2214
+ */
2215
+ function isAbort(value) {
2216
+ if (value === ABORT)
2217
+ return true;
2218
+ return typeof value === 'symbol' && value.description === 'sygnal.ABORT';
2219
+ }
2195
2220
  function normalizeCalculatedEntry(field, entry) {
2196
2221
  if (typeof entry === 'function') {
2197
2222
  return { fn: entry, deps: null };
@@ -2913,7 +2938,7 @@ class Component {
2913
2938
  const enhancedState = this.addCalculated(_state);
2914
2939
  props.state = enhancedState;
2915
2940
  const newState = reducer(enhancedState, data, next, props);
2916
- if (newState === ABORT)
2941
+ if (isAbort(newState))
2917
2942
  return _state;
2918
2943
  return this.cleanupCalculated(newState);
2919
2944
  }
@@ -2942,7 +2967,7 @@ class Component {
2942
2967
  return ABORT;
2943
2968
  }
2944
2969
  }
2945
- }).filter((result) => result !== ABORT);
2970
+ }).filter((result) => !isAbort(result));
2946
2971
  }
2947
2972
  else if (reducer === undefined || reducer === true) {
2948
2973
  returnStream$ = filtered$.map(({ data }) => data);
@@ -5536,7 +5561,7 @@ const createElementWithModules = (modules) => {
5536
5561
  console.error('JSX Error: Capitalized HTML element without corresponding factory function. Components with names where the first letter is capital MUST be defined or included at the parent component\'s file scope.');
5537
5562
  }
5538
5563
  if (fun(sel)) {
5539
- if (sel.name === 'Fragment') {
5564
+ if (sel.__sygnalFragment || sel.name === 'Fragment') {
5540
5565
  return sel(data || {}, children);
5541
5566
  }
5542
5567
  data || (data = {});
@@ -6312,6 +6337,12 @@ function vnodeToHtml(vnode) {
6312
6337
  if (VOID_ELEMENTS.has(tag)) {
6313
6338
  return html;
6314
6339
  }
6340
+ // If innerHTML is set via props, use it as raw content (no escaping)
6341
+ if (vnode.data?.props?.innerHTML != null) {
6342
+ html += String(vnode.data.props.innerHTML);
6343
+ html += `</${tag}>`;
6344
+ return html;
6345
+ }
6315
6346
  // Children — snabbdom uses `text` for single text children (even when
6316
6347
  // `children` holds a text element object). Prioritize `text` when set.
6317
6348
  if (vnode.text != null) {
package/dist/index.esm.js CHANGED
@@ -9,7 +9,7 @@ export { h } from 'snabbdom/build/h';
9
9
  import { init as init$1 } from 'snabbdom/build/init';
10
10
  import { toVNode } from 'snabbdom/build/tovnode';
11
11
  import { vnode as vnode$1 } from 'snabbdom/build/vnode';
12
- import 'snabbdom/build/jsx';
12
+ import { Fragment } from 'snabbdom/build/jsx';
13
13
  import { classModule } from 'snabbdom/build/modules/class';
14
14
  import { propsModule } from 'snabbdom/build/modules/props';
15
15
  import { attributesModule } from 'snabbdom/build/modules/attributes';
@@ -603,6 +603,20 @@ function withState(main, name = 'state') {
603
603
  };
604
604
  }
605
605
 
606
+ /**
607
+ * Local snabbdom re-export barrel.
608
+ *
609
+ * Imports from specific snabbdom subpaths instead of the main barrel to avoid
610
+ * triggering the broken `styleModule` top-level `window` reference in
611
+ * snabbdom 3.6.3 (which causes ReferenceError in Node.js environments).
612
+ *
613
+ * The styleModule is provided separately by ./styleModule.ts with a fixed
614
+ * window guard.
615
+ */
616
+ // Core
617
+ // Tag Fragment so we can identify it even after minification mangles Function.name
618
+ Fragment.__sygnalFragment = true;
619
+
606
620
  function copyToThunk(vnode, thunkVNode) {
607
621
  thunkVNode.elm = vnode.elm;
608
622
  vnode.data.fn = thunkVNode.data.fn;
@@ -2170,7 +2184,18 @@ function wrapDOMSource(domSource) {
2170
2184
  }
2171
2185
  });
2172
2186
  }
2173
- const ABORT = Symbol('ABORT');
2187
+ const ABORT = Symbol.for('sygnal.ABORT');
2188
+ /**
2189
+ * Check if a value is the ABORT sentinel.
2190
+ * Uses Symbol.for() identity first, then falls back to description check
2191
+ * in case bundlers (e.g. Vite) create duplicate module instances with
2192
+ * separate Symbol.for() registries.
2193
+ */
2194
+ function isAbort(value) {
2195
+ if (value === ABORT)
2196
+ return true;
2197
+ return typeof value === 'symbol' && value.description === 'sygnal.ABORT';
2198
+ }
2174
2199
  function normalizeCalculatedEntry(field, entry) {
2175
2200
  if (typeof entry === 'function') {
2176
2201
  return { fn: entry, deps: null };
@@ -2892,7 +2917,7 @@ class Component {
2892
2917
  const enhancedState = this.addCalculated(_state);
2893
2918
  props.state = enhancedState;
2894
2919
  const newState = reducer(enhancedState, data, next, props);
2895
- if (newState === ABORT)
2920
+ if (isAbort(newState))
2896
2921
  return _state;
2897
2922
  return this.cleanupCalculated(newState);
2898
2923
  }
@@ -2921,7 +2946,7 @@ class Component {
2921
2946
  return ABORT;
2922
2947
  }
2923
2948
  }
2924
- }).filter((result) => result !== ABORT);
2949
+ }).filter((result) => !isAbort(result));
2925
2950
  }
2926
2951
  else if (reducer === undefined || reducer === true) {
2927
2952
  returnStream$ = filtered$.map(({ data }) => data);
@@ -5515,7 +5540,7 @@ const createElementWithModules = (modules) => {
5515
5540
  console.error('JSX Error: Capitalized HTML element without corresponding factory function. Components with names where the first letter is capital MUST be defined or included at the parent component\'s file scope.');
5516
5541
  }
5517
5542
  if (fun(sel)) {
5518
- if (sel.name === 'Fragment') {
5543
+ if (sel.__sygnalFragment || sel.name === 'Fragment') {
5519
5544
  return sel(data || {}, children);
5520
5545
  }
5521
5546
  data || (data = {});
@@ -6291,6 +6316,12 @@ function vnodeToHtml(vnode) {
6291
6316
  if (VOID_ELEMENTS.has(tag)) {
6292
6317
  return html;
6293
6318
  }
6319
+ // If innerHTML is set via props, use it as raw content (no escaping)
6320
+ if (vnode.data?.props?.innerHTML != null) {
6321
+ html += String(vnode.data.props.innerHTML);
6322
+ html += `</${tag}>`;
6323
+ return html;
6324
+ }
6294
6325
  // Children — snabbdom uses `text` for single text children (even when
6295
6326
  // `children` holds a text element object). Prioritize `text` when set.
6296
6327
  if (vnode.text != null) {
@@ -227,7 +227,7 @@ const createElementWithModules = (modules) => {
227
227
  console.error('JSX Error: Capitalized HTML element without corresponding factory function. Components with names where the first letter is capital MUST be defined or included at the parent component\'s file scope.');
228
228
  }
229
229
  if (fun(sel)) {
230
- if (sel.name === 'Fragment') {
230
+ if (sel.__sygnalFragment || sel.name === 'Fragment') {
231
231
  return sel(data || {}, children);
232
232
  }
233
233
  data || (data = {});
@@ -276,7 +276,7 @@ function vnode(sel, data, children, text, elm) {
276
276
  }
277
277
 
278
278
  /* eslint-disable @typescript-eslint/no-namespace, import/export */
279
- function Fragment(data, ...children) {
279
+ function Fragment$1(data, ...children) {
280
280
  const flatChildren = flattenAndFilter(children, []);
281
281
  if (flatChildren.length === 1 &&
282
282
  !flatChildren[0].sel &&
@@ -311,6 +311,21 @@ function flattenAndFilter(children, flattened) {
311
311
  return flattened;
312
312
  }
313
313
 
314
+ /**
315
+ * Local snabbdom re-export barrel.
316
+ *
317
+ * Imports from specific snabbdom subpaths instead of the main barrel to avoid
318
+ * triggering the broken `styleModule` top-level `window` reference in
319
+ * snabbdom 3.6.3 (which causes ReferenceError in Node.js environments).
320
+ *
321
+ * The styleModule is provided separately by ./styleModule.ts with a fixed
322
+ * window guard.
323
+ */
324
+ // Core
325
+ // Tag Fragment so we can identify it even after minification mangles Function.name
326
+ Fragment$1.__sygnalFragment = true;
327
+ const Fragment = Fragment$1;
328
+
314
329
  function jsx(type, props, key) {
315
330
  if (props == null)
316
331
  return createElement(type, null);
@@ -225,7 +225,7 @@ const createElementWithModules = (modules) => {
225
225
  console.error('JSX Error: Capitalized HTML element without corresponding factory function. Components with names where the first letter is capital MUST be defined or included at the parent component\'s file scope.');
226
226
  }
227
227
  if (fun(sel)) {
228
- if (sel.name === 'Fragment') {
228
+ if (sel.__sygnalFragment || sel.name === 'Fragment') {
229
229
  return sel(data || {}, children);
230
230
  }
231
231
  data || (data = {});
@@ -274,7 +274,7 @@ function vnode(sel, data, children, text, elm) {
274
274
  }
275
275
 
276
276
  /* eslint-disable @typescript-eslint/no-namespace, import/export */
277
- function Fragment(data, ...children) {
277
+ function Fragment$1(data, ...children) {
278
278
  const flatChildren = flattenAndFilter(children, []);
279
279
  if (flatChildren.length === 1 &&
280
280
  !flatChildren[0].sel &&
@@ -309,6 +309,21 @@ function flattenAndFilter(children, flattened) {
309
309
  return flattened;
310
310
  }
311
311
 
312
+ /**
313
+ * Local snabbdom re-export barrel.
314
+ *
315
+ * Imports from specific snabbdom subpaths instead of the main barrel to avoid
316
+ * triggering the broken `styleModule` top-level `window` reference in
317
+ * snabbdom 3.6.3 (which causes ReferenceError in Node.js environments).
318
+ *
319
+ * The styleModule is provided separately by ./styleModule.ts with a fixed
320
+ * window guard.
321
+ */
322
+ // Core
323
+ // Tag Fragment so we can identify it even after minification mangles Function.name
324
+ Fragment$1.__sygnalFragment = true;
325
+ const Fragment = Fragment$1;
326
+
312
327
  function jsx(type, props, key) {
313
328
  if (props == null)
314
329
  return createElement(type, null);
@@ -227,7 +227,7 @@ const createElementWithModules = (modules) => {
227
227
  console.error('JSX Error: Capitalized HTML element without corresponding factory function. Components with names where the first letter is capital MUST be defined or included at the parent component\'s file scope.');
228
228
  }
229
229
  if (fun(sel)) {
230
- if (sel.name === 'Fragment') {
230
+ if (sel.__sygnalFragment || sel.name === 'Fragment') {
231
231
  return sel(data || {}, children);
232
232
  }
233
233
  data || (data = {});
@@ -276,7 +276,7 @@ function vnode(sel, data, children, text, elm) {
276
276
  }
277
277
 
278
278
  /* eslint-disable @typescript-eslint/no-namespace, import/export */
279
- function Fragment(data, ...children) {
279
+ function Fragment$1(data, ...children) {
280
280
  const flatChildren = flattenAndFilter(children, []);
281
281
  if (flatChildren.length === 1 &&
282
282
  !flatChildren[0].sel &&
@@ -311,6 +311,21 @@ function flattenAndFilter(children, flattened) {
311
311
  return flattened;
312
312
  }
313
313
 
314
+ /**
315
+ * Local snabbdom re-export barrel.
316
+ *
317
+ * Imports from specific snabbdom subpaths instead of the main barrel to avoid
318
+ * triggering the broken `styleModule` top-level `window` reference in
319
+ * snabbdom 3.6.3 (which causes ReferenceError in Node.js environments).
320
+ *
321
+ * The styleModule is provided separately by ./styleModule.ts with a fixed
322
+ * window guard.
323
+ */
324
+ // Core
325
+ // Tag Fragment so we can identify it even after minification mangles Function.name
326
+ Fragment$1.__sygnalFragment = true;
327
+ const Fragment = Fragment$1;
328
+
314
329
  function jsx(type, props, key) {
315
330
  if (props == null)
316
331
  return createElement(type, null);