@sigx/runtime-core 0.1.3 → 0.1.5

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/dist/index.js CHANGED
@@ -28,6 +28,29 @@ function registerComponentPlugin(plugin) {
28
28
  function getComponentPlugins() {
29
29
  return plugins;
30
30
  }
31
+ const contextExtensions = [];
32
+ /**
33
+ * Register a function that will be called to extend every component context.
34
+ * Extensions are called in order of registration.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * // In @sigx/server-renderer/client
39
+ * registerContextExtension((ctx) => {
40
+ * ctx.ssr = { load: () => {} };
41
+ * });
42
+ * ```
43
+ */
44
+ function registerContextExtension(extension) {
45
+ contextExtensions.push(extension);
46
+ }
47
+ /**
48
+ * Apply all registered context extensions to a context object.
49
+ * Called internally by the renderer when creating component contexts.
50
+ */
51
+ function applyContextExtensions(ctx) {
52
+ for (const extension of contextExtensions) extension(ctx);
53
+ }
31
54
 
32
55
  //#endregion
33
56
  //#region src/app.ts
@@ -405,6 +428,190 @@ function jsxs(type, props, key) {
405
428
  }
406
429
  const jsxDEV = jsx;
407
430
 
431
+ //#endregion
432
+ //#region src/lazy.tsx
433
+ /**
434
+ * Lazy loading utilities for sigx components.
435
+ *
436
+ * Provides runtime-only lazy loading with no build dependencies.
437
+ * Works with any bundler that supports dynamic import().
438
+ */
439
+ let currentSuspenseBoundary = null;
440
+ /**
441
+ * Register a promise with the current Suspense boundary
442
+ * @internal
443
+ */
444
+ function registerPendingPromise(promise) {
445
+ const boundary = currentSuspenseBoundary;
446
+ if (boundary) {
447
+ boundary.pending.add(promise);
448
+ promise.finally(() => {
449
+ boundary.pending.delete(promise);
450
+ if (boundary.pending.size === 0) boundary.onResolve();
451
+ });
452
+ return true;
453
+ }
454
+ return false;
455
+ }
456
+ /**
457
+ * Create a lazy-loaded component wrapper.
458
+ *
459
+ * The component will be loaded on first render. Use with `<Suspense>` to show
460
+ * a fallback while loading.
461
+ *
462
+ * @param loader - Function that returns a Promise resolving to the component
463
+ * @returns A component factory that loads the real component on demand
464
+ *
465
+ * @example
466
+ * ```tsx
467
+ * import { lazy, Suspense } from 'sigx';
468
+ *
469
+ * // Component will be in a separate chunk
470
+ * const HeavyChart = lazy(() => import('./components/HeavyChart'));
471
+ *
472
+ * // Usage
473
+ * <Suspense fallback={<Spinner />}>
474
+ * <HeavyChart data={chartData} />
475
+ * </Suspense>
476
+ *
477
+ * // Preload on hover
478
+ * <button onMouseEnter={() => HeavyChart.preload()}>
479
+ * Show Chart
480
+ * </button>
481
+ * ```
482
+ */
483
+ function lazy(loader) {
484
+ let Component = null;
485
+ let promise = null;
486
+ let error = null;
487
+ let state = "pending";
488
+ const LazyWrapper = defineComponent((ctx) => {
489
+ const loadState = ctx.signal({
490
+ state,
491
+ tick: 0
492
+ });
493
+ if (!promise) promise = loader().then((mod) => {
494
+ Component = "default" in mod ? mod.default : mod;
495
+ state = "resolved";
496
+ loadState.state = "resolved";
497
+ loadState.tick++;
498
+ return Component;
499
+ }).catch((err) => {
500
+ error = err instanceof Error ? err : new Error(String(err));
501
+ state = "rejected";
502
+ loadState.state = "rejected";
503
+ loadState.tick++;
504
+ throw error;
505
+ });
506
+ if (state === "resolved" && Component) return () => {
507
+ return jsx(Component, {});
508
+ };
509
+ if (state === "rejected" && error) throw error;
510
+ if (!registerPendingPromise(promise)) promise.catch(() => {});
511
+ return () => {
512
+ const currentState = loadState.state;
513
+ loadState.tick;
514
+ if (currentState === "resolved" && Component) return jsx(Component, {});
515
+ if (currentState === "rejected" && error) throw error;
516
+ return null;
517
+ };
518
+ }, { name: "LazyComponent" });
519
+ LazyWrapper.__lazy = true;
520
+ LazyWrapper.preload = () => {
521
+ if (!promise) promise = loader().then((mod) => {
522
+ Component = "default" in mod ? mod.default : mod;
523
+ state = "resolved";
524
+ return Component;
525
+ }).catch((err) => {
526
+ error = err instanceof Error ? err : new Error(String(err));
527
+ state = "rejected";
528
+ throw error;
529
+ });
530
+ return promise;
531
+ };
532
+ LazyWrapper.isLoaded = () => {
533
+ return state === "resolved";
534
+ };
535
+ return LazyWrapper;
536
+ }
537
+ /**
538
+ * Suspense boundary component for handling async loading states.
539
+ *
540
+ * Wraps lazy-loaded components and shows a fallback while they load.
541
+ *
542
+ * @example
543
+ * ```tsx
544
+ * import { lazy, Suspense } from 'sigx';
545
+ *
546
+ * const LazyDashboard = lazy(() => import('./Dashboard'));
547
+ *
548
+ * // Basic usage
549
+ * <Suspense fallback={<div>Loading...</div>}>
550
+ * <LazyDashboard />
551
+ * </Suspense>
552
+ *
553
+ * // With spinner component
554
+ * <Suspense fallback={<Spinner size="large" />}>
555
+ * <LazyDashboard />
556
+ * <LazyCharts />
557
+ * </Suspense>
558
+ * ```
559
+ */
560
+ const Suspense = defineComponent((ctx) => {
561
+ const { props, slots } = ctx;
562
+ const state = ctx.signal({
563
+ isReady: false,
564
+ pendingCount: 0
565
+ });
566
+ const boundary = {
567
+ pending: /* @__PURE__ */ new Set(),
568
+ onResolve: () => {
569
+ state.pendingCount = boundary.pending.size;
570
+ if (boundary.pending.size === 0) state.isReady = true;
571
+ }
572
+ };
573
+ ctx.onMount(() => {
574
+ if (boundary.pending.size === 0) state.isReady = true;
575
+ });
576
+ return () => {
577
+ state.isReady;
578
+ state.pendingCount;
579
+ const prevBoundary = currentSuspenseBoundary;
580
+ currentSuspenseBoundary = boundary;
581
+ try {
582
+ const children = slots.default();
583
+ if (boundary.pending.size > 0) {
584
+ const fallback = props.fallback;
585
+ if (typeof fallback === "function") return fallback();
586
+ return fallback ?? null;
587
+ }
588
+ if (Array.isArray(children)) {
589
+ const filtered = children.filter((c) => c != null && c !== false && c !== true);
590
+ if (filtered.length === 0) return null;
591
+ if (filtered.length === 1) return filtered[0];
592
+ return filtered;
593
+ }
594
+ return children;
595
+ } catch (err) {
596
+ if (err instanceof Promise) {
597
+ registerPendingPromise(err);
598
+ const fallback = props.fallback;
599
+ if (typeof fallback === "function") return fallback();
600
+ return fallback ?? null;
601
+ }
602
+ throw err;
603
+ } finally {
604
+ currentSuspenseBoundary = prevBoundary;
605
+ }
606
+ };
607
+ }, { name: "Suspense" });
608
+ /**
609
+ * Check if a component is a lazy-loaded component
610
+ */
611
+ function isLazyComponent(component) {
612
+ return component && component.__lazy === true;
613
+ }
614
+
408
615
  //#endregion
409
616
  //#region src/utils/index.ts
410
617
  var Utils = class {
@@ -419,6 +626,195 @@ function guid$1() {
419
626
  });
420
627
  }
