eleva 1.0.0-rc.3 → 1.0.0-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +135 -2
  2. package/dist/eleva-plugins.cjs.js +1693 -0
  3. package/dist/eleva-plugins.cjs.js.map +1 -0
  4. package/dist/eleva-plugins.esm.js +1689 -0
  5. package/dist/eleva-plugins.esm.js.map +1 -0
  6. package/dist/eleva-plugins.umd.js +1699 -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 +15 -34
  11. package/dist/eleva.cjs.js.map +1 -1
  12. package/dist/eleva.d.ts +0 -1
  13. package/dist/eleva.esm.js +15 -34
  14. package/dist/eleva.esm.js.map +1 -1
  15. package/dist/eleva.umd.js +15 -34
  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 +231 -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 +373 -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 +1115 -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/package.json +56 -17
  32. package/src/core/Eleva.js +6 -7
  33. package/src/modules/Renderer.js +8 -36
  34. package/src/plugins/Attr.js +252 -0
  35. package/src/plugins/Props.js +385 -0
  36. package/src/plugins/Router.js +1217 -0
  37. package/src/plugins/index.js +35 -0
  38. package/types/core/Eleva.d.ts +0 -1
  39. package/types/core/Eleva.d.ts.map +1 -1
  40. package/types/modules/Renderer.d.ts.map +1 -1
  41. package/types/plugins/Attr.d.ts +28 -0
  42. package/types/plugins/Attr.d.ts.map +1 -0
  43. package/types/plugins/Props.d.ts +48 -0
  44. package/types/plugins/Props.d.ts.map +1 -0
  45. package/types/plugins/Router.d.ts +500 -0
  46. package/types/plugins/Router.d.ts.map +1 -0
  47. package/types/plugins/index.d.ts +4 -0
  48. package/types/plugins/index.d.ts.map +1 -0
