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

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