421
628
 
629
+ //#endregion
630
+ //#region src/utils/props-accessor.ts
631
+ /**
632
+ * Creates a props accessor that can be called with defaults or accessed directly.
633
+ * After calling with defaults, direct property access uses those defaults.
634
+ *
635
+ * @example
636
+ * ```ts
637
+ * // In component setup:
638
+ * const props = createPropsAccessor(reactiveProps);
639
+ *
640
+ * // Set defaults
641
+ * props({ count: 0, label: 'Default' });
642
+ *
643
+ * // Access props (falls back to defaults if not provided)
644
+ * const count = props.count;
645
+ * ```
646
+ */
647
+ function createPropsAccessor(reactiveProps) {
648
+ let defaults = {};
649
+ const proxy = new Proxy(function propsAccessor() {}, {
650
+ get(_, key) {
651
+ if (typeof key === "symbol") return void 0;
652
+ const value = reactiveProps[key];
653
+ return value != null ? value : defaults[key];
654
+ },
655
+ apply(_, __, args) {
656
+ if (args[0] && typeof args[0] === "object") defaults = {
657
+ ...defaults,
658
+ ...args[0]
659
+ };
660
+ return proxy;
661
+ },
662
+ has(_, key) {
663
+ if (typeof key === "symbol") return false;
664
+ return key in reactiveProps || key in defaults;
665
+ },
666
+ ownKeys() {
667
+ return [...new Set([...Object.keys(reactiveProps), ...Object.keys(defaults)])];
668
+ },
669
+ getOwnPropertyDescriptor(_, key) {
670
+ if (typeof key === "symbol") return void 0;
671
+ if (key in reactiveProps || key in defaults) return {
672
+ enumerable: true,
673
+ configurable: true,
674
+ writable: false
675
+ };
676
+ }
677
+ });
678
+ return proxy;
679
+ }
680
+
681
+ //#endregion
682
+ //#region src/utils/slots.ts
683
+ /**
684
+ * Slots system for component children.
685
+ * Supports default and named slots with reactivity.
686
+ */
687
+ /**
688
+ * Create slots object from children and slots prop.
689
+ * Uses a version signal to trigger re-renders when children change.
690
+ *
691
+ * Supports named slots via:
692
+ * - `slots` prop object (e.g., `slots={{ header: () => <div>...</div> }}`)
693
+ * - `slot` prop on children (e.g., `<div slot="header">...</div>`)
694
+ *
695
+ * @example
696
+ * ```tsx
697
+ * // Parent component
698
+ * <Card slots={{ header: () => <h1>Title</h1> }}>
699
+ * <p>Default content</p>
700
+ * <span slot="footer">Footer text</span>
701
+ * </Card>
702
+ *
703
+ * // Card component setup
704
+ * const slots = createSlots(children, slotsFromProps);
705
+ * return () => (
706
+ * <div>
707
+ * {slots.header()}
708
+ * {slots.default()}
709
+ * {slots.footer()}
710
+ * </div>
711
+ * );
712
+ * ```
713
+ */
714
+ function createSlots(children, slotsFromProps) {
715
+ const versionSignal = signal$1({ v: 0 });
716
+ function extractNamedSlotsFromChildren(c) {
717
+ const defaultChildren = [];
718
+ const namedSlots = {};
719
+ if (c == null) return {
720
+ defaultChildren,
721
+ namedSlots
722
+ };
723
+ const items = Array.isArray(c) ? c : [c];
724
+ for (const child of items) if (child && typeof child === "object" && child.props && child.props.slot) {
725
+ const slotName = child.props.slot;
726
+ if (!namedSlots[slotName]) namedSlots[slotName] = [];
727
+ namedSlots[slotName].push(child);
728
+ } else defaultChildren.push(child);
729
+ return {
730
+ defaultChildren,
731
+ namedSlots
732
+ };
733
+ }
734
+ const slotsObj = {
735
+ _children: children,
736
+ _slotsFromProps: slotsFromProps || {},
737
+ _version: versionSignal,
738
+ _isPatching: false,
739
+ default: function() {
740
+ this._version.v;
741
+ const c = this._children;
742
+ const { defaultChildren } = extractNamedSlotsFromChildren(c);
743
+ return defaultChildren.filter((child) => child != null && child !== false && child !== true);
744
+ }
745
+ };
746
+ return new Proxy(slotsObj, { get(target, prop) {
747
+ if (prop in target) return target[prop];
748
+ if (typeof prop === "string") return function(scopedProps) {
749
+ target._version.v;
750
+ if (target._slotsFromProps && typeof target._slotsFromProps[prop] === "function") {
751
+ const result = target._slotsFromProps[prop](scopedProps);
752
+ if (result == null) return [];
753
+ return Array.isArray(result) ? result : [result];
754
+ }
755
+ const { namedSlots } = extractNamedSlotsFromChildren(target._children);
756
+ return namedSlots[prop] || [];
757
+ };
758
+ } });
759
+ }
760
+
761
+ //#endregion
762
+ //#region src/utils/normalize.ts
763
+ /**
764
+ * VNode normalization utilities.
765
+ * Converts render results into proper VNode structures.
766
+ */
767
+ /**
768
+ * Normalize render result to a VNode (wrapping arrays in Fragment).
769
+ * Handles null, undefined, false, true by returning an empty Text node.
770
+ *
771
+ * This is used to normalize the return value of component render functions
772
+ * into a consistent VNode structure for the renderer to process.
773
+ *
774
+ * @example
775
+ * ```ts
776
+ * // Conditional rendering returns null/false
777
+ * normalizeSubTree(null) // → empty Text node
778
+ * normalizeSubTree(false) // → empty Text node
779
+ *
780
+ * // Arrays become Fragments
781
+ * normalizeSubTree([<A/>, <B/>]) // → Fragment with children
782
+ *
783
+ * // Primitives become Text nodes
784
+ * normalizeSubTree("hello") // → Text node
785
+ * normalizeSubTree(42) // → Text node
786
+ *
787
+ * // VNodes pass through
788
+ * normalizeSubTree(<div/>) // → same VNode
789
+ * ```
790
+ */
791
+ function normalizeSubTree(result) {
792
+ if (result == null || result === false || result === true) return {
793
+ type: Text,
794
+ props: {},
795
+ key: null,
796
+ children: [],
797
+ dom: null,
798
+ text: ""
799
+ };
800
+ if (Array.isArray(result)) return {
801
+ type: Fragment,
802
+ props: {},
803
+ key: null,
804
+ children: result,
805
+ dom: null
806
+ };
807
+ if (typeof result === "string" || typeof result === "number") return {
808
+ type: Text,
809
+ props: {},
810
+ key: null,
811
+ children: [],
812
+ dom: null,
813
+ text: result
814
+ };
815
+ return result;
816
+ }
817
+
422
818
  //#endregion
