eleva 1.0.0-rc.4 → 1.0.0-rc.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.
package/README.md CHANGED
@@ -40,9 +40,9 @@ Pure JavaScript, Pure Performance, Simply Elegant.
40
40
  **A minimalist, lightweight, pure vanilla JavaScript frontend runtime framework.**
41
41
  _Built with love for native JavaScript and designed with a minimal core that can be extended through a powerful plugin system-because sometimes, less really is more!_ 😊
42
42
 
43
- > **Stability Notice**: This is `v1.0.0-rc.4` - The core functionality is stable. Seeking community feedback before the final v1.0.0 release.
43
+ > **Stability Notice**: This is `v1.0.0-rc.6` - The core functionality is stable. Seeking community feedback before the final v1.0.0 release.
44
44
 
45
- **Version:** `1.0.0-rc.4`
45
+ **Version:** `1.0.0-rc.6`
46
46
 
47
47
 
48
48
 
@@ -1,4 +1,4 @@
1
- /*! Eleva Plugins v1.0.0-rc.4 | MIT License | https://elevajs.com */
1
+ /*! Eleva Plugins v1.0.0-rc.6 | MIT License | https://elevajs.com */
2
2
  'use strict';
3
3
 
4
4
  /**
@@ -1325,6 +1325,599 @@ const RouterPlugin = {
1325
1325
  }
1326
1326
  };
1327
1327
 
1328
+ /**
1329
+ * @class 🔒 TemplateEngine
1330
+ * @classdesc A secure template engine that handles interpolation and dynamic attribute parsing.
1331
+ * Provides a safe way to evaluate expressions in templates while preventing XSS attacks.
1332
+ * All methods are static and can be called directly on the class.
1333
+ *
1334
+ * @example
1335
+ * const template = "Hello, {{name}}!";
1336
+ * const data = { name: "World" };
1337
+ * const result = TemplateEngine.parse(template, data); // Returns: "Hello, World!"
1338
+ */
1339
+ class TemplateEngine {
1340
+ /**
1341
+ * @private {RegExp} Regular expression for matching template expressions in the format {{ expression }}
1342
+ * @type {RegExp}
1343
+ */
1344
+ static expressionPattern = /\{\{\s*(.*?)\s*\}\}/g;
1345
+
1346
+ /**
1347
+ * Parses a template string, replacing expressions with their evaluated values.
1348
+ * Expressions are evaluated in the provided data context.
1349
+ *
1350
+ * @public
1351
+ * @static
1352
+ * @param {string} template - The template string to parse.
1353
+ * @param {Record<string, unknown>} data - The data context for evaluating expressions.
1354
+ * @returns {string} The parsed template with expressions replaced by their values.
1355
+ * @example
1356
+ * const result = TemplateEngine.parse("{{user.name}} is {{user.age}} years old", {
1357
+ * user: { name: "John", age: 30 }
1358
+ * }); // Returns: "John is 30 years old"
1359
+ */
1360
+ static parse(template, data) {
1361
+ if (typeof template !== "string") return template;
1362
+ return template.replace(this.expressionPattern, (_, expression) => this.evaluate(expression, data));
1363
+ }
1364
+
1365
+ /**
1366
+ * Evaluates an expression in the context of the provided data object.
1367
+ * Note: This does not provide a true sandbox and evaluated expressions may access global scope.
1368
+ * The use of the `with` statement is necessary for expression evaluation but has security implications.
1369
+ * Expressions should be carefully validated before evaluation.
1370
+ *
1371
+ * @public
1372
+ * @static
1373
+ * @param {string} expression - The expression to evaluate.
1374
+ * @param {Record<string, unknown>} data - The data context for evaluation.
1375
+ * @returns {unknown} The result of the evaluation, or an empty string if evaluation fails.
1376
+ * @example
1377
+ * const result = TemplateEngine.evaluate("user.name", { user: { name: "John" } }); // Returns: "John"
1378
+ * const age = TemplateEngine.evaluate("user.age", { user: { age: 30 } }); // Returns: 30
1379
+ */
1380
+ static evaluate(expression, data) {
1381
+ if (typeof expression !== "string") return expression;
1382
+ try {
1383
+ return new Function("data", `with(data) { return ${expression}; }`)(data);
1384
+ } catch {
1385
+ return "";
1386
+ }
1387
+ }
1388
+ }
1389
+
1390
+ /**
1391
+ * @class 🎯 PropsPlugin
1392
+ * @classdesc A plugin that extends Eleva's props data handling to support any type of data structure
1393
+ * with automatic type detection, parsing, and reactive prop updates. This plugin enables seamless
1394
+ * passing of complex data types from parent to child components without manual parsing.
1395
+ *
1396
+ * Core Features:
1397
+ * - Automatic type detection and parsing (strings, numbers, booleans, objects, arrays, dates, etc.)
1398
+ * - Support for complex data structures including nested objects and arrays
1399
+ * - Reactive props that automatically update when parent data changes
1400
+ * - Comprehensive error handling with custom error callbacks
1401
+ * - Simple configuration with minimal setup required
1402
+ *
1403
+ * @example
1404
+ * // Install the plugin
1405
+ * const app = new Eleva("myApp");
1406
+ * app.use(PropsPlugin, {
1407
+ * enableAutoParsing: true,
1408
+ * enableReactivity: true,
1409
+ * onError: (error, value) => {
1410
+ * console.error('Props parsing error:', error, value);
1411
+ * }
1412
+ * });
1413
+ *
1414
+ * // Use complex props in components
1415
+ * app.component("UserCard", {
1416
+ * template: (ctx) => `
1417
+ * <div class="user-info-container"
1418
+ * :user='${JSON.stringify(ctx.user.value)}'
1419
+ * :permissions='${JSON.stringify(ctx.permissions.value)}'
1420
+ * :settings='${JSON.stringify(ctx.settings.value)}'>
1421
+ * </div>
1422
+ * `,
1423
+ * children: {
1424
+ * '.user-info-container': 'UserInfo'
1425
+ * }
1426
+ * });
1427
+ *
1428
+ * app.component("UserInfo", {
1429
+ * setup({ props }) {
1430
+ * return {
1431
+ * user: props.user, // Automatically parsed object
1432
+ * permissions: props.permissions, // Automatically parsed array
1433
+ * settings: props.settings // Automatically parsed object
1434
+ * };
1435
+ * }
1436
+ * });
1437
+ */
1438
+ const PropsPlugin = {
1439
+ /**
1440
+ * Unique identifier for the plugin
1441
+ * @type {string}
1442
+ */
1443
+ name: "props",
1444
+ /**
1445
+ * Plugin version
1446
+ * @type {string}
1447
+ */
1448
+ version: "1.0.0-rc.2",
1449
+ /**
1450
+ * Plugin description
1451
+ * @type {string}
1452
+ */
1453
+ description: "Advanced props data handling for complex data structures with automatic type detection and reactivity",
1454
+ /**
1455
+ * Installs the plugin into the Eleva instance
1456
+ *
1457
+ * @param {Object} eleva - The Eleva instance
1458
+ * @param {Object} options - Plugin configuration options
1459
+ * @param {boolean} [options.enableAutoParsing=true] - Enable automatic type detection and parsing
1460
+ * @param {boolean} [options.enableReactivity=true] - Enable reactive prop updates using Eleva's signal system
1461
+ * @param {Function} [options.onError=null] - Error handler function called when parsing fails
1462
+ *
1463
+ * @example
1464
+ * // Basic installation
1465
+ * app.use(PropsPlugin);
1466
+ *
1467
+ * // Installation with custom options
1468
+ * app.use(PropsPlugin, {
1469
+ * enableAutoParsing: true,
1470
+ * enableReactivity: false,
1471
+ * onError: (error, value) => {
1472
+ * console.error('Props parsing error:', error, value);
1473
+ * }
1474
+ * });
1475
+ */
1476
+ install(eleva, options = {}) {
1477
+ const {
1478
+ enableAutoParsing = true,
1479
+ enableReactivity = true,
1480
+ onError = null
1481
+ } = options;
1482
+
1483
+ /**
1484
+ * Detects the type of a given value
1485
+ * @private
1486
+ * @param {any} value - The value to detect type for
1487
+ * @returns {string} The detected type ('string', 'number', 'boolean', 'object', 'array', 'date', 'map', 'set', 'function', 'null', 'undefined', 'unknown')
1488
+ *
1489
+ * @example
1490
+ * detectType("hello") // → "string"
1491
+ * detectType(42) // → "number"
1492
+ * detectType(true) // → "boolean"
1493
+ * detectType([1, 2, 3]) // → "array"
1494
+ * detectType({}) // → "object"
1495
+ * detectType(new Date()) // → "date"
1496
+ * detectType(null) // → "null"
1497
+ */
1498
+ const detectType = value => {
1499
+ if (value === null) return "null";
1500
+ if (value === undefined) return "undefined";
1501
+ if (typeof value === "boolean") return "boolean";
1502
+ if (typeof value === "number") return "number";
1503
+ if (typeof value === "string") return "string";
1504
+ if (typeof value === "function") return "function";
1505
+ if (value instanceof Date) return "date";
1506
+ if (value instanceof Map) return "map";
1507
+ if (value instanceof Set) return "set";
1508
+ if (Array.isArray(value)) return "array";
1509
+ if (typeof value === "object") return "object";
1510
+ return "unknown";
1511
+ };
1512
+
1513
+ /**
1514
+ * Parses a prop value with automatic type detection
1515
+ * @private
1516
+ * @param {any} value - The value to parse
1517
+ * @returns {any} The parsed value with appropriate type
1518
+ *
1519
+ * @description
1520
+ * This function automatically detects and parses different data types from string values:
1521
+ * - Special strings: "true" → true, "false" → false, "null" → null, "undefined" → undefined
1522
+ * - JSON objects/arrays: '{"key": "value"}' → {key: "value"}, '[1, 2, 3]' → [1, 2, 3]
1523
+ * - Boolean-like strings: "1" → true, "0" → false, "" → true
1524
+ * - Numeric strings: "42" → 42, "3.14" → 3.14
1525
+ * - Date strings: "2023-01-01T00:00:00.000Z" → Date object
1526
+ * - Other strings: returned as-is
1527
+ *
1528
+ * @example
1529
+ * parsePropValue("true") // → true
1530
+ * parsePropValue("42") // → 42
1531
+ * parsePropValue('{"key": "val"}') // → {key: "val"}
1532
+ * parsePropValue('[1, 2, 3]') // → [1, 2, 3]
1533
+ * parsePropValue("hello") // → "hello"
1534
+ */
1535
+ const parsePropValue = value => {
1536
+ try {
1537
+ // Handle non-string values - return as-is
1538
+ if (typeof value !== "string") {
1539
+ return value;
1540
+ }
1541
+
1542
+ // Handle special string patterns first
1543
+ if (value === "true") return true;
1544
+ if (value === "false") return false;
1545
+ if (value === "null") return null;
1546
+ if (value === "undefined") return undefined;
1547
+
1548
+ // Try to parse as JSON (for objects and arrays)
1549
+ // This handles complex data structures like objects and arrays
1550
+ if (value.startsWith("{") || value.startsWith("[")) {
1551
+ try {
1552
+ return JSON.parse(value);
1553
+ } catch (e) {
1554
+ // Not valid JSON, throw error to trigger error handler
1555
+ throw new Error(`Invalid JSON: ${value}`);
1556
+ }
1557
+ }
1558
+
1559
+ // Handle boolean-like strings (including "1" and "0")
1560
+ // These are common in HTML attributes and should be treated as booleans
1561
+ if (value === "1") return true;
1562
+ if (value === "0") return false;
1563
+ if (value === "") return true; // Empty string is truthy in HTML attributes
1564
+
1565
+ // Handle numeric strings (after boolean check to avoid conflicts)
1566
+ // This ensures "0" is treated as boolean false, not number 0
1567
+ if (!isNaN(value) && value !== "" && !isNaN(parseFloat(value))) {
1568
+ return Number(value);
1569
+ }
1570
+
1571
+ // Handle date strings (ISO format)
1572
+ // Recognizes standard ISO date format and converts to Date object
1573
+ if (value.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)) {
1574
+ const date = new Date(value);
1575
+ if (!isNaN(date.getTime())) {
1576
+ return date;
1577
+ }
1578
+ }
1579
+
1580
+ // Return as string if no other parsing applies
1581
+ // This is the fallback for regular text strings
1582
+ return value;
1583
+ } catch (error) {
1584
+ // Call error handler if provided
1585
+ if (onError) {
1586
+ onError(error, value);
1587
+ }
1588
+ // Fallback to original value to prevent breaking the application
1589
+ return value;
1590
+ }
1591
+ };
1592
+
1593
+ /**
1594
+ * Enhanced props extraction with automatic type detection
1595
+ * @private
1596
+ * @param {HTMLElement} element - The DOM element to extract props from
1597
+ * @returns {Object} Object containing parsed props with appropriate types
1598
+ *
1599
+ * @description
1600
+ * Extracts props from DOM element attributes that start with ":" and automatically
1601
+ * parses them to their appropriate types. Removes the attributes from the element
1602
+ * after extraction.
1603
+ *
1604
+ * @example
1605
+ * // HTML: <div :name="John" :age="30" :active="true" :data='{"key": "value"}'></div>
1606
+ * const props = extractProps(element);
1607
+ * // Result: { name: "John", age: 30, active: true, data: {key: "value"} }
1608
+ */
1609
+ const extractProps = element => {
1610
+ const props = {};
1611
+ const attrs = element.attributes;
1612
+
1613
+ // Iterate through attributes in reverse order to handle removal correctly
1614
+ for (let i = attrs.length - 1; i >= 0; i--) {
1615
+ const attr = attrs[i];
1616
+ // Only process attributes that start with ":" (prop attributes)
1617
+ if (attr.name.startsWith(":")) {
1618
+ const propName = attr.name.slice(1); // Remove the ":" prefix
1619
+ // Parse the value if auto-parsing is enabled, otherwise use as-is
1620
+ const parsedValue = enableAutoParsing ? parsePropValue(attr.value) : attr.value;
1621
+ props[propName] = parsedValue;
1622
+ // Remove the attribute from the DOM element after extraction
1623
+ element.removeAttribute(attr.name);
1624
+ }
1625
+ }
1626
+ return props;
1627
+ };
1628
+
1629
+ /**
1630
+ * Creates reactive props using Eleva's signal system
1631
+ * @private
1632
+ * @param {Object} props - The props object to make reactive
1633
+ * @returns {Object} Object containing reactive props (Eleva signals)
1634
+ *
1635
+ * @description
1636
+ * Converts regular prop values into Eleva signals for reactive updates.
1637
+ * If a value is already a signal, it's passed through unchanged.
1638
+ *
1639
+ * @example
1640
+ * const props = { name: "John", age: 30, active: true };
1641
+ * const reactiveProps = createReactiveProps(props);
1642
+ * // Result: {
1643
+ * // name: Signal("John"),
1644
+ * // age: Signal(30),
1645
+ * // active: Signal(true)
1646
+ * // }
1647
+ */
1648
+ const createReactiveProps = props => {
1649
+ const reactiveProps = {};
1650
+
1651
+ // Convert each prop value to a reactive signal
1652
+ Object.entries(props).forEach(([key, value]) => {
1653
+ // Check if value is already a signal (has 'value' and 'watch' properties)
1654
+ if (value && typeof value === "object" && "value" in value && "watch" in value) {
1655
+ // Value is already a signal, use it as-is
1656
+ reactiveProps[key] = value;
1657
+ } else {
1658
+ // Create new signal for the prop value to make it reactive
1659
+ reactiveProps[key] = new eleva.signal(value);
1660
+ }
1661
+ });
1662
+ return reactiveProps;
1663
+ };
1664
+
1665
+ // Override Eleva's internal _extractProps method with our enhanced version
1666
+ eleva._extractProps = extractProps;
1667
+
1668
+ // Override Eleva's mount method to apply enhanced prop handling
1669
+ const originalMount = eleva.mount;
1670
+ eleva.mount = async (container, compName, props = {}) => {
1671
+ // Create reactive props if reactivity is enabled
1672
+ const enhancedProps = enableReactivity ? createReactiveProps(props) : props;
1673
+
1674
+ // Call the original mount method with enhanced props
1675
+ return await originalMount.call(eleva, container, compName, enhancedProps);
1676
+ };
1677
+
1678
+ // Override Eleva's _mountComponents method to enable signal reference passing
1679
+ const originalMountComponents = eleva._mountComponents;
1680
+
1681
+ // Cache to store parent contexts by container element
1682
+ const parentContextCache = new WeakMap();
1683
+ // Store child instances that need signal linking
1684
+ const pendingSignalLinks = new Set();
1685
+ eleva._mountComponents = async (container, children, childInstances) => {
1686
+ for (const [selector, component] of Object.entries(children)) {
1687
+ if (!selector) continue;
1688
+ for (const el of container.querySelectorAll(selector)) {
1689
+ if (!(el instanceof HTMLElement)) continue;
1690
+
1691
+ // Extract props from DOM attributes
1692
+ const extractedProps = eleva._extractProps(el);
1693
+
1694
+ // Get parent context to check for signal references
1695
+ let enhancedProps = extractedProps;
1696
+
1697
+ // Try to find parent context by looking up the DOM tree
1698
+ let parentContext = parentContextCache.get(container);
1699
+ if (!parentContext) {
1700
+ let currentElement = container;
1701
+ while (currentElement && !parentContext) {
1702
+ if (currentElement._eleva_instance && currentElement._eleva_instance.data) {
1703
+ parentContext = currentElement._eleva_instance.data;
1704
+ // Cache the parent context for future use
1705
+ parentContextCache.set(container, parentContext);
1706
+ break;
1707
+ }
1708
+ currentElement = currentElement.parentElement;
1709
+ }
1710
+ }
1711
+ if (enableReactivity && parentContext) {
1712
+ const signalProps = {};
1713
+
1714
+ // Check each extracted prop to see if there's a matching signal in parent context
1715
+ Object.keys(extractedProps).forEach(propName => {
1716
+ if (parentContext[propName] && parentContext[propName] instanceof eleva.signal) {
1717
+ // Found a signal in parent context with the same name as the prop
1718
+ // Pass the signal reference instead of creating a new one
1719
+ signalProps[propName] = parentContext[propName];
1720
+ }
1721
+ });
1722
+
1723
+ // Merge signal props with regular props (signal props take precedence)
1724
+ enhancedProps = {
1725
+ ...extractedProps,
1726
+ ...signalProps
1727
+ };
1728
+ }
1729
+
1730
+ // Create reactive props for non-signal props only
1731
+ let finalProps = enhancedProps;
1732
+ if (enableReactivity) {
1733
+ // Only create reactive props for values that aren't already signals
1734
+ const nonSignalProps = {};
1735
+ Object.entries(enhancedProps).forEach(([key, value]) => {
1736
+ if (!(value && typeof value === "object" && "value" in value && "watch" in value)) {
1737
+ // This is not a signal, create a reactive prop for it
1738
+ nonSignalProps[key] = value;
1739
+ }
1740
+ });
1741
+
1742
+ // Create reactive props only for non-signal values
1743
+ const reactiveNonSignalProps = createReactiveProps(nonSignalProps);
1744
+
1745
+ // Merge signal props with reactive non-signal props
1746
+ finalProps = {
1747
+ ...reactiveNonSignalProps,
1748
+ ...enhancedProps // Signal props take precedence
1749
+ };
1750
+ }
1751
+
1752
+ /** @type {MountResult} */
1753
+ const instance = await eleva.mount(el, component, finalProps);
1754
+ if (instance && !childInstances.includes(instance)) {
1755
+ childInstances.push(instance);
1756
+
1757
+ // If we have extracted props but no parent context yet, mark for later signal linking
1758
+ if (enableReactivity && Object.keys(extractedProps).length > 0 && !parentContext) {
1759
+ pendingSignalLinks.add({
1760
+ instance,
1761
+ extractedProps,
1762
+ container,
1763
+ component
1764
+ });
1765
+ }
1766
+ }
1767
+ }
1768
+ }
1769
+
1770
+ // After mounting all children, try to link signals for pending instances
1771
+ if (enableReactivity && pendingSignalLinks.size > 0) {
1772
+ for (const pending of pendingSignalLinks) {
1773
+ const {
1774
+ instance,
1775
+ extractedProps,
1776
+ container,
1777
+ component
1778
+ } = pending;
1779
+
1780
+ // Try to find parent context again
1781
+ let parentContext = parentContextCache.get(container);
1782
+ if (!parentContext) {
1783
+ let currentElement = container;
1784
+ while (currentElement && !parentContext) {
1785
+ if (currentElement._eleva_instance && currentElement._eleva_instance.data) {
1786
+ parentContext = currentElement._eleva_instance.data;
1787
+ parentContextCache.set(container, parentContext);
1788
+ break;
1789
+ }
1790
+ currentElement = currentElement.parentElement;
1791
+ }
1792
+ }
1793
+ if (parentContext) {
1794
+ const signalProps = {};
1795
+
1796
+ // Check each extracted prop to see if there's a matching signal in parent context
1797
+ Object.keys(extractedProps).forEach(propName => {
1798
+ if (parentContext[propName] && parentContext[propName] instanceof eleva.signal) {
1799
+ signalProps[propName] = parentContext[propName];
1800
+ }
1801
+ });
1802
+
1803
+ // Update the child instance's data with signal references
1804
+ if (Object.keys(signalProps).length > 0) {
1805
+ Object.assign(instance.data, signalProps);
1806
+
1807
+ // Set up signal watchers for the newly linked signals
1808
+ Object.keys(signalProps).forEach(propName => {
1809
+ const signal = signalProps[propName];
1810
+ if (signal && typeof signal.watch === "function") {
1811
+ signal.watch(newValue => {
1812
+ // Trigger a re-render of the child component when the signal changes
1813
+ const childComponent = eleva._components.get(component) || component;
1814
+ if (childComponent && childComponent.template) {
1815
+ const templateResult = typeof childComponent.template === "function" ? childComponent.template(instance.data) : childComponent.template;
1816
+ const newHtml = TemplateEngine.parse(templateResult, instance.data);
1817
+ eleva.renderer.patchDOM(instance.container, newHtml);
1818
+ }
1819
+ });
1820
+ }
1821
+ });
1822
+
1823
+ // Initial re-render to show the correct signal values
1824
+ const childComponent = eleva._components.get(component) || component;
1825
+ if (childComponent && childComponent.template) {
1826
+ const templateResult = typeof childComponent.template === "function" ? childComponent.template(instance.data) : childComponent.template;
1827
+ const newHtml = TemplateEngine.parse(templateResult, instance.data);
1828
+ eleva.renderer.patchDOM(instance.container, newHtml);
1829
+ }
1830
+ }
1831
+
1832
+ // Remove from pending list
1833
+ pendingSignalLinks.delete(pending);
1834
+ }
1835
+ }
1836
+ }
1837
+ };
1838
+
1839
+ /**
1840
+ * Expose utility methods on the Eleva instance
1841
+ * @namespace eleva.props
1842
+ */
1843
+ eleva.props = {
1844
+ /**
1845
+ * Parse a single value with automatic type detection
1846
+ * @param {any} value - The value to parse
1847
+ * @returns {any} The parsed value with appropriate type
1848
+ *
1849
+ * @example
1850
+ * app.props.parse("42") // → 42
1851
+ * app.props.parse("true") // → true
1852
+ * app.props.parse('{"key": "val"}') // → {key: "val"}
1853
+ */
1854
+ parse: value => {
1855
+ // Return value as-is if auto parsing is disabled
1856
+ if (!enableAutoParsing) {
1857
+ return value;
1858
+ }
1859
+ // Use our enhanced parsing function
1860
+ return parsePropValue(value);
1861
+ },
1862
+ /**
1863
+ * Detect the type of a value
1864
+ * @param {any} value - The value to detect type for
1865
+ * @returns {string} The detected type
1866
+ *
1867
+ * @example
1868
+ * app.props.detectType("hello") // → "string"
1869
+ * app.props.detectType(42) // → "number"
1870
+ * app.props.detectType([1, 2, 3]) // → "array"
1871
+ */
1872
+ detectType
1873
+ };
1874
+
1875
+ // Store original methods for uninstall
1876
+ eleva._originalExtractProps = eleva._extractProps;
1877
+ eleva._originalMount = originalMount;
1878
+ eleva._originalMountComponents = originalMountComponents;
1879
+ },
1880
+ /**
1881
+ * Uninstalls the plugin from the Eleva instance
1882
+ *
1883
+ * @param {Object} eleva - The Eleva instance
1884
+ *
1885
+ * @description
1886
+ * Restores the original Eleva methods and removes all plugin-specific
1887
+ * functionality. This method should be called when the plugin is no
1888
+ * longer needed.
1889
+ *
1890
+ * @example
1891
+ * // Uninstall the plugin
1892
+ * PropsPlugin.uninstall(app);
1893
+ */
1894
+ uninstall(eleva) {
1895
+ // Restore original _extractProps method
1896
+ if (eleva._originalExtractProps) {
1897
+ eleva._extractProps = eleva._originalExtractProps;
1898
+ delete eleva._originalExtractProps;
1899
+ }
1900
+
1901
+ // Restore original mount method
1902
+ if (eleva._originalMount) {
1903
+ eleva.mount = eleva._originalMount;
1904
+ delete eleva._originalMount;
1905
+ }
1906
+
1907
+ // Restore original _mountComponents method
1908
+ if (eleva._originalMountComponents) {
1909
+ eleva._mountComponents = eleva._originalMountComponents;
1910
+ delete eleva._originalMountComponents;
1911
+ }
1912
+
1913
+ // Remove plugin utility methods
1914
+ if (eleva.props) {
1915
+ delete eleva.props;
1916
+ }
1917
+ }
1918
+ };
1919
+
1328
1920
  exports.Attr = AttrPlugin;
1921
+ exports.Props = PropsPlugin;
1329
1922
  exports.Router = RouterPlugin;
1330
1923
  //# sourceMappingURL=eleva-plugins.cjs.js.map