@sigx/runtime-core 0.1.4 → 0.1.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 (44) hide show
  1. package/dist/app-types.d.ts +35 -15
  2. package/dist/app-types.d.ts.map +1 -1
  3. package/dist/app.d.ts +4 -8
  4. package/dist/app.d.ts.map +1 -1
  5. package/dist/component.d.ts +147 -39
  6. package/dist/component.d.ts.map +1 -1
  7. package/dist/compound.d.ts +34 -0
  8. package/dist/compound.d.ts.map +1 -0
  9. package/dist/di/factory.d.ts +3 -2
  10. package/dist/di/factory.d.ts.map +1 -1
  11. package/dist/di/injectable.d.ts +80 -6
  12. package/dist/di/injectable.d.ts.map +1 -1
  13. package/dist/hydration/index.d.ts +88 -0
  14. package/dist/hydration/index.d.ts.map +1 -0
  15. package/dist/index.d.ts +7 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +556 -682
  18. package/dist/index.js.map +1 -1
  19. package/dist/jsx-runtime.d.ts +3 -2
  20. package/dist/jsx-runtime.d.ts.map +1 -1
  21. package/dist/lazy.d.ts.map +1 -1
  22. package/dist/model.d.ts +68 -0
  23. package/dist/model.d.ts.map +1 -0
  24. package/dist/platform.d.ts +9 -9
  25. package/dist/platform.d.ts.map +1 -1
  26. package/dist/plugins.d.ts +25 -1
  27. package/dist/plugins.d.ts.map +1 -1
  28. package/dist/renderer.d.ts +22 -21
  29. package/dist/renderer.d.ts.map +1 -1
  30. package/dist/utils/is-component.d.ts +30 -0
  31. package/dist/utils/is-component.d.ts.map +1 -0
  32. package/dist/utils/normalize.d.ts +34 -0
  33. package/dist/utils/normalize.d.ts.map +1 -0
  34. package/dist/utils/props-accessor.d.ts +20 -0
  35. package/dist/utils/props-accessor.d.ts.map +1 -0
  36. package/dist/utils/slots.d.ts +46 -0
  37. package/dist/utils/slots.d.ts.map +1 -0
  38. package/package.json +7 -6
  39. package/dist/sheet.d.ts +0 -51
  40. package/dist/sheet.d.ts.map +0 -1
  41. package/dist/stores/store.d.ts +0 -70
  42. package/dist/stores/store.d.ts.map +0 -1
  43. package/dist/styled.d.ts +0 -15
  44. package/dist/styled.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,84 +1,139 @@
