eleva 1.0.1 → 1.1.0

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 (97) hide show
  1. package/README.md +21 -10
  2. package/dist/{eleva-plugins.cjs.js → eleva-plugins.cjs} +1002 -292
  3. package/dist/eleva-plugins.cjs.map +1 -0
  4. package/dist/eleva-plugins.d.cts +1352 -0
  5. package/dist/eleva-plugins.d.cts.map +1 -0
  6. package/dist/eleva-plugins.d.ts +1352 -0
  7. package/dist/eleva-plugins.d.ts.map +1 -0
  8. package/dist/{eleva-plugins.esm.js → eleva-plugins.js} +1002 -292
  9. package/dist/eleva-plugins.js.map +1 -0
  10. package/dist/eleva-plugins.umd.js +1001 -291
  11. package/dist/eleva-plugins.umd.js.map +1 -1
  12. package/dist/eleva-plugins.umd.min.js +1 -1
  13. package/dist/eleva-plugins.umd.min.js.map +1 -1
  14. package/dist/{eleva.cjs.js → eleva.cjs} +421 -191
  15. package/dist/eleva.cjs.map +1 -0
  16. package/dist/eleva.d.cts +1329 -0
  17. package/dist/eleva.d.cts.map +1 -0
  18. package/dist/eleva.d.ts +473 -226
  19. package/dist/eleva.d.ts.map +1 -0
  20. package/dist/{eleva.esm.js → eleva.js} +422 -192
  21. package/dist/eleva.js.map +1 -0
  22. package/dist/eleva.umd.js +420 -190
  23. package/dist/eleva.umd.js.map +1 -1
  24. package/dist/eleva.umd.min.js +1 -1
  25. package/dist/eleva.umd.min.js.map +1 -1
  26. package/dist/plugins/attr.cjs +279 -0
  27. package/dist/plugins/attr.cjs.map +1 -0
  28. package/dist/plugins/attr.d.cts +101 -0
  29. package/dist/plugins/attr.d.cts.map +1 -0
  30. package/dist/plugins/attr.d.ts +101 -0
  31. package/dist/plugins/attr.d.ts.map +1 -0
  32. package/dist/plugins/attr.js +276 -0
  33. package/dist/plugins/attr.js.map +1 -0
  34. package/dist/plugins/attr.umd.js +111 -22
  35. package/dist/plugins/attr.umd.js.map +1 -1
  36. package/dist/plugins/attr.umd.min.js +1 -1
  37. package/dist/plugins/attr.umd.min.js.map +1 -1
  38. package/dist/plugins/router.cjs +1873 -0
  39. package/dist/plugins/router.cjs.map +1 -0
  40. package/dist/plugins/router.d.cts +1296 -0
  41. package/dist/plugins/router.d.cts.map +1 -0
  42. package/dist/plugins/router.d.ts +1296 -0
  43. package/dist/plugins/router.d.ts.map +1 -0
  44. package/dist/plugins/router.js +1870 -0
  45. package/dist/plugins/router.js.map +1 -0
  46. package/dist/plugins/router.umd.js +482 -186
  47. package/dist/plugins/router.umd.js.map +1 -1
  48. package/dist/plugins/router.umd.min.js +1 -1
  49. package/dist/plugins/router.umd.min.js.map +1 -1
  50. package/dist/plugins/store.cjs +920 -0
  51. package/dist/plugins/store.cjs.map +1 -0
  52. package/dist/plugins/store.d.cts +266 -0
  53. package/dist/plugins/store.d.cts.map +1 -0
  54. package/dist/plugins/store.d.ts +266 -0
  55. package/dist/plugins/store.d.ts.map +1 -0
  56. package/dist/plugins/store.js +917 -0
  57. package/dist/plugins/store.js.map +1 -0
  58. package/dist/plugins/store.umd.js +410 -85
  59. package/dist/plugins/store.umd.js.map +1 -1
  60. package/dist/plugins/store.umd.min.js +1 -1
  61. package/dist/plugins/store.umd.min.js.map +1 -1
  62. package/package.json +112 -68
  63. package/src/core/Eleva.js +195 -115
  64. package/src/index.cjs +10 -0
  65. package/src/index.js +11 -0
  66. package/src/modules/Emitter.js +68 -20
  67. package/src/modules/Renderer.js +82 -20
  68. package/src/modules/Signal.js +43 -15
  69. package/src/modules/TemplateEngine.js +50 -9
  70. package/src/plugins/Attr.js +121 -19
  71. package/src/plugins/Router.js +526 -181
  72. package/src/plugins/Store.js +448 -69
  73. package/src/plugins/index.js +1 -0
  74. package/types/core/Eleva.d.ts +263 -169
  75. package/types/core/Eleva.d.ts.map +1 -1
  76. package/types/index.d.cts +3 -0
  77. package/types/index.d.cts.map +1 -0
  78. package/types/index.d.ts +5 -0
  79. package/types/index.d.ts.map +1 -1
  80. package/types/modules/Emitter.d.ts +73 -30
  81. package/types/modules/Emitter.d.ts.map +1 -1
  82. package/types/modules/Renderer.d.ts +48 -18
  83. package/types/modules/Renderer.d.ts.map +1 -1
  84. package/types/modules/Signal.d.ts +44 -16
  85. package/types/modules/Signal.d.ts.map +1 -1
  86. package/types/modules/TemplateEngine.d.ts +46 -11
  87. package/types/modules/TemplateEngine.d.ts.map +1 -1
  88. package/types/plugins/Attr.d.ts +83 -16
  89. package/types/plugins/Attr.d.ts.map +1 -1
  90. package/types/plugins/Router.d.ts +498 -207
  91. package/types/plugins/Router.d.ts.map +1 -1
  92. package/types/plugins/Store.d.ts +211 -37
  93. package/types/plugins/Store.d.ts.map +1 -1
  94. package/dist/eleva-plugins.cjs.js.map +0 -1
  95. package/dist/eleva-plugins.esm.js.map +0 -1
  96. package/dist/eleva.cjs.js.map +0 -1
  97. package/dist/eleva.esm.js.map +0 -1