@@ -0,0 +1,252 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * A regular expression to match hyphenated lowercase letters.
5
+ * @private
6
+ * @type {RegExp}
7
+ */
8
+ const CAMEL_RE = /-([a-z])/g;
9
+
10
+ /**
11
+ * @class 🎯 AttrPlugin
12
+ * @classdesc A plugin that provides advanced attribute handling for Eleva components.
13
+ * This plugin extends the renderer with sophisticated attribute processing including:
14
+ * - ARIA attribute handling with proper property mapping
15
+ * - Data attribute management
16
+ * - Boolean attribute processing
17
+ * - Dynamic property detection and mapping
18
+ * - Attribute cleanup and removal
19
+ *
20
+ * @example
21
+ * // Install the plugin
22
+ * const app = new Eleva("myApp");
23
+ * app.use(AttrPlugin);
24
+ *
25
+ * // Use advanced attributes in components
26
+ * app.component("myComponent", {
27
+ * template: (ctx) => `
28
+ * <button
29
+ * aria-expanded="${ctx.isExpanded.value}"
30
+ * data-user-id="${ctx.userId.value}"
31
+ * disabled="${ctx.isLoading.value}"
32
+ * class="btn ${ctx.variant.value}"
33
+ * >
34
+ * ${ctx.text.value}
35
+ * </button>
36
+ * `
37
+ * });
38
+ */
39
+ export const AttrPlugin = {
40
+ /**
41
+ * Unique identifier for the plugin
42
+ * @type {string}
43
+ */
44
+ name: "attr",
45
+
46
+ /**
47
+ * Plugin version
48
+ * @type {string}
49
+ */
50
+ version: "1.0.0-rc.1",
51
+
52
+ /**
53
+ * Plugin description
54
+ * @type {string}
55
+ */
56
+ description: "Advanced attribute handling for Eleva components",
57
+
58
+ /**
59
+ * Installs the plugin into the Eleva instance
60
+ *
61
+ * @param {Object} eleva - The Eleva instance
62
+ * @param {Object} options - Plugin configuration options
63
+ * @param {boolean} [options.enableAria=true] - Enable ARIA attribute handling
64
+ * @param {boolean} [options.enableData=true] - Enable data attribute handling
65
+ * @param {boolean} [options.enableBoolean=true] - Enable boolean attribute handling
66
+ * @param {boolean} [options.enableDynamic=true] - Enable dynamic property detection
67
+ */
68
+ install(eleva, options = {}) {
69
+ const {
70
+ enableAria = true,
71
+ enableData = true,
72
+ enableBoolean = true,
73
+ enableDynamic = true,
74
+ } = options;
75
+
76
+ /**
77
+ * Updates the attributes of an element to match a new element's attributes.
78
+ * This method provides sophisticated attribute processing including:
79
+ * - ARIA attribute handling with proper property mapping
80
+ * - Data attribute management
81
+ * - Boolean attribute processing
82
+ * - Dynamic property detection and mapping
83
+ * - Attribute cleanup and removal
84
+ *
85
+ * @param {HTMLElement} oldEl - The original element to update
86
+ * @param {HTMLElement} newEl - The new element to update
87
+ * @returns {void}
88
+ */
89
+ const updateAttributes = (oldEl, newEl) => {
90
+ const oldAttrs = oldEl.attributes;
91
+ const newAttrs = newEl.attributes;
92
+
93
+ // Process new attributes
94
+ for (let i = 0; i < newAttrs.length; i++) {
95
+ const { name, value } = newAttrs[i];
96
+
97
+ // Skip event attributes (handled by event system)
98
+ if (name.startsWith("@")) continue;
99
+
100
+ // Skip if attribute hasn't changed
101
+ if (oldEl.getAttribute(name) === value) continue;
102
+
103
+ // Handle ARIA attributes
104
+ if (enableAria && name.startsWith("aria-")) {
105
+ const prop =
106
+ "aria" + name.slice(5).replace(CAMEL_RE, (_, l) => l.toUpperCase());
107
+ oldEl[prop] = value;
108
+ oldEl.setAttribute(name, value);
109
+ }
110
+ // Handle data attributes
111
+ else if (enableData && name.startsWith("data-")) {
112
+ oldEl.dataset[name.slice(5)] = value;
113
+ oldEl.setAttribute(name, value);
114
+ }
115
+ // Handle other attributes
116
+ else {
117
+ let prop = name.replace(CAMEL_RE, (_, l) => l.toUpperCase());
118
+
119
+ // Dynamic property detection
120
+ if (
121
+ enableDynamic &&
122
+ !(prop in oldEl) &&
123
+ !Object.getOwnPropertyDescriptor(Object.getPrototypeOf(oldEl), prop)
124
+ ) {
125
+ const elementProps = Object.getOwnPropertyNames(
126
+ Object.getPrototypeOf(oldEl)
127
+ );
128
+ const matchingProp = elementProps.find(
129
+ (p) =>
130
+ p.toLowerCase() === name.toLowerCase() ||
131
+ p.toLowerCase().includes(name.toLowerCase()) ||
132
+ name.toLowerCase().includes(p.toLowerCase())
133
+ );
134
+
135
+ if (matchingProp) {
136
+ prop = matchingProp;
137
+ }
138
+ }
139
+
140
+ const descriptor = Object.getOwnPropertyDescriptor(
141
+ Object.getPrototypeOf(oldEl),
142
+ prop
143
+ );
144
+ const hasProperty = prop in oldEl || descriptor;
145
+
146
+ if (hasProperty) {
147
+ // Boolean attribute handling
148
+ if (enableBoolean) {
149
+ const isBoolean =
150
+ typeof oldEl[prop] === "boolean" ||
151
+ (descriptor?.get &&
152
+ typeof descriptor.get.call(oldEl) === "boolean");
153
+
154
+ if (isBoolean) {
155
+ const boolValue =
156
+ value !== "false" &&
157
+ (value === "" || value === prop || value === "true");
158
+ oldEl[prop] = boolValue;
159
+
160
+ if (boolValue) {
161
+ oldEl.setAttribute(name, "");
162
+ } else {
163
+ oldEl.removeAttribute(name);
164
+ }
165
+ } else {
166
+ oldEl[prop] = value;
167
+ oldEl.setAttribute(name, value);
168
+ }
169
+ } else {
170
+ oldEl[prop] = value;
171
+ oldEl.setAttribute(name, value);
172
+ }
173
+ } else {
174
+ oldEl.setAttribute(name, value);
175
+ }
176
+ }
177
+ }
178
+
179
+ // Remove old attributes that are no longer present
180
+ for (let i = oldAttrs.length - 1; i >= 0; i--) {
181
+ const name = oldAttrs[i].name;
182
+ if (!newEl.hasAttribute(name)) {
183
+ oldEl.removeAttribute(name);
184
+ }
185
+ }
186
+ };
187
+
188
+ // Extend the renderer with the advanced attribute handler
189
+ if (eleva.renderer) {
190
+ eleva.renderer.updateAttributes = updateAttributes;
191
+
192
+ // Store the original _patchNode method
193
+ const originalPatchNode = eleva.renderer._patchNode;
194
+ eleva.renderer._originalPatchNode = originalPatchNode;
195
+
196
+ // Override the _patchNode method to use our attribute handler
197
+ eleva.renderer._patchNode = function (oldNode, newNode) {
198
+ if (oldNode?._eleva_instance) return;
199
+
200
+ if (!this._isSameNode(oldNode, newNode)) {
201
+ oldNode.replaceWith(newNode.cloneNode(true));
202
+ return;
203
+ }
204
+
205
+ if (oldNode.nodeType === Node.ELEMENT_NODE) {
206
+ updateAttributes(oldNode, newNode);
207
+ this._diff(oldNode, newNode);
208
+ } else if (
209
+ oldNode.nodeType === Node.TEXT_NODE &&
210
+ oldNode.nodeValue !== newNode.nodeValue
211
+ ) {
212
+ oldNode.nodeValue = newNode.nodeValue;
213
+ }
214
+ };
215
+ }
216
+
217
+ // Add plugin metadata to the Eleva instance
218
+ if (!eleva.plugins) {
219
+ eleva.plugins = new Map();
220
+ }
221
+ eleva.plugins.set(this.name, {
222
+ name: this.name,
223
+ version: this.version,
224
+ description: this.description,
225
+ options,
226
+ });
227
+
228
+ // Add utility methods for manual attribute updates
229
+ eleva.updateElementAttributes = updateAttributes;
230
+ },
231
+
232
+ /**
233
+ * Uninstalls the plugin from the Eleva instance
234
+ *
235
+ * @param {Object} eleva - The Eleva instance
236
+ */
237
+ uninstall(eleva) {
238
+ // Restore original _patchNode method if it exists
239
+ if (eleva.renderer && eleva.renderer._originalPatchNode) {
240
+ eleva.renderer._patchNode = eleva.renderer._originalPatchNode;
241
+ delete eleva.renderer._originalPatchNode;
242
+ }
243
+
244
+ // Remove plugin metadata
245
+ if (eleva.plugins) {
246
+ eleva.plugins.delete(this.name);
247
+ }
248
+
249
+ // Remove utility methods
250
+ delete eleva.updateElementAttributes;
251
+ },
252
+ };
@@ -0,0 +1,385 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * @class 🎯 PropsPlugin
5
+ * @classdesc A plugin that extends Eleva's props data handling to support any type of data structure
6
+ * with automatic type detection, parsing, and reactive prop updates. This plugin enables seamless
7
+ * passing of complex data types from parent to child components without manual parsing.
8
+ *
9
+ * Core Features:
10
+ * - Automatic type detection and parsing (strings, numbers, booleans, objects, arrays, dates, etc.)
11
+ * - Support for complex data structures including nested objects and arrays
12
+ * - Reactive props that automatically update when parent data changes
13
+ * - Comprehensive error handling with custom error callbacks
14
+ * - Simple configuration with minimal setup required
15
+ *
16
+ * @example
17
+ * // Install the plugin
18
+ * const app = new Eleva("myApp");
19
+ * app.use(PropsPlugin, {
20
+ * enableAutoParsing: true,
21
+ * enableReactivity: true,
22
+ * onError: (error, value) => {
23
+ * console.error('Props parsing error:', error, value);
24
+ * }
25
+ * });
26
+ *
27
+ * // Use complex props in components
28
+ * app.component("UserCard", {
29
+ * template: (ctx) => `
30
+ * <div class="user-info-container"
31
+ * :user='${JSON.stringify(ctx.user.value)}'
32
+ * :permissions='${JSON.stringify(ctx.permissions.value)}'
33
+ * :settings='${JSON.stringify(ctx.settings.value)}'>
34
+ * </div>
35
+ * `,
36
+ * children: {
37
+ * '.user-info-container': 'UserInfo'
38
+ * }
39
+ * });
40
+ *
41
+ * app.component("UserInfo", {
42
+ * setup({ props }) {
43
+ * return {
44
+ * user: props.user, // Automatically parsed object
45
+ * permissions: props.permissions, // Automatically parsed array
46
+ * settings: props.settings // Automatically parsed object
47
+ * };
48
+ * }
49
+ * });
50
+ */
51
+ export const PropsPlugin = {
52
+ /**
53
+ * Unique identifier for the plugin
54
+ * @type {string}
55
+ */
56
+ name: "props",
57
+
58
+ /**
59
+ * Plugin version
60
+ * @type {string}
61
+ */
62
+ version: "1.0.0-rc.1",
63
+
64
+ /**
65
+ * Plugin description
66
+ * @type {string}
67
+ */
68
+ description:
69
+ "Advanced props data handling for complex data structures with automatic type detection and reactivity",
70
+
71
+ /**
72
+ * Installs the plugin into the Eleva instance
73
+ *
74
+ * @param {Object} eleva - The Eleva instance
75
+ * @param {Object} options - Plugin configuration options
76
+ * @param {boolean} [options.enableAutoParsing=true] - Enable automatic type detection and parsing
77
+ * @param {boolean} [options.enableReactivity=true] - Enable reactive prop updates using Eleva's signal system
78
+ * @param {Function} [options.onError=null] - Error handler function called when parsing fails
79
+ *
80
+ * @example
81
+ * // Basic installation
82
+ * app.use(PropsPlugin);
83
+ *
84
+ * // Installation with custom options
85
+ * app.use(PropsPlugin, {
86
+ * enableAutoParsing: true,
87
+ * enableReactivity: false,
88
+ * onError: (error, value) => {
89
+ * console.error('Props parsing error:', error, value);
90
+ * }
91
+ * });
92
+ */
93
+ install(eleva, options = {}) {
94
+ const {
95
+ enableAutoParsing = true,
96
+ enableReactivity = true,
97
+ onError = null,
98
+ } = options;
99
+
100
+ /**
101
+ * Detects the type of a given value
102
+ * @private
103
+ * @param {any} value - The value to detect type for
104
+ * @returns {string} The detected type ('string', 'number', 'boolean', 'object', 'array', 'date', 'map', 'set', 'function', 'null', 'undefined', 'unknown')
105
+ *
106
+ * @example
107
+ * detectType("hello") // → "string"
108
+ * detectType(42) // → "number"
109
+ * detectType(true) // → "boolean"
110
+ * detectType([1, 2, 3]) // → "array"
111
+ * detectType({}) // → "object"
112
+ * detectType(new Date()) // → "date"
113
+ * detectType(null) // → "null"
114
+ */
115
+ const detectType = (value) => {
116
+ if (value === null) return "null";
117
+ if (value === undefined) return "undefined";
118
+ if (typeof value === "boolean") return "boolean";
119
+ if (typeof value === "number") return "number";
120
+ if (typeof value === "string") return "string";
121
+ if (typeof value === "function") return "function";
122
+ if (value instanceof Date) return "date";
123
+ if (value instanceof Map) return "map";
124
+ if (value instanceof Set) return "set";
125
+ if (Array.isArray(value)) return "array";
126
+ if (typeof value === "object") return "object";
127
+ return "unknown";
128
+ };
129
+
130
+ /**
131
+ * Parses a prop value with automatic type detection
132
+ * @private
133
+ * @param {any} value - The value to parse
134
+ * @returns {any} The parsed value with appropriate type
135
+ *
136
+ * @description
137
+ * This function automatically detects and parses different data types from string values:
138
+ * - Special strings: "true" → true, "false" → false, "null" → null, "undefined" → undefined
139
+ * - JSON objects/arrays: '{"key": "value"}' → {key: "value"}, '[1, 2, 3]' → [1, 2, 3]
140
+ * - Boolean-like strings: "1" → true, "0" → false, "" → true
141
+ * - Numeric strings: "42" → 42, "3.14" → 3.14
142
+ * - Date strings: "2023-01-01T00:00:00.000Z" → Date object
143
+ * - Other strings: returned as-is
144
+ *
145
+ * @example
146
+ * parsePropValue("true") // → true
147
+ * parsePropValue("42") // → 42
148
+ * parsePropValue('{"key": "val"}') // → {key: "val"}
149
+ * parsePropValue('[1, 2, 3]') // → [1, 2, 3]
150
+ * parsePropValue("hello") // → "hello"
151
+ */
152
+ const parsePropValue = (value) => {
153
+ try {
154
+ // Handle non-string values - return as-is
155
+ if (typeof value !== "string") {
156
+ return value;
157
+ }
158
+
159
+ // Handle special string patterns first
160
+ if (value === "true") return true;
161
+ if (value === "false") return false;
162
+ if (value === "null") return null;
163
+ if (value === "undefined") return undefined;
164
+
165
+ // Try to parse as JSON (for objects and arrays)
166
+ // This handles complex data structures like objects and arrays
167
+ if (value.startsWith("{") || value.startsWith("[")) {
168
+ try {
169
+ return JSON.parse(value);
170
+ } catch (e) {
171
+ // Not valid JSON, throw error to trigger error handler
172
+ throw new Error(`Invalid JSON: ${value}`);
173
+ }
174
+ }
175
+
176
+ // Handle boolean-like strings (including "1" and "0")
177
+ // These are common in HTML attributes and should be treated as booleans
178
+ if (value === "1") return true;
179
+ if (value === "0") return false;
180
+ if (value === "") return true; // Empty string is truthy in HTML attributes
181
+
182
+ // Handle numeric strings (after boolean check to avoid conflicts)
183
+ // This ensures "0" is treated as boolean false, not number 0
184
+ if (!isNaN(value) && value !== "" && !isNaN(parseFloat(value))) {
185
+ return Number(value);
186
+ }
187
+
188
+ // Handle date strings (ISO format)
189
+ // Recognizes standard ISO date format and converts to Date object
190
+ if (value.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)) {
191
+ const date = new Date(value);
192
+ if (!isNaN(date.getTime())) {
193
+ return date;
194
+ }
195
+ }
196
+
197
+ // Return as string if no other parsing applies
198
+ // This is the fallback for regular text strings
199
+ return value;
200
+ } catch (error) {
201
+ // Call error handler if provided
202
+ if (onError) {
203
+ onError(error, value);
204
+ }
205
+ // Fallback to original value to prevent breaking the application
206
+ return value;
207
+ }
208
+ };
209
+
210
+ /**
211
+ * Enhanced props extraction with automatic type detection
212
+ * @private
213
+ * @param {HTMLElement} element - The DOM element to extract props from
214
+ * @returns {Object} Object containing parsed props with appropriate types
215
+ *
216
+ * @description
217
+ * Extracts props from DOM element attributes that start with ":" and automatically
218
+ * parses them to their appropriate types. Removes the attributes from the element
219
+ * after extraction.
220
+ *
221
+ * @example
222
+ * // HTML: <div :name="John" :age="30" :active="true" :data='{"key": "value"}'></div>
223
+ * const props = extractProps(element);
224
+ * // Result: { name: "John", age: 30, active: true, data: {key: "value"} }
225
+ */
226
+ const extractProps = (element) => {
227
+ const props = {};
228
+ const attrs = element.attributes;
229
+
230
+ // Iterate through attributes in reverse order to handle removal correctly
231
+ for (let i = attrs.length - 1; i >= 0; i--) {
232
+ const attr = attrs[i];
233
+ // Only process attributes that start with ":" (prop attributes)
234
+ if (attr.name.startsWith(":")) {
235
+ const propName = attr.name.slice(1); // Remove the ":" prefix
236
+ // Parse the value if auto-parsing is enabled, otherwise use as-is
237
+ const parsedValue = enableAutoParsing
238
+ ? parsePropValue(attr.value)
239
+ : attr.value;
240
+ props[propName] = parsedValue;
241
+ // Remove the attribute from the DOM element after extraction
242
+ element.removeAttribute(attr.name);
243
+ }
244
+ }
245
+
246
+ return props;
247
+ };
248
+
249
+ /**
250
+ * Creates reactive props using Eleva's signal system
251
+ * @private
252
+ * @param {Object} props - The props object to make reactive
253
+ * @returns {Object} Object containing reactive props (Eleva signals)
254
+ *
255
+ * @description
256
+ * Converts regular prop values into Eleva signals for reactive updates.
257
+ * If a value is already a signal, it's passed through unchanged.
258
+ *
259
+ * @example
260
+ * const props = { name: "John", age: 30, active: true };
261
+ * const reactiveProps = createReactiveProps(props);
262
+ * // Result: {
263
+ * // name: Signal("John"),
264
+ * // age: Signal(30),
265
+ * // active: Signal(true)
266
+ * // }
267
+ */
268
+ const createReactiveProps = (props) => {
269
+ const reactiveProps = {};
270
+
271
+ // Convert each prop value to a reactive signal
272
+ Object.entries(props).forEach(([key, value]) => {
273
+ // Check if value is already a signal (has 'value' and 'watch' properties)
274
+ if (
275
+ value &&
276
+ typeof value === "object" &&
277
+ "value" in value &&
278
+ "watch" in value
279
+ ) {
280
+ // Value is already a signal, use it as-is
281
+ reactiveProps[key] = value;
282
+ } else {
283
+ // Create new signal for the prop value to make it reactive
284
+ reactiveProps[key] = new eleva.signal(value);
285
+ }
286
+ });
287
+
288
+ return reactiveProps;
289
+ };
290
+
291
+ // Override Eleva's internal _extractProps method with our enhanced version
292
+ eleva._extractProps = extractProps;
293
+
294
+ // Override Eleva's mount method to apply enhanced prop handling
295
+ const originalMount = eleva.mount;
296
+ eleva.mount = async (container, compName, props = {}) => {
297
+ // Create reactive props if reactivity is enabled
298
+ const enhancedProps = enableReactivity
299
+ ? createReactiveProps(props)
300
+ : props;
301
+
302
+ // Call the original mount method with enhanced props
303
+ return await originalMount.call(
304
+ eleva,
305
+ container,
306
+ compName,
307
+ enhancedProps
308
+ );
309
+ };
310
+
311
+ /**
312
+ * Expose utility methods on the Eleva instance
313
+ * @namespace eleva.props
314
+ */
315
+ eleva.props = {
316
+ /**
317
+ * Parse a single value with automatic type detection
318
+ * @param {any} value - The value to parse
319
+ * @returns {any} The parsed value with appropriate type
320
+ *
321
+ * @example
322
+ * app.props.parse("42") // → 42
323
+ * app.props.parse("true") // → true
324
+ * app.props.parse('{"key": "val"}') // → {key: "val"}
325
+ */
326
+ parse: (value) => {
327
+ // Return value as-is if auto parsing is disabled
328
+ if (!enableAutoParsing) {
329
+ return value;
330
+ }
331
+ // Use our enhanced parsing function
332
+ return parsePropValue(value);
333
+ },
334
+
335
+ /**
336
+ * Detect the type of a value
337
+ * @param {any} value - The value to detect type for
338
+ * @returns {string} The detected type
339
+ *
340
+ * @example
341
+ * app.props.detectType("hello") // → "string"
342
+ * app.props.detectType(42) // → "number"
343
+ * app.props.detectType([1, 2, 3]) // → "array"
344
+ */
345
+ detectType,
346
+ };
347
+
348
+ // Store original methods for uninstall
349
+ eleva._originalExtractProps = eleva._extractProps;
350
+ eleva._originalMount = originalMount;
351
+ },
352
+
353
+ /**
354
+ * Uninstalls the plugin from the Eleva instance
355
+ *
356
+ * @param {Object} eleva - The Eleva instance
357
+ *
358
+ * @description
359
+ * Restores the original Eleva methods and removes all plugin-specific
360
+ * functionality. This method should be called when the plugin is no
361
+ * longer needed.
362
+ *
363
+ * @example
364
+ * // Uninstall the plugin
365
+ * PropsPlugin.uninstall(app);
366
+ */
367
+ uninstall(eleva) {
368
+ // Restore original _extractProps method
369
+ if (eleva._originalExtractProps) {
370
+ eleva._extractProps = eleva._originalExtractProps;
371
+ delete eleva._originalExtractProps;
372
+ }
373
+
374
+ // Restore original mount method
375
+ if (eleva._originalMount) {
376
+ eleva.mount = eleva._originalMount;
377
+ delete eleva._originalMount;
378
+ }
379
+
380
+ // Remove plugin utility methods
381
+ if (eleva.props) {
382
+ delete eleva.props;
383
+ }
384
+ },
385
+ };