1
- import { detectAccess, effect, effectScope, signal, signal as signal$1, untrack, watch } from "@sigx/reactivity";
2
-
3
- //#region src/platform.ts
4
- let platformSyncProcessor = null;
5
- /**
6
- * Set the platform-specific sync processor for intrinsic elements.
7
- * Called by runtime-dom to handle checkbox/radio/select sync bindings.
8
- */
9
- function setPlatformSyncProcessor(fn) {
10
- platformSyncProcessor = fn;
1
+ import { detectAccess, effect, isComputed, signal, signal as signal$1, untrack } from "@sigx/reactivity";
2
+ var platformModelProcessor = null;
3
+ function setPlatformModelProcessor(fn) {
4
+ platformModelProcessor = fn;
11
5
  }
12
- /**
13
- * Get the current platform sync processor (for internal use).
14
- */
15
- function getPlatformSyncProcessor() {
16
- return platformSyncProcessor;
6
+ function getPlatformModelProcessor() {
7
+ return platformModelProcessor;
17
8
  }
18
-
19
- //#endregion
20
- //#region src/plugins.ts
21
- const plugins = [];
9
+ var plugins = [];
22
10
  function registerComponentPlugin(plugin) {
23
11
  plugins.push(plugin);
24
12
  }
25
- /**
26
- * Get all registered plugins (internal use)
27
- */
28
13
  function getComponentPlugins() {
29
14
  return plugins;
30
15
  }
31
-
32
- //#endregion
33
- //#region src/app.ts
34
- const isDev = typeof process !== "undefined" && true || true;
35
- /**
36
- * Unique symbol for app context injection
37
- */
38
- const AppContextKey = Symbol("sigx:app");
39
- let defaultMountFn = null;
40
- /**
41
- * Set the default mount function for the platform.
42
- * Called by platform packages (runtime-dom, runtime-terminal) on import.
43
- *
44
- * @example
45
- * ```typescript
46
- * // In @sigx/runtime-dom
47
- * import { setDefaultMount } from '@sigx/runtime-core';
48
- * setDefaultMount(domMount);
49
- * ```
50
- */
16
+ var contextExtensions = [];
17
+ function registerContextExtension(extension) {
18
+ contextExtensions.push(extension);
19
+ }
20
+ function applyContextExtensions(ctx) {
21
+ for (const extension of contextExtensions) extension(ctx);
22
+ }
23
+ var currentComponentContext = null;
24
+ function getCurrentInstance() {
25
+ return currentComponentContext;
26
+ }
27
+ function setCurrentInstance(ctx) {
28
+ const prev = currentComponentContext;
29
+ currentComponentContext = ctx;
30
+ return prev;
31
+ }
32
+ function onMounted(fn) {
33
+ if (currentComponentContext) currentComponentContext.onMounted(fn);
34
+ else console.warn("onMounted called outside of component setup");
35
+ }
36
+ function onUnmounted(fn) {
37
+ if (currentComponentContext) currentComponentContext.onUnmounted(fn);
38
+ else console.warn("onUnmounted called outside of component setup");
39
+ }
40
+ function onCreated(fn) {
41
+ if (currentComponentContext) currentComponentContext.onCreated(fn);
42
+ else console.warn("onCreated called outside of component setup");
43
+ }
44
+ function onUpdated(fn) {
45
+ if (currentComponentContext) currentComponentContext.onUpdated(fn);
46
+ else console.warn("onUpdated called outside of component setup");
47
+ }
48
+ var componentRegistry = /* @__PURE__ */ new Map();
49
+ function getComponentMeta(factory) {
50
+ return componentRegistry.get(factory);
51
+ }
52
+ function createPropsProxy(target, onAccess) {
53
+ return new Proxy(target, { get(obj, prop) {
54
+ if (typeof prop === "string" && onAccess) onAccess(prop);
55
+ return obj[prop];
56
+ } });
57
+ }
58
+ function component(setup, options) {
59
+ const factory = function(props) {
60
+ return {
61
+ type: factory,
62
+ props: props || {},
63
+ key: props?.key || null,
64
+ children: [],
65
+ dom: null
66
+ };
67
+ };
68
+ factory.__setup = setup;
69
+ factory.__name = options?.name;
70
+ factory.__props = null;
71
+ factory.__events = null;
72
+ factory.__ref = null;
73
+ factory.__slots = null;
74
+ componentRegistry.set(factory, {
75
+ name: options?.name,
76
+ setup
77
+ });
78
+ getComponentPlugins().forEach((p) => p.onDefine?.(options?.name, factory, setup));
79
+ return factory;
80
+ }
81
+ var globalInstances = /* @__PURE__ */ new Map();
82
+ var appContextToken = Symbol("sigx:appContext");
83
+ function lookupProvided(token) {
84
+ const ctx = getCurrentInstance();
85
+ if (!ctx) return;
86
+ let current = ctx;
87
+ while (current) {
88
+ if (current.provides && current.provides.has(token)) return current.provides.get(token);
89
+ current = current.parent;
90
+ }
91
+ }
92
+ function provideAtComponent(token, value) {
93
+ const ctx = getCurrentInstance();
94
+ if (!ctx) throw new Error("defineProvide must be called inside a component setup function");
95
+ if (!ctx.provides) ctx.provides = /* @__PURE__ */ new Map();
96
+ ctx.provides.set(token, value);
97
+ }
98
+ function defineInjectable(factory) {
99
+ const token = Symbol();
100
+ const useFn = (() => {
101
+ const provided = lookupProvided(token);
102
+ if (provided !== void 0) return provided;
103
+ if (!globalInstances.has(token)) globalInstances.set(token, factory());
104
+ return globalInstances.get(token);
105
+ });
106
+ useFn._factory = factory;
107
+ useFn._token = token;
108
+ return useFn;
109
+ }
110
+ function defineProvide(useFn, factory) {
111
+ const actualFactory = factory ?? useFn._factory;
112
+ const token = useFn._token;
113
+ if (!actualFactory || !token) throw new Error("defineProvide must be called with a function created by defineInjectable");
114
+ const instance = actualFactory();
115
+ provideAtComponent(token, instance);
116
+ return instance;
117
+ }
118
+ function useAppContext() {
119
+ return lookupProvided(appContextToken) ?? null;
120
+ }
121
+ function getAppContextToken() {
122
+ return appContextToken;
123
+ }
124
+ function provideAppContext(ctx, appContext) {
125
+ if (!ctx.provides) ctx.provides = /* @__PURE__ */ new Map();
126
+ ctx.provides.set(appContextToken, appContext);
127
+ if (appContext.provides) for (const [token, value] of appContext.provides) ctx.provides.set(token, value);
128
+ }
129
+ var isDev = typeof process !== "undefined" && process.env.NODE_ENV !== "production" || true;
130
+ var defaultMountFn = null;
51
131
  function setDefaultMount(mountFn) {
52
132
  defaultMountFn = mountFn;
53
133
  }
54
- /**
55
- * Get the current default mount function.
56
- * @internal
57
- */
58
134
  function getDefaultMount() {
59
135
  return defaultMountFn;
60
136
  }
61
- /**
62
- * Create an application instance.
63
- *
64
- * @example
65
- * ```tsx
66
- * import { defineApp, defineInjectable } from '@sigx/runtime-core';
67
- * import { render } from '@sigx/runtime-dom';
68
- *
69
- * // Define an injectable service
70
- * const useApiConfig = defineInjectable(() => ({ baseUrl: 'https://api.example.com' }));
71
- *
72
- * const app = defineApp(<App />);
73
- *
74
- * app.use(myPlugin, { option: 'value' });
75
- *
76
- * // Provide using the injectable token (works with inject())
77
- * app.provide(useApiConfig, { baseUrl: 'https://custom.api.com' });
78
- *
79
- * app.mount(document.getElementById('app')!, render);
80
- * ```
81
- */
82
137
  function defineApp(rootComponent) {
83
138
  const installedPlugins = /* @__PURE__ */ new Set();
84
139
  const context = {
@@ -103,11 +158,13 @@ function defineApp(rootComponent) {
103
158
  else if (isDev) console.warn("Invalid plugin: must be a function or have an install() method.");
104
159
  return app;
105
160
  },
106
- provide(token, value) {
107
- const actualToken = token?._token ?? token;
108
- if (isDev && context.provides.has(actualToken)) console.warn(`App-level provide: token is being overwritten.`);
109
- context.provides.set(actualToken, value);
110
- return app;
161
+ defineProvide(useFn, factory) {
162
+ const actualFactory = factory ?? useFn._factory;
163
+ const token = useFn._token;
164
+ if (!actualFactory || !token) throw new Error("defineProvide must be called with a function created by defineInjectable");
165
+ const instance = actualFactory();
166
+ context.provides.set(token, instance);
167
+ return instance;
111
168
  },
112
169
  hook(hooks) {
113
170
  context.hooks.push(hooks);
@@ -144,15 +201,16 @@ function defineApp(rootComponent) {
144
201
  },
145
202
  get _container() {
146
203
  return container;
204
+ },
205
+ get _rootComponent() {
206
+ return rootComponent;
147
207
  }
148
208
  };
149
209
  context.app = app;
210
+ const appContextToken = getAppContextToken();
211
+ context.provides.set(appContextToken, context);
150
212
  return app;
151
213
  }
152
- /**
153
- * Notify all app hooks that a component was created.
154
- * Called by the renderer after setup() returns.
155
- */
156
214
  function notifyComponentCreated(context, instance) {
157
215
  if (!context) return;
158
216
  for (const hooks of context.hooks) try {
@@ -161,10 +219,6 @@ function notifyComponentCreated(context, instance) {
161
219
  handleHookError(context, err, instance, "onComponentCreated");
162
220
  }
163
221
  }
164
- /**
165
- * Notify all app hooks that a component was mounted.
166
- * Called by the renderer after mount hooks run.
167
- */
168
222
  function notifyComponentMounted(context, instance) {
169
223
  if (!context) return;
170
224
  for (const hooks of context.hooks) try {
@@ -173,10 +227,6 @@ function notifyComponentMounted(context, instance) {
173
227
  handleHookError(context, err, instance, "onComponentMounted");
174
228
  }
175
229
  }
176
- /**
177
- * Notify all app hooks that a component was unmounted.
178
- * Called by the renderer before cleanup.
179
- */
180
230
  function notifyComponentUnmounted(context, instance) {
181
231
  if (!context) return;
182
232
  for (const hooks of context.hooks) try {
@@ -185,10 +235,6 @@ function notifyComponentUnmounted(context, instance) {
185
235
  handleHookError(context, err, instance, "onComponentUnmounted");
186
236
  }
187
237
  }
188
- /**
189
- * Notify all app hooks that a component updated.
190
- * Called by the renderer after re-render.
191
- */
192
238
  function notifyComponentUpdated(context, instance) {
193
239
  if (!context) return;
194
240
  for (const hooks of context.hooks) try {
@@ -197,10 +243,6 @@ function notifyComponentUpdated(context, instance) {
197
243
  handleHookError(context, err, instance, "onComponentUpdated");
198
244
  }
199
245
  }
200
- /**
201
- * Handle an error in a component. Returns true if the error was handled.
202
- * Called by the renderer when an error occurs in setup or render.
203
- */
204
246
  function handleComponentError(context, err, instance, info) {
205
247
  if (!context) return false;
206
248
  for (const hooks of context.hooks) try {
@@ -215,104 +257,53 @@ function handleComponentError(context, err, instance, info) {
215
257
  }
216
258
  return false;
217
259
  }
218
- /**
219
- * Handle errors that occur in hooks themselves
220
- */
221
260
  function handleHookError(context, err, instance, hookName) {
222
261
  console.error(`Error in ${hookName} hook:`, err);
223
262
  if (context.config.errorHandler) try {
224
263
  context.config.errorHandler(err, instance, `plugin hook: ${hookName}`);
225
264
  } catch {}
226
265
  }
227
-
228
- //#endregion
229
- //#region src/component.ts
230
- let currentComponentContext = null;
231
- function getCurrentInstance() {
232
- return currentComponentContext;
233
- }
234
- function setCurrentInstance(ctx) {
235
- const prev = currentComponentContext;
236
- currentComponentContext = ctx;
237
- return prev;
266
+ function compound(main, sub) {
267
+ return Object.assign(main, sub);
238
268
  }
239
- function onMount(fn) {
240
- if (currentComponentContext) currentComponentContext.onMount(fn);
241
- else console.warn("onMount called outside of component setup");
269
+ var MODEL_SYMBOL = Symbol.for("sigx.model");
270
+ function createModel(tuple, updateHandler) {
271
+ const [obj, key] = tuple;
272
+ return {
273
+ get value() {
274
+ return obj[key];
275
+ },
276
+ set value(v) {
277
+ updateHandler(v);
278
+ },
279
+ get binding() {
280
+ return [
281
+ obj,
282
+ key,
283
+ updateHandler
284
+ ];
285
+ },
286
+ [MODEL_SYMBOL]: true
287
+ };
242
288
  }
243
- function onCleanup(fn) {
244
- if (currentComponentContext) currentComponentContext.onCleanup(fn);
245
- else console.warn("onCleanup called outside of component setup");
289
+ function createModelFromBinding(binding) {
290
+ const [obj, key, handler] = binding;
291
+ return createModel([obj, key], handler);
246
292
  }
247
- const componentRegistry = /* @__PURE__ */ new Map();
248
- /**
249
- * Get component metadata (for DevTools)
250
- */
251
- function getComponentMeta(factory) {
252
- return componentRegistry.get(factory);
293
+ function isModel(value) {
294
+ return value !== null && typeof value === "object" && MODEL_SYMBOL in value && value[MODEL_SYMBOL] === true;
253
295
  }
254
- /**
255
- * Helper to create a proxy that tracks property access
256
- */
257
- function createPropsProxy(target, onAccess) {
258
- return new Proxy(target, { get(obj, prop) {
259
- if (typeof prop === "string" && onAccess) onAccess(prop);
260
- return obj[prop];
261
- } });
296
+ function getModelSymbol() {
297
+ return MODEL_SYMBOL;
262
298
  }
263
- /**
264
- * Define a component. Returns a JSX factory function.
265
- *
266
- * @param setup - Setup function that receives context and returns a render function
267
- * @param options - Optional configuration (e.g., name for DevTools)
268
- *
269
- * @example
270
- * ```tsx
271
- * type CardProps = DefineProp<"title", string> & DefineSlot<"header">;
272
- *
273
- * export const Card = defineComponent<CardProps>((ctx) => {
274
- * const { title } = ctx.props;
275
- * const { slots } = ctx;
276
- *
277
- * return () => (
278
- * <div class="card">
279
- * {slots.header?.() ?? <h2>{title}</h2>}
280
- * {slots.default()}
281
- * </div>
282
- * );
283
- * });
284
- * ```
285
- */
286
- function defineComponent(setup, options) {
287
- const factory = function(props) {
288
- return {
289
- type: factory,
290
- props: props || {},
291
- key: props?.key || null,
292
- children: [],
293
- dom: null
294
- };
295
- };
296
- factory.__setup = setup;
297
- factory.__name = options?.name;
298
- factory.__props = null;
299
- factory.__events = null;
300
- factory.__ref = null;
301
- factory.__slots = null;
302
- componentRegistry.set(factory, {
303
- name: options?.name,
304
- setup
305
- });
306
- getComponentPlugins().forEach((p) => p.onDefine?.(options?.name, factory, setup));
307
- return factory;
299
+ function isComponent(type) {
300
+ return typeof type === "function" && "__setup" in type;
308
301
  }
309
-
310
- //#endregion
311
- //#region src/jsx-runtime.ts
312
302
  const Fragment = Symbol.for("sigx.Fragment");
313
303
  const Text = Symbol.for("sigx.Text");
314
304
  function normalizeChildren(children) {
315
305
  if (children == null || children === false || children === true) return [];
306
+ if (isComputed(children)) return normalizeChildren(children.value);
316
307
  if (Array.isArray(children)) return children.flatMap((c) => normalizeChildren(c));
317
308
  if (typeof children === "string" || typeof children === "number") return [{
318
309
  type: Text,
@@ -325,64 +316,92 @@ function normalizeChildren(children) {
325
316
  if (children.type) return [children];
326
317
  return [];
327
318
  }
328
- /**
329
- * Check if a type is a sigx component (has __setup)
330
- */
331
- function isComponent$1(type) {
332
- return typeof type === "function" && "__setup" in type;
333
- }
334
- /**
335
- * Create a JSX element - this is the core function called by TSX transpilation
336
- */
337
319
  function jsx(type, props, key) {
338
320
  const processedProps = { ...props || {} };
321
+ const models = {};
322
+ const isComponentType = isComponent(type);
339
323
  if (props) {
340
- for (const propKey in props) if (propKey === "sync") {
341
- let syncBinding = props[propKey];
342
- if (typeof syncBinding === "function") {
343
- const detected = detectAccess(syncBinding);
344
- if (detected) syncBinding = detected;
345
- }
346
- if (Array.isArray(syncBinding) && syncBinding.length === 2) {
347
- const [stateObj, key$1] = syncBinding;
324
+ for (const propKey in props) if (propKey === "model") {
325
+ let modelBinding = props[propKey];
326
+ let tuple = null;
327
+ let updateHandler = null;
328
+ if (isModel(modelBinding)) {
329
+ const [obj, key, handler] = modelBinding.binding;
330
+ tuple = [obj, key];
331
+ updateHandler = handler;
332
+ } else if (typeof modelBinding === "function") {
333
+ const detected = detectAccess(modelBinding);
334
+ if (detected && typeof detected[1] === "string") tuple = detected;
335
+ } else if (Array.isArray(modelBinding) && modelBinding.length === 2 && typeof modelBinding[1] === "string") tuple = modelBinding;
336
+ if (tuple) {
337
+ const [stateObj, stateKey] = tuple;
348
338
  let handled = false;
349
- const platformProcessor = getPlatformSyncProcessor();
350
- if (typeof type === "string" && platformProcessor) handled = platformProcessor(type, processedProps, [stateObj, key$1], props);
351
- if (!handled) {
352
- processedProps.value = stateObj[key$1];
353
- const existingHandler = processedProps["onUpdate:value"];
354
- processedProps["onUpdate:value"] = (v) => {
355
- stateObj[key$1] = v;
339
+ if (!updateHandler) {
340
+ const existingHandler = processedProps["onUpdate:modelValue"];
341
+ updateHandler = (v) => {
342
+ const customHandler = stateObj[`onUpdate:${stateKey}`];
343
+ if (typeof customHandler === "function") customHandler(v);
344
+ else stateObj[stateKey] = v;
356
345
  if (existingHandler) existingHandler(v);
357
346
  };
358
347
  }
359
- delete processedProps.sync;
348
+ const platformProcessor = getPlatformModelProcessor();
349
+ if (typeof type === "string" && platformProcessor) handled = platformProcessor(type, processedProps, tuple, props);
350
+ if (isComponentType) {
351
+ models.model = createModel(tuple, updateHandler);
352
+ processedProps["onUpdate:modelValue"] = updateHandler;
353
+ } else if (!handled) {
354
+ processedProps.modelValue = stateObj[stateKey];
355
+ processedProps["onUpdate:modelValue"] = updateHandler;
356
+ }
357
+ delete processedProps.model;
360
358
  }
361
- } else if (propKey.startsWith("sync:")) {
362
- const syncBinding = props[propKey];
363
- if (Array.isArray(syncBinding) && syncBinding.length === 2) {
364
- const [stateObj, key$1] = syncBinding;
365
- const name = propKey.slice(5);
366
- processedProps[name] = stateObj[key$1];
359
+ } else if (propKey.startsWith("model:")) {
360
+ let modelBinding = props[propKey];
361
+ const name = propKey.slice(6);
362
+ let tuple = null;
363
+ let updateHandler = null;
364
+ if (isModel(modelBinding)) {
365
+ const [obj, key, handler] = modelBinding.binding;
366
+ tuple = [obj, key];
367
+ updateHandler = handler;
368
+ } else if (typeof modelBinding === "function") {
369
+ const detected = detectAccess(modelBinding);
370
+ if (detected && typeof detected[1] === "string") tuple = detected;
371
+ } else if (Array.isArray(modelBinding) && modelBinding.length === 2 && typeof modelBinding[1] === "string") tuple = modelBinding;
372
+ if (tuple) {
373
+ const [stateObj, stateKey] = tuple;
367
374
  const eventName = `onUpdate:${name}`;
368
- const existingHandler = processedProps[eventName];
369
- processedProps[eventName] = (v) => {
370
- stateObj[key$1] = v;
371
- if (existingHandler) existingHandler(v);
372
- };
375
+ if (!updateHandler) {
376
+ const existingHandler = processedProps[eventName];
377
+ updateHandler = (v) => {
378
+ const customHandler = stateObj[`onUpdate:${stateKey}`];
379
+ if (typeof customHandler === "function") customHandler(v);
380
+ else stateObj[stateKey] = v;
381
+ if (existingHandler) existingHandler(v);
382
+ };
383
+ }
384
+ if (isComponentType) {
385
+ models[name] = createModel(tuple, updateHandler);
386
+ processedProps[eventName] = updateHandler;
387
+ } else {
388
+ processedProps[name] = stateObj[stateKey];
389
+ processedProps[eventName] = updateHandler;
390
+ }
373
391
  delete processedProps[propKey];
374
392
  }
375
393
  }
376
394
  }
377
- if (isComponent$1(type)) {
378
- const { children: children$1, ...rest$1 } = processedProps;
395
+ if (Object.keys(models).length > 0) processedProps.$models = models;
396
+ if (isComponent(type)) {
397
+ const { children, ...rest } = processedProps;
379
398
  return {
380
399
  type,
381
400
  props: {
382
- ...rest$1,
383
- children: children$1
401
+ ...rest,
402
+ children
384
403
  },
385
- key: key || rest$1.key || null,
404
+ key: key || rest.key || null,
386
405
  children: [],
387
406
  dom: null
388
407
  };
@@ -397,27 +416,11 @@ function jsx(type, props, key) {
397
416
  dom: null
398
417
  };
399
418
  }
400
- /**
401
- * JSX Factory for fragments
402
- */
403
419
  function jsxs(type, props, key) {
404
420
  return jsx(type, props, key);
405
421
  }
406
422
  const jsxDEV = jsx;
407
-
408
- //#endregion
409
- //#region src/lazy.tsx
410
- /**
411
- * Lazy loading utilities for sigx components.
412
- *
413
- * Provides runtime-only lazy loading with no build dependencies.
414
- * Works with any bundler that supports dynamic import().
415
- */
416
- let currentSuspenseBoundary = null;
417
- /**
418
- * Register a promise with the current Suspense boundary
419
- * @internal
420
- */
423
+ var currentSuspenseBoundary = null;
421
424
  function registerPendingPromise(promise) {
422
425
  const boundary = currentSuspenseBoundary;
423
426
  if (boundary) {
@@ -430,39 +433,12 @@ function registerPendingPromise(promise) {
430
433
  }
431
434
  return false;
432
435
  }
433
- /**
434
- * Create a lazy-loaded component wrapper.
435
- *
436
- * The component will be loaded on first render. Use with `<Suspense>` to show
437
- * a fallback while loading.
438
- *
439
- * @param loader - Function that returns a Promise resolving to the component
440
- * @returns A component factory that loads the real component on demand
441
- *
442
- * @example
443
- * ```tsx
444
- * import { lazy, Suspense } from 'sigx';
445
- *
446
- * // Component will be in a separate chunk
447
- * const HeavyChart = lazy(() => import('./components/HeavyChart'));
448
- *
449
- * // Usage
450
- * <Suspense fallback={<Spinner />}>
451
- * <HeavyChart data={chartData} />
452
- * </Suspense>
453
- *
454
- * // Preload on hover
455
- * <button onMouseEnter={() => HeavyChart.preload()}>
456
- * Show Chart
457
- * </button>
458
- * ```
459
- */
460
436
  function lazy(loader) {
461
437
  let Component = null;
462
438
  let promise = null;
463
439
  let error = null;
464
440
  let state = "pending";
465
- const LazyWrapper = defineComponent((ctx) => {
441
+ const LazyWrapper = component((ctx) => {
466
442
  const loadState = ctx.signal({
467
443
  state,
468
444
  tick: 0
@@ -511,30 +487,7 @@ function lazy(loader) {
511
487
  };
512
488
  return LazyWrapper;
513
489
  }
514
- /**
515
- * Suspense boundary component for handling async loading states.
516
- *
517
- * Wraps lazy-loaded components and shows a fallback while they load.
518
- *
519
- * @example
520
- * ```tsx
521
- * import { lazy, Suspense } from 'sigx';
522
- *
523
- * const LazyDashboard = lazy(() => import('./Dashboard'));
524
- *
525
- * // Basic usage
526
- * <Suspense fallback={<div>Loading...</div>}>
527
- * <LazyDashboard />
528
- * </Suspense>
529
- *
530
- * // With spinner component
531
- * <Suspense fallback={<Spinner size="large" />}>
532
- * <LazyDashboard />
533
- * <LazyCharts />
534
- * </Suspense>
535
- * ```
536
- */
537
- const Suspense = defineComponent((ctx) => {
490
+ const Suspense = component((ctx) => {
538
491
  const { props, slots } = ctx;
539
492
  const state = ctx.signal({
540
493
  isReady: false,
@@ -547,7 +500,7 @@ const Suspense = defineComponent((ctx) => {
547
500
  if (boundary.pending.size === 0) state.isReady = true;
548
501
  }
549
502
  };
550
- ctx.onMount(() => {
503
+ ctx.onMounted(() => {
551
504
  if (boundary.pending.size === 0) state.isReady = true;
552
505
  });
553
506
  return () => {
@@ -582,15 +535,9 @@ const Suspense = defineComponent((ctx) => {
582
535
  }
583
536
  };
584
537
  }, { name: "Suspense" });
585
- /**
586
- * Check if a component is a lazy-loaded component
587
- */
588
538
  function isLazyComponent(component) {
589
539
  return component && component.__lazy === true;
590
540
  }
591
-
592
- //#endregion
593
- //#region src/utils/index.ts
594
541
  var Utils = class {
595
542
  static isPromise(value) {
596
543
  return !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
@@ -602,22 +549,112 @@ function guid$1() {
602
549
  return (c == "x" ? r : r & 3 | 8).toString(16);
603
550
  });
604
551
  }
605
-
606
- //#endregion
607
- //#region src/models/index.ts
552
+ function createPropsAccessor(reactiveProps) {
553
+ return new Proxy(reactiveProps, {
554
+ get(target, key) {
555
+ if (typeof key === "symbol") return void 0;
556
+ return target[key];
557
+ },
558
+ has(target, key) {
559
+ if (typeof key === "symbol") return false;
560
+ return key in target;
561
+ },
562
+ ownKeys(target) {
563
+ return Object.keys(target);
564
+ },
565
+ getOwnPropertyDescriptor(target, key) {
566
+ if (typeof key === "symbol") return void 0;
567
+ if (key in target) return {
568
+ enumerable: true,
569
+ configurable: true,
570
+ writable: false
571
+ };
572
+ }
573
+ });
574
+ }
575
+ function createSlots(children, slotsFromProps) {
576
+ const versionSignal = signal$1({ v: 0 });
577
+ function extractNamedSlotsFromChildren(c) {
578
+ const defaultChildren = [];
579
+ const namedSlots = {};
580
+ if (c == null) return {
581
+ defaultChildren,
582
+ namedSlots
583
+ };
584
+ const items = Array.isArray(c) ? c : [c];
585
+ for (const child of items) if (child && typeof child === "object" && child.props && child.props.slot) {
586
+ const slotName = child.props.slot;
587
+ if (!namedSlots[slotName]) namedSlots[slotName] = [];
588
+ namedSlots[slotName].push(child);
589
+ } else defaultChildren.push(child);
590
+ return {
591
+ defaultChildren,
592
+ namedSlots
593
+ };
594
+ }
595
+ const slotsObj = {
596
+ _children: children,
597
+ _slotsFromProps: slotsFromProps || {},
598
+ _version: versionSignal,
599
+ _isPatching: false,
600
+ default: function() {
601
+ this._version.v;
602
+ const c = this._children;
603
+ const { defaultChildren } = extractNamedSlotsFromChildren(c);
604
+ return defaultChildren.filter((child) => child != null && child !== false && child !== true);
605
+ }
606
+ };
607
+ return new Proxy(slotsObj, { get(target, prop) {
608
+ if (prop in target) return target[prop];
609
+ if (typeof prop === "string") return function(scopedProps) {
610
+ target._version.v;
611
+ if (target._slotsFromProps && typeof target._slotsFromProps[prop] === "function") {
612
+ const result = target._slotsFromProps[prop](scopedProps);
613
+ if (result == null) return [];
614
+ return Array.isArray(result) ? result : [result];
615
+ }
616
+ const { namedSlots } = extractNamedSlotsFromChildren(target._children);
617
+ return namedSlots[prop] || [];
618
+ };
619
+ } });
620
+ }
621
+ function normalizeSubTree(result) {
622
+ if (result == null || result === false || result === true) return {
623
+ type: Text,
624
+ props: {},
625
+ key: null,
626
+ children: [],
627
+ dom: null,
628
+ text: ""
629
+ };
630
+ if (isComputed(result)) return normalizeSubTree(result.value);
631
+ if (Array.isArray(result)) return {
632
+ type: Fragment,
633
+ props: {},
634
+ key: null,
635
+ children: result,
636
+ dom: null
637
+ };
638
+ if (typeof result === "string" || typeof result === "number") return {
639
+ type: Text,
640
+ props: {},
641
+ key: null,
642
+ children: [],
643
+ dom: null,
644
+ text: result
645
+ };
646
+ return result;
647
+ }
608
648
  const guid = guid$1;
609
- let InstanceLifetimes = /* @__PURE__ */ function(InstanceLifetimes$1) {
610
- InstanceLifetimes$1[InstanceLifetimes$1["Transient"] = 0] = "Transient";
611
- InstanceLifetimes$1[InstanceLifetimes$1["Scoped"] = 1] = "Scoped";
612
- InstanceLifetimes$1[InstanceLifetimes$1["Singleton"] = 2] = "Singleton";
613
- return InstanceLifetimes$1;
649
+ let InstanceLifetimes = /* @__PURE__ */ function(InstanceLifetimes) {
650
+ InstanceLifetimes[InstanceLifetimes["Transient"] = 0] = "Transient";
651
+ InstanceLifetimes[InstanceLifetimes["Scoped"] = 1] = "Scoped";
652
+ InstanceLifetimes[InstanceLifetimes["Singleton"] = 2] = "Singleton";
653
+ return InstanceLifetimes;
614
654
  }({});
615
655
  function valueOf(obj) {
616
656
  return obj;
617
657
  }
618
-
619
- //#endregion
620
- //#region src/messaging/index.ts
621
658
  function createTopic(options) {
622
659
  let subscribers = [];
623
660
  const publish = (data) => {
@@ -630,7 +667,7 @@ function createTopic(options) {
630
667
  if (idx > -1) subscribers.splice(idx, 1);
631
668
  };
632
669
  try {
633
- onCleanup(unsubscribe);
670
+ onUnmounted(unsubscribe);
634
671
  } catch (e) {}
635
672
  return { unsubscribe };
636
673
  };
@@ -646,72 +683,10 @@ function createTopic(options) {
646
683
  function toSubscriber(topic) {
647
684
  return { subscribe: (handler) => topic.subscribe(handler) };
648
685
  }
649
-
650
- //#endregion
651
- //#region src/di/injectable.ts
652
- function inject(token) {
653
- const ctx = getCurrentInstance();
654
- if (!ctx) return void 0;
655
- let current = ctx;
656
- while (current) {
657
- if (current.provides && current.provides.has(token)) return current.provides.get(token);
658
- current = current.parent;
659
- }
660
- const appContext = getAppContext(ctx);
661
- if (appContext && appContext.provides.has(token)) return appContext.provides.get(token);
662
- }
663
- /**
664
- * Get the app context from the current component context
665
- */
666
- function getAppContext(ctx) {
667
- let current = ctx;
668
- while (current) {
669
- if (current._appContext) return current._appContext;
670
- current = current.parent;
671
- }
672
- return null;
673
- }
674
- /**
675
- * Inject the App instance (useful for plugins)
676
- */
677
- function injectApp() {
678
- return inject(AppContextKey);
679
- }
680
- function provide(token, value) {
681
- const ctx = getCurrentInstance();
682
- if (!ctx) {
683
- console.warn("provide called outside of component setup");
684
- return;
685
- }
686
- if (!ctx.provides) ctx.provides = /* @__PURE__ */ new Map();
687
- ctx.provides.set(token, value);
688
- }
689
- const globalInstances = /* @__PURE__ */ new Map();
690
- function defineInjectable(factory) {
691
- const token = factory;
692
- const useFn = () => {
693
- const injected = inject(token);
694
- if (injected) return injected;
695
- if (!globalInstances.has(token)) globalInstances.set(token, factory());
696
- return globalInstances.get(token);
697
- };
698
- useFn._factory = factory;
699
- useFn._token = token;
700
- return useFn;
701
- }
702
- function defineProvide(useFn) {
703
- const factory = useFn._factory;
704
- const token = useFn._token;
705
- if (!factory || !token) throw new Error("defineProvide must be called with a function created by defineInjectable");
706
- const instance = factory();
707
- provide(token, instance);
708
- return instance;
709
- }
710
-
711
- //#endregion
712
- //#region src/di/factory.ts
713
686
  var SubscriptionHandler = class {
714
- unsubs = [];
687
+ constructor() {
688
+ this.unsubs = [];
689
+ }
715
690
  add(unsub) {
716
691
  this.unsubs.push(unsub);
717
692
  }
@@ -737,7 +712,7 @@ function defineFactory(setup, lifetime, typeIdentifier) {
737
712
  };
738
713
  if (customDispose) customDispose(dispose);
739
714
  else try {
740
- onCleanup(() => dispose());
715
+ onUnmounted(() => dispose());
741
716
  } catch (e) {}
742
717
  return {
743
718
  ...result,
@@ -747,155 +722,59 @@ function defineFactory(setup, lifetime, typeIdentifier) {
747
722
  if (setup.length <= 1) return defineInjectable(() => factoryCreator());
748
723
  return factoryCreator;
749
724
  }
750
-
751
- //#endregion
752
- //#region src/stores/store.ts
753
- function defineStore(name, setup, lifetime = InstanceLifetimes.Scoped) {
754
- return defineFactory((ctxFactory, ...args) => {
755
- const scope = effectScope(true);
756
- let messages = [];
757
- const id = `${name}_${guid()}`;
758
- const result = setup({
759
- ...ctxFactory,
760
- defineState: (state) => {
761
- return defineState(state, id, scope, messages);
762
- },
763
- defineActions: (actions) => {
764
- return defineActions(actions, id, messages);
765
- }
766
- }, ...args);
767
- ctxFactory.onDeactivated(() => {
768
- scope.stop();
769
- messages?.forEach((m) => m.destroy());
770
- messages = null;
771
- });
772
- if (!result.name) result.name = id;
773
- return result;
774
- }, lifetime);
725
+ const CLIENT_DIRECTIVE_PREFIX = "client:";
726
+ const CLIENT_DIRECTIVES = [
727
+ "client:load",
728
+ "client:idle",
729
+ "client:visible",
730
+ "client:media",
731
+ "client:only"
732
+ ];
733
+ function filterClientDirectives(props) {
734
+ const filtered = {};
735
+ for (const key in props) if (!key.startsWith("client:")) filtered[key] = props[key];
736
+ return filtered;
775
737
  }
776
- function defineActions(actions, storeInstanceName, messages) {
777
- const events = {};
778
- const namespace = `${storeInstanceName}.actions.${guid()}`;
779
- const onDispatching = {};
780
- const onDispatched = {};
781
- const onFailure = {};
782
- const result = {
783
- onDispatching,
784
- onDispatched,
785
- onFailure
738
+ function getHydrationDirective(props) {
739
+ if (props["client:load"] !== void 0) return { strategy: "load" };
740
+ if (props["client:idle"] !== void 0) return { strategy: "idle" };
741
+ if (props["client:visible"] !== void 0) return { strategy: "visible" };
742
+ if (props["client:only"] !== void 0) return { strategy: "only" };
743
+ if (props["client:media"] !== void 0) return {
744
+ strategy: "media",
745
+ media: props["client:media"]
786
746
  };
787
- function getEvent(actionName, type) {
788
- const name = `${actionName}.${type}`;
789
- if (!events[name]) {
790
- events[name] = createTopic({
791
- namespace,
792
- name
793
- });
794
- messages.push(events[name]);
795
- }
796
- return events[name];
797
- }
798
- Object.keys(actions).forEach((actionName) => {
799
- onDispatching[actionName] = { subscribe: (fn) => {
800
- return getEvent(actionName, "onDispatching").subscribe(function() {
801
- fn.apply(this, arguments[0]);
802
- });
803
- } };
804
- onDispatched[actionName] = { subscribe: (fn) => {
805
- return getEvent(actionName, "onDispatched").subscribe(function() {
806
- const msg = arguments[0];
807
- const allArguments = [msg.result].concat(Array.from(msg.args));
808
- fn.apply(this, allArguments);
809
- });
810
- } };
811
- onFailure[actionName] = { subscribe: (fn) => {
812
- return getEvent(actionName, "onFailure").subscribe(function() {
813
- const msg = arguments[0];
814
- const allArguments = [msg.reason].concat(Array.from(msg.args));
815
- fn.apply(this, allArguments);
816
- });
817
- } };
818
- result[actionName] = function() {
819
- try {
820
- const currentArguments = arguments;
821
- getEvent(actionName, "onDispatching").publish(currentArguments);
822
- const returnedResult = actions[actionName].apply(this, currentArguments);
823
- if (Utils.isPromise(returnedResult)) returnedResult.then((result$1) => {
824
- getEvent(actionName, "onDispatched").publish({
825
- result: returnedResult,
826
- args: currentArguments
827
- });
828
- });
829
- else getEvent(actionName, "onDispatched").publish({
830
- result: returnedResult,
831
- args: currentArguments
832
- });
833
- return returnedResult;
834
- } catch (err) {
835
- console.error(err);
836
- getEvent(actionName, "onFailure").publish({
837
- reason: err,
838
- args: arguments
839
- });
840
- }
841
- };
842
- });
843
- return result;
747
+ return null;
844
748
  }
845
- function defineState(value, storeInstanceName, scope, messages) {
846
- const state = signal$1(value);
847
- const events = {};
848
- const mutate = {};
849
- function initProperty(key) {
850
- scope.run(() => {
851
- watch(() => state[key], (newValue) => {
852
- triggerEvent(key, newValue);
853
- }, {
854
- deep: true,
855
- immediate: true
856
- });
857
- });
858
- mutate[key] = (val) => {
859
- try {
860
- let newValue;
861
- if (typeof val === "function") newValue = val(state[key]);
862
- else newValue = val;
863
- state[key] = newValue;
864
- } catch (err) {
865
- console.error(err);
866
- }
867
- };
868
- const eventKey = `onMutated${key.charAt(0).toUpperCase()}${key.slice(1)}`;
869
- if (!events[eventKey]) {
870
- const topic = createTopic({
871
- namespace: `${storeInstanceName}.events`,
872
- name: eventKey
873
- });
874
- events[eventKey] = topic;
875
- messages.push(topic);
876
- }
877
- }
878
- function triggerEvent(name, value$1) {
879
- const keyString = name;
880
- events[`onMutated${keyString.charAt(0).toUpperCase()}${keyString.slice(1)}`]?.publish(value$1);
749
+ function hasClientDirective(props) {
750
+ for (const key in props) if (key.startsWith("client:")) return true;
751
+ return false;
752
+ }
753
+ function serializeProps(props) {
754
+ const filtered = filterClientDirectives(props);
755
+ const result = {};
756
+ let hasProps = false;
757
+ for (const key in filtered) {
758
+ const value = filtered[key];
759
+ if (key === "children" || key === "key" || key === "ref" || key === "slots") continue;
760
+ if (typeof value === "function") continue;
761
+ if (typeof value === "symbol") continue;
762
+ if (value === void 0) continue;
763
+ if (key.startsWith("on") && key.length > 2 && key[2] === key[2].toUpperCase()) continue;
764
+ try {
765
+ JSON.stringify(value);
766
+ result[key] = value;
767
+ hasProps = true;
768
+ } catch {}
881
769
  }
882
- if (value) Object.keys(value).forEach((key) => {
883
- initProperty(key);
884
- });
885
- return {
886
- state,
887
- events,
888
- mutate
889
- };
770
+ return hasProps ? result : void 0;
890
771
  }
891
-
892
- //#endregion
893
- //#region src/renderer.ts
894
- /**
895
- * Check if a vnode type is a component (has __setup)
896
- */
897
- function isComponent(type) {
898
- return typeof type === "function" && "__setup" in type;
772
+ function createEmit(reactiveProps) {
773
+ return (event, ...args) => {
774
+ const eventName = `on${event[0].toUpperCase() + event.slice(1)}`;
775
+ const handler = ("value" in reactiveProps ? reactiveProps.value : reactiveProps)?.[eventName];
776
+ if (handler && typeof handler === "function") handler(...args);
777
+ };
899
778
  }
900
779
  function createRenderer(options) {
901
780
  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;
@@ -929,7 +808,72 @@ function createRenderer(options) {
929
808
  container._vnode = null;
930
809
  }
931
810
  }
932
- function mount(vnode, container, before = null) {
811
+ const svgTags = new Set([
812
+ "svg",
813
+ "animate",
814
+ "animateMotion",
815
+ "animateTransform",
816
+ "circle",
817
+ "clipPath",
818
+ "defs",
819
+ "desc",
820
+ "ellipse",
821
+ "feBlend",
822
+ "feColorMatrix",
823
+ "feComponentTransfer",
824
+ "feComposite",
825
+ "feConvolveMatrix",
826
+ "feDiffuseLighting",
827
+ "feDisplacementMap",
828
+ "feDistantLight",
829
+ "feDropShadow",
830
+ "feFlood",
831
+ "feFuncA",
832
+ "feFuncB",
833
+ "feFuncG",
834
+ "feFuncR",
835
+ "feGaussianBlur",
836
+ "feImage",
837
+ "feMerge",
838
+ "feMergeNode",
839
+ "feMorphology",
840
+ "feOffset",
841
+ "fePointLight",
842
+ "feSpecularLighting",
843
+ "feSpotLight",
844
+ "feTile",
845
+ "feTurbulence",
846
+ "filter",
847
+ "foreignObject",
848
+ "g",
849
+ "image",
850
+ "line",
851
+ "linearGradient",
852
+ "marker",
853
+ "mask",
854
+ "metadata",
855
+ "mpath",
856
+ "path",
857
+ "pattern",
858
+ "polygon",
859
+ "polyline",
860
+ "radialGradient",
861
+ "rect",
862
+ "set",
863
+ "stop",
864
+ "switch",
865
+ "symbol",
866
+ "text",
867
+ "textPath",
868
+ "title",
869
+ "tspan",
870
+ "use",
871
+ "view"
872
+ ]);
873
+ function isSvgTag(tag) {
874
+ return svgTags.has(tag);
875
+ }
876
+ function mount(vnode, container, before = null, parentIsSVG = false) {
933
877
  if (vnode == null || vnode === false || vnode === true) return;
934
878
  if (vnode.type === Text) {
935
879
  const node = hostCreateText(String(vnode.text));
@@ -942,26 +886,29 @@ function createRenderer(options) {
942
886
  const anchor = hostCreateComment("");
943
887
  vnode.dom = anchor;
944
888
  hostInsert(anchor, container, before);
945
- if (vnode.children) vnode.children.forEach((child) => mount(child, container, anchor));
889
+ if (vnode.children) vnode.children.forEach((child) => mount(child, container, anchor, parentIsSVG));
946
890
  return;
947
891
  }
948
892
  if (isComponent(vnode.type)) {
949
893
  mountComponent(vnode, container, before, vnode.type.__setup);
950
894
  return;
951
895
  }
952
- const element = hostCreateElement(vnode.type);
896
+ const tag = vnode.type;
897
+ const isSVG = tag === "svg" || parentIsSVG && tag !== "foreignObject";
898
+ const element = hostCreateElement(tag, isSVG);
953
899
  vnode.dom = element;
954
900
  element.__vnode = vnode;
955
901
  if (vnode.props) {
956
- for (const key in vnode.props) if (key !== "children" && key !== "key" && key !== "ref") hostPatchProp(element, key, null, vnode.props[key]);
957
- if (vnode.props.ref) {
902
+ for (const key in vnode.props) if (key !== "children" && key !== "key" && key !== "ref") hostPatchProp(element, key, null, vnode.props[key], isSVG);
903
+ if (vnode.props.ref) untrack(() => {
958
904
  if (typeof vnode.props.ref === "function") vnode.props.ref(element);
959
905
  else if (typeof vnode.props.ref === "object") vnode.props.ref.current = element;
960
- }
906
+ });
961
907
  }
908
+ const childIsSVG = isSVG && tag !== "foreignObject";
962
909
  if (vnode.children) vnode.children.forEach((child) => {
963
910
  child.parent = vnode;
964
- mount(child, element);
911
+ mount(child, element, null, childIsSVG);
965
912
  });
966
913
  hostInsert(element, container, before);
967
914
  }
@@ -973,10 +920,10 @@ function createRenderer(options) {
973
920
  const subTree = internalVNode._subTree;
974
921
  if (subTree) unmount(subTree, container);
975
922
  if (vnode.dom) hostRemove(vnode.dom);
976
- if (vnode.props?.ref) {
923
+ if (vnode.props?.ref) untrack(() => {
977
924
  if (typeof vnode.props.ref === "function") vnode.props.ref(null);
978
925
  else if (typeof vnode.props.ref === "object") vnode.props.ref.current = null;
979
- }
926
+ });
980
927
  return;
981
928
  }
982
929
  if (vnode.type === Fragment) {
@@ -984,10 +931,10 @@ function createRenderer(options) {
984
931
  if (vnode.dom) hostRemove(vnode.dom);
985
932
  return;
986
933
  }
987
- if (vnode.props?.ref) {
934
+ if (vnode.props?.ref) untrack(() => {
988
935
  if (typeof vnode.props.ref === "function") vnode.props.ref(null);
989
936
  else if (vnode.props.ref && typeof vnode.props.ref === "object") vnode.props.ref.current = null;
990
- }
937
+ });
991
938
  if (vnode.children && vnode.children.length > 0) vnode.children.forEach((child) => unmount(child, vnode.dom));
992
939
  if (vnode.dom) hostRemove(vnode.dom);
993
940
  }
@@ -995,7 +942,7 @@ function createRenderer(options) {
995
942
  if (oldVNode === newVNode) return;
996
943
  if (!isSameVNode(oldVNode, newVNode)) {
997
944
  const parent = hostParentNode(oldVNode.dom) || container;
998
- const nextSibling = hostNextSibling(oldVNode.dom);
945
+ const nextSibling = oldVNode.dom ? hostNextSibling(oldVNode.dom) : null;
999
946
  unmount(oldVNode, parent);
1000
947
  mount(newVNode, parent, nextSibling);
1001
948
  return;
@@ -1010,12 +957,25 @@ function createRenderer(options) {
1010
957
  const props = oldInternal._componentProps;
1011
958
  newInternal._componentProps = props;
1012
959
  if (props) {
1013
- const newProps$1 = newVNode.props || {};
960
+ const newProps = newVNode.props || {};
961
+ const newModels = newVNode.props?.$models || {};
1014
962
  untrack(() => {
1015
- for (const key in newProps$1) if (key !== "children" && key !== "key" && key !== "ref") {
1016
- if (props[key] !== newProps$1[key]) props[key] = newProps$1[key];
963
+ for (const key in newProps) if (key !== "children" && key !== "key" && key !== "ref" && key !== "$models") {
964
+ if (props[key] !== newProps[key]) props[key] = newProps[key];
1017
965
  }
1018
- for (const key in props) if (!(key in newProps$1) && key !== "children" && key !== "key" && key !== "ref") delete props[key];
966
+ for (const modelKey in newModels) {
967
+ const newModel = newModels[modelKey];
968
+ const oldModel = props[modelKey];
969
+ if (isModel(newModel)) {
970
+ if (isModel(oldModel)) {
971
+ const [newObj, newKey] = newModel.binding;
972
+ const [oldObj, oldKey] = oldModel.binding;
973
+ if (newObj === oldObj && newKey === oldKey) continue;
974
+ }
975
+ props[modelKey] = newModel;
976
+ }
977
+ }
978
+ for (const key in props) if (!(key in newProps) && !(key in newModels) && key !== "children" && key !== "key" && key !== "ref" && key !== "$models") delete props[key];
1019
979
  });
1020
980
  }
1021
981
  const slotsRef = oldInternal._slots;
@@ -1043,27 +1003,43 @@ function createRenderer(options) {
1043
1003
  return;
1044
1004
  }
1045
1005
  if (newVNode.type === Fragment) {
1046
- patchChildren(oldVNode, newVNode, container);
1006
+ patchChildren(oldVNode, newVNode, container, false);
1047
1007
  return;
1048
1008
  }
1049
1009
  const element = newVNode.dom = oldVNode.dom;
1010
+ if (!element) {
1011
+ mount(newVNode, container);
1012
+ return;
1013
+ }
1014
+ const tag = newVNode.type;
1015
+ const isSVG = tag === "svg" || isSvgTag(tag);
1050
1016
  const oldProps = oldVNode.props || {};
1051
1017
  const newProps = newVNode.props || {};
1052
- for (const key in oldProps) if (!(key in newProps) && key !== "children" && key !== "key" && key !== "ref") hostPatchProp(element, key, oldProps[key], null);
1018
+ for (const key in oldProps) if (!(key in newProps) && key !== "children" && key !== "key" && key !== "ref") hostPatchProp(element, key, oldProps[key], null, isSVG);
1053
1019
  for (const key in newProps) {
1054
1020
  const oldValue = oldProps[key];
1055
1021
  const newValue = newProps[key];
1056
- if (key !== "children" && key !== "key" && key !== "ref" && oldValue !== newValue) hostPatchProp(element, key, oldValue, newValue);
1022
+ if (key !== "children" && key !== "key" && key !== "ref" && oldValue !== newValue) hostPatchProp(element, key, oldValue, newValue, isSVG);
1057
1023
  }
1058
- patchChildren(oldVNode, newVNode, element);
1024
+ patchChildren(oldVNode, newVNode, element, isSVG && tag !== "foreignObject");
1059
1025
  }
1060
- function patchChildren(oldVNode, newVNode, container) {
1026
+ function patchChildren(oldVNode, newVNode, container, parentIsSVG = false) {
1061
1027
  const oldChildren = oldVNode.children;
1062
1028
  const newChildren = newVNode.children;
1063
1029
  newChildren.forEach((c) => c.parent = newVNode);
1064
- reconcileChildrenArray(container, oldChildren, newChildren);
1030
+ reconcileChildrenArray(container, oldChildren, newChildren, parentIsSVG);
1065
1031
  }
1066
- function reconcileChildrenArray(parent, oldChildren, newChildren) {
1032
+ function checkDuplicateKeys(children) {
1033
+ if (process.env.NODE_ENV === "production") return;
1034
+ const seenKeys = /* @__PURE__ */ new Set();
1035
+ for (const child of children) if (child?.key != null) {
1036
+ const keyStr = String(child.key);
1037
+ if (seenKeys.has(keyStr)) console.warn(`[SignalX] Duplicate key "${child.key}" detected in list. Keys should be unique among siblings to ensure correct reconciliation. This may cause unexpected behavior when items are reordered, added, or removed.`);
1038
+ seenKeys.add(keyStr);
1039
+ }
1040
+ }
1041
+ function reconcileChildrenArray(parent, oldChildren, newChildren, parentIsSVG = false) {
1042
+ if (process.env.NODE_ENV !== "production") checkDuplicateKeys(newChildren);
1067
1043
  let oldStartIdx = 0;
1068
1044
  let oldEndIdx = oldChildren.length - 1;
1069
1045
  let oldStartVNode = oldChildren[0];
@@ -1105,13 +1081,13 @@ function createRenderer(options) {
1105
1081
  patch(vnodeToMove, newStartVNode, parent);
1106
1082
  oldChildren[idxInOld] = void 0;
1107
1083
  if (vnodeToMove.dom && oldStartVNode.dom) hostInsert(vnodeToMove.dom, parent, oldStartVNode.dom);
1108
- } else mount(newStartVNode, parent, oldStartVNode.dom);
1084
+ } else mount(newStartVNode, parent, oldStartVNode.dom, parentIsSVG);
1109
1085
  newStartVNode = newChildren[++newStartIdx];
1110
1086
  }
1111
1087
  if (oldStartIdx > oldEndIdx) {
1112
1088
  if (newStartIdx <= newEndIdx) {
1113
1089
  const anchor = newChildren[newEndIdx + 1] == null ? null : newChildren[newEndIdx + 1].dom;
1114
- for (let i = newStartIdx; i <= newEndIdx; i++) mount(newChildren[i], parent, anchor);
1090
+ for (let i = newStartIdx; i <= newEndIdx; i++) mount(newChildren[i], parent, anchor, parentIsSVG);
1115
1091
  }
1116
1092
  } else if (newStartIdx > newEndIdx) {
1117
1093
  for (let i = oldStartIdx; i <= oldEndIdx; i++) if (oldChildren[i]) unmount(oldChildren[i], parent);
@@ -1128,7 +1104,11 @@ function createRenderer(options) {
1128
1104
  const map = /* @__PURE__ */ new Map();
1129
1105
  for (let i = beginIdx; i <= endIdx; i++) {
1130
1106
  const key = children[i]?.key;
1131
- if (key != null) map.set(String(key), i);
1107
+ if (key != null) {
1108
+ const keyStr = String(key);
1109
+ if (process.env.NODE_ENV !== "production" && map.has(keyStr)) console.warn(`[SignalX] Duplicate key "${key}" detected in list. Keys should be unique among siblings to ensure correct reconciliation. This may cause unexpected behavior when items are reordered, added, or removed.`);
1110
+ map.set(keyStr, i);
1111
+ }
1132
1112
  }
1133
1113
  return map;
1134
1114
  }
@@ -1136,43 +1116,6 @@ function createRenderer(options) {
1136
1116
  for (let i = beginIdx; i <= endIdx; i++) if (children[i] && isSameVNode(children[i], newChild)) return i;
1137
1117
  return null;
1138
1118
  }
1139
- /**
1140
- * Creates a props accessor that can be called with defaults or accessed directly.
1141
- * After calling with defaults, direct property access uses those defaults.
1142
- */
1143
- function createPropsAccessor(reactiveProps) {
1144
- let defaults = {};
1145
- const proxy = new Proxy(function propsAccessor() {}, {
1146
- get(_, key) {
1147
- if (typeof key === "symbol") return void 0;
1148
- const value = reactiveProps[key];
1149
- return value != null ? value : defaults[key];
1150
- },
1151
- apply(_, __, args) {
1152
- if (args[0] && typeof args[0] === "object") defaults = {
1153
- ...defaults,
1154
- ...args[0]
1155
- };
1156
- return proxy;
1157
- },
1158
- has(_, key) {
1159
- if (typeof key === "symbol") return false;
1160
- return key in reactiveProps || key in defaults;
1161
- },
1162
- ownKeys() {
1163
- return [...new Set([...Object.keys(reactiveProps), ...Object.keys(defaults)])];
1164
- },
1165
- getOwnPropertyDescriptor(_, key) {
1166
- if (typeof key === "symbol") return void 0;
1167
- if (key in reactiveProps || key in defaults) return {
1168
- enumerable: true,
1169
- configurable: true,
1170
- writable: false
1171
- };
1172
- }
1173
- });
1174
- return proxy;
1175
- }
1176
1119
  function mountComponent(vnode, container, before, setup) {
1177
1120
  const anchor = hostCreateComment("");
1178
1121
  vnode.dom = anchor;
@@ -1180,14 +1123,21 @@ function createRenderer(options) {
1180
1123
  hostInsert(anchor, container, before);
1181
1124
  let exposed = null;
1182
1125
  let exposeCalled = false;
1183
- const { children, slots: slotsFromProps, ...propsData } = vnode.props || {};
1184
- const reactiveProps = signal$1(propsData);
1126
+ const { children, slots: slotsFromProps, $models: modelsData, ...propsData } = vnode.props || {};
1127
+ const propsWithModels = { ...propsData };
1128
+ if (modelsData) for (const modelKey in modelsData) {
1129
+ const modelValue = modelsData[modelKey];
1130
+ if (isModel(modelValue)) propsWithModels[modelKey] = modelValue;
1131
+ }
1132
+ const reactiveProps = signal$1(propsWithModels);
1185
1133
  const internalVNode = vnode;
1186
1134
  internalVNode._componentProps = reactiveProps;
1187
1135
  const slots = createSlots(children, slotsFromProps);
1188
1136
  internalVNode._slots = slots;
1137
+ const createdHooks = [];
1189
1138
  const mountHooks = [];
1190
- const cleanupHooks = [];
1139
+ const updatedHooks = [];
1140
+ const unmountHooks = [];
1191
1141
  const parentInstance = getCurrentInstance();
1192
1142
  const componentName = vnode.type.__name;
1193
1143
  const ctx = {
@@ -1195,16 +1145,19 @@ function createRenderer(options) {
1195
1145
  signal: signal$1,
1196
1146
  props: createPropsAccessor(reactiveProps),
1197
1147
  slots,
1198
- emit: (event, ...args) => {
1199
- const handler = reactiveProps[`on${event[0].toUpperCase() + event.slice(1)}`];
1200
- if (handler && typeof handler === "function") handler(...args);
1201
- },
1148
+ emit: createEmit(reactiveProps),
1202
1149
  parent: parentInstance,
1203
- onMount: (fn) => {
1150
+ onMounted: (fn) => {
1204
1151
  mountHooks.push(fn);
1205
1152
  },
1206
- onCleanup: (fn) => {
1207
- cleanupHooks.push(fn);
1153
+ onUnmounted: (fn) => {
1154
+ unmountHooks.push(fn);
1155
+ },
1156
+ onCreated: (fn) => {
1157
+ createdHooks.push(fn);
1158
+ },
1159
+ onUpdated: (fn) => {
1160
+ updatedHooks.push(fn);
1208
1161
  },
1209
1162
  expose: (exposedValue) => {
1210
1163
  exposed = exposedValue;
@@ -1213,8 +1166,9 @@ function createRenderer(options) {
1213
1166
  renderFn: null,
1214
1167
  update: () => {}
1215
1168
  };
1169
+ applyContextExtensions(ctx);
1216
1170
  ctx.__name = componentName;
1217
- if (currentAppContext) ctx._appContext = currentAppContext;
1171
+ if (!parentInstance && currentAppContext) provideAppContext(ctx, currentAppContext);
1218
1172
  const componentInstance = {
1219
1173
  name: componentName,
1220
1174
  ctx,
@@ -1223,8 +1177,11 @@ function createRenderer(options) {
1223
1177
  const prev = setCurrentInstance(ctx);
1224
1178
  let renderFn;
1225
1179
  try {
1226
- renderFn = setup(ctx);
1180
+ const setupResult = setup(ctx);
1181
+ 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 onMounted.`);
1182
+ renderFn = setupResult;
1227
1183
  notifyComponentCreated(currentAppContext, componentInstance);
1184
+ createdHooks.forEach((hook) => hook());
1228
1185
  } catch (err) {
1229
1186
  if (!handleComponentError(currentAppContext, err, componentInstance, "setup")) throw err;
1230
1187
  } finally {
@@ -1232,8 +1189,10 @@ function createRenderer(options) {
1232
1189
  }
1233
1190
  if (vnode.props?.ref) {
1234
1191
  const refValue = exposeCalled ? exposed : null;
1235
- if (typeof vnode.props.ref === "function") vnode.props.ref(refValue);
1236
- else if (vnode.props.ref && typeof vnode.props.ref === "object") vnode.props.ref.current = refValue;
1192
+ untrack(() => {
1193
+ if (typeof vnode.props.ref === "function") vnode.props.ref(refValue);
1194
+ else if (vnode.props.ref && typeof vnode.props.ref === "object") vnode.props.ref.current = refValue;
1195
+ });
1237
1196
  }
1238
1197
  if (renderFn) {
1239
1198
  ctx.renderFn = renderFn;
@@ -1247,6 +1206,7 @@ function createRenderer(options) {
1247
1206
  if (prevSubTree) {
1248
1207
  patch(prevSubTree, subTree, container);
1249
1208
  notifyComponentUpdated(currentAppContext, componentInstance);
1209
+ updatedHooks.forEach((hook) => hook());
1250
1210
  } else mount(subTree, container, anchor);
1251
1211
  internalVNode._subTree = subTree;
1252
1212
  } catch (err) {
@@ -1265,103 +1225,17 @@ function createRenderer(options) {
1265
1225
  notifyComponentMounted(currentAppContext, componentInstance);
1266
1226
  vnode.cleanup = () => {
1267
1227
  notifyComponentUnmounted(currentAppContext, componentInstance);
1268
- cleanupHooks.forEach((hook) => hook(mountCtx));
1228
+ unmountHooks.forEach((hook) => hook(mountCtx));
1269
1229
  };
1270
1230
  }
1271
- /**
1272
- * Create slots object from children and slots prop.
1273
- * Uses a version signal to trigger re-renders when children change.
1274
- * Supports named slots via:
1275
- * - `slots` prop object (e.g., slots={{ header: () => <div>...</div> }})
1276
- * - `slot` prop on children (e.g., <div slot="header">...</div>)
1277
- */
1278
- function createSlots(children, slotsFromProps) {
1279
- const versionSignal = signal$1({ v: 0 });
1280
- function extractNamedSlotsFromChildren(c) {
1281
- const defaultChildren = [];
1282
- const namedSlots = {};
1283
- if (c == null) return {
1284
- defaultChildren,
1285
- namedSlots
1286
- };
1287
- const items = Array.isArray(c) ? c : [c];
1288
- for (const child of items) if (child && typeof child === "object" && child.props && child.props.slot) {
1289
- const slotName = child.props.slot;
1290
- if (!namedSlots[slotName]) namedSlots[slotName] = [];
1291
- namedSlots[slotName].push(child);
1292
- } else defaultChildren.push(child);
1293
- return {
1294
- defaultChildren,
1295
- namedSlots
1296
- };
1297
- }
1298
- const slotsObj = {
1299
- _children: children,
1300
- _slotsFromProps: slotsFromProps || {},
1301
- _version: versionSignal,
1302
- _isPatching: false,
1303
- default: function() {
1304
- this._version.v;
1305
- const c = this._children;
1306
- const { defaultChildren } = extractNamedSlotsFromChildren(c);
1307
- return defaultChildren.filter((child) => child != null && child !== false && child !== true);
1308
- }
1309
- };
1310
- return new Proxy(slotsObj, { get(target, prop) {
1311
- if (prop in target) return target[prop];
1312
- if (typeof prop === "string") return function(scopedProps) {
1313
- target._version.v;
1314
- if (target._slotsFromProps && typeof target._slotsFromProps[prop] === "function") {
1315
- const result = target._slotsFromProps[prop](scopedProps);
1316
- if (result == null) return [];
1317
- return Array.isArray(result) ? result : [result];
1318
- }
1319
- const { namedSlots } = extractNamedSlotsFromChildren(target._children);
1320
- return namedSlots[prop] || [];
1321
- };
1322
- } });
1323
- }
1324
- /**
1325
- * Normalize render result to a VNode (wrapping arrays in Fragment)
1326
- * Note: Falsy values (null, undefined, false, true) from conditional rendering
1327
- * are handled by mount() which guards against them, so no filtering needed here.
1328
- */
1329
- function normalizeSubTree(result) {
1330
- if (Array.isArray(result)) return {
1331
- type: Fragment,
1332
- props: {},
1333
- key: null,
1334
- children: result,
1335
- dom: null
1336
- };
1337
- if (typeof result === "string" || typeof result === "number") return {
1338
- type: Text,
1339
- props: {},
1340
- key: null,
1341
- children: [],
1342
- dom: null,
1343
- text: result
1344
- };
1345
- return result;
1346
- }
1347
1231
  return {
1348
1232
  render,
1349
- createApp: (rootComponent) => {
1350
- return { mount(selectorOrContainer) {
1351
- let container = null;
1352
- if (typeof selectorOrContainer === "string") {
1353
- if (options.querySelector) container = options.querySelector(selectorOrContainer);
1354
- } else container = selectorOrContainer;
1355
- if (!container) {
1356
- console.warn(`Container not found: ${selectorOrContainer}`);
1357
- return;
1358
- }
1359
- render(rootComponent, container);
1360
- } };
1361
- }
1233
+ patch,
1234
+ mount,
1235
+ unmount,
1236
+ mountComponent
1362
1237
  };
1363
1238
  }
1239
+ export { CLIENT_DIRECTIVES, CLIENT_DIRECTIVE_PREFIX, Fragment, InstanceLifetimes, SubscriptionHandler, Suspense, Text, Utils, applyContextExtensions, component, compound, createEmit, createModel, createModelFromBinding, createPropsAccessor, createPropsProxy, createRenderer, createSlots, createTopic, defineApp, defineFactory, defineInjectable, defineProvide, filterClientDirectives, getAppContextToken, getComponentMeta, getComponentPlugins, getCurrentInstance, getDefaultMount, getHydrationDirective, getModelSymbol, getPlatformModelProcessor, guid, handleComponentError, hasClientDirective, isComponent, isLazyComponent, isModel, jsx, jsxDEV, jsxs, lazy, normalizeSubTree, notifyComponentCreated, notifyComponentMounted, notifyComponentUnmounted, notifyComponentUpdated, onCreated, onMounted, onUnmounted, onUpdated, provideAppContext, registerComponentPlugin, registerContextExtension, registerPendingPromise, serializeProps, setCurrentInstance, setDefaultMount, setPlatformModelProcessor, signal, toSubscriber, useAppContext, valueOf };
1364
1240
 
1365
- //#endregion
1366
- export { AppContextKey, Fragment, InstanceLifetimes, SubscriptionHandler, Suspense, Text, Utils, createPropsProxy, createRenderer, createTopic, defineApp, defineComponent, defineFactory, defineInjectable, defineProvide, defineStore, getComponentMeta, getComponentPlugins, getCurrentInstance, getDefaultMount, getPlatformSyncProcessor, guid, handleComponentError, inject, injectApp, isLazyComponent, jsx, jsxDEV, jsxs, lazy, notifyComponentCreated, notifyComponentMounted, notifyComponentUnmounted, notifyComponentUpdated, onCleanup, onMount, provide, registerComponentPlugin, registerPendingPromise, setCurrentInstance, setDefaultMount, setPlatformSyncProcessor, signal, toSubscriber, valueOf };
1367
1241
  //# sourceMappingURL=index.js.map