eleva 1.0.0-rc.6 → 1.0.0-rc.7
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 +133 -4
- package/dist/eleva-plugins.cjs.js +645 -43
- package/dist/eleva-plugins.cjs.js.map +1 -1
- package/dist/eleva-plugins.esm.js +645 -44
- package/dist/eleva-plugins.esm.js.map +1 -1
- package/dist/eleva-plugins.umd.js +645 -43
- package/dist/eleva-plugins.umd.js.map +1 -1
- package/dist/eleva-plugins.umd.min.js +2 -2
- package/dist/eleva-plugins.umd.min.js.map +1 -1
- package/dist/eleva.cjs.js +31 -25
- package/dist/eleva.cjs.js.map +1 -1
- package/dist/eleva.esm.js +31 -25
- package/dist/eleva.esm.js.map +1 -1
- package/dist/eleva.umd.js +31 -25
- package/dist/eleva.umd.js.map +1 -1
- package/dist/eleva.umd.min.js +2 -2
- package/dist/eleva.umd.min.js.map +1 -1
- package/dist/plugins/attr.umd.js +2 -2
- package/dist/plugins/attr.umd.js.map +1 -1
- package/dist/plugins/attr.umd.min.js +1 -1
- package/dist/plugins/attr.umd.min.js.map +1 -1
- package/dist/plugins/props.umd.js +18 -15
- package/dist/plugins/props.umd.js.map +1 -1
- package/dist/plugins/props.umd.min.js +1 -1
- package/dist/plugins/props.umd.min.js.map +1 -1
- package/dist/plugins/router.umd.js +22 -25
- package/dist/plugins/router.umd.js.map +1 -1
- package/dist/plugins/router.umd.min.js +1 -1
- package/dist/plugins/router.umd.min.js.map +1 -1
- package/dist/plugins/store.umd.js +632 -0
- package/dist/plugins/store.umd.js.map +1 -0
- package/dist/plugins/store.umd.min.js +3 -0
- package/dist/plugins/store.umd.min.js.map +1 -0
- package/package.json +4 -4
- package/src/plugins/Store.js +741 -0
- package/src/plugins/index.js +6 -1
- package/types/plugins/Store.d.ts +86 -0
- package/types/plugins/Store.d.ts.map +1 -0
- package/types/plugins/index.d.ts +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Eleva Plugins v1.0.0-rc.
|
|
1
|
+
/*! Eleva Plugins v1.0.0-rc.7 | MIT License | https://elevajs.com */
|
|
2
2
|
/**
|
|
3
3
|
* A regular expression to match hyphenated lowercase letters.
|
|
4
4
|
* @private
|
|
@@ -127,7 +127,7 @@ const AttrPlugin = {
|
|
|
127
127
|
if (hasProperty) {
|
|
128
128
|
// Boolean attribute handling
|
|
129
129
|
if (enableBoolean) {
|
|
130
|
-
const isBoolean = typeof oldEl[prop] === "boolean" || descriptor
|
|
130
|
+
const isBoolean = typeof oldEl[prop] === "boolean" || (descriptor == null ? void 0 : descriptor.get) && typeof descriptor.get.call(oldEl) === "boolean";
|
|
131
131
|
if (isBoolean) {
|
|
132
132
|
const boolValue = value !== "false" && (value === "" || value === prop || value === "true");
|
|
133
133
|
oldEl[prop] = boolValue;
|
|
@@ -169,7 +169,7 @@ const AttrPlugin = {
|
|
|
169
169
|
|
|
170
170
|
// Override the _patchNode method to use our attribute handler
|
|
171
171
|
eleva.renderer._patchNode = function (oldNode, newNode) {
|
|
172
|
-
if (oldNode
|
|
172
|
+
if (oldNode != null && oldNode._eleva_instance) return;
|
|
173
173
|
if (!this._isSameNode(oldNode, newNode)) {
|
|
174
174
|
oldNode.replaceWith(newNode.cloneNode(true));
|
|
175
175
|
return;
|
|
@@ -219,18 +219,16 @@ const AttrPlugin = {
|
|
|
219
219
|
}
|
|
220
220
|
};
|
|
221
221
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
222
|
+
function _extends() {
|
|
223
|
+
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
224
|
+
for (var e = 1; e < arguments.length; e++) {
|
|
225
|
+
var t = arguments[e];
|
|
226
|
+
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
|
|
227
|
+
}
|
|
228
|
+
return n;
|
|
229
|
+
}, _extends.apply(null, arguments);
|
|
230
|
+
}
|
|
227
231
|
|
|
228
|
-
/**
|
|
229
|
-
* Simple error handler for the core router.
|
|
230
|
-
* Can be overridden by error handling plugins.
|
|
231
|
-
* Provides consistent error formatting and logging for router operations.
|
|
232
|
-
* @private
|
|
233
|
-
*/
|
|
234
232
|
const CoreErrorHandler = {
|
|
235
233
|
/**
|
|
236
234
|
* Handles router errors with basic formatting.
|
|
@@ -339,12 +337,11 @@ class Router {
|
|
|
339
337
|
this.eleva = eleva;
|
|
340
338
|
|
|
341
339
|
/** @type {RouterOptions} The merged router options. */
|
|
342
|
-
this.options = {
|
|
340
|
+
this.options = _extends({
|
|
343
341
|
mode: "hash",
|
|
344
342
|
queryParam: "view",
|
|
345
|
-
viewSelector: "root"
|
|
346
|
-
|
|
347
|
-
};
|
|
343
|
+
viewSelector: "root"
|
|
344
|
+
}, options);
|
|
348
345
|
|
|
349
346
|
/** @private @type {RouteDefinition[]} The processed list of route definitions. */
|
|
350
347
|
this.routes = this._processRoutes(options.routes || []);
|
|
@@ -408,10 +405,9 @@ class Router {
|
|
|
408
405
|
const processedRoutes = [];
|
|
409
406
|
for (const route of routes) {
|
|
410
407
|
try {
|
|
411
|
-
processedRoutes.push({
|
|
412
|
-
...route,
|
|
408
|
+
processedRoutes.push(_extends({}, route, {
|
|
413
409
|
segments: this._parsePathIntoSegments(route.path)
|
|
414
|
-
});
|
|
410
|
+
}));
|
|
415
411
|
} catch (error) {
|
|
416
412
|
this.errorHandler.warn(`Invalid path in route definition "${route.path || "undefined"}": ${error.message}`, {
|
|
417
413
|
route,
|
|
@@ -666,13 +662,12 @@ class Router {
|
|
|
666
662
|
return false;
|
|
667
663
|
}
|
|
668
664
|
}
|
|
669
|
-
const to = {
|
|
670
|
-
...toLocation,
|
|
665
|
+
const to = _extends({}, toLocation, {
|
|
671
666
|
params: toMatch.params,
|
|
672
667
|
meta: toMatch.route.meta || {},
|
|
673
668
|
name: toMatch.route.name,
|
|
674
669
|
matched: toMatch.route
|
|
675
|
-
};
|
|
670
|
+
});
|
|
676
671
|
try {
|
|
677
672
|
// 1. Run all *pre-navigation* guards.
|
|
678
673
|
const canNavigate = await this._runGuards(to, from, toMatch.route);
|
|
@@ -903,7 +898,10 @@ class Router {
|
|
|
903
898
|
* @returns {Function} A getter function.
|
|
904
899
|
*/
|
|
905
900
|
_createRouteGetter(property, defaultValue) {
|
|
906
|
-
return () =>
|
|
901
|
+
return () => {
|
|
902
|
+
var _this$currentRoute$va, _this$currentRoute$va2;
|
|
903
|
+
return (_this$currentRoute$va = (_this$currentRoute$va2 = this.currentRoute.value) == null ? void 0 : _this$currentRoute$va2[property]) != null ? _this$currentRoute$va : defaultValue;
|
|
904
|
+
};
|
|
907
905
|
}
|
|
908
906
|
|
|
909
907
|
/**
|
|
@@ -915,8 +913,7 @@ class Router {
|
|
|
915
913
|
_wrapComponent(component) {
|
|
916
914
|
const originalSetup = component.setup;
|
|
917
915
|
const self = this;
|
|
918
|
-
return {
|
|
919
|
-
...component,
|
|
916
|
+
return _extends({}, component, {
|
|
920
917
|
async setup(ctx) {
|
|
921
918
|
ctx.router = {
|
|
922
919
|
navigate: self.navigate.bind(self),
|
|
@@ -941,7 +938,7 @@ class Router {
|
|
|
941
938
|
};
|
|
942
939
|
return originalSetup ? await originalSetup(ctx) : {};
|
|
943
940
|
}
|
|
944
|
-
};
|
|
941
|
+
});
|
|
945
942
|
}
|
|
946
943
|
|
|
947
944
|
/**
|
|
@@ -1335,12 +1332,6 @@ const RouterPlugin = {
|
|
|
1335
1332
|
* const result = TemplateEngine.parse(template, data); // Returns: "Hello, World!"
|
|
1336
1333
|
*/
|
|
1337
1334
|
class TemplateEngine {
|
|
1338
|
-
/**
|
|
1339
|
-
* @private {RegExp} Regular expression for matching template expressions in the format {{ expression }}
|
|
1340
|
-
* @type {RegExp}
|
|
1341
|
-
*/
|
|
1342
|
-
static expressionPattern = /\{\{\s*(.*?)\s*\}\}/g;
|
|
1343
|
-
|
|
1344
1335
|
/**
|
|
1345
1336
|
* Parses a template string, replacing expressions with their evaluated values.
|
|
1346
1337
|
* Expressions are evaluated in the provided data context.
|
|
@@ -1379,11 +1370,16 @@ class TemplateEngine {
|
|
|
1379
1370
|
if (typeof expression !== "string") return expression;
|
|
1380
1371
|
try {
|
|
1381
1372
|
return new Function("data", `with(data) { return ${expression}; }`)(data);
|
|
1382
|
-
} catch {
|
|
1373
|
+
} catch (_unused) {
|
|
1383
1374
|
return "";
|
|
1384
1375
|
}
|
|
1385
1376
|
}
|
|
1386
1377
|
}
|
|
1378
|
+
/**
|
|
1379
|
+
* @private {RegExp} Regular expression for matching template expressions in the format {{ expression }}
|
|
1380
|
+
* @type {RegExp}
|
|
1381
|
+
*/
|
|
1382
|
+
TemplateEngine.expressionPattern = /\{\{\s*(.*?)\s*\}\}/g;
|
|
1387
1383
|
|
|
1388
1384
|
/**
|
|
1389
1385
|
* @class 🎯 PropsPlugin
|
|
@@ -1719,10 +1715,7 @@ const PropsPlugin = {
|
|
|
1719
1715
|
});
|
|
1720
1716
|
|
|
1721
1717
|
// Merge signal props with regular props (signal props take precedence)
|
|
1722
|
-
enhancedProps = {
|
|
1723
|
-
...extractedProps,
|
|
1724
|
-
...signalProps
|
|
1725
|
-
};
|
|
1718
|
+
enhancedProps = _extends({}, extractedProps, signalProps);
|
|
1726
1719
|
}
|
|
1727
1720
|
|
|
1728
1721
|
// Create reactive props for non-signal props only
|
|
@@ -1741,10 +1734,7 @@ const PropsPlugin = {
|
|
|
1741
1734
|
const reactiveNonSignalProps = createReactiveProps(nonSignalProps);
|
|
1742
1735
|
|
|
1743
1736
|
// Merge signal props with reactive non-signal props
|
|
1744
|
-
finalProps = {
|
|
1745
|
-
...reactiveNonSignalProps,
|
|
1746
|
-
...enhancedProps // Signal props take precedence
|
|
1747
|
-
};
|
|
1737
|
+
finalProps = _extends({}, reactiveNonSignalProps, enhancedProps);
|
|
1748
1738
|
}
|
|
1749
1739
|
|
|
1750
1740
|
/** @type {MountResult} */
|
|
@@ -1915,5 +1905,616 @@ const PropsPlugin = {
|
|
|
1915
1905
|
}
|
|
1916
1906
|
};
|
|
1917
1907
|
|
|
1918
|
-
|
|
1908
|
+
const StorePlugin = {
|
|
1909
|
+
/**
|
|
1910
|
+
* Unique identifier for the plugin
|
|
1911
|
+
* @type {string}
|
|
1912
|
+
*/
|
|
1913
|
+
name: "store",
|
|
1914
|
+
/**
|
|
1915
|
+
* Plugin version
|
|
1916
|
+
* @type {string}
|
|
1917
|
+
*/
|
|
1918
|
+
version: "1.0.0-rc.1",
|
|
1919
|
+
/**
|
|
1920
|
+
* Plugin description
|
|
1921
|
+
* @type {string}
|
|
1922
|
+
*/
|
|
1923
|
+
description: "Reactive state management for sharing data across the entire Eleva application",
|
|
1924
|
+
/**
|
|
1925
|
+
* Installs the plugin into the Eleva instance
|
|
1926
|
+
*
|
|
1927
|
+
* @param {Object} eleva - The Eleva instance
|
|
1928
|
+
* @param {Object} options - Plugin configuration options
|
|
1929
|
+
* @param {Object} [options.state={}] - Initial state object
|
|
1930
|
+
* @param {Object} [options.actions={}] - Action functions for state mutations
|
|
1931
|
+
* @param {Object} [options.namespaces={}] - Namespaced modules for organizing store
|
|
1932
|
+
* @param {Object} [options.persistence] - Persistence configuration
|
|
1933
|
+
* @param {boolean} [options.persistence.enabled=false] - Enable state persistence
|
|
1934
|
+
* @param {string} [options.persistence.key="eleva-store"] - Storage key
|
|
1935
|
+
* @param {"localStorage" | "sessionStorage"} [options.persistence.storage="localStorage"] - Storage type
|
|
1936
|
+
* @param {Array<string>} [options.persistence.include] - State keys to persist (if not provided, all state is persisted)
|
|
1937
|
+
* @param {Array<string>} [options.persistence.exclude] - State keys to exclude from persistence
|
|
1938
|
+
* @param {boolean} [options.devTools=false] - Enable development tools integration
|
|
1939
|
+
* @param {Function} [options.onError=null] - Error handler function
|
|
1940
|
+
*
|
|
1941
|
+
* @example
|
|
1942
|
+
* // Basic installation
|
|
1943
|
+
* app.use(StorePlugin, {
|
|
1944
|
+
* state: { count: 0, user: null },
|
|
1945
|
+
* actions: {
|
|
1946
|
+
* increment: (state) => state.count.value++,
|
|
1947
|
+
* setUser: (state, user) => state.user.value = user
|
|
1948
|
+
* }
|
|
1949
|
+
* });
|
|
1950
|
+
*
|
|
1951
|
+
* // Advanced installation with persistence and namespaces
|
|
1952
|
+
* app.use(StorePlugin, {
|
|
1953
|
+
* state: { theme: "light" },
|
|
1954
|
+
* namespaces: {
|
|
1955
|
+
* auth: {
|
|
1956
|
+
* state: { user: null, token: null },
|
|
1957
|
+
* actions: {
|
|
1958
|
+
* login: (state, { user, token }) => {
|
|
1959
|
+
* state.user.value = user;
|
|
1960
|
+
* state.token.value = token;
|
|
1961
|
+
* },
|
|
1962
|
+
* logout: (state) => {
|
|
1963
|
+
* state.user.value = null;
|
|
1964
|
+
* state.token.value = null;
|
|
1965
|
+
* }
|
|
1966
|
+
* }
|
|
1967
|
+
* }
|
|
1968
|
+
* },
|
|
1969
|
+
* persistence: {
|
|
1970
|
+
* enabled: true,
|
|
1971
|
+
* include: ["theme", "auth.user"]
|
|
1972
|
+
* }
|
|
1973
|
+
* });
|
|
1974
|
+
*/
|
|
1975
|
+
install(eleva, options = {}) {
|
|
1976
|
+
const {
|
|
1977
|
+
state = {},
|
|
1978
|
+
actions = {},
|
|
1979
|
+
namespaces = {},
|
|
1980
|
+
persistence = {},
|
|
1981
|
+
devTools = false,
|
|
1982
|
+
onError = null
|
|
1983
|
+
} = options;
|
|
1984
|
+
|
|
1985
|
+
/**
|
|
1986
|
+
* Store instance that manages all state and provides the API
|
|
1987
|
+
* @private
|
|
1988
|
+
*/
|
|
1989
|
+
class Store {
|
|
1990
|
+
constructor() {
|
|
1991
|
+
this.state = {};
|
|
1992
|
+
this.actions = {};
|
|
1993
|
+
this.subscribers = new Set();
|
|
1994
|
+
this.mutations = [];
|
|
1995
|
+
this.persistence = _extends({
|
|
1996
|
+
enabled: false,
|
|
1997
|
+
key: "eleva-store",
|
|
1998
|
+
storage: "localStorage",
|
|
1999
|
+
include: null,
|
|
2000
|
+
exclude: null
|
|
2001
|
+
}, persistence);
|
|
2002
|
+
this.devTools = devTools;
|
|
2003
|
+
this.onError = onError;
|
|
2004
|
+
this._initializeState(state, actions);
|
|
2005
|
+
this._initializeNamespaces(namespaces);
|
|
2006
|
+
this._loadPersistedState();
|
|
2007
|
+
this._setupDevTools();
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
/**
|
|
2011
|
+
* Initializes the root state and actions
|
|
2012
|
+
* @private
|
|
2013
|
+
*/
|
|
2014
|
+
_initializeState(initialState, initialActions) {
|
|
2015
|
+
// Create reactive signals for each state property
|
|
2016
|
+
Object.entries(initialState).forEach(([key, value]) => {
|
|
2017
|
+
this.state[key] = new eleva.signal(value);
|
|
2018
|
+
});
|
|
2019
|
+
|
|
2020
|
+
// Set up actions
|
|
2021
|
+
this.actions = _extends({}, initialActions);
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
/**
|
|
2025
|
+
* Initializes namespaced modules
|
|
2026
|
+
* @private
|
|
2027
|
+
*/
|
|
2028
|
+
_initializeNamespaces(namespaces) {
|
|
2029
|
+
Object.entries(namespaces).forEach(([namespace, module]) => {
|
|
2030
|
+
const {
|
|
2031
|
+
state: moduleState = {},
|
|
2032
|
+
actions: moduleActions = {}
|
|
2033
|
+
} = module;
|
|
2034
|
+
|
|
2035
|
+
// Create namespace object if it doesn't exist
|
|
2036
|
+
if (!this.state[namespace]) {
|
|
2037
|
+
this.state[namespace] = {};
|
|
2038
|
+
}
|
|
2039
|
+
if (!this.actions[namespace]) {
|
|
2040
|
+
this.actions[namespace] = {};
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// Initialize namespaced state
|
|
2044
|
+
Object.entries(moduleState).forEach(([key, value]) => {
|
|
2045
|
+
this.state[namespace][key] = new eleva.signal(value);
|
|
2046
|
+
});
|
|
2047
|
+
|
|
2048
|
+
// Set up namespaced actions
|
|
2049
|
+
this.actions[namespace] = _extends({}, moduleActions);
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
/**
|
|
2054
|
+
* Loads persisted state from storage
|
|
2055
|
+
* @private
|
|
2056
|
+
*/
|
|
2057
|
+
_loadPersistedState() {
|
|
2058
|
+
if (!this.persistence.enabled || typeof window === "undefined") {
|
|
2059
|
+
return;
|
|
2060
|
+
}
|
|
2061
|
+
try {
|
|
2062
|
+
const storage = window[this.persistence.storage];
|
|
2063
|
+
const persistedData = storage.getItem(this.persistence.key);
|
|
2064
|
+
if (persistedData) {
|
|
2065
|
+
const data = JSON.parse(persistedData);
|
|
2066
|
+
this._applyPersistedData(data);
|
|
2067
|
+
}
|
|
2068
|
+
} catch (error) {
|
|
2069
|
+
if (this.onError) {
|
|
2070
|
+
this.onError(error, "Failed to load persisted state");
|
|
2071
|
+
} else {
|
|
2072
|
+
console.warn("[StorePlugin] Failed to load persisted state:", error);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
/**
|
|
2078
|
+
* Applies persisted data to the current state
|
|
2079
|
+
* @private
|
|
2080
|
+
*/
|
|
2081
|
+
_applyPersistedData(data, currentState = this.state, path = "") {
|
|
2082
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
2083
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
2084
|
+
if (this._shouldPersist(fullPath)) {
|
|
2085
|
+
if (currentState[key] && typeof currentState[key] === "object" && "value" in currentState[key]) {
|
|
2086
|
+
// This is a signal, update its value
|
|
2087
|
+
currentState[key].value = value;
|
|
2088
|
+
} else if (typeof value === "object" && value !== null && currentState[key]) {
|
|
2089
|
+
// This is a nested object, recurse
|
|
2090
|
+
this._applyPersistedData(value, currentState[key], fullPath);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
/**
|
|
2097
|
+
* Determines if a state path should be persisted
|
|
2098
|
+
* @private
|
|
2099
|
+
*/
|
|
2100
|
+
_shouldPersist(path) {
|
|
2101
|
+
const {
|
|
2102
|
+
include,
|
|
2103
|
+
exclude
|
|
2104
|
+
} = this.persistence;
|
|
2105
|
+
if (include && include.length > 0) {
|
|
2106
|
+
return include.some(includePath => path.startsWith(includePath));
|
|
2107
|
+
}
|
|
2108
|
+
if (exclude && exclude.length > 0) {
|
|
2109
|
+
return !exclude.some(excludePath => path.startsWith(excludePath));
|
|
2110
|
+
}
|
|
2111
|
+
return true;
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
/**
|
|
2115
|
+
* Saves current state to storage
|
|
2116
|
+
* @private
|
|
2117
|
+
*/
|
|
2118
|
+
_saveState() {
|
|
2119
|
+
if (!this.persistence.enabled || typeof window === "undefined") {
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
try {
|
|
2123
|
+
const storage = window[this.persistence.storage];
|
|
2124
|
+
const dataToSave = this._extractPersistedData();
|
|
2125
|
+
storage.setItem(this.persistence.key, JSON.stringify(dataToSave));
|
|
2126
|
+
} catch (error) {
|
|
2127
|
+
if (this.onError) {
|
|
2128
|
+
this.onError(error, "Failed to save state");
|
|
2129
|
+
} else {
|
|
2130
|
+
console.warn("[StorePlugin] Failed to save state:", error);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
/**
|
|
2136
|
+
* Extracts data that should be persisted
|
|
2137
|
+
* @private
|
|
2138
|
+
*/
|
|
2139
|
+
_extractPersistedData(currentState = this.state, path = "") {
|
|
2140
|
+
const result = {};
|
|
2141
|
+
Object.entries(currentState).forEach(([key, value]) => {
|
|
2142
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
2143
|
+
if (this._shouldPersist(fullPath)) {
|
|
2144
|
+
if (value && typeof value === "object" && "value" in value) {
|
|
2145
|
+
// This is a signal, extract its value
|
|
2146
|
+
result[key] = value.value;
|
|
2147
|
+
} else if (typeof value === "object" && value !== null) {
|
|
2148
|
+
// This is a nested object, recurse
|
|
2149
|
+
const nestedData = this._extractPersistedData(value, fullPath);
|
|
2150
|
+
if (Object.keys(nestedData).length > 0) {
|
|
2151
|
+
result[key] = nestedData;
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
});
|
|
2156
|
+
return result;
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
/**
|
|
2160
|
+
* Sets up development tools integration
|
|
2161
|
+
* @private
|
|
2162
|
+
*/
|
|
2163
|
+
_setupDevTools() {
|
|
2164
|
+
if (!this.devTools || typeof window === "undefined" || !window.__ELEVA_DEVTOOLS__) {
|
|
2165
|
+
return;
|
|
2166
|
+
}
|
|
2167
|
+
window.__ELEVA_DEVTOOLS__.registerStore(this);
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
/**
|
|
2171
|
+
* Dispatches an action to mutate the state
|
|
2172
|
+
* @param {string} actionName - The name of the action to dispatch (supports namespaced actions like "auth.login")
|
|
2173
|
+
* @param {any} payload - The payload to pass to the action
|
|
2174
|
+
* @returns {Promise<any>} The result of the action
|
|
2175
|
+
*/
|
|
2176
|
+
async dispatch(actionName, payload) {
|
|
2177
|
+
try {
|
|
2178
|
+
const action = this._getAction(actionName);
|
|
2179
|
+
if (!action) {
|
|
2180
|
+
const error = new Error(`Action "${actionName}" not found`);
|
|
2181
|
+
if (this.onError) {
|
|
2182
|
+
this.onError(error, actionName);
|
|
2183
|
+
}
|
|
2184
|
+
throw error;
|
|
2185
|
+
}
|
|
2186
|
+
const mutation = {
|
|
2187
|
+
type: actionName,
|
|
2188
|
+
payload,
|
|
2189
|
+
timestamp: Date.now()
|
|
2190
|
+
};
|
|
2191
|
+
|
|
2192
|
+
// Record mutation for devtools
|
|
2193
|
+
this.mutations.push(mutation);
|
|
2194
|
+
if (this.mutations.length > 100) {
|
|
2195
|
+
this.mutations.shift(); // Keep only last 100 mutations
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
// Execute the action
|
|
2199
|
+
const result = await action.call(null, this.state, payload);
|
|
2200
|
+
|
|
2201
|
+
// Save state if persistence is enabled
|
|
2202
|
+
this._saveState();
|
|
2203
|
+
|
|
2204
|
+
// Notify subscribers
|
|
2205
|
+
this.subscribers.forEach(callback => {
|
|
2206
|
+
try {
|
|
2207
|
+
callback(mutation, this.state);
|
|
2208
|
+
} catch (error) {
|
|
2209
|
+
if (this.onError) {
|
|
2210
|
+
this.onError(error, "Subscriber callback failed");
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
});
|
|
2214
|
+
|
|
2215
|
+
// Notify devtools
|
|
2216
|
+
if (this.devTools && typeof window !== "undefined" && window.__ELEVA_DEVTOOLS__) {
|
|
2217
|
+
window.__ELEVA_DEVTOOLS__.notifyMutation(mutation, this.state);
|
|
2218
|
+
}
|
|
2219
|
+
return result;
|
|
2220
|
+
} catch (error) {
|
|
2221
|
+
if (this.onError) {
|
|
2222
|
+
this.onError(error, `Action dispatch failed: ${actionName}`);
|
|
2223
|
+
}
|
|
2224
|
+
throw error;
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
/**
|
|
2229
|
+
* Gets an action by name (supports namespaced actions)
|
|
2230
|
+
* @private
|
|
2231
|
+
*/
|
|
2232
|
+
_getAction(actionName) {
|
|
2233
|
+
const parts = actionName.split(".");
|
|
2234
|
+
let current = this.actions;
|
|
2235
|
+
for (const part of parts) {
|
|
2236
|
+
if (current[part] === undefined) {
|
|
2237
|
+
return null;
|
|
2238
|
+
}
|
|
2239
|
+
current = current[part];
|
|
2240
|
+
}
|
|
2241
|
+
return typeof current === "function" ? current : null;
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
/**
|
|
2245
|
+
* Subscribes to store mutations
|
|
2246
|
+
* @param {Function} callback - Callback function to call on mutations
|
|
2247
|
+
* @returns {Function} Unsubscribe function
|
|
2248
|
+
*/
|
|
2249
|
+
subscribe(callback) {
|
|
2250
|
+
if (typeof callback !== "function") {
|
|
2251
|
+
throw new Error("Subscribe callback must be a function");
|
|
2252
|
+
}
|
|
2253
|
+
this.subscribers.add(callback);
|
|
2254
|
+
|
|
2255
|
+
// Return unsubscribe function
|
|
2256
|
+
return () => {
|
|
2257
|
+
this.subscribers.delete(callback);
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
/**
|
|
2262
|
+
* Gets a deep copy of the current state values (not signals)
|
|
2263
|
+
* @returns {Object} The current state values
|
|
2264
|
+
*/
|
|
2265
|
+
getState() {
|
|
2266
|
+
return this._extractPersistedData();
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
/**
|
|
2270
|
+
* Replaces the entire state (useful for testing or state hydration)
|
|
2271
|
+
* @param {Object} newState - The new state object
|
|
2272
|
+
*/
|
|
2273
|
+
replaceState(newState) {
|
|
2274
|
+
this._applyPersistedData(newState);
|
|
2275
|
+
this._saveState();
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
/**
|
|
2279
|
+
* Clears persisted state from storage
|
|
2280
|
+
*/
|
|
2281
|
+
clearPersistedState() {
|
|
2282
|
+
if (!this.persistence.enabled || typeof window === "undefined") {
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
try {
|
|
2286
|
+
const storage = window[this.persistence.storage];
|
|
2287
|
+
storage.removeItem(this.persistence.key);
|
|
2288
|
+
} catch (error) {
|
|
2289
|
+
if (this.onError) {
|
|
2290
|
+
this.onError(error, "Failed to clear persisted state");
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
/**
|
|
2296
|
+
* Registers a new namespaced module at runtime
|
|
2297
|
+
* @param {string} namespace - The namespace for the module
|
|
2298
|
+
* @param {Object} module - The module definition
|
|
2299
|
+
* @param {Object} module.state - The module's initial state
|
|
2300
|
+
* @param {Object} module.actions - The module's actions
|
|
2301
|
+
*/
|
|
2302
|
+
registerModule(namespace, module) {
|
|
2303
|
+
if (this.state[namespace] || this.actions[namespace]) {
|
|
2304
|
+
console.warn(`[StorePlugin] Module "${namespace}" already exists`);
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
// Initialize the module
|
|
2309
|
+
this.state[namespace] = {};
|
|
2310
|
+
this.actions[namespace] = {};
|
|
2311
|
+
const namespaces = {
|
|
2312
|
+
[namespace]: module
|
|
2313
|
+
};
|
|
2314
|
+
this._initializeNamespaces(namespaces);
|
|
2315
|
+
this._saveState();
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
/**
|
|
2319
|
+
* Unregisters a namespaced module
|
|
2320
|
+
* @param {string} namespace - The namespace to unregister
|
|
2321
|
+
*/
|
|
2322
|
+
unregisterModule(namespace) {
|
|
2323
|
+
if (!this.state[namespace] && !this.actions[namespace]) {
|
|
2324
|
+
console.warn(`[StorePlugin] Module "${namespace}" does not exist`);
|
|
2325
|
+
return;
|
|
2326
|
+
}
|
|
2327
|
+
delete this.state[namespace];
|
|
2328
|
+
delete this.actions[namespace];
|
|
2329
|
+
this._saveState();
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
/**
|
|
2333
|
+
* Creates a new reactive state property at runtime
|
|
2334
|
+
* @param {string} key - The state key
|
|
2335
|
+
* @param {*} initialValue - The initial value
|
|
2336
|
+
* @returns {Object} The created signal
|
|
2337
|
+
*/
|
|
2338
|
+
createState(key, initialValue) {
|
|
2339
|
+
if (this.state[key]) {
|
|
2340
|
+
return this.state[key]; // Return existing state
|
|
2341
|
+
}
|
|
2342
|
+
this.state[key] = new eleva.signal(initialValue);
|
|
2343
|
+
this._saveState();
|
|
2344
|
+
return this.state[key];
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
/**
|
|
2348
|
+
* Creates a new action at runtime
|
|
2349
|
+
* @param {string} name - The action name
|
|
2350
|
+
* @param {Function} actionFn - The action function
|
|
2351
|
+
*/
|
|
2352
|
+
createAction(name, actionFn) {
|
|
2353
|
+
if (typeof actionFn !== "function") {
|
|
2354
|
+
throw new Error("Action must be a function");
|
|
2355
|
+
}
|
|
2356
|
+
this.actions[name] = actionFn;
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
// Create the store instance
|
|
2361
|
+
const store = new Store();
|
|
2362
|
+
|
|
2363
|
+
// Store the original mount method to override it
|
|
2364
|
+
const originalMount = eleva.mount;
|
|
2365
|
+
|
|
2366
|
+
/**
|
|
2367
|
+
* Override the mount method to inject store context into components
|
|
2368
|
+
*/
|
|
2369
|
+
eleva.mount = async (container, compName, props = {}) => {
|
|
2370
|
+
// Get the component definition
|
|
2371
|
+
const componentDef = typeof compName === "string" ? eleva._components.get(compName) || compName : compName;
|
|
2372
|
+
if (!componentDef) {
|
|
2373
|
+
return await originalMount.call(eleva, container, compName, props);
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
// Create a wrapped component that injects store into setup
|
|
2377
|
+
const wrappedComponent = _extends({}, componentDef, {
|
|
2378
|
+
async setup(ctx) {
|
|
2379
|
+
// Inject store into the context with enhanced API
|
|
2380
|
+
ctx.store = {
|
|
2381
|
+
// Core store functionality
|
|
2382
|
+
state: store.state,
|
|
2383
|
+
dispatch: store.dispatch.bind(store),
|
|
2384
|
+
subscribe: store.subscribe.bind(store),
|
|
2385
|
+
getState: store.getState.bind(store),
|
|
2386
|
+
// Module management
|
|
2387
|
+
registerModule: store.registerModule.bind(store),
|
|
2388
|
+
unregisterModule: store.unregisterModule.bind(store),
|
|
2389
|
+
// Utilities for dynamic state/action creation
|
|
2390
|
+
createState: store.createState.bind(store),
|
|
2391
|
+
createAction: store.createAction.bind(store),
|
|
2392
|
+
// Access to signal constructor for manual state creation
|
|
2393
|
+
signal: eleva.signal
|
|
2394
|
+
};
|
|
2395
|
+
|
|
2396
|
+
// Call original setup if it exists
|
|
2397
|
+
const originalSetup = componentDef.setup;
|
|
2398
|
+
const result = originalSetup ? await originalSetup(ctx) : {};
|
|
2399
|
+
return result;
|
|
2400
|
+
}
|
|
2401
|
+
});
|
|
2402
|
+
|
|
2403
|
+
// Call original mount with wrapped component
|
|
2404
|
+
return await originalMount.call(eleva, container, wrappedComponent, props);
|
|
2405
|
+
};
|
|
2406
|
+
|
|
2407
|
+
// Override _mountComponents to ensure child components also get store context
|
|
2408
|
+
const originalMountComponents = eleva._mountComponents;
|
|
2409
|
+
eleva._mountComponents = async (container, children, childInstances) => {
|
|
2410
|
+
// Create wrapped children with store injection
|
|
2411
|
+
const wrappedChildren = {};
|
|
2412
|
+
for (const [selector, childComponent] of Object.entries(children)) {
|
|
2413
|
+
const componentDef = typeof childComponent === "string" ? eleva._components.get(childComponent) || childComponent : childComponent;
|
|
2414
|
+
if (componentDef && typeof componentDef === "object") {
|
|
2415
|
+
wrappedChildren[selector] = _extends({}, componentDef, {
|
|
2416
|
+
async setup(ctx) {
|
|
2417
|
+
// Inject store into the context with enhanced API
|
|
2418
|
+
ctx.store = {
|
|
2419
|
+
// Core store functionality
|
|
2420
|
+
state: store.state,
|
|
2421
|
+
dispatch: store.dispatch.bind(store),
|
|
2422
|
+
subscribe: store.subscribe.bind(store),
|
|
2423
|
+
getState: store.getState.bind(store),
|
|
2424
|
+
// Module management
|
|
2425
|
+
registerModule: store.registerModule.bind(store),
|
|
2426
|
+
unregisterModule: store.unregisterModule.bind(store),
|
|
2427
|
+
// Utilities for dynamic state/action creation
|
|
2428
|
+
createState: store.createState.bind(store),
|
|
2429
|
+
createAction: store.createAction.bind(store),
|
|
2430
|
+
// Access to signal constructor for manual state creation
|
|
2431
|
+
signal: eleva.signal
|
|
2432
|
+
};
|
|
2433
|
+
|
|
2434
|
+
// Call original setup if it exists
|
|
2435
|
+
const originalSetup = componentDef.setup;
|
|
2436
|
+
const result = originalSetup ? await originalSetup(ctx) : {};
|
|
2437
|
+
return result;
|
|
2438
|
+
}
|
|
2439
|
+
});
|
|
2440
|
+
} else {
|
|
2441
|
+
wrappedChildren[selector] = childComponent;
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
// Call original _mountComponents with wrapped children
|
|
2446
|
+
return await originalMountComponents.call(eleva, container, wrappedChildren, childInstances);
|
|
2447
|
+
};
|
|
2448
|
+
|
|
2449
|
+
// Expose store instance and utilities on the Eleva instance
|
|
2450
|
+
eleva.store = store;
|
|
2451
|
+
|
|
2452
|
+
/**
|
|
2453
|
+
* Expose utility methods on the Eleva instance
|
|
2454
|
+
* @namespace eleva.store
|
|
2455
|
+
*/
|
|
2456
|
+
eleva.createAction = (name, actionFn) => {
|
|
2457
|
+
store.actions[name] = actionFn;
|
|
2458
|
+
};
|
|
2459
|
+
eleva.dispatch = (actionName, payload) => {
|
|
2460
|
+
return store.dispatch(actionName, payload);
|
|
2461
|
+
};
|
|
2462
|
+
eleva.getState = () => {
|
|
2463
|
+
return store.getState();
|
|
2464
|
+
};
|
|
2465
|
+
eleva.subscribe = callback => {
|
|
2466
|
+
return store.subscribe(callback);
|
|
2467
|
+
};
|
|
2468
|
+
|
|
2469
|
+
// Store original methods for cleanup
|
|
2470
|
+
eleva._originalMount = originalMount;
|
|
2471
|
+
eleva._originalMountComponents = originalMountComponents;
|
|
2472
|
+
},
|
|
2473
|
+
/**
|
|
2474
|
+
* Uninstalls the plugin from the Eleva instance
|
|
2475
|
+
*
|
|
2476
|
+
* @param {Object} eleva - The Eleva instance
|
|
2477
|
+
*
|
|
2478
|
+
* @description
|
|
2479
|
+
* Restores the original Eleva methods and removes all plugin-specific
|
|
2480
|
+
* functionality. This method should be called when the plugin is no
|
|
2481
|
+
* longer needed.
|
|
2482
|
+
*
|
|
2483
|
+
* @example
|
|
2484
|
+
* // Uninstall the plugin
|
|
2485
|
+
* StorePlugin.uninstall(app);
|
|
2486
|
+
*/
|
|
2487
|
+
uninstall(eleva) {
|
|
2488
|
+
// Restore original mount method
|
|
2489
|
+
if (eleva._originalMount) {
|
|
2490
|
+
eleva.mount = eleva._originalMount;
|
|
2491
|
+
delete eleva._originalMount;
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
// Restore original _mountComponents method
|
|
2495
|
+
if (eleva._originalMountComponents) {
|
|
2496
|
+
eleva._mountComponents = eleva._originalMountComponents;
|
|
2497
|
+
delete eleva._originalMountComponents;
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
// Remove store instance and utility methods
|
|
2501
|
+
if (eleva.store) {
|
|
2502
|
+
delete eleva.store;
|
|
2503
|
+
}
|
|
2504
|
+
if (eleva.createAction) {
|
|
2505
|
+
delete eleva.createAction;
|
|
2506
|
+
}
|
|
2507
|
+
if (eleva.dispatch) {
|
|
2508
|
+
delete eleva.dispatch;
|
|
2509
|
+
}
|
|
2510
|
+
if (eleva.getState) {
|
|
2511
|
+
delete eleva.getState;
|
|
2512
|
+
}
|
|
2513
|
+
if (eleva.subscribe) {
|
|
2514
|
+
delete eleva.subscribe;
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
};
|
|
2518
|
+
|
|
2519
|
+
export { AttrPlugin as Attr, PropsPlugin as Props, RouterPlugin as Router, StorePlugin as Store };
|
|
1919
2520
|
//# sourceMappingURL=eleva-plugins.esm.js.map
|