@@ -1,5 +1,222 @@
1
1
  "use strict";
2
2
 
3
+ /**
4
+ * @module eleva/plugins/store
5
+ * @fileoverview Reactive state management plugin with namespaced modules,
6
+ * persistence, and subscription system.
7
+ */
8
+
9
+ // ============================================================================
10
+ // TYPE DEFINITIONS
11
+ // ============================================================================
12
+
13
+ // -----------------------------------------------------------------------------
14
+ // External Type Imports
15
+ // -----------------------------------------------------------------------------
16
+
17
+ /**
18
+ * Type imports from the Eleva core library.
19
+ * @typedef {import('eleva').Eleva} Eleva
20
+ * @typedef {import('eleva').ComponentDefinition} ComponentDefinition
21
+ * @typedef {import('eleva').ComponentContext} ComponentContext
22
+ * @typedef {import('eleva').SetupResult} SetupResult
23
+ * @typedef {import('eleva').ComponentProps} ComponentProps
24
+ * @typedef {import('eleva').ChildrenMap} ChildrenMap
25
+ * @typedef {import('eleva').MountResult} MountResult
26
+ */
27
+
28
+ /**
29
+ * Generic type import.
30
+ * @template T
31
+ * @typedef {import('eleva').Signal<T>} Signal
32
+ */
33
+
34
+ // -----------------------------------------------------------------------------
35
+ // Store Type Definitions
36
+ // -----------------------------------------------------------------------------
37
+
38
+ /**
39
+ * Mutation record emitted to subscribers.
40
+ * @typedef {Object} StoreMutation
41
+ * @property {string} type
42
+ * The action name that was dispatched.
43
+ * @property {unknown} payload
44
+ * The payload passed to the action.
45
+ * @property {number} timestamp
46
+ * Unix timestamp of when the mutation occurred.
47
+ * @description Record passed to subscribers when state changes via dispatch.
48
+ * @example
49
+ * store.subscribe((mutation, state) => {
50
+ * console.log(`Action: ${mutation.type}`);
51
+ * console.log(`Payload: ${mutation.payload}`);
52
+ * console.log(`Time: ${new Date(mutation.timestamp)}`);
53
+ * });
54
+ */
55
+
56
+ /**
57
+ * Store configuration options.
58
+ * @typedef {Object} StoreOptions
59
+ * @property {Record<string, unknown>} [state]
60
+ * Initial state object.
61
+ * @property {Record<string, ActionFunction>} [actions]
62
+ * Action functions for state mutations.
63
+ * @property {Record<string, StoreModule>} [namespaces]
64
+ * Namespaced modules for organizing store.
65
+ * @property {StorePersistenceOptions} [persistence]
66
+ * Persistence configuration.
67
+ * @property {boolean} [devTools]
68
+ * Enable development tools integration.
69
+ * @property {StoreErrorHandler} [onError]
70
+ * Error handler function.
71
+ * @description Configuration options passed to StorePlugin.install().
72
+ * @example
73
+ * app.use(StorePlugin, {
74
+ * state: { count: 0, user: null },
75
+ * actions: {
76
+ * increment: (state) => state.count.value++,
77
+ * setUser: (state, user) => state.user.value = user
78
+ * },
79
+ * persistence: { enabled: true, key: 'my-app' }
80
+ * });
81
+ */
82
+
83
+ /**
84
+ * Namespaced store module definition.
85
+ * @typedef {Object} StoreModule
86
+ * @property {Record<string, unknown>} state
87
+ * Module state.
88
+ * @property {Record<string, ActionFunction>} [actions]
89
+ * Module actions.
90
+ * @description Defines a namespaced module for organizing related state and actions.
91
+ * @example
92
+ * // Define a module
93
+ * const authModule = {
94
+ * state: { user: null, token: null },
95
+ * actions: {
96
+ * login: (state, { user, token }) => {
97
+ * state.auth.user.value = user;
98
+ * state.auth.token.value = token;
99
+ * }
100
+ * }
101
+ * };
102
+ *
103
+ * // Register dynamically
104
+ * store.registerModule('auth', authModule);
105
+ */
106
+
107
+ /**
108
+ * Store persistence configuration.
109
+ * @typedef {Object} StorePersistenceOptions
110
+ * @property {boolean} [enabled]
111
+ * Enable state persistence.
112
+ * @property {string} [key]
113
+ * Storage key (default: "eleva-store").
114
+ * @property {'localStorage' | 'sessionStorage'} [storage]
115
+ * Storage type.
116
+ * @property {string[]} [include]
117
+ * Dot-path prefixes to persist (e.g., "auth.user").
118
+ * @property {string[]} [exclude]
119
+ * Dot-path prefixes to exclude.
120
+ * @description Configuration for persisting store state to localStorage or sessionStorage.
121
+ * @example
122
+ * // Persist only specific state paths
123
+ * persistence: {
124
+ * enabled: true,
125
+ * key: 'my-app-store',
126
+ * storage: 'localStorage',
127
+ * include: ['user', 'settings.theme']
128
+ * }
129
+ *
130
+ * @example
131
+ * // Exclude sensitive data
132
+ * persistence: {
133
+ * enabled: true,
134
+ * exclude: ['auth.token', 'temp']
135
+ * }
136
+ */
137
+
138
+ /**
139
+ * Store error handler callback.
140
+ * @typedef {(error: Error, context: string) => void} StoreErrorHandler
141
+ * @description Custom error handler for store operations.
142
+ * @example
143
+ * app.use(StorePlugin, {
144
+ * onError: (error, context) => {
145
+ * console.error(`Store error in ${context}:`, error);
146
+ * // Send to error tracking service
147
+ * errorTracker.capture(error, { context });
148
+ * }
149
+ * });
150
+ */
151
+
152
+ /**
153
+ * Reactive state tree containing signals and nested namespaces.
154
+ * @typedef {Record<string, Signal<unknown> | Record<string, unknown>>} StoreState
155
+ * @description Represents the store's reactive state structure with support for nested modules.
156
+ */
157
+
158
+ /**
159
+ * Action function signature for store actions.
160
+ * @typedef {(state: StoreState, payload?: unknown) => unknown} ActionFunction
161
+ * @description Function that receives state and optional payload, returns action result.
162
+ */
163
+
164
+ /**
165
+ * Dispatch function signature for triggering actions.
166
+ * @typedef {(actionName: string, payload?: unknown) => Promise<unknown>} DispatchFunction
167
+ * @description Dispatches an action by name with optional payload, returns action result.
168
+ */
169
+
170
+ /**
171
+ * Subscribe callback signature for mutation listeners.
172
+ * @typedef {(mutation: StoreMutation, state: StoreState) => void} SubscribeCallback
173
+ * @description Called after each successful action dispatch with mutation details and current state.
174
+ */
175
+
176
+ /**
177
+ * Store API exposed to components via ctx.store.
178
+ * @typedef {Object} StoreApi
179
+ * @property {StoreState} state
180
+ * Reactive state signals (supports nested modules).
181
+ * @property {DispatchFunction} dispatch
182
+ * Dispatch an action by name with optional payload.
183
+ * @property {(callback: SubscribeCallback) => () => void} subscribe
184
+ * Subscribe to state mutations. Returns unsubscribe function.
185
+ * @property {() => Record<string, unknown>} getState
186
+ * Get a snapshot of current state values.
187
+ * @property {(namespace: string, module: StoreModule) => void} registerModule
188
+ * Register a namespaced module dynamically.
189
+ * @property {(namespace: string) => void} unregisterModule
190
+ * Unregister a namespaced module.
191
+ * @property {(key: string, initialValue: unknown) => Signal<unknown>} createState
192
+ * Create a new state signal dynamically.
193
+ * @property {(name: string, actionFn: ActionFunction) => void} createAction
194
+ * Register a new action dynamically.
195
+ * @property {new <T>(value: T) => Signal<T>} signal
196
+ * Signal class constructor for manual state creation.
197
+ * @description The store API injected into component setup as `ctx.store`.
198
+ * @example
199
+ * app.component('Counter', {
200
+ * setup({ store }) {
201
+ * // Access reactive state
202
+ * const count = store.state.count;
203
+ *
204
+ * // Dispatch actions
205
+ * const increment = () => store.dispatch('increment');
206
+ *
207
+ * // Subscribe to changes
208
+ * const unsub = store.subscribe((mutation) => {
209
+ * console.log('State changed:', mutation.type);
210
+ * });
211
+ *
212
+ * return { count, increment, onUnmount: () => unsub() };
213
+ * },
214
+ * template: (ctx) => `<button @click="increment">${ctx.count.value}</button>`
215
+ * });
216
+ * @see StoreMutation - Mutation record structure.
217
+ * @see StoreModule - Module definition for namespaces.
218
+ */
219
+
3
220
  /**
4
221
  * @class 🏪 StorePlugin
5
222
  * @classdesc A powerful reactive state management plugin for Eleva that enables sharing
@@ -27,7 +244,7 @@
27
244
  * },
28
245
  * actions: {
29
246
  * increment: (state) => state.counter.value++,
30
- * addTodo: (state, todo) => state.todos.value.push(todo),
247
+ * addTodo: (state, todo) => state.todos.value = [...state.todos.value, todo],
31
248
  * setUser: (state, user) => state.user.value = user
32
249
  * },
33
250
  * persistence: {
@@ -50,7 +267,7 @@
50
267
  * <div>
51
268
  * <p>Hello ${ctx.user.value.name}!</p>
52
269
  * <p>Count: ${ctx.count.value}</p>
53
- * <button onclick="ctx.increment()">+</button>
270
+ * <button @click="increment">+</button>
54
271
  * </div>
55
272
  * `
56
273
  * });
@@ -66,7 +283,7 @@ export const StorePlugin = {
66
283
  * Plugin version
67
284
  * @type {string}
68
285
  */
