eleva 1.0.0-rc.1 → 1.0.0-rc.11

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 (65) hide show
  1. package/README.md +505 -41
  2. package/dist/eleva-plugins.cjs.js +3397 -0
  3. package/dist/eleva-plugins.cjs.js.map +1 -0
  4. package/dist/eleva-plugins.esm.js +3392 -0
  5. package/dist/eleva-plugins.esm.js.map +1 -0
  6. package/dist/eleva-plugins.umd.js +3403 -0
  7. package/dist/eleva-plugins.umd.js.map +1 -0
  8. package/dist/eleva-plugins.umd.min.js +3 -0
  9. package/dist/eleva-plugins.umd.min.js.map +1 -0
  10. package/dist/eleva.cjs.js +617 -118
  11. package/dist/eleva.cjs.js.map +1 -1
  12. package/dist/eleva.d.ts +612 -75
  13. package/dist/eleva.esm.js +617 -118
  14. package/dist/eleva.esm.js.map +1 -1
  15. package/dist/eleva.umd.js +617 -118
  16. package/dist/eleva.umd.js.map +1 -1
  17. package/dist/eleva.umd.min.js +2 -2
  18. package/dist/eleva.umd.min.js.map +1 -1
  19. package/dist/plugins/attr.umd.js +232 -0
  20. package/dist/plugins/attr.umd.js.map +1 -0
  21. package/dist/plugins/attr.umd.min.js +3 -0
  22. package/dist/plugins/attr.umd.min.js.map +1 -0
  23. package/dist/plugins/props.umd.js +712 -0
  24. package/dist/plugins/props.umd.js.map +1 -0
  25. package/dist/plugins/props.umd.min.js +3 -0
  26. package/dist/plugins/props.umd.min.js.map +1 -0
  27. package/dist/plugins/router.umd.js +1808 -0
  28. package/dist/plugins/router.umd.js.map +1 -0
  29. package/dist/plugins/router.umd.min.js +3 -0
  30. package/dist/plugins/router.umd.min.js.map +1 -0
  31. package/dist/plugins/store.umd.js +685 -0
  32. package/dist/plugins/store.umd.js.map +1 -0
  33. package/dist/plugins/store.umd.min.js +3 -0
  34. package/dist/plugins/store.umd.min.js.map +1 -0
  35. package/package.json +107 -45
  36. package/src/core/Eleva.js +247 -63
  37. package/src/modules/Emitter.js +98 -8
  38. package/src/modules/Renderer.js +66 -36
  39. package/src/modules/Signal.js +85 -8
  40. package/src/modules/TemplateEngine.js +121 -13
  41. package/src/plugins/Attr.js +255 -0
  42. package/src/plugins/Props.js +593 -0
  43. package/src/plugins/Router.js +1922 -0
  44. package/src/plugins/Store.js +744 -0
  45. package/src/plugins/index.js +40 -0
  46. package/types/core/Eleva.d.ts +217 -50
  47. package/types/core/Eleva.d.ts.map +1 -1
  48. package/types/modules/Emitter.d.ts +111 -12
  49. package/types/modules/Emitter.d.ts.map +1 -1
  50. package/types/modules/Renderer.d.ts +68 -3
  51. package/types/modules/Renderer.d.ts.map +1 -1
  52. package/types/modules/Signal.d.ts +92 -10
  53. package/types/modules/Signal.d.ts.map +1 -1
  54. package/types/modules/TemplateEngine.d.ts +131 -15
  55. package/types/modules/TemplateEngine.d.ts.map +1 -1
  56. package/types/plugins/Attr.d.ts +29 -0
  57. package/types/plugins/Attr.d.ts.map +1 -0
  58. package/types/plugins/Props.d.ts +49 -0
  59. package/types/plugins/Props.d.ts.map +1 -0
  60. package/types/plugins/Router.d.ts +1000 -0
  61. package/types/plugins/Router.d.ts.map +1 -0
  62. package/types/plugins/Store.d.ts +87 -0
  63. package/types/plugins/Store.d.ts.map +1 -0
  64. package/types/plugins/index.d.ts +5 -0
  65. package/types/plugins/index.d.ts.map +1 -0