423
819
  //#region src/models/index.ts
424
820
  const guid = guid$1;
@@ -715,7 +1111,6 @@ function isComponent(type) {
715
1111
  }
716
1112
  function createRenderer(options) {
717
1113
  const { insert: hostInsert, remove: hostRemove, patchProp: hostPatchProp, createElement: hostCreateElement, createText: hostCreateText, createComment: hostCreateComment, setText: hostSetText, setElementText: hostSetElementText, parentNode: hostParentNode, nextSibling: hostNextSibling, cloneNode: hostCloneNode, insertStaticContent: hostInsertStaticContent } = options;
718
- let isPatching = false;
719
1114
  let currentAppContext = null;
720
1115
  function render(element, container, appContext) {
721
1116
  if (appContext) currentAppContext = appContext;
@@ -729,6 +1124,13 @@ function createRenderer(options) {
729
1124
  dom: null,
730
1125
  text: element
731
1126
  };
1127
+ else if (isComponent(element)) vnode = {
1128
+ type: element,
1129
+ props: {},
1130
+ key: null,
1131
+ children: [],
1132
+ dom: null
1133
+ };
732
1134
  else vnode = element;
733
1135
  if (vnode) {
734
1136
  if (oldVNode) patch(oldVNode, vnode, container);
@@ -740,6 +1142,7 @@ function createRenderer(options) {
740
1142
  }
741
1143
  }
742
1144
  function mount(vnode, container, before = null) {
1145
+ if (vnode == null || vnode === false || vnode === true) return;
743
1146
  if (vnode.type === Text) {
744
1147
  const node = hostCreateText(String(vnode.text));
745
1148
  vnode.dom = node;
@@ -751,7 +1154,7 @@ function createRenderer(options) {
751
1154
  const anchor = hostCreateComment("");
752
1155
  vnode.dom = anchor;
753
1156
  hostInsert(anchor, container, before);
754
- vnode.children.forEach((child) => mount(child, container, anchor));
1157
+ if (vnode.children) vnode.children.forEach((child) => mount(child, container, anchor));
755
1158
  return;
756
1159
  }
757
1160
  if (isComponent(vnode.type)) {
@@ -768,17 +1171,18 @@ function createRenderer(options) {
768
1171
  else if (typeof vnode.props.ref === "object") vnode.props.ref.current = element;
769
1172
  }
770
1173
  }
771
- vnode.children.forEach((child) => {
1174
+ if (vnode.children) vnode.children.forEach((child) => {
772
1175
  child.parent = vnode;
773
1176
  mount(child, element);
774
1177
  });
775
1178
  hostInsert(element, container, before);
776
1179
  }
777
1180
  function unmount(vnode, container) {
778
- if (vnode._effect) vnode._effect();
1181
+ const internalVNode = vnode;
1182
+ if (internalVNode._effect) internalVNode._effect.stop();
779
1183
  if (vnode.cleanup) vnode.cleanup();
780
1184
  if (isComponent(vnode.type)) {
781
- const subTree = vnode._subTree;
1185
+ const subTree = internalVNode._subTree;
782
1186
  if (subTree) unmount(subTree, container);
783
1187
  if (vnode.dom) hostRemove(vnode.dom);
784
1188
  if (vnode.props?.ref) {
@@ -788,7 +1192,7 @@ function createRenderer(options) {
788
1192
  return;
789
1193
  }
790
1194
  if (vnode.type === Fragment) {
791
- vnode.children.forEach((child) => unmount(child, container));
1195
+ if (vnode.children) vnode.children.forEach((child) => unmount(child, container));
792
1196
  if (vnode.dom) hostRemove(vnode.dom);
793
1197
  return;
794
1198
  }
@@ -808,13 +1212,15 @@ function createRenderer(options) {
808
1212
  mount(newVNode, parent, nextSibling);
809
1213
  return;
810
1214
  }
811
- if (oldVNode._effect) {
1215
+ const oldInternal = oldVNode;
1216
+ const newInternal = newVNode;
1217
+ if (oldInternal._effect) {
812
1218
  newVNode.dom = oldVNode.dom;
813
- newVNode._effect = oldVNode._effect;
814
- newVNode._subTree = oldVNode._subTree;
815
- newVNode._slots = oldVNode._slots;
816
- const props = oldVNode._componentProps;
817
- newVNode._componentProps = props;
1219
+ newInternal._effect = oldInternal._effect;
1220
+ newInternal._subTree = oldInternal._subTree;
1221
+ newInternal._slots = oldInternal._slots;
1222
+ const props = oldInternal._componentProps;
1223
+ newInternal._componentProps = props;
818
1224
  if (props) {
819
1225
  const newProps$1 = newVNode.props || {};
820
1226
  untrack(() => {
@@ -824,20 +1230,20 @@ function createRenderer(options) {
824
1230
  for (const key in props) if (!(key in newProps$1) && key !== "children" && key !== "key" && key !== "ref") delete props[key];
825
1231
  });
826
1232
  }
827
- const slotsRef = oldVNode._slots;
1233
+ const slotsRef = oldInternal._slots;
828
1234
  const newChildren = newVNode.props?.children;
829
1235
  const newSlotsFromProps = newVNode.props?.slots;
830
1236
  if (slotsRef) {
831
1237
  if (newChildren !== void 0) slotsRef._children = newChildren;
832
1238
  if (newSlotsFromProps !== void 0) slotsRef._slotsFromProps = newSlotsFromProps;
833
- if (!isPatching) {
834
- isPatching = true;
1239
+ if (!slotsRef._isPatching) {
1240
+ slotsRef._isPatching = true;
835
1241
  try {
836
1242
  untrack(() => {
837
1243
  slotsRef._version.v++;
838
1244
  });
839
1245
  } finally {
840
- isPatching = false;
1246
+ slotsRef._isPatching = false;
841
1247
  }
842
1248
  }
843
1249
  }
@@ -951,9 +1357,10 @@ function createRenderer(options) {
951
1357
  let exposeCalled = false;
952
1358
  const { children, slots: slotsFromProps, ...propsData } = vnode.props || {};
953
1359
  const reactiveProps = signal$1(propsData);
954
- vnode._componentProps = reactiveProps;
1360
+ const internalVNode = vnode;
1361
+ internalVNode._componentProps = reactiveProps;
955
1362
  const slots = createSlots(children, slotsFromProps);
956
- vnode._slots = slots;
1363
+ internalVNode._slots = slots;
957
1364
  const mountHooks = [];
958
1365
  const cleanupHooks = [];
959
1366
  const parentInstance = getCurrentInstance();
@@ -961,7 +1368,7 @@ function createRenderer(options) {
961
1368
  const ctx = {
962
1369
  el: container,
963
1370
  signal: signal$1,
964
- props: reactiveProps,
1371
+ props: createPropsAccessor(reactiveProps),
965
1372
  slots,
966
1373
  emit: (event, ...args) => {
967
1374
  const handler = reactiveProps[`on${event[0].toUpperCase() + event.slice(1)}`];
@@ -977,8 +1384,11 @@ function createRenderer(options) {
977
1384
  expose: (exposedValue) => {
978
1385
  exposed = exposedValue;
979
1386
  exposeCalled = true;
980
- }
1387
+ },
1388
+ renderFn: null,
1389
+ update: () => {}
981
1390
  };
1391
+ applyContextExtensions(ctx);
982
1392
  ctx.__name = componentName;
983
1393
  if (currentAppContext) ctx._appContext = currentAppContext;
984
1394
  const componentInstance = {
@@ -989,7 +1399,9 @@ function createRenderer(options) {
989
1399
  const prev = setCurrentInstance(ctx);
990
1400
  let renderFn;
991
1401
  try {
992
- renderFn = setup(ctx);
1402
+ const setupResult = setup(ctx);
1403
+ if (setupResult && typeof setupResult.then === "function") throw new Error(`Async setup in component "${componentName}" is only supported during SSR. On the client, use pre-loaded data from hydration or fetch in onMount.`);
1404
+ renderFn = setupResult;
993
1405
  notifyComponentCreated(currentAppContext, componentInstance);
994
1406
  } catch (err) {
995
1407
  if (!handleComponentError(currentAppContext, err, componentInstance, "setup")) throw err;
@@ -1001,24 +1413,31 @@ function createRenderer(options) {
1001
1413
  if (typeof vnode.props.ref === "function") vnode.props.ref(refValue);
1002
1414
  else if (vnode.props.ref && typeof vnode.props.ref === "object") vnode.props.ref.current = refValue;
1003
1415
  }
1004
- if (renderFn) vnode._effect = effect(() => {
1005
- const prevInstance = setCurrentInstance(ctx);
1006
- try {
1007
- const subTreeResult = renderFn();
1008
- if (subTreeResult == null) return;
1009
- const subTree = normalizeSubTree(subTreeResult);
1010
- const prevSubTree = vnode._subTree;
1011
- if (prevSubTree) {
1012
- patch(prevSubTree, subTree, container);
1013
- notifyComponentUpdated(currentAppContext, componentInstance);
1014
- } else mount(subTree, container, anchor);
1015
- vnode._subTree = subTree;
1016
- } catch (err) {
1017
- if (!handleComponentError(currentAppContext, err, componentInstance, "render")) throw err;
1018
- } finally {
1019
- setCurrentInstance(prevInstance);
1020
- }
1021
- });
1416
+ if (renderFn) {
1417
+ ctx.renderFn = renderFn;
1418
+ const componentEffect = effect(() => {
1419
+ const prevInstance = setCurrentInstance(ctx);
1420
+ try {
1421
+ const subTreeResult = ctx.renderFn();
1422
+ if (subTreeResult == null) return;
1423
+ const subTree = normalizeSubTree(subTreeResult);
1424
+ const prevSubTree = internalVNode._subTree;
1425
+ if (prevSubTree) {
1426
+ patch(prevSubTree, subTree, container);
1427
+ notifyComponentUpdated(currentAppContext, componentInstance);
1428
+ } else mount(subTree, container, anchor);
1429
+ internalVNode._subTree = subTree;
1430
+ } catch (err) {
1431
+ if (!handleComponentError(currentAppContext, err, componentInstance, "render")) throw err;
1432
+ } finally {
1433
+ setCurrentInstance(prevInstance);
1434
+ }
1435
+ });
1436
+ internalVNode._effect = componentEffect;
1437
+ ctx.update = () => {
1438
+ componentEffect();
1439
+ };
1440
+ }
1022
1441
  const mountCtx = { el: container };
1023
1442
  mountHooks.forEach((hook) => hook(mountCtx));
1024
1443
  notifyComponentMounted(currentAppContext, componentInstance);
@@ -1027,81 +1446,12 @@ function createRenderer(options) {
1027
1446
  cleanupHooks.forEach((hook) => hook(mountCtx));
1028
1447
  };
1029
1448
  }
1030
- /**
1031
- * Create slots object from children and slots prop.
1032
- * Uses a version signal to trigger re-renders when children change.
1033
- * Supports named slots via:
1034
- * - `slots` prop object (e.g., slots={{ header: () => <div>...</div> }})
1035
- * - `slot` prop on children (e.g., <div slot="header">...</div>)
1036
- */
1037
- function createSlots(children, slotsFromProps) {
1038
- const versionSignal = signal$1({ v: 0 });
1039
- function extractNamedSlotsFromChildren(c) {
1040
- const defaultChildren = [];
1041
- const namedSlots = {};
1042
- if (c == null) return {
1043
- defaultChildren,
1044
- namedSlots
1045
- };
1046
- const items = Array.isArray(c) ? c : [c];
1047
- for (const child of items) if (child && typeof child === "object" && child.props && child.props.slot) {
1048
- const slotName = child.props.slot;
1049
- if (!namedSlots[slotName]) namedSlots[slotName] = [];
1050
- namedSlots[slotName].push(child);
1051
- } else defaultChildren.push(child);
1052
- return {
1053
- defaultChildren,
1054
- namedSlots
1055
- };
1056
- }
1057
- const slotsObj = {
1058
- _children: children,
1059
- _slotsFromProps: slotsFromProps || {},
1060
- _version: versionSignal,
1061
- default: function() {
1062
- this._version.v;
1063
- const c = this._children;
1064
- const { defaultChildren } = extractNamedSlotsFromChildren(c);
1065
- return defaultChildren;
1066
- }
1067
- };
1068
- return new Proxy(slotsObj, { get(target, prop) {
1069
- if (prop in target) return target[prop];
1070
- if (typeof prop === "string") return function(scopedProps) {
1071
- target._version.v;
1072
- if (target._slotsFromProps && typeof target._slotsFromProps[prop] === "function") {
1073
- const result = target._slotsFromProps[prop](scopedProps);
1074
- if (result == null) return [];
1075
- return Array.isArray(result) ? result : [result];
1076
- }
1077
- const { namedSlots } = extractNamedSlotsFromChildren(target._children);
1078
- return namedSlots[prop] || [];
1079
- };
1080
- } });
1081
- }
1082
- /**
1083
- * Normalize render result to a VNode (wrapping arrays in Fragment)
1084
- */
1085
- function normalizeSubTree(result) {
1086
- if (Array.isArray(result)) return {
1087
- type: Fragment,
1088
- props: {},
1089
- key: null,
1090
- children: result,
1091
- dom: null
1092
- };
1093
- if (typeof result === "string" || typeof result === "number") return {
1094
- type: Text,
1095
- props: {},
1096
- key: null,
1097
- children: [],
1098
- dom: null,
1099
- text: result
1100
- };
1101
- return result;
1102
- }
1103
1449
  return {
1104
1450
  render,
1451
+ patch,
1452
+ mount,
1453
+ unmount,
1454
+ mountComponent,
1105
1455
  createApp: (rootComponent) => {
1106
1456
  return { mount(selectorOrContainer) {
1107
1457
  let container = null;
@@ -1119,5 +1469,5 @@ function createRenderer(options) {
1119
1469
  }
1120
1470
 
1121
1471
  //#endregion
1122
- export { AppContextKey, Fragment, InstanceLifetimes, SubscriptionHandler, Text, Utils, createPropsProxy, createRenderer, createTopic, defineApp, defineComponent, defineFactory, defineInjectable, defineProvide, defineStore, getComponentMeta, getComponentPlugins, getCurrentInstance, getDefaultMount, getPlatformSyncProcessor, guid, handleComponentError, inject, injectApp, jsx, jsxDEV, jsxs, notifyComponentCreated, notifyComponentMounted, notifyComponentUnmounted, notifyComponentUpdated, onCleanup, onMount, provide, registerComponentPlugin, setCurrentInstance, setDefaultMount, setPlatformSyncProcessor, signal, toSubscriber, valueOf };
1472
+ export { AppContextKey, Fragment, InstanceLifetimes, SubscriptionHandler, Suspense, Text, Utils, applyContextExtensions, createPropsAccessor, createPropsProxy, createRenderer, createSlots, createTopic, defineApp, defineComponent, defineFactory, defineInjectable, defineProvide, defineStore, getComponentMeta, getComponentPlugins, getCurrentInstance, getDefaultMount, getPlatformSyncProcessor, guid, handleComponentError, inject, injectApp, isLazyComponent, jsx, jsxDEV, jsxs, lazy, normalizeSubTree, notifyComponentCreated, notifyComponentMounted, notifyComponentUnmounted, notifyComponentUpdated, onCleanup, onMount, provide, registerComponentPlugin, registerContextExtension, registerPendingPromise, setCurrentInstance, setDefaultMount, setPlatformSyncProcessor, signal, toSubscriber, valueOf };
1123
1473
  //# sourceMappingURL=index.js.map