69
- version: "1.0.0",
286
+ version: "1.1.0",
70
287
 
71
288
  /**
72
289
  * Plugin description
@@ -76,21 +293,28 @@ export const StorePlugin = {
76
293
  "Reactive state management for sharing data across the entire Eleva application",
77
294
 
78
295
  /**
79
- * Installs the plugin into the Eleva instance
296
+ * Installs the plugin into the Eleva instance.
80
297
  *
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
298
+ * @public
299
+ * @param {Eleva} eleva - The Eleva instance.
300
+ * @param {StoreOptions} options - Plugin configuration options.
301
+ * @param {Record<string, unknown>} [options.state={}] - Initial state object.
302
+ * @param {Record<string, ActionFunction>} [options.actions={}] - Action functions for state mutations.
303
+ * @param {Record<string, StoreModule>} [options.namespaces={}] - Namespaced modules for organizing store.
304
+ * @param {StorePersistenceOptions} [options.persistence] - Persistence configuration.
305
+ * @param {boolean} [options.persistence.enabled=false] - Enable state persistence.
306
+ * @param {string} [options.persistence.key="eleva-store"] - Storage key.
307
+ * @param {'localStorage' | 'sessionStorage'} [options.persistence.storage="localStorage"] - Storage type.
308
+ * @param {string[]} [options.persistence.include] - Dot-path prefixes to persist (e.g., "auth.user")
309
+ * @param {string[]} [options.persistence.exclude] - Dot-path prefixes to exclude (applies when include is empty).
310
+ * @param {boolean} [options.devTools=false] - Enable development tools integration.
311
+ * @param {(error: Error, context: string) => void} [options.onError=null] - Error handler function.
312
+ * @returns {void}
313
+ * @description
314
+ * Installs the store and injects `store` into component setup context by wrapping
315
+ * `eleva.mount` and `eleva._mountComponents`. Also exposes `eleva.store` and
316
+ * helper methods (`eleva.dispatch`, `eleva.getState`, `eleva.subscribe`, `eleva.createAction`).
317
+ * Uninstall restores the originals.
94
318
  *
95
319
  * @example
96
320
  * // Basic installation
@@ -110,12 +334,12 @@ export const StorePlugin = {
110
334
  * state: { user: null, token: null },
111
335
  * actions: {
112
336
  * login: (state, { user, token }) => {
113
- * state.user.value = user;
114
- * state.token.value = token;
337
+ * state.auth.user.value = user;
338
+ * state.auth.token.value = token;
115
339
  * },
116
340
  * logout: (state) => {
117
- * state.user.value = null;
118
- * state.token.value = null;
341
+ * state.auth.user.value = null;
342
+ * state.auth.token.value = null;
119
343
  * }
120
344
  * }
121
345
  * }
@@ -137,15 +361,27 @@ export const StorePlugin = {
137
361
  } = options;
138
362
 
139
363
  /**
140
- * Store instance that manages all state and provides the API
364
+ * @class Store
365
+ * @classdesc Store instance that manages all state and provides the API.
141
366
  * @private
142
367
  */