@@ -0,0 +1,712 @@
1
+ /*! Eleva Props Plugin v1.0.0-rc.11 | MIT License | https://elevajs.com */
2
+ (function (global, factory) {
3
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
4
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
5
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ElevaPropsPlugin = {}));
6
+ })(this, (function (exports) { 'use strict';
7
+
8
+ // ============================================================================
9
+ // TYPE DEFINITIONS - TypeScript-friendly JSDoc types for IDE support
10
+ // ============================================================================
11
+
12
+ /**
13
+ * @typedef {Record<string, unknown>} TemplateData
14
+ * Data context for template interpolation
15
+ */
16
+
17
+ /**
18
+ * @typedef {string} TemplateString
19
+ * A string containing {{ expression }} interpolation markers
20
+ */
21
+
22
+ /**
23
+ * @typedef {string} Expression
24
+ * A JavaScript expression to be evaluated in the data context
25
+ */
26
+
27
+ /**
28
+ * @typedef {unknown} EvaluationResult
29
+ * The result of evaluating an expression (string, number, boolean, object, etc.)
30
+ */
31
+
32
+ /**
33
+ * @class 🔒 TemplateEngine
34
+ * @classdesc A secure template engine that handles interpolation and dynamic attribute parsing.
35
+ * Provides a way to evaluate expressions in templates.
36
+ * All methods are static and can be called directly on the class.
37
+ *
38
+ * Template Syntax:
39
+ * - `{{ expression }}` - Interpolate any JavaScript expression
40
+ * - `{{ variable }}` - Access data properties directly
41
+ * - `{{ object.property }}` - Access nested properties
42
+ * - `{{ condition ? a : b }}` - Ternary expressions
43
+ * - `{{ func(arg) }}` - Call functions from data context
44
+ *
45
+ * @example
46
+ * // Basic interpolation
47
+ * const template = "Hello, {{name}}!";
48
+ * const data = { name: "World" };
49
+ * const result = TemplateEngine.parse(template, data);
50
+ * // Result: "Hello, World!"
51
+ *
52
+ * @example
53
+ * // Nested properties
54
+ * const template = "Welcome, {{user.name}}!";
55
+ * const data = { user: { name: "John" } };
56
+ * const result = TemplateEngine.parse(template, data);
57
+ * // Result: "Welcome, John!"
58
+ *
59
+ * @example
60
+ * // Expressions
61
+ * const template = "Status: {{active ? 'Online' : 'Offline'}}";
62
+ * const data = { active: true };
63
+ * const result = TemplateEngine.parse(template, data);
64
+ * // Result: "Status: Online"
65
+ *
66
+ * @example
67
+ * // With Signal values
68
+ * const template = "Count: {{count.value}}";
69
+ * const data = { count: { value: 42 } };
70
+ * const result = TemplateEngine.parse(template, data);
71
+ * // Result: "Count: 42"
72
+ */
73
+ class TemplateEngine {
74
+ /**
75
+ * Regular expression for matching template expressions in the format {{ expression }}
76
+ * Matches: {{ anything }} with optional whitespace inside braces
77
+ *
78
+ * @static
79
+ * @private
80
+ * @type {RegExp}
81
+ */
82
+ static expressionPattern = /\{\{\s*(.*?)\s*\}\}/g;
83
+
84
+ /**
85
+ * Parses a template string, replacing expressions with their evaluated values.
86
+ * Expressions are evaluated in the provided data context.
87
+ *
88
+ * @public
89
+ * @static
90
+ * @param {TemplateString|unknown} template - The template string to parse.
91
+ * @param {TemplateData} data - The data context for evaluating expressions.
92
+ * @returns {string} The parsed template with expressions replaced by their values.
93
+ *
94
+ * @example
95
+ * // Simple variables
96
+ * TemplateEngine.parse("Hello, {{name}}!", { name: "World" });
97
+ * // Result: "Hello, World!"
98
+ *
99
+ * @example
100
+ * // Nested properties
101
+ * TemplateEngine.parse("{{user.name}} is {{user.age}} years old", {
102
+ * user: { name: "John", age: 30 }
103
+ * });
104
+ * // Result: "John is 30 years old"
105
+ *
106
+ * @example
107
+ * // Multiple expressions
108
+ * TemplateEngine.parse("{{greeting}}, {{name}}! You have {{count}} messages.", {
109
+ * greeting: "Hello",
110
+ * name: "User",
111
+ * count: 5
112
+ * });
113
+ * // Result: "Hello, User! You have 5 messages."
114
+ *
115
+ * @example
116
+ * // With conditionals
117
+ * TemplateEngine.parse("Status: {{online ? 'Active' : 'Inactive'}}", {
118
+ * online: true
119
+ * });
120
+ * // Result: "Status: Active"
121
+ */
122
+ static parse(template, data) {
123
+ if (typeof template !== "string") return template;
124
+ return template.replace(this.expressionPattern, (_, expression) => this.evaluate(expression, data));
125
+ }
126
+
127
+ /**
128
+ * Evaluates an expression in the context of the provided data object.
129
+ *
130
+ * Note: This does not provide a true sandbox and evaluated expressions may access global scope.
131
+ * The use of the `with` statement is necessary for expression evaluation but has security implications.
132
+ * Only use with trusted templates. User input should never be directly interpolated.
133
+ *
134
+ * @public
135
+ * @static
136
+ * @param {Expression|unknown} expression - The expression to evaluate.
137
+ * @param {TemplateData} data - The data context for evaluation.
138
+ * @returns {EvaluationResult} The result of the evaluation, or an empty string if evaluation fails.
139
+ *
140
+ * @example
141
+ * // Property access
142
+ * TemplateEngine.evaluate("user.name", { user: { name: "John" } });
143
+ * // Result: "John"
144
+ *
145
+ * @example
146
+ * // Numeric values
147
+ * TemplateEngine.evaluate("user.age", { user: { age: 30 } });
148
+ * // Result: 30
149
+ *
150
+ * @example
151
+ * // Expressions
152
+ * TemplateEngine.evaluate("items.length > 0", { items: [1, 2, 3] });
153
+ * // Result: true
154
+ *
155
+ * @example
156
+ * // Function calls
157
+ * TemplateEngine.evaluate("formatDate(date)", {
158
+ * date: new Date(),
159
+ * formatDate: (d) => d.toISOString()
160
+ * });
161
+ * // Result: "2024-01-01T00:00:00.000Z"
162
+ *
163
+ * @example
164
+ * // Failed evaluation returns empty string
165
+ * TemplateEngine.evaluate("nonexistent.property", {});
166
+ * // Result: ""
167
+ */
168
+ static evaluate(expression, data) {
169
+ if (typeof expression !== "string") return expression;
170
+ try {
171
+ return new Function("data", `with(data) { return ${expression}; }`)(data);
172
+ } catch {
173
+ return "";
174
+ }
175
+ }
176
+ }
177
+
178
+ /**
179
+ * @class 🎯 PropsPlugin
180
+ * @classdesc A plugin that extends Eleva's props data handling to support any type of data structure
181
+ * with automatic type detection, parsing, and reactive prop updates. This plugin enables seamless
182
+ * passing of complex data types from parent to child components without manual parsing.
183
+ *
184
+ * Core Features:
185
+ * - Automatic type detection and parsing (strings, numbers, booleans, objects, arrays, dates, etc.)
186
+ * - Support for complex data structures including nested objects and arrays
187
+ * - Reactive props that automatically update when parent data changes
188
+ * - Comprehensive error handling with custom error callbacks
189
+ * - Simple configuration with minimal setup required
190
+ *
191
+ * @example
192
+ * // Install the plugin
193
+ * const app = new Eleva("myApp");
194
+ * app.use(PropsPlugin, {
195
+ * enableAutoParsing: true,
196
+ * enableReactivity: true,
197
+ * onError: (error, value) => {
198
+ * console.error('Props parsing error:', error, value);
199
+ * }
200
+ * });
201
+ *
202
+ * // Use complex props in components
203
+ * app.component("UserCard", {
204
+ * template: (ctx) => `
205
+ * <div class="user-info-container"
206
+ * :user='${JSON.stringify(ctx.user.value)}'
207
+ * :permissions='${JSON.stringify(ctx.permissions.value)}'
208
+ * :settings='${JSON.stringify(ctx.settings.value)}'>
209
+ * </div>
210
+ * `,
211
+ * children: {
212
+ * '.user-info-container': 'UserInfo'
213
+ * }
214
+ * });
215
+ *
216
+ * app.component("UserInfo", {
217
+ * setup({ props }) {
218
+ * return {
219
+ * user: props.user, // Automatically parsed object
220
+ * permissions: props.permissions, // Automatically parsed array
221
+ * settings: props.settings // Automatically parsed object
222
+ * };
223
+ * }
224
+ * });
225
+ */
226
+ const PropsPlugin = {
227
+ /**
228
+ * Unique identifier for the plugin
229
+ * @type {string}
230
+ */
231
+ name: "props",
232
+ /**
233
+ * Plugin version
234
+ * @type {string}
235
+ */
236
+ version: "1.0.0-rc.11",
237
+ /**
238
+ * Plugin description
239
+ * @type {string}
240
+ */
241
+ description: "Advanced props data handling for complex data structures with automatic type detection and reactivity",
242
+ /**
243
+ * Installs the plugin into the Eleva instance
244
+ *
245
+ * @param {Object} eleva - The Eleva instance
246
+ * @param {Object} options - Plugin configuration options
247
+ * @param {boolean} [options.enableAutoParsing=true] - Enable automatic type detection and parsing
248
+ * @param {boolean} [options.enableReactivity=true] - Enable reactive prop updates using Eleva's signal system
249
+ * @param {Function} [options.onError=null] - Error handler function called when parsing fails
250
+ *
251
+ * @example
252
+ * // Basic installation
253
+ * app.use(PropsPlugin);
254
+ *
255
+ * // Installation with custom options
256
+ * app.use(PropsPlugin, {
257
+ * enableAutoParsing: true,
258
+ * enableReactivity: false,
259
+ * onError: (error, value) => {
260
+ * console.error('Props parsing error:', error, value);
261
+ * }
262
+ * });
263
+ */
264
+ install(eleva, options = {}) {
265
+ const {
266
+ enableAutoParsing = true,
267
+ enableReactivity = true,
268
+ onError = null
269
+ } = options;
270
+
271
+ /**
272
+ * Detects the type of a given value
273
+ * @private
274
+ * @param {any} value - The value to detect type for
275
+ * @returns {string} The detected type ('string', 'number', 'boolean', 'object', 'array', 'date', 'map', 'set', 'function', 'null', 'undefined', 'unknown')
276
+ *
277
+ * @example
278
+ * detectType("hello") // → "string"
279
+ * detectType(42) // → "number"
280
+ * detectType(true) // → "boolean"
281
+ * detectType([1, 2, 3]) // → "array"
282
+ * detectType({}) // → "object"
283
+ * detectType(new Date()) // → "date"
284
+ * detectType(null) // → "null"
285
+ */
286
+ const detectType = value => {
287
+ if (value === null) return "null";
288
+ if (value === undefined) return "undefined";
289
+ if (typeof value === "boolean") return "boolean";
290
+ if (typeof value === "number") return "number";
291
+ if (typeof value === "string") return "string";
292
+ if (typeof value === "function") return "function";
293
+ if (value instanceof Date) return "date";
294
+ if (value instanceof Map) return "map";
295
+ if (value instanceof Set) return "set";
296
+ if (Array.isArray(value)) return "array";
297
+ if (typeof value === "object") return "object";
298
+ return "unknown";
299
+ };
300
+
301
+ /**
302
+ * Parses a prop value with automatic type detection
303
+ * @private
304
+ * @param {any} value - The value to parse
305
+ * @returns {any} The parsed value with appropriate type
306
+ *
307
+ * @description
308
+ * This function automatically detects and parses different data types from string values:
309
+ * - Special strings: "true" → true, "false" → false, "null" → null, "undefined" → undefined
310
+ * - JSON objects/arrays: '{"key": "value"}' → {key: "value"}, '[1, 2, 3]' → [1, 2, 3]
311
+ * - Boolean-like strings: "1" → true, "0" → false, "" → true
312
+ * - Numeric strings: "42" → 42, "3.14" → 3.14
313
+ * - Date strings: "2023-01-01T00:00:00.000Z" → Date object
314
+ * - Other strings: returned as-is
315
+ *
316
+ * @example
317
+ * parsePropValue("true") // → true
318
+ * parsePropValue("42") // → 42
319
+ * parsePropValue('{"key": "val"}') // → {key: "val"}
320
+ * parsePropValue('[1, 2, 3]') // → [1, 2, 3]
321
+ * parsePropValue("hello") // → "hello"
322
+ */
323
+ const parsePropValue = value => {
324
+ try {
325
+ // Handle non-string values - return as-is
326
+ if (typeof value !== "string") {
327
+ return value;
328
+ }
329
+
330
+ // Handle special string patterns first
331
+ if (value === "true") return true;
332
+ if (value === "false") return false;
333
+ if (value === "null") return null;
334
+ if (value === "undefined") return undefined;
335
+
336
+ // Try to parse as JSON (for objects and arrays)
337
+ // This handles complex data structures like objects and arrays
338
+ if (value.startsWith("{") || value.startsWith("[")) {
339
+ try {
340
+ return JSON.parse(value);
341
+ } catch (e) {
342
+ // Not valid JSON, throw error to trigger error handler
343
+ throw new Error(`Invalid JSON: ${value}`);
344
+ }
345
+ }
346
+
347
+ // Handle boolean-like strings (including "1" and "0")
348
+ // These are common in HTML attributes and should be treated as booleans
349
+ if (value === "1") return true;
350
+ if (value === "0") return false;
351
+ if (value === "") return true; // Empty string is truthy in HTML attributes
352
+
353
+ // Handle numeric strings (after boolean check to avoid conflicts)
354
+ // This ensures "0" is treated as boolean false, not number 0
355
+ if (!isNaN(value) && value !== "" && !isNaN(parseFloat(value))) {
356
+ return Number(value);
357
+ }
358
+
359
+ // Handle date strings (ISO format)
360
+ // Recognizes standard ISO date format and converts to Date object
361
+ if (value.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)) {
362
+ const date = new Date(value);
363
+ if (!isNaN(date.getTime())) {
364
+ return date;
365
+ }
366
+ }
367
+
368
+ // Return as string if no other parsing applies
369
+ // This is the fallback for regular text strings
370
+ return value;
371
+ } catch (error) {
372
+ // Call error handler if provided
373
+ if (onError) {
374
+ onError(error, value);
375
+ }
376
+ // Fallback to original value to prevent breaking the application
377
+ return value;
378
+ }
379
+ };
380
+
381
+ /**
382
+ * Enhanced props extraction with automatic type detection
383
+ * @private
384
+ * @param {HTMLElement} element - The DOM element to extract props from
385
+ * @returns {Object} Object containing parsed props with appropriate types
386
+ *
387
+ * @description
388
+ * Extracts props from DOM element attributes that start with ":" and automatically
389
+ * parses them to their appropriate types. Removes the attributes from the element
390
+ * after extraction.
391
+ *
392
+ * @example
393
+ * // HTML: <div :name="John" :age="30" :active="true" :data='{"key": "value"}'></div>
394
+ * const props = extractProps(element);
395
+ * // Result: { name: "John", age: 30, active: true, data: {key: "value"} }
396
+ */
397
+ const extractProps = element => {
398
+ const props = {};
399
+ const attrs = element.attributes;
400
+
401
+ // Iterate through attributes in reverse order to handle removal correctly
402
+ for (let i = attrs.length - 1; i >= 0; i--) {
403
+ const attr = attrs[i];
404
+ // Only process attributes that start with ":" (prop attributes)
405
+ if (attr.name.startsWith(":")) {
406
+ const propName = attr.name.slice(1); // Remove the ":" prefix
407
+ // Parse the value if auto-parsing is enabled, otherwise use as-is
408
+ const parsedValue = enableAutoParsing ? parsePropValue(attr.value) : attr.value;
409
+ props[propName] = parsedValue;
410
+ // Remove the attribute from the DOM element after extraction
411
+ element.removeAttribute(attr.name);
412
+ }
413
+ }
414
+ return props;
415
+ };
416
+
417
+ /**
418
+ * Creates reactive props using Eleva's signal system
419
+ * @private
420
+ * @param {Object} props - The props object to make reactive
421
+ * @returns {Object} Object containing reactive props (Eleva signals)
422
+ *
423
+ * @description
424
+ * Converts regular prop values into Eleva signals for reactive updates.
425
+ * If a value is already a signal, it's passed through unchanged.
426
+ *
427
+ * @example
428
+ * const props = { name: "John", age: 30, active: true };
429
+ * const reactiveProps = createReactiveProps(props);
430
+ * // Result: {
431
+ * // name: Signal("John"),
432
+ * // age: Signal(30),
433
+ * // active: Signal(true)
434
+ * // }
435
+ */
436
+ const createReactiveProps = props => {
437
+ const reactiveProps = {};
438
+
439
+ // Convert each prop value to a reactive signal
440
+ Object.entries(props).forEach(([key, value]) => {
441
+ // Check if value is already a signal (has 'value' and 'watch' properties)
442
+ if (value && typeof value === "object" && "value" in value && "watch" in value) {
443
+ // Value is already a signal, use it as-is
444
+ reactiveProps[key] = value;
445
+ } else {
446
+ // Create new signal for the prop value to make it reactive
447
+ reactiveProps[key] = new eleva.signal(value);
448
+ }
449
+ });
450
+ return reactiveProps;
451
+ };
452
+
453
+ // Override Eleva's internal _extractProps method with our enhanced version
454
+ eleva._extractProps = extractProps;
455
+
456
+ // Override Eleva's mount method to apply enhanced prop handling
457
+ const originalMount = eleva.mount;
458
+ eleva.mount = async (container, compName, props = {}) => {
459
+ // Create reactive props if reactivity is enabled
460
+ const enhancedProps = enableReactivity ? createReactiveProps(props) : props;
461
+
462
+ // Call the original mount method with enhanced props
463
+ return await originalMount.call(eleva, container, compName, enhancedProps);
464
+ };
465
+
466
+ // Override Eleva's _mountComponents method to enable signal reference passing
467
+ const originalMountComponents = eleva._mountComponents;
468
+
469
+ // Cache to store parent contexts by container element
470
+ const parentContextCache = new WeakMap();
471
+ // Store child instances that need signal linking
472
+ const pendingSignalLinks = new Set();
473
+ eleva._mountComponents = async (container, children, childInstances) => {
474
+ for (const [selector, component] of Object.entries(children)) {
475
+ if (!selector) continue;
476
+ for (const el of container.querySelectorAll(selector)) {
477
+ if (!(el instanceof HTMLElement)) continue;
478
+
479
+ // Extract props from DOM attributes
480
+ const extractedProps = eleva._extractProps(el);
481
+
482
+ // Get parent context to check for signal references
483
+ let enhancedProps = extractedProps;
484
+
485
+ // Try to find parent context by looking up the DOM tree
486
+ let parentContext = parentContextCache.get(container);
487
+ if (!parentContext) {
488
+ let currentElement = container;
489
+ while (currentElement && !parentContext) {
490
+ if (currentElement._eleva_instance && currentElement._eleva_instance.data) {
491
+ parentContext = currentElement._eleva_instance.data;
492
+ // Cache the parent context for future use
493
+ parentContextCache.set(container, parentContext);
494
+ break;
495
+ }
496
+ currentElement = currentElement.parentElement;
497
+ }
498
+ }
499
+ if (enableReactivity && parentContext) {
500
+ const signalProps = {};
501
+
502
+ // Check each extracted prop to see if there's a matching signal in parent context
503
+ Object.keys(extractedProps).forEach(propName => {
504
+ if (parentContext[propName] && parentContext[propName] instanceof eleva.signal) {
505
+ // Found a signal in parent context with the same name as the prop
506
+ // Pass the signal reference instead of creating a new one
507
+ signalProps[propName] = parentContext[propName];
508
+ }
509
+ });
510
+
511
+ // Merge signal props with regular props (signal props take precedence)
512
+ enhancedProps = {
513
+ ...extractedProps,
514
+ ...signalProps
515
+ };
516
+ }
517
+
518
+ // Create reactive props for non-signal props only
519
+ let finalProps = enhancedProps;
520
+ if (enableReactivity) {
521
+ // Only create reactive props for values that aren't already signals
522
+ const nonSignalProps = {};
523
+ Object.entries(enhancedProps).forEach(([key, value]) => {
524
+ if (!(value && typeof value === "object" && "value" in value && "watch" in value)) {
525
+ // This is not a signal, create a reactive prop for it
526
+ nonSignalProps[key] = value;
527
+ }
528
+ });
529
+
530
+ // Create reactive props only for non-signal values
531
+ const reactiveNonSignalProps = createReactiveProps(nonSignalProps);
532
+
533
+ // Merge signal props with reactive non-signal props
534
+ finalProps = {
535
+ ...reactiveNonSignalProps,
536
+ ...enhancedProps // Signal props take precedence
537
+ };
538
+ }
539
+
540
+ /** @type {MountResult} */
541
+ const instance = await eleva.mount(el, component, finalProps);
542
+ if (instance && !childInstances.includes(instance)) {
543
+ childInstances.push(instance);
544
+
545
+ // If we have extracted props but no parent context yet, mark for later signal linking
546
+ if (enableReactivity && Object.keys(extractedProps).length > 0 && !parentContext) {
547
+ pendingSignalLinks.add({
548
+ instance,
549
+ extractedProps,
550
+ container,
551
+ component
552
+ });
553
+ }
554
+ }
555
+ }
556
+ }
557
+
558
+ // After mounting all children, try to link signals for pending instances
559
+ if (enableReactivity && pendingSignalLinks.size > 0) {
560
+ for (const pending of pendingSignalLinks) {
561
+ const {
562
+ instance,
563
+ extractedProps,
564
+ container,
565
+ component
566
+ } = pending;
567
+
568
+ // Try to find parent context again
569
+ let parentContext = parentContextCache.get(container);
570
+ if (!parentContext) {
571
+ let currentElement = container;
572
+ while (currentElement && !parentContext) {
573
+ if (currentElement._eleva_instance && currentElement._eleva_instance.data) {
574
+ parentContext = currentElement._eleva_instance.data;
575
+ parentContextCache.set(container, parentContext);
576
+ break;
577
+ }
578
+ currentElement = currentElement.parentElement;
579
+ }
580
+ }
581
+ if (parentContext) {
582
+ const signalProps = {};
583
+
584
+ // Check each extracted prop to see if there's a matching signal in parent context
585
+ Object.keys(extractedProps).forEach(propName => {
586
+ if (parentContext[propName] && parentContext[propName] instanceof eleva.signal) {
587
+ signalProps[propName] = parentContext[propName];
588
+ }
589
+ });
590
+
591
+ // Update the child instance's data with signal references
592
+ if (Object.keys(signalProps).length > 0) {
593
+ Object.assign(instance.data, signalProps);
594
+
595
+ // Set up signal watchers for the newly linked signals
596
+ Object.keys(signalProps).forEach(propName => {
597
+ const signal = signalProps[propName];
598
+ if (signal && typeof signal.watch === "function") {
599
+ signal.watch(newValue => {
600
+ // Trigger a re-render of the child component when the signal changes
601
+ const childComponent = eleva._components.get(component) || component;
602
+ if (childComponent && childComponent.template) {
603
+ const templateResult = typeof childComponent.template === "function" ? childComponent.template(instance.data) : childComponent.template;
604
+ const newHtml = TemplateEngine.parse(templateResult, instance.data);
605
+ eleva.renderer.patchDOM(instance.container, newHtml);
606
+ }
607
+ });
608
+ }
609
+ });
610
+
611
+ // Initial re-render to show the correct signal values
612
+ const childComponent = eleva._components.get(component) || component;
613
+ if (childComponent && childComponent.template) {
614
+ const templateResult = typeof childComponent.template === "function" ? childComponent.template(instance.data) : childComponent.template;
615
+ const newHtml = TemplateEngine.parse(templateResult, instance.data);
616
+ eleva.renderer.patchDOM(instance.container, newHtml);
617
+ }
618
+ }
619
+
620
+ // Remove from pending list
621
+ pendingSignalLinks.delete(pending);
622
+ }
623
+ }
624
+ }
625
+ };
626
+
627
+ /**
628
+ * Expose utility methods on the Eleva instance
629
+ * @namespace eleva.props
630
+ */
631
+ eleva.props = {
632
+ /**
633
+ * Parse a single value with automatic type detection
634
+ * @param {any} value - The value to parse
635
+ * @returns {any} The parsed value with appropriate type
636
+ *
637
+ * @example
638
+ * app.props.parse("42") // → 42
639
+ * app.props.parse("true") // → true
640
+ * app.props.parse('{"key": "val"}') // → {key: "val"}
641
+ */
642
+ parse: value => {
643
+ // Return value as-is if auto parsing is disabled
644
+ if (!enableAutoParsing) {
645
+ return value;
646
+ }
647
+ // Use our enhanced parsing function
648
+ return parsePropValue(value);
649
+ },
650
+ /**
651
+ * Detect the type of a value
652
+ * @param {any} value - The value to detect type for
653
+ * @returns {string} The detected type
654
+ *
655
+ * @example
656
+ * app.props.detectType("hello") // → "string"
657
+ * app.props.detectType(42) // → "number"
658
+ * app.props.detectType([1, 2, 3]) // → "array"
659
+ */
660
+ detectType
661
+ };
662
+
663
+ // Store original methods for uninstall
664
+ eleva._originalExtractProps = eleva._extractProps;
665
+ eleva._originalMount = originalMount;
666
+ eleva._originalMountComponents = originalMountComponents;
667
+ },
668
+ /**
669
+ * Uninstalls the plugin from the Eleva instance
670
+ *
671
+ * @param {Object} eleva - The Eleva instance
672
+ *
673
+ * @description
674
+ * Restores the original Eleva methods and removes all plugin-specific
675
+ * functionality. This method should be called when the plugin is no
676
+ * longer needed.
677
+ *
678
+ * @example
679
+ * // Uninstall the plugin
680
+ * PropsPlugin.uninstall(app);
681
+ */
682
+ uninstall(eleva) {
683
+ // Restore original _extractProps method
684
+ if (eleva._originalExtractProps) {
685
+ eleva._extractProps = eleva._originalExtractProps;
686
+ delete eleva._originalExtractProps;
687
+ }
688
+
689
+ // Restore original mount method
690
+ if (eleva._originalMount) {
691
+ eleva.mount = eleva._originalMount;
692
+ delete eleva._originalMount;
693
+ }
694
+
695
+ // Restore original _mountComponents method
696
+ if (eleva._originalMountComponents) {
697
+ eleva._mountComponents = eleva._originalMountComponents;
698
+ delete eleva._originalMountComponents;
699
+ }
700
+
701
+ // Remove plugin utility methods
702
+ if (eleva.props) {
703
+ delete eleva.props;
704
+ }
705
+ }
706
+ };
707
+
708
+ exports.Props = PropsPlugin;
709
+ exports.PropsPlugin = PropsPlugin;
710
+
711
+ }));
712
+ //# sourceMappingURL=props.umd.js.map