eleva 1.0.0-alpha → 1.0.0-rc.10
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/LICENSE +1 -1
- package/README.md +554 -137
- package/dist/eleva-plugins.cjs.js +3397 -0
- package/dist/eleva-plugins.cjs.js.map +1 -0
- package/dist/eleva-plugins.esm.js +3392 -0
- package/dist/eleva-plugins.esm.js.map +1 -0
- package/dist/eleva-plugins.umd.js +3403 -0
- package/dist/eleva-plugins.umd.js.map +1 -0
- package/dist/eleva-plugins.umd.min.js +3 -0
- package/dist/eleva-plugins.umd.min.js.map +1 -0
- package/dist/eleva.cjs.js +1448 -0
- package/dist/eleva.cjs.js.map +1 -0
- package/dist/eleva.d.ts +1057 -80
- package/dist/eleva.esm.js +1230 -274
- package/dist/eleva.esm.js.map +1 -1
- package/dist/eleva.umd.js +1230 -274
- package/dist/eleva.umd.js.map +1 -1
- package/dist/eleva.umd.min.js +3 -0
- package/dist/eleva.umd.min.js.map +1 -0
- package/dist/plugins/attr.umd.js +231 -0
- package/dist/plugins/attr.umd.js.map +1 -0
- package/dist/plugins/attr.umd.min.js +3 -0
- package/dist/plugins/attr.umd.min.js.map +1 -0
- package/dist/plugins/props.umd.js +711 -0
- package/dist/plugins/props.umd.js.map +1 -0
- package/dist/plugins/props.umd.min.js +3 -0
- package/dist/plugins/props.umd.min.js.map +1 -0
- package/dist/plugins/router.umd.js +1807 -0
- package/dist/plugins/router.umd.js.map +1 -0
- package/dist/plugins/router.umd.min.js +3 -0
- package/dist/plugins/router.umd.min.js.map +1 -0
- package/dist/plugins/store.umd.js +684 -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 +240 -62
- package/src/core/Eleva.js +552 -145
- package/src/modules/Emitter.js +154 -18
- package/src/modules/Renderer.js +288 -86
- package/src/modules/Signal.js +132 -13
- package/src/modules/TemplateEngine.js +153 -27
- package/src/plugins/Attr.js +252 -0
- package/src/plugins/Props.js +590 -0
- package/src/plugins/Router.js +1919 -0
- package/src/plugins/Store.js +741 -0
- package/src/plugins/index.js +40 -0
- package/types/core/Eleva.d.ts +482 -48
- package/types/core/Eleva.d.ts.map +1 -1
- package/types/modules/Emitter.d.ts +151 -20
- package/types/modules/Emitter.d.ts.map +1 -1
- package/types/modules/Renderer.d.ts +151 -12
- package/types/modules/Renderer.d.ts.map +1 -1
- package/types/modules/Signal.d.ts +130 -16
- package/types/modules/Signal.d.ts.map +1 -1
- package/types/modules/TemplateEngine.d.ts +154 -14
- package/types/modules/TemplateEngine.d.ts.map +1 -1
- package/types/plugins/Attr.d.ts +28 -0
- package/types/plugins/Attr.d.ts.map +1 -0
- package/types/plugins/Props.d.ts +48 -0
- package/types/plugins/Props.d.ts.map +1 -0
- package/types/plugins/Router.d.ts +1000 -0
- package/types/plugins/Router.d.ts.map +1 -0
- package/types/plugins/Store.d.ts +86 -0
- package/types/plugins/Store.d.ts.map +1 -0
- package/types/plugins/index.d.ts +5 -0
- package/types/plugins/index.d.ts.map +1 -0
- package/dist/eleva.min.js +0 -2
- package/dist/eleva.min.js.map +0 -1
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
/*! Eleva Store Plugin v1.0.0-rc.10 | 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.ElevaStorePlugin = {}));
|
|
6
|
+
})(this, (function (exports) { 'use strict';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @class 🏪 StorePlugin
|
|
10
|
+
* @classdesc A powerful reactive state management plugin for Eleva.js that enables sharing
|
|
11
|
+
* reactive data across the entire application. The Store plugin provides a centralized,
|
|
12
|
+
* reactive data store that can be accessed from any component's setup function.
|
|
13
|
+
*
|
|
14
|
+
* Core Features:
|
|
15
|
+
* - Centralized reactive state management using Eleva's signal system
|
|
16
|
+
* - Global state accessibility through component setup functions
|
|
17
|
+
* - Namespace support for organizing store modules
|
|
18
|
+
* - Built-in persistence with localStorage/sessionStorage support
|
|
19
|
+
* - Action-based state mutations with validation
|
|
20
|
+
* - Subscription system for reactive updates
|
|
21
|
+
* - DevTools integration for debugging
|
|
22
|
+
* - Plugin architecture for extensibility
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Install the plugin
|
|
26
|
+
* const app = new Eleva("myApp");
|
|
27
|
+
* app.use(StorePlugin, {
|
|
28
|
+
* state: {
|
|
29
|
+
* user: { name: "John", email: "john@example.com" },
|
|
30
|
+
* counter: 0,
|
|
31
|
+
* todos: []
|
|
32
|
+
* },
|
|
33
|
+
* actions: {
|
|
34
|
+
* increment: (state) => state.counter.value++,
|
|
35
|
+
* addTodo: (state, todo) => state.todos.value.push(todo),
|
|
36
|
+
* setUser: (state, user) => state.user.value = user
|
|
37
|
+
* },
|
|
38
|
+
* persistence: {
|
|
39
|
+
* enabled: true,
|
|
40
|
+
* key: "myApp-store",
|
|
41
|
+
* storage: "localStorage"
|
|
42
|
+
* }
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* // Use store in components
|
|
46
|
+
* app.component("Counter", {
|
|
47
|
+
* setup({ store }) {
|
|
48
|
+
* return {
|
|
49
|
+
* count: store.state.counter,
|
|
50
|
+
* increment: () => store.dispatch("increment"),
|
|
51
|
+
* user: store.state.user
|
|
52
|
+
* };
|
|
53
|
+
* },
|
|
54
|
+
* template: (ctx) => `
|
|
55
|
+
* <div>
|
|
56
|
+
* <p>Hello ${ctx.user.value.name}!</p>
|
|
57
|
+
* <p>Count: ${ctx.count.value}</p>
|
|
58
|
+
* <button onclick="ctx.increment()">+</button>
|
|
59
|
+
* </div>
|
|
60
|
+
* `
|
|
61
|
+
* });
|
|
62
|
+
*/
|
|
63
|
+
const StorePlugin = {
|
|
64
|
+
/**
|
|
65
|
+
* Unique identifier for the plugin
|
|
66
|
+
* @type {string}
|
|
67
|
+
*/
|
|
68
|
+
name: "store",
|
|
69
|
+
/**
|
|
70
|
+
* Plugin version
|
|
71
|
+
* @type {string}
|
|
72
|
+
*/
|
|
73
|
+
version: "1.0.0-rc.10",
|
|
74
|
+
/**
|
|
75
|
+
* Plugin description
|
|
76
|
+
* @type {string}
|
|
77
|
+
*/
|
|
78
|
+
description: "Reactive state management for sharing data across the entire Eleva application",
|
|
79
|
+
/**
|
|
80
|
+
* Installs the plugin into the Eleva instance
|
|
81
|
+
*
|
|
82
|
+
* @param {Object} eleva - The Eleva instance
|
|
83
|
+
* @param {Object} options - Plugin configuration options
|
|
84
|
+
* @param {Object} [options.state={}] - Initial state object
|
|
85
|
+
* @param {Object} [options.actions={}] - Action functions for state mutations
|
|
86
|
+
* @param {Object} [options.namespaces={}] - Namespaced modules for organizing store
|
|
87
|
+
* @param {Object} [options.persistence] - Persistence configuration
|
|
88
|
+
* @param {boolean} [options.persistence.enabled=false] - Enable state persistence
|
|
89
|
+
* @param {string} [options.persistence.key="eleva-store"] - Storage key
|
|
90
|
+
* @param {"localStorage" | "sessionStorage"} [options.persistence.storage="localStorage"] - Storage type
|
|
91
|
+
* @param {Array<string>} [options.persistence.include] - State keys to persist (if not provided, all state is persisted)
|
|
92
|
+
* @param {Array<string>} [options.persistence.exclude] - State keys to exclude from persistence
|
|
93
|
+
* @param {boolean} [options.devTools=false] - Enable development tools integration
|
|
94
|
+
* @param {Function} [options.onError=null] - Error handler function
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* // Basic installation
|
|
98
|
+
* app.use(StorePlugin, {
|
|
99
|
+
* state: { count: 0, user: null },
|
|
100
|
+
* actions: {
|
|
101
|
+
* increment: (state) => state.count.value++,
|
|
102
|
+
* setUser: (state, user) => state.user.value = user
|
|
103
|
+
* }
|
|
104
|
+
* });
|
|
105
|
+
*
|
|
106
|
+
* // Advanced installation with persistence and namespaces
|
|
107
|
+
* app.use(StorePlugin, {
|
|
108
|
+
* state: { theme: "light" },
|
|
109
|
+
* namespaces: {
|
|
110
|
+
* auth: {
|
|
111
|
+
* state: { user: null, token: null },
|
|
112
|
+
* actions: {
|
|
113
|
+
* login: (state, { user, token }) => {
|
|
114
|
+
* state.user.value = user;
|
|
115
|
+
* state.token.value = token;
|
|
116
|
+
* },
|
|
117
|
+
* logout: (state) => {
|
|
118
|
+
* state.user.value = null;
|
|
119
|
+
* state.token.value = null;
|
|
120
|
+
* }
|
|
121
|
+
* }
|
|
122
|
+
* }
|
|
123
|
+
* },
|
|
124
|
+
* persistence: {
|
|
125
|
+
* enabled: true,
|
|
126
|
+
* include: ["theme", "auth.user"]
|
|
127
|
+
* }
|
|
128
|
+
* });
|
|
129
|
+
*/
|
|
130
|
+
install(eleva, options = {}) {
|
|
131
|
+
const {
|
|
132
|
+
state = {},
|
|
133
|
+
actions = {},
|
|
134
|
+
namespaces = {},
|
|
135
|
+
persistence = {},
|
|
136
|
+
devTools = false,
|
|
137
|
+
onError = null
|
|
138
|
+
} = options;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Store instance that manages all state and provides the API
|
|
142
|
+
* @private
|
|
143
|
+
*/
|
|
144
|
+
class Store {
|
|
145
|
+
constructor() {
|
|
146
|
+
this.state = {};
|
|
147
|
+
this.actions = {};
|
|
148
|
+
this.subscribers = new Set();
|
|
149
|
+
this.mutations = [];
|
|
150
|
+
this.persistence = {
|
|
151
|
+
enabled: false,
|
|
152
|
+
key: "eleva-store",
|
|
153
|
+
storage: "localStorage",
|
|
154
|
+
include: null,
|
|
155
|
+
exclude: null,
|
|
156
|
+
...persistence
|
|
157
|
+
};
|
|
158
|
+
this.devTools = devTools;
|
|
159
|
+
this.onError = onError;
|
|
160
|
+
this._initializeState(state, actions);
|
|
161
|
+
this._initializeNamespaces(namespaces);
|
|
162
|
+
this._loadPersistedState();
|
|
163
|
+
this._setupDevTools();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Initializes the root state and actions
|
|
168
|
+
* @private
|
|
169
|
+
*/
|
|
170
|
+
_initializeState(initialState, initialActions) {
|
|
171
|
+
// Create reactive signals for each state property
|
|
172
|
+
Object.entries(initialState).forEach(([key, value]) => {
|
|
173
|
+
this.state[key] = new eleva.signal(value);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Set up actions
|
|
177
|
+
this.actions = {
|
|
178
|
+
...initialActions
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Initializes namespaced modules
|
|
184
|
+
* @private
|
|
185
|
+
*/
|
|
186
|
+
_initializeNamespaces(namespaces) {
|
|
187
|
+
Object.entries(namespaces).forEach(([namespace, module]) => {
|
|
188
|
+
const {
|
|
189
|
+
state: moduleState = {},
|
|
190
|
+
actions: moduleActions = {}
|
|
191
|
+
} = module;
|
|
192
|
+
|
|
193
|
+
// Create namespace object if it doesn't exist
|
|
194
|
+
if (!this.state[namespace]) {
|
|
195
|
+
this.state[namespace] = {};
|
|
196
|
+
}
|
|
197
|
+
if (!this.actions[namespace]) {
|
|
198
|
+
this.actions[namespace] = {};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Initialize namespaced state
|
|
202
|
+
Object.entries(moduleState).forEach(([key, value]) => {
|
|
203
|
+
this.state[namespace][key] = new eleva.signal(value);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Set up namespaced actions
|
|
207
|
+
this.actions[namespace] = {
|
|
208
|
+
...moduleActions
|
|
209
|
+
};
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Loads persisted state from storage
|
|
215
|
+
* @private
|
|
216
|
+
*/
|
|
217
|
+
_loadPersistedState() {
|
|
218
|
+
if (!this.persistence.enabled || typeof window === "undefined") {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
const storage = window[this.persistence.storage];
|
|
223
|
+
const persistedData = storage.getItem(this.persistence.key);
|
|
224
|
+
if (persistedData) {
|
|
225
|
+
const data = JSON.parse(persistedData);
|
|
226
|
+
this._applyPersistedData(data);
|
|
227
|
+
}
|
|
228
|
+
} catch (error) {
|
|
229
|
+
if (this.onError) {
|
|
230
|
+
this.onError(error, "Failed to load persisted state");
|
|
231
|
+
} else {
|
|
232
|
+
console.warn("[StorePlugin] Failed to load persisted state:", error);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Applies persisted data to the current state
|
|
239
|
+
* @private
|
|
240
|
+
*/
|
|
241
|
+
_applyPersistedData(data, currentState = this.state, path = "") {
|
|
242
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
243
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
244
|
+
if (this._shouldPersist(fullPath)) {
|
|
245
|
+
if (currentState[key] && typeof currentState[key] === "object" && "value" in currentState[key]) {
|
|
246
|
+
// This is a signal, update its value
|
|
247
|
+
currentState[key].value = value;
|
|
248
|
+
} else if (typeof value === "object" && value !== null && currentState[key]) {
|
|
249
|
+
// This is a nested object, recurse
|
|
250
|
+
this._applyPersistedData(value, currentState[key], fullPath);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Determines if a state path should be persisted
|
|
258
|
+
* @private
|
|
259
|
+
*/
|
|
260
|
+
_shouldPersist(path) {
|
|
261
|
+
const {
|
|
262
|
+
include,
|
|
263
|
+
exclude
|
|
264
|
+
} = this.persistence;
|
|
265
|
+
if (include && include.length > 0) {
|
|
266
|
+
return include.some(includePath => path.startsWith(includePath));
|
|
267
|
+
}
|
|
268
|
+
if (exclude && exclude.length > 0) {
|
|
269
|
+
return !exclude.some(excludePath => path.startsWith(excludePath));
|
|
270
|
+
}
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Saves current state to storage
|
|
276
|
+
* @private
|
|
277
|
+
*/
|
|
278
|
+
_saveState() {
|
|
279
|
+
if (!this.persistence.enabled || typeof window === "undefined") {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
const storage = window[this.persistence.storage];
|
|
284
|
+
const dataToSave = this._extractPersistedData();
|
|
285
|
+
storage.setItem(this.persistence.key, JSON.stringify(dataToSave));
|
|
286
|
+
} catch (error) {
|
|
287
|
+
if (this.onError) {
|
|
288
|
+
this.onError(error, "Failed to save state");
|
|
289
|
+
} else {
|
|
290
|
+
console.warn("[StorePlugin] Failed to save state:", error);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Extracts data that should be persisted
|
|
297
|
+
* @private
|
|
298
|
+
*/
|
|
299
|
+
_extractPersistedData(currentState = this.state, path = "") {
|
|
300
|
+
const result = {};
|
|
301
|
+
Object.entries(currentState).forEach(([key, value]) => {
|
|
302
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
303
|
+
if (this._shouldPersist(fullPath)) {
|
|
304
|
+
if (value && typeof value === "object" && "value" in value) {
|
|
305
|
+
// This is a signal, extract its value
|
|
306
|
+
result[key] = value.value;
|
|
307
|
+
} else if (typeof value === "object" && value !== null) {
|
|
308
|
+
// This is a nested object, recurse
|
|
309
|
+
const nestedData = this._extractPersistedData(value, fullPath);
|
|
310
|
+
if (Object.keys(nestedData).length > 0) {
|
|
311
|
+
result[key] = nestedData;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Sets up development tools integration
|
|
321
|
+
* @private
|
|
322
|
+
*/
|
|
323
|
+
_setupDevTools() {
|
|
324
|
+
if (!this.devTools || typeof window === "undefined" || !window.__ELEVA_DEVTOOLS__) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
window.__ELEVA_DEVTOOLS__.registerStore(this);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Dispatches an action to mutate the state
|
|
332
|
+
* @param {string} actionName - The name of the action to dispatch (supports namespaced actions like "auth.login")
|
|
333
|
+
* @param {any} payload - The payload to pass to the action
|
|
334
|
+
* @returns {Promise<any>} The result of the action
|
|
335
|
+
*/
|
|
336
|
+
async dispatch(actionName, payload) {
|
|
337
|
+
try {
|
|
338
|
+
const action = this._getAction(actionName);
|
|
339
|
+
if (!action) {
|
|
340
|
+
const error = new Error(`Action "${actionName}" not found`);
|
|
341
|
+
if (this.onError) {
|
|
342
|
+
this.onError(error, actionName);
|
|
343
|
+
}
|
|
344
|
+
throw error;
|
|
345
|
+
}
|
|
346
|
+
const mutation = {
|
|
347
|
+
type: actionName,
|
|
348
|
+
payload,
|
|
349
|
+
timestamp: Date.now()
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// Record mutation for devtools
|
|
353
|
+
this.mutations.push(mutation);
|
|
354
|
+
if (this.mutations.length > 100) {
|
|
355
|
+
this.mutations.shift(); // Keep only last 100 mutations
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Execute the action
|
|
359
|
+
const result = await action.call(null, this.state, payload);
|
|
360
|
+
|
|
361
|
+
// Save state if persistence is enabled
|
|
362
|
+
this._saveState();
|
|
363
|
+
|
|
364
|
+
// Notify subscribers
|
|
365
|
+
this.subscribers.forEach(callback => {
|
|
366
|
+
try {
|
|
367
|
+
callback(mutation, this.state);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
if (this.onError) {
|
|
370
|
+
this.onError(error, "Subscriber callback failed");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// Notify devtools
|
|
376
|
+
if (this.devTools && typeof window !== "undefined" && window.__ELEVA_DEVTOOLS__) {
|
|
377
|
+
window.__ELEVA_DEVTOOLS__.notifyMutation(mutation, this.state);
|
|
378
|
+
}
|
|
379
|
+
return result;
|
|
380
|
+
} catch (error) {
|
|
381
|
+
if (this.onError) {
|
|
382
|
+
this.onError(error, `Action dispatch failed: ${actionName}`);
|
|
383
|
+
}
|
|
384
|
+
throw error;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Gets an action by name (supports namespaced actions)
|
|
390
|
+
* @private
|
|
391
|
+
*/
|
|
392
|
+
_getAction(actionName) {
|
|
393
|
+
const parts = actionName.split(".");
|
|
394
|
+
let current = this.actions;
|
|
395
|
+
for (const part of parts) {
|
|
396
|
+
if (current[part] === undefined) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
current = current[part];
|
|
400
|
+
}
|
|
401
|
+
return typeof current === "function" ? current : null;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Subscribes to store mutations
|
|
406
|
+
* @param {Function} callback - Callback function to call on mutations
|
|
407
|
+
* @returns {Function} Unsubscribe function
|
|
408
|
+
*/
|
|
409
|
+
subscribe(callback) {
|
|
410
|
+
if (typeof callback !== "function") {
|
|
411
|
+
throw new Error("Subscribe callback must be a function");
|
|
412
|
+
}
|
|
413
|
+
this.subscribers.add(callback);
|
|
414
|
+
|
|
415
|
+
// Return unsubscribe function
|
|
416
|
+
return () => {
|
|
417
|
+
this.subscribers.delete(callback);
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Gets a deep copy of the current state values (not signals)
|
|
423
|
+
* @returns {Object} The current state values
|
|
424
|
+
*/
|
|
425
|
+
getState() {
|
|
426
|
+
return this._extractPersistedData();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Replaces the entire state (useful for testing or state hydration)
|
|
431
|
+
* @param {Object} newState - The new state object
|
|
432
|
+
*/
|
|
433
|
+
replaceState(newState) {
|
|
434
|
+
this._applyPersistedData(newState);
|
|
435
|
+
this._saveState();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Clears persisted state from storage
|
|
440
|
+
*/
|
|
441
|
+
clearPersistedState() {
|
|
442
|
+
if (!this.persistence.enabled || typeof window === "undefined") {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
try {
|
|
446
|
+
const storage = window[this.persistence.storage];
|
|
447
|
+
storage.removeItem(this.persistence.key);
|
|
448
|
+
} catch (error) {
|
|
449
|
+
if (this.onError) {
|
|
450
|
+
this.onError(error, "Failed to clear persisted state");
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Registers a new namespaced module at runtime
|
|
457
|
+
* @param {string} namespace - The namespace for the module
|
|
458
|
+
* @param {Object} module - The module definition
|
|
459
|
+
* @param {Object} module.state - The module's initial state
|
|
460
|
+
* @param {Object} module.actions - The module's actions
|
|
461
|
+
*/
|
|
462
|
+
registerModule(namespace, module) {
|
|
463
|
+
if (this.state[namespace] || this.actions[namespace]) {
|
|
464
|
+
console.warn(`[StorePlugin] Module "${namespace}" already exists`);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Initialize the module
|
|
469
|
+
this.state[namespace] = {};
|
|
470
|
+
this.actions[namespace] = {};
|
|
471
|
+
const namespaces = {
|
|
472
|
+
[namespace]: module
|
|
473
|
+
};
|
|
474
|
+
this._initializeNamespaces(namespaces);
|
|
475
|
+
this._saveState();
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Unregisters a namespaced module
|
|
480
|
+
* @param {string} namespace - The namespace to unregister
|
|
481
|
+
*/
|
|
482
|
+
unregisterModule(namespace) {
|
|
483
|
+
if (!this.state[namespace] && !this.actions[namespace]) {
|
|
484
|
+
console.warn(`[StorePlugin] Module "${namespace}" does not exist`);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
delete this.state[namespace];
|
|
488
|
+
delete this.actions[namespace];
|
|
489
|
+
this._saveState();
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Creates a new reactive state property at runtime
|
|
494
|
+
* @param {string} key - The state key
|
|
495
|
+
* @param {*} initialValue - The initial value
|
|
496
|
+
* @returns {Object} The created signal
|
|
497
|
+
*/
|
|
498
|
+
createState(key, initialValue) {
|
|
499
|
+
if (this.state[key]) {
|
|
500
|
+
return this.state[key]; // Return existing state
|
|
501
|
+
}
|
|
502
|
+
this.state[key] = new eleva.signal(initialValue);
|
|
503
|
+
this._saveState();
|
|
504
|
+
return this.state[key];
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Creates a new action at runtime
|
|
509
|
+
* @param {string} name - The action name
|
|
510
|
+
* @param {Function} actionFn - The action function
|
|
511
|
+
*/
|
|
512
|
+
createAction(name, actionFn) {
|
|
513
|
+
if (typeof actionFn !== "function") {
|
|
514
|
+
throw new Error("Action must be a function");
|
|
515
|
+
}
|
|
516
|
+
this.actions[name] = actionFn;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Create the store instance
|
|
521
|
+
const store = new Store();
|
|
522
|
+
|
|
523
|
+
// Store the original mount method to override it
|
|
524
|
+
const originalMount = eleva.mount;
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Override the mount method to inject store context into components
|
|
528
|
+
*/
|
|
529
|
+
eleva.mount = async (container, compName, props = {}) => {
|
|
530
|
+
// Get the component definition
|
|
531
|
+
const componentDef = typeof compName === "string" ? eleva._components.get(compName) || compName : compName;
|
|
532
|
+
if (!componentDef) {
|
|
533
|
+
return await originalMount.call(eleva, container, compName, props);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Create a wrapped component that injects store into setup
|
|
537
|
+
const wrappedComponent = {
|
|
538
|
+
...componentDef,
|
|
539
|
+
async setup(ctx) {
|
|
540
|
+
// Inject store into the context with enhanced API
|
|
541
|
+
ctx.store = {
|
|
542
|
+
// Core store functionality
|
|
543
|
+
state: store.state,
|
|
544
|
+
dispatch: store.dispatch.bind(store),
|
|
545
|
+
subscribe: store.subscribe.bind(store),
|
|
546
|
+
getState: store.getState.bind(store),
|
|
547
|
+
// Module management
|
|
548
|
+
registerModule: store.registerModule.bind(store),
|
|
549
|
+
unregisterModule: store.unregisterModule.bind(store),
|
|
550
|
+
// Utilities for dynamic state/action creation
|
|
551
|
+
createState: store.createState.bind(store),
|
|
552
|
+
createAction: store.createAction.bind(store),
|
|
553
|
+
// Access to signal constructor for manual state creation
|
|
554
|
+
signal: eleva.signal
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
// Call original setup if it exists
|
|
558
|
+
const originalSetup = componentDef.setup;
|
|
559
|
+
const result = originalSetup ? await originalSetup(ctx) : {};
|
|
560
|
+
return result;
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// Call original mount with wrapped component
|
|
565
|
+
return await originalMount.call(eleva, container, wrappedComponent, props);
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
// Override _mountComponents to ensure child components also get store context
|
|
569
|
+
const originalMountComponents = eleva._mountComponents;
|
|
570
|
+
eleva._mountComponents = async (container, children, childInstances) => {
|
|
571
|
+
// Create wrapped children with store injection
|
|
572
|
+
const wrappedChildren = {};
|
|
573
|
+
for (const [selector, childComponent] of Object.entries(children)) {
|
|
574
|
+
const componentDef = typeof childComponent === "string" ? eleva._components.get(childComponent) || childComponent : childComponent;
|
|
575
|
+
if (componentDef && typeof componentDef === "object") {
|
|
576
|
+
wrappedChildren[selector] = {
|
|
577
|
+
...componentDef,
|
|
578
|
+
async setup(ctx) {
|
|
579
|
+
// Inject store into the context with enhanced API
|
|
580
|
+
ctx.store = {
|
|
581
|
+
// Core store functionality
|
|
582
|
+
state: store.state,
|
|
583
|
+
dispatch: store.dispatch.bind(store),
|
|
584
|
+
subscribe: store.subscribe.bind(store),
|
|
585
|
+
getState: store.getState.bind(store),
|
|
586
|
+
// Module management
|
|
587
|
+
registerModule: store.registerModule.bind(store),
|
|
588
|
+
unregisterModule: store.unregisterModule.bind(store),
|
|
589
|
+
// Utilities for dynamic state/action creation
|
|
590
|
+
createState: store.createState.bind(store),
|
|
591
|
+
createAction: store.createAction.bind(store),
|
|
592
|
+
// Access to signal constructor for manual state creation
|
|
593
|
+
signal: eleva.signal
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
// Call original setup if it exists
|
|
597
|
+
const originalSetup = componentDef.setup;
|
|
598
|
+
const result = originalSetup ? await originalSetup(ctx) : {};
|
|
599
|
+
return result;
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
} else {
|
|
603
|
+
wrappedChildren[selector] = childComponent;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Call original _mountComponents with wrapped children
|
|
608
|
+
return await originalMountComponents.call(eleva, container, wrappedChildren, childInstances);
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
// Expose store instance and utilities on the Eleva instance
|
|
612
|
+
eleva.store = store;
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Expose utility methods on the Eleva instance
|
|
616
|
+
* @namespace eleva.store
|
|
617
|
+
*/
|
|
618
|
+
eleva.createAction = (name, actionFn) => {
|
|
619
|
+
store.actions[name] = actionFn;
|
|
620
|
+
};
|
|
621
|
+
eleva.dispatch = (actionName, payload) => {
|
|
622
|
+
return store.dispatch(actionName, payload);
|
|
623
|
+
};
|
|
624
|
+
eleva.getState = () => {
|
|
625
|
+
return store.getState();
|
|
626
|
+
};
|
|
627
|
+
eleva.subscribe = callback => {
|
|
628
|
+
return store.subscribe(callback);
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
// Store original methods for cleanup
|
|
632
|
+
eleva._originalMount = originalMount;
|
|
633
|
+
eleva._originalMountComponents = originalMountComponents;
|
|
634
|
+
},
|
|
635
|
+
/**
|
|
636
|
+
* Uninstalls the plugin from the Eleva instance
|
|
637
|
+
*
|
|
638
|
+
* @param {Object} eleva - The Eleva instance
|
|
639
|
+
*
|
|
640
|
+
* @description
|
|
641
|
+
* Restores the original Eleva methods and removes all plugin-specific
|
|
642
|
+
* functionality. This method should be called when the plugin is no
|
|
643
|
+
* longer needed.
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* // Uninstall the plugin
|
|
647
|
+
* StorePlugin.uninstall(app);
|
|
648
|
+
*/
|
|
649
|
+
uninstall(eleva) {
|
|
650
|
+
// Restore original mount method
|
|
651
|
+
if (eleva._originalMount) {
|
|
652
|
+
eleva.mount = eleva._originalMount;
|
|
653
|
+
delete eleva._originalMount;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Restore original _mountComponents method
|
|
657
|
+
if (eleva._originalMountComponents) {
|
|
658
|
+
eleva._mountComponents = eleva._originalMountComponents;
|
|
659
|
+
delete eleva._originalMountComponents;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// Remove store instance and utility methods
|
|
663
|
+
if (eleva.store) {
|
|
664
|
+
delete eleva.store;
|
|
665
|
+
}
|
|
666
|
+
if (eleva.createAction) {
|
|
667
|
+
delete eleva.createAction;
|
|
668
|
+
}
|
|
669
|
+
if (eleva.dispatch) {
|
|
670
|
+
delete eleva.dispatch;
|
|
671
|
+
}
|
|
672
|
+
if (eleva.getState) {
|
|
673
|
+
delete eleva.getState;
|
|
674
|
+
}
|
|
675
|
+
if (eleva.subscribe) {
|
|
676
|
+
delete eleva.subscribe;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
exports.StorePlugin = StorePlugin;
|
|
682
|
+
|
|
683
|
+
}));
|
|
684
|
+
//# sourceMappingURL=store.umd.js.map
|