143
368
  class Store {
369
+ /**
370
+ * Creates a new Store instance.
371
+ * Initializes state signals, actions, persistence, and devtools integration.
372
+ *
373
+ * @constructor
374
+ */
144
375
  constructor() {
376
+ /** @type {Record<string, Signal | Record<string, unknown>>} */
145
377
  this.state = {};
378
+ /** @type {Record<string, ActionFunction | Record<string, ActionFunction>>} */
146
379
  this.actions = {};
380
+ /** @type {Set<SubscribeCallback>} */
147
381
  this.subscribers = new Set();
382
+ /** @type {StoreMutation[]} */
148
383
  this.mutations = [];
384
+ /** @type {{enabled: boolean, key: string, storage: string, include: string[]|null, exclude: string[]|null}} */
149
385
  this.persistence = {
150
386
  enabled: false,
151
387
  key: "eleva-store",
@@ -154,7 +390,9 @@ export const StorePlugin = {
154
390
  exclude: null,
155
391
  ...persistence,
156
392
  };
393
+ /** @type {boolean} */
157
394
  this.devTools = devTools;
395
+ /** @type {((error: Error, context: string) => void)|null} */
158
396
  this.onError = onError;
159
397
 
160
398
  this._initializeState(state, actions);
@@ -164,8 +402,13 @@ export const StorePlugin = {
164
402
  }
165
403
 
166
404
  /**
167
- * Initializes the root state and actions
405
+ * Initializes the root state and actions.
406
+ * Creates reactive signals for each state property and copies actions.
407
+ *
168
408
  * @private
409
+ * @param {Record<string, unknown>} initialState - The initial state key-value pairs.
410
+ * @param {Record<string, ActionFunction>} initialActions - The action functions to register.
411
+ * @returns {void}
169
412
  */
170
413
  _initializeState(initialState, initialActions) {
171
414
  // Create reactive signals for each state property
@@ -178,8 +421,12 @@ export const StorePlugin = {
178
421
  }
179
422
 
180
423
  /**
181
- * Initializes namespaced modules
424
+ * Initializes namespaced modules.
425
+ * Creates namespace objects and populates them with state signals and actions.
426
+ *
182
427
  * @private
428
+ * @param {Record<string, StoreModule>} namespaces - Map of namespace names to module definitions.
429
+ * @returns {void}
183
430
  */
184
431
  _initializeNamespaces(namespaces) {
185
432
  Object.entries(namespaces).forEach(([namespace, module]) => {
@@ -205,8 +452,12 @@ export const StorePlugin = {
205
452
  }
206
453
 
207
454
  /**
208
- * Loads persisted state from storage
455
+ * Loads persisted state from storage.
456
+ * Reads from localStorage/sessionStorage and applies values to state signals.
457
+ * Does nothing if persistence is disabled or running in SSR environment.
458
+ *
209
459
  * @private
460
+ * @returns {void}
210
461
  */
211
462
  _loadPersistedState() {
212
463
  if (!this.persistence.enabled || typeof window === "undefined") {
@@ -234,8 +485,14 @@ export const StorePlugin = {
234
485
  }
235
486
 
236
487
  /**
237
- * Applies persisted data to the current state
488
+ * Applies persisted data to the current state.
489
+ * Recursively updates signal values for paths that should be persisted.
490
+ *
238
491
  * @private
492
+ * @param {Record<string, unknown>} data - The persisted data object to apply.
493
+ * @param {Record<string, unknown>} [currentState=this.state] - The current state object to update.
494
+ * @param {string} [path=""] - The current dot-notation path (for include/exclude filtering).
495
+ * @returns {void}
239
496
  */
240
497
  _applyPersistedData(data, currentState = this.state, path = "") {
241
498
  Object.entries(data).forEach(([key, value]) => {
@@ -262,8 +519,12 @@ export const StorePlugin = {
262
519
  }
263
520
 
264
521
  /**
265
- * Determines if a state path should be persisted
522
+ * Determines if a state path should be persisted.
523
+ * Checks against include/exclude filters configured in persistence options.
524
+ *
266
525
  * @private
526
+ * @param {string} path - The dot-notation path to check (e.g., "auth.user").
527
+ * @returns {boolean} True if the path should be persisted, false otherwise.
267
528
  */
268
529
  _shouldPersist(path) {
269
530
  const { include, exclude } = this.persistence;
@@ -280,8 +541,12 @@ export const StorePlugin = {
280
541
  }
281
542
 
282
543
  /**
283
- * Saves current state to storage
544
+ * Saves current state to storage.
545
+ * Extracts persistable data and writes to localStorage/sessionStorage.
546
+ * Does nothing if persistence is disabled or running in SSR environment.
547
+ *
284
548
  * @private
549
+ * @returns {void}
285
550
  */
286
551
  _saveState() {
287
552
  if (!this.persistence.enabled || typeof window === "undefined") {
@@ -302,8 +567,13 @@ export const StorePlugin = {
302
567
  }
303
568
 
304
569
  /**
305
- * Extracts data that should be persisted
570
+ * Extracts data that should be persisted.
571
+ * Recursively extracts signal values for paths that pass persistence filters.
572
+ *
306
573
  * @private
574
+ * @param {Record<string, unknown>} [currentState=this.state] - The state object to extract from.
575
+ * @param {string} [path=""] - The current dot-notation path (for include/exclude filtering).
576
+ * @returns {Record<string, unknown>} The extracted data object with raw values (not signals).
307
577
  */
308
578
  _extractPersistedData(currentState = this.state, path = "") {
309
579
  const result = {};
@@ -329,8 +599,12 @@ export const StorePlugin = {
329
599
  }
330
600
 
331
601
  /**
332
- * Sets up development tools integration
602
+ * Sets up development tools integration.
603
+ * Registers the store with Eleva DevTools if available and enabled.
604
+ * Does nothing if devTools is disabled, running in SSR, or DevTools not installed.
605
+ *
333
606
  * @private
607
+ * @returns {void}
334
608
  */
335
609
  _setupDevTools() {
336
610
  if (
@@ -345,10 +619,26 @@ export const StorePlugin = {
345
619
  }
346
620
 
347
621
  /**
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
622
+ * Dispatches an action to mutate the state.
623
+ *
624
+ * Execution flow:
625
+ * 1. Retrieves the action function (supports namespaced actions like "auth.login")
626
+ * 2. Records mutation for devtools/history (keeps last 100 mutations)
627
+ * 3. Executes action with await (actions can be sync or async)
628
+ * 4. Saves state if persistence is enabled
629
+ * 5. Notifies all subscribers with (mutation, state)
630
+ * 6. Notifies devtools if enabled
631
+ *
632
+ * @note Always returns a Promise regardless of whether the action is sync or async.
633
+ * Subscriber callbacks that throw are caught and passed to onError handler.
634
+ *
635
+ * @async
636
+ * @param {string} actionName - The name of the action to dispatch (supports dot notation for namespaces).
637
+ * @param {unknown} payload - The payload to pass to the action.
638
+ * @returns {Promise<unknown>} The result of the action (undefined if action returns nothing).
639
+ * @throws {Error} If action is not found or action function throws.
640
+ * @see subscribe - Listen for mutations triggered by dispatch.
641
+ * @see getState - Get current state values.
352
642
  */
353
643
  async dispatch(actionName, payload) {
354
644
  try {
@@ -410,8 +700,12 @@ export const StorePlugin = {
410
700
  }
411
701
 
412
702
  /**
413
- * Gets an action by name (supports namespaced actions)
703
+ * Gets an action by name (supports namespaced actions).
704
+ * Traverses the actions object using dot-notation path segments.
705
+ *
414
706
  * @private
707
+ * @param {string} actionName - The action name, supports dot notation for namespaces (e.g., "auth.login").
708
+ * @returns {ActionFunction | null} The action function if found and is a function, null otherwise.
415
709
  */
416
710
  _getAction(actionName) {
417
711
  const parts = actionName.split(".");
@@ -428,9 +722,18 @@ export const StorePlugin = {
428
722
  }
429
723
 
430
724
  /**
431
- * Subscribes to store mutations
432
- * @param {Function} callback - Callback function to call on mutations
433
- * @returns {Function} Unsubscribe function
725
+ * Subscribes to store mutations.
726
+ * Callback is invoked after every successful action dispatch.
727
+ *
728
+ * @param {SubscribeCallback} callback
729
+ * Called after each mutation with:
730
+ * - mutation.type: The action name that was dispatched
731
+ * - mutation.payload: The payload passed to the action
732
+ * - mutation.timestamp: When the mutation occurred (Date.now())
733
+ * - state: The current state object (contains Signals)
734
+ * @returns {() => void} Unsubscribe function. Call to stop receiving notifications.
735
+ * @throws {Error} If callback is not a function.
736
+ * @see dispatch - Triggers mutations that notify subscribers.
434
737
  */
435
738
  subscribe(callback) {
436
739
  if (typeof callback !== "function") {
@@ -446,16 +749,26 @@ export const StorePlugin = {
446
749
  }
447
750
 
448
751
  /**
449
- * Gets a deep copy of the current state values (not signals)
450
- * @returns {Object} The current state values
752
+ * Gets current state values (not signals).
753
+ *
754
+ * @note When persistence include/exclude filters are configured,
755
+ * this returns only the filtered subset of state.
756
+ * @returns {Record<string, unknown>} The current state values (filtered by persistence config if set).
757
+ * @see replaceState - Set state values.
758
+ * @see subscribe - Listen for state changes.
451
759
  */
452
760
  getState() {
453
761
  return this._extractPersistedData();
454
762
  }
455
763
 
456
764
  /**
457
- * Replaces the entire state (useful for testing or state hydration)
458
- * @param {Object} newState - The new state object
765
+ * Replaces state values (useful for testing or state hydration).
766
+ *
767
+ * @note When persistence include/exclude filters are configured,
768
+ * this only updates the filtered subset of state.
769
+ * @param {Record<string, unknown>} newState - The new state object.
770
+ * @returns {void}
771
+ * @see getState - Get current state values.
459
772
  */
460
773
  replaceState(newState) {
461
774
  this._applyPersistedData(newState);
@@ -463,7 +776,9 @@ export const StorePlugin = {
463
776
  }
464
777
 
465
778
  /**
466
- * Clears persisted state from storage
779
+ * Clears persisted state from storage.
780
+ * Does nothing if persistence is disabled or running in SSR.
781
+ * @returns {void}
467
782
  */
468
783
  clearPersistedState() {
469
784
  if (!this.persistence.enabled || typeof window === "undefined") {
@@ -481,11 +796,14 @@ export const StorePlugin = {
481
796
  }
482
797
 
483
798
  /**
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
799
+ * Registers a new namespaced module at runtime.
800
+ * Logs a warning if the namespace already exists.
801
+ * Module state is nested under `state[namespace]` and actions under `actions[namespace]`.
802
+ * @param {string} namespace - The namespace for the module.
803
+ * @param {StoreModule} module - The module definition.
804
+ * @param {Record<string, unknown>} module.state - The module's initial state.
805
+ * @param {Record<string, ActionFunction>} module.actions - The module's actions.
806
+ * @returns {void}
489
807
  */
490
808
  registerModule(namespace, module) {
491
809
  if (this.state[namespace] || this.actions[namespace]) {
@@ -504,8 +822,11 @@ export const StorePlugin = {
504
822
  }
505
823
 
506
824
  /**
507
- * Unregisters a namespaced module
508
- * @param {string} namespace - The namespace to unregister
825
+ * Unregisters a namespaced module.
826
+ * Logs a warning if the namespace doesn't exist.
827
+ * Removes both state and actions under the namespace.
828
+ * @param {string} namespace - The namespace to unregister.
829
+ * @returns {void}
509
830
  */
510
831
  unregisterModule(namespace) {
511
832
  if (!this.state[namespace] && !this.actions[namespace]) {
@@ -519,10 +840,11 @@ export const StorePlugin = {
519
840
  }
520
841
 
521
842
  /**
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
843
+ * Creates a new reactive state property at runtime.
844
+ *
845
+ * @param {string} key - The state key.
846
+ * @param {*} initialValue - The initial value.
847
+ * @returns {Signal} The created signal, or existing signal if key exists.
526
848
  */
527
849
  createState(key, initialValue) {
528
850
  if (this.state[key]) {
@@ -535,16 +857,42 @@ export const StorePlugin = {
535
857
  }
536
858
 
537
859
  /**
538
- * Creates a new action at runtime
539
- * @param {string} name - The action name
540
- * @param {Function} actionFn - The action function
860
+ * Creates a new action at runtime.
861
+ * Overwrites existing action if name already exists.
862
+ * Supports dot-notation for namespaced actions (e.g., "auth.login").
863
+ * @param {string} name - The action name (supports dot notation for namespaces).
864
+ * @param {ActionFunction} actionFn - The action function (receives state and payload).
865
+ * @returns {void}
866
+ * @throws {Error} If actionFn is not a function.
867
+ * @example
868
+ * // Root-level action
869
+ * store.createAction("increment", (state) => state.count.value++);
870
+ *
871
+ * // Namespaced action
872
+ * store.createAction("auth.login", async (state, credentials) => {
873
+ * // ... login logic
874
+ * });
541
875
  */
542
876
  createAction(name, actionFn) {
543
877
  if (typeof actionFn !== "function") {
544
878
  throw new Error("Action must be a function");
545
879
  }
546
880
 
547
- this.actions[name] = actionFn;
881
+ // Fast path: no dot means simple action (avoids array allocation)
882
+ if (name.indexOf(".") === -1) {
883
+ this.actions[name] = actionFn;
884
+ return;
885
+ }
886
+
887
+ // Namespaced action, traverse/create nested structure
888
+ const parts = name.split(".");
889
+ const lastIndex = parts.length - 1;
890
+ let current = this.actions;
891
+
892
+ for (let i = 0; i < lastIndex; i++) {
893
+ current = current[parts[i]] || (current[parts[i]] = {});
894
+ }
895
+ current[parts[lastIndex]] = actionFn;
548
896
  }
549
897
  }
550
898
 
@@ -555,7 +903,13 @@ export const StorePlugin = {
555
903
  const originalMount = eleva.mount;
556
904
 
557
905
  /**
558
- * Override the mount method to inject store context into components
906
+ * Overridden mount method that injects store context into components.
907
+ * Wraps the original mount to add `ctx.store` to the component's setup context.
908
+ *
909
+ * @param {HTMLElement} container - The DOM element where the component will be mounted.
910
+ * @param {string | ComponentDefinition} compName - Component name or definition.
911
+ * @param {ComponentProps} [props={}] - Optional properties to pass to the component.
912
+ * @returns {Promise<MountResult>} The mount result.
559
913
  */
560
914
  eleva.mount = async (container, compName, props = {}) => {
561
915
  // Get the component definition
@@ -572,7 +926,7 @@ export const StorePlugin = {
572
926
  const wrappedComponent = {
573
927
  ...componentDef,
574
928
  async setup(ctx) {
575
- // Inject store into the context with enhanced API
929
+ /** @type {StoreApi} */
576
930
  ctx.store = {
577
931
  // Core store functionality
578
932
  state: store.state,
@@ -611,7 +965,23 @@ export const StorePlugin = {
611
965
 
612
966
  // Override _mountComponents to ensure child components also get store context
613
967
  const originalMountComponents = eleva._mountComponents;
614
- eleva._mountComponents = async (container, children, childInstances) => {
968
+
969
+ /**
970
+ * Overridden _mountComponents method that injects store context into child components.
971
+ * Wraps each child component's setup function to add `ctx.store` before mounting.
972
+ *
973
+ * @param {HTMLElement} container - The parent container element.
974
+ * @param {ChildrenMap} children - Map of selectors to component definitions.
975
+ * @param {MountResult[]} childInstances - Array to store mounted instances.
976
+ * @param {ComponentContext & SetupResult} context - Parent component context.
977
+ * @returns {Promise<void>}
978
+ */
979
+ eleva._mountComponents = async (
980
+ container,
981
+ children,
982
+ childInstances,
983
+ context
984
+ ) => {
615
985
  // Create wrapped children with store injection
616
986
  const wrappedChildren = {};
617
987
 
@@ -625,7 +995,7 @@ export const StorePlugin = {
625
995
  wrappedChildren[selector] = {
626
996
  ...componentDef,
627
997
  async setup(ctx) {
628
- // Inject store into the context with enhanced API
998
+ /** @type {StoreApi} */
629
999
  ctx.store = {
630
1000
  // Core store functionality
631
1001
  state: store.state,
@@ -662,29 +1032,35 @@ export const StorePlugin = {
662
1032
  eleva,
663
1033
  container,
664
1034
  wrappedChildren,
665
- childInstances
1035
+ childInstances,
1036
+ context
666
1037
  );
667
1038
  };
668
1039
 
669
1040
  // Expose store instance and utilities on the Eleva instance
1041
+ /** @type {StoreApi} */
670
1042
  eleva.store = store;
671
1043
 
672
1044
  /**
673
- * Expose utility methods on the Eleva instance
674
- * @namespace eleva.store
1045
+ * Expose utility methods on the Eleva instance.
1046
+ * These are top-level helpers (e.g., `eleva.dispatch`) in addition to `eleva.store`.
675
1047
  */
1048
+ /** @type {(name: string, actionFn: ActionFunction) => void} */
676
1049
  eleva.createAction = (name, actionFn) => {
677
- store.actions[name] = actionFn;
1050
+ store.createAction(name, actionFn);
678
1051
  };
679
1052
 
1053
+ /** @type {DispatchFunction} */
680
1054
  eleva.dispatch = (actionName, payload) => {
681
1055
  return store.dispatch(actionName, payload);
682
1056
  };
683
1057
 
1058
+ /** @type {() => Record<string, unknown>} */
684
1059
  eleva.getState = () => {
685
1060
  return store.getState();
686
1061
  };
687
1062
 
1063
+ /** @type {(callback: SubscribeCallback) => () => void} */
688
1064
  eleva.subscribe = (callback) => {
689
1065
  return store.subscribe(callback);
690
1066
  };
@@ -695,14 +1071,17 @@ export const StorePlugin = {
695
1071
  },
696
1072
 
697
1073
  /**
698
- * Uninstalls the plugin from the Eleva instance
699
- *
700
- * @param {Object} eleva - The Eleva instance
1074
+ * Uninstalls the plugin from the Eleva instance.
701
1075
  *
1076
+ * @public
1077
+ * @param {Eleva} eleva - The Eleva instance.
1078
+ * @returns {void}
702
1079
  * @description
703
1080
  * Restores the original Eleva methods and removes all plugin-specific
704
1081
  * functionality. This method should be called when the plugin is no
705
1082
  * longer needed.
1083
+ * Also removes `eleva.store` and top-level helpers (`eleva.dispatch`,
1084
+ * `eleva.getState`, `eleva.subscribe`, `eleva.createAction`).
706
1085
  *
707
1086
  * @example
708
1087
  * // Uninstall the plugin