@coherent.js/state 1.0.0-beta.2

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/dist/index.js ADDED
@@ -0,0 +1,1696 @@
1
+ // src/reactive-state.js
2
+ import { globalErrorHandler, StateError } from "@coherent.js/core/src/utils/_error-handler.js";
3
+ var Observable = class _Observable {
4
+ constructor(value, options = {}) {
5
+ this._value = value;
6
+ this._observers = /* @__PURE__ */ new Set();
7
+ this._computedDependents = /* @__PURE__ */ new Set();
8
+ this._options = {
9
+ deep: options.deep !== false,
10
+ immediate: options.immediate !== false,
11
+ ...options
12
+ };
13
+ }
14
+ get value() {
15
+ if (_Observable._currentComputed) {
16
+ this._computedDependents.add(_Observable._currentComputed);
17
+ }
18
+ return this._value;
19
+ }
20
+ set value(newValue) {
21
+ if (this._value === newValue && !this._options.deep) {
22
+ return;
23
+ }
24
+ const oldValue = this._value;
25
+ this._value = newValue;
26
+ this._observers.forEach((observer) => {
27
+ try {
28
+ observer(newValue, oldValue);
29
+ } catch (_error) {
30
+ globalErrorHandler.handle(_error, {
31
+ type: "watcher-_error",
32
+ context: { newValue, oldValue }
33
+ });
34
+ }
35
+ });
36
+ this._computedDependents.forEach((computed2) => {
37
+ computed2._invalidate();
38
+ });
39
+ }
40
+ watch(callback, options = {}) {
41
+ if (typeof callback !== "function") {
42
+ throw new StateError("Watch callback must be a function");
43
+ }
44
+ const observer = (newValue, oldValue) => {
45
+ callback(newValue, oldValue, () => this.unwatch(observer));
46
+ };
47
+ this._observers.add(observer);
48
+ if (options.immediate !== false) {
49
+ observer(this._value, void 0);
50
+ }
51
+ return () => this.unwatch(observer);
52
+ }
53
+ unwatch(observer) {
54
+ this._observers.delete(observer);
55
+ }
56
+ unwatchAll() {
57
+ this._observers.clear();
58
+ this._computedDependents.clear();
59
+ }
60
+ };
61
+ var Computed = class extends Observable {
62
+ constructor(getter, options = {}) {
63
+ super(void 0, options);
64
+ this._getter = getter;
65
+ this._cached = false;
66
+ this._dirty = true;
67
+ if (typeof getter !== "function") {
68
+ throw new StateError("Computed getter must be a function");
69
+ }
70
+ }
71
+ get value() {
72
+ if (this._dirty || !this._cached) {
73
+ this._compute();
74
+ }
75
+ return this._value;
76
+ }
77
+ set value(newValue) {
78
+ throw new StateError("Cannot set value on computed property");
79
+ }
80
+ _compute() {
81
+ const prevComputed = Observable._currentComputed;
82
+ Observable._currentComputed = this;
83
+ try {
84
+ const newValue = this._getter();
85
+ if (newValue !== this._value) {
86
+ const oldValue = this._value;
87
+ this._value = newValue;
88
+ this._observers.forEach((observer) => {
89
+ observer(newValue, oldValue);
90
+ });
91
+ }
92
+ this._cached = true;
93
+ this._dirty = false;
94
+ } catch (_error) {
95
+ globalErrorHandler.handle(_error, {
96
+ type: "computed-_error",
97
+ context: { getter: this._getter.toString() }
98
+ });
99
+ } finally {
100
+ Observable._currentComputed = prevComputed;
101
+ }
102
+ }
103
+ _invalidate() {
104
+ this._dirty = true;
105
+ this._computedDependents.forEach((computed2) => {
106
+ computed2._invalidate();
107
+ });
108
+ }
109
+ };
110
+ Observable._currentComputed = null;
111
+ var ReactiveState = class {
112
+ constructor(initialState = {}, options = {}) {
113
+ this._state = /* @__PURE__ */ new Map();
114
+ this._computed = /* @__PURE__ */ new Map();
115
+ this._watchers = /* @__PURE__ */ new Map();
116
+ this._middleware = [];
117
+ this._history = [];
118
+ this._options = {
119
+ enableHistory: options.enableHistory !== false,
120
+ maxHistorySize: options.maxHistorySize || 50,
121
+ enableMiddleware: options.enableMiddleware !== false,
122
+ deep: options.deep !== false,
123
+ ...options
124
+ };
125
+ Object.entries(initialState).forEach(([key, value]) => {
126
+ this.set(key, value);
127
+ });
128
+ }
129
+ /**
130
+ * Get reactive state value
131
+ */
132
+ get(key) {
133
+ const observable2 = this._state.get(key);
134
+ return observable2 ? observable2.value : void 0;
135
+ }
136
+ /**
137
+ * Set reactive state value
138
+ */
139
+ set(key, value, options = {}) {
140
+ const config = { ...this._options, ...options };
141
+ if (config.enableMiddleware) {
142
+ const middlewareResult = this._runMiddleware("set", { key, value, oldValue: this.get(key) });
143
+ if (middlewareResult.cancelled) {
144
+ return false;
145
+ }
146
+ value = middlewareResult.value !== void 0 ? middlewareResult.value : value;
147
+ }
148
+ let observable2 = this._state.get(key);
149
+ if (!observable2) {
150
+ observable2 = new Observable(value, config);
151
+ this._state.set(key, observable2);
152
+ } else {
153
+ if (config.enableHistory) {
154
+ this._addToHistory("set", key, observable2.value, value);
155
+ }
156
+ observable2.value = value;
157
+ }
158
+ return true;
159
+ }
160
+ /**
161
+ * Check if state has a key
162
+ */
163
+ has(key) {
164
+ return this._state.has(key);
165
+ }
166
+ /**
167
+ * Delete state key
168
+ */
169
+ delete(key) {
170
+ const observable2 = this._state.get(key);
171
+ if (observable2) {
172
+ if (this._options.enableHistory) {
173
+ this._addToHistory("delete", key, observable2.value, void 0);
174
+ }
175
+ observable2.unwatchAll();
176
+ this._state.delete(key);
177
+ return true;
178
+ }
179
+ return false;
180
+ }
181
+ /**
182
+ * Clear all state
183
+ */
184
+ clear() {
185
+ if (this._options.enableHistory) {
186
+ this._addToHistory("clear", null, this.toObject(), {});
187
+ }
188
+ for (const observable2 of this._state.values()) {
189
+ observable2.unwatchAll();
190
+ }
191
+ this._state.clear();
192
+ this._computed.clear();
193
+ this._watchers.clear();
194
+ }
195
+ /**
196
+ * Create computed property
197
+ */
198
+ computed(key, getter, options = {}) {
199
+ if (typeof getter !== "function") {
200
+ throw new StateError(`Computed property '${key}' getter must be a function`);
201
+ }
202
+ const computed2 = new Computed(getter, { ...this._options, ...options });
203
+ this._computed.set(key, computed2);
204
+ return computed2;
205
+ }
206
+ /**
207
+ * Get computed property value
208
+ */
209
+ getComputed(key) {
210
+ const computed2 = this._computed.get(key);
211
+ return computed2 ? computed2.value : void 0;
212
+ }
213
+ /**
214
+ * Watch state changes
215
+ */
216
+ watch(key, callback, options = {}) {
217
+ if (typeof key === "function") {
218
+ return this._watchComputed(key, callback, options);
219
+ }
220
+ const observable2 = this._state.get(key);
221
+ if (!observable2) {
222
+ throw new StateError(`Cannot watch undefined state key: ${key}`);
223
+ }
224
+ const unwatch = observable2.watch(callback, options);
225
+ if (!this._watchers.has(key)) {
226
+ this._watchers.set(key, /* @__PURE__ */ new Set());
227
+ }
228
+ this._watchers.get(key).add(unwatch);
229
+ return unwatch;
230
+ }
231
+ /**
232
+ * Watch computed expression
233
+ */
234
+ _watchComputed(expression, callback, options = {}) {
235
+ const computed2 = new Computed(expression, options);
236
+ const unwatch = computed2.watch(callback, options);
237
+ return unwatch;
238
+ }
239
+ /**
240
+ * Batch state updates
241
+ */
242
+ batch(updates) {
243
+ if (typeof updates === "function") {
244
+ const oldEnableHistory = this._options.enableHistory;
245
+ this._options.enableHistory = false;
246
+ try {
247
+ const result = updates(this);
248
+ if (oldEnableHistory) {
249
+ this._addToHistory("batch", null, null, this.toObject());
250
+ }
251
+ return result;
252
+ } finally {
253
+ this._options.enableHistory = oldEnableHistory;
254
+ }
255
+ } else if (typeof updates === "object") {
256
+ return this.batch(() => {
257
+ Object.entries(updates).forEach(([key, value]) => {
258
+ this.set(key, value);
259
+ });
260
+ });
261
+ }
262
+ }
263
+ /**
264
+ * Subscribe to multiple state changes
265
+ */
266
+ subscribe(keys, callback, options = {}) {
267
+ if (!Array.isArray(keys)) {
268
+ keys = [keys];
269
+ }
270
+ const unwatchers = keys.map((key) => {
271
+ return this.watch(key, (newValue, oldValue) => {
272
+ callback({
273
+ key,
274
+ newValue,
275
+ oldValue,
276
+ state: this.toObject()
277
+ });
278
+ }, options);
279
+ });
280
+ return () => {
281
+ unwatchers.forEach((unwatch) => unwatch());
282
+ };
283
+ }
284
+ /**
285
+ * Add middleware for state changes
286
+ */
287
+ use(middleware) {
288
+ if (typeof middleware !== "function") {
289
+ throw new StateError("Middleware must be a function");
290
+ }
291
+ this._middleware.push(middleware);
292
+ }
293
+ /**
294
+ * Run middleware chain
295
+ */
296
+ _runMiddleware(action, context) {
297
+ let result = { ...context, cancelled: false };
298
+ for (const middleware of this._middleware) {
299
+ try {
300
+ const middlewareResult = middleware(action, result);
301
+ if (middlewareResult) {
302
+ result = { ...result, ...middlewareResult };
303
+ if (result.cancelled) {
304
+ break;
305
+ }
306
+ }
307
+ } catch (_error) {
308
+ globalErrorHandler.handle(_error, {
309
+ type: "middleware-_error",
310
+ context: { action, middleware: middleware.toString() }
311
+ });
312
+ }
313
+ }
314
+ return result;
315
+ }
316
+ /**
317
+ * Add action to history
318
+ */
319
+ _addToHistory(action, key, oldValue, newValue) {
320
+ if (!this._options.enableHistory) return;
321
+ this._history.unshift({
322
+ action,
323
+ key,
324
+ oldValue,
325
+ newValue,
326
+ timestamp: Date.now()
327
+ });
328
+ if (this._history.length > this._options.maxHistorySize) {
329
+ this._history = this._history.slice(0, this._options.maxHistorySize);
330
+ }
331
+ }
332
+ /**
333
+ * Get state history
334
+ */
335
+ getHistory(limit = 10) {
336
+ return this._history.slice(0, limit);
337
+ }
338
+ /**
339
+ * Undo last action
340
+ */
341
+ undo() {
342
+ const lastAction = this._history.shift();
343
+ if (!lastAction) return false;
344
+ const { action, key, oldValue } = lastAction;
345
+ const oldEnableHistory = this._options.enableHistory;
346
+ this._options.enableHistory = false;
347
+ try {
348
+ switch (action) {
349
+ case "set":
350
+ if (oldValue === void 0) {
351
+ this.delete(key);
352
+ } else {
353
+ this.set(key, oldValue);
354
+ }
355
+ break;
356
+ case "delete":
357
+ this.set(key, oldValue);
358
+ break;
359
+ case "clear":
360
+ this.clear();
361
+ Object.entries(oldValue || {}).forEach(([k, v]) => {
362
+ this.set(k, v);
363
+ });
364
+ break;
365
+ }
366
+ return true;
367
+ } finally {
368
+ this._options.enableHistory = oldEnableHistory;
369
+ }
370
+ }
371
+ /**
372
+ * Convert state to plain object
373
+ */
374
+ toObject() {
375
+ const result = {};
376
+ for (const [key, observable2] of this._state.entries()) {
377
+ result[key] = observable2.value;
378
+ }
379
+ return result;
380
+ }
381
+ /**
382
+ * Convert computed properties to object
383
+ */
384
+ getComputedValues() {
385
+ const result = {};
386
+ for (const [key, computed2] of this._computed.entries()) {
387
+ result[key] = computed2.value;
388
+ }
389
+ return result;
390
+ }
391
+ /**
392
+ * Get state statistics
393
+ */
394
+ getStats() {
395
+ return {
396
+ stateKeys: this._state.size,
397
+ computedKeys: this._computed.size,
398
+ watcherKeys: this._watchers.size,
399
+ historyLength: this._history.length,
400
+ middlewareCount: this._middleware.length
401
+ };
402
+ }
403
+ /**
404
+ * Cleanup and destroy
405
+ */
406
+ destroy() {
407
+ for (const observable2 of this._state.values()) {
408
+ observable2.unwatchAll();
409
+ }
410
+ for (const computed2 of this._computed.values()) {
411
+ computed2.unwatchAll();
412
+ }
413
+ this._state.clear();
414
+ this._computed.clear();
415
+ this._watchers.clear();
416
+ this._middleware.length = 0;
417
+ this._history.length = 0;
418
+ }
419
+ };
420
+ function createReactiveState(initialState, options = {}) {
421
+ return new ReactiveState(initialState, options);
422
+ }
423
+ function observable(value, options = {}) {
424
+ return new Observable(value, options);
425
+ }
426
+ function computed(getter, options = {}) {
427
+ return new Computed(getter, options);
428
+ }
429
+ var stateUtils = {
430
+ /**
431
+ * Create a toggle state
432
+ */
433
+ toggle(initialValue = false) {
434
+ const obs = observable(initialValue);
435
+ obs.toggle = () => {
436
+ obs.value = !obs.value;
437
+ };
438
+ return obs;
439
+ },
440
+ /**
441
+ * Create a counter state
442
+ */
443
+ counter(initialValue = 0) {
444
+ const obs = observable(initialValue);
445
+ obs.increment = (by = 1) => {
446
+ obs.value += by;
447
+ };
448
+ obs.decrement = (by = 1) => {
449
+ obs.value -= by;
450
+ };
451
+ obs.reset = () => {
452
+ obs.value = initialValue;
453
+ };
454
+ return obs;
455
+ },
456
+ /**
457
+ * Create an array state with utilities
458
+ */
459
+ array(initialArray = []) {
460
+ const obs = observable([...initialArray]);
461
+ obs.push = (...items) => {
462
+ obs.value = [...obs.value, ...items];
463
+ };
464
+ obs.pop = () => {
465
+ const newArray = [...obs.value];
466
+ const result = newArray.pop();
467
+ obs.value = newArray;
468
+ return result;
469
+ };
470
+ obs.filter = (predicate) => {
471
+ obs.value = obs.value.filter(predicate);
472
+ };
473
+ obs.clear = () => {
474
+ obs.value = [];
475
+ };
476
+ return obs;
477
+ },
478
+ /**
479
+ * Create object state with deep reactivity
480
+ */
481
+ object(initialObject = {}) {
482
+ const state = createReactiveState(initialObject, { deep: true });
483
+ return state;
484
+ }
485
+ };
486
+
487
+ // src/state-manager.js
488
+ var globalState = /* @__PURE__ */ new Map();
489
+ function createState(initialState = {}) {
490
+ const state = new Map(Object.entries(initialState));
491
+ return {
492
+ get(key) {
493
+ return state.get(key);
494
+ },
495
+ set(key, value) {
496
+ state.set(key, value);
497
+ return this;
498
+ },
499
+ has(key) {
500
+ return state.has(key);
501
+ },
502
+ delete(key) {
503
+ return state.delete(key);
504
+ },
505
+ clear() {
506
+ state.clear();
507
+ return this;
508
+ },
509
+ toObject() {
510
+ return Object.fromEntries(state);
511
+ },
512
+ // For debugging
513
+ _internal: state
514
+ };
515
+ }
516
+ var globalStateManager = {
517
+ set(key, value) {
518
+ globalState.set(key, value);
519
+ },
520
+ get(key) {
521
+ return globalState.get(key);
522
+ },
523
+ has(key) {
524
+ return globalState.has(key);
525
+ },
526
+ clear() {
527
+ globalState.clear();
528
+ },
529
+ // Create isolated state for each request
530
+ createRequestState() {
531
+ return createState();
532
+ }
533
+ };
534
+ var contextStacks = /* @__PURE__ */ new Map();
535
+ function provideContext(key, value) {
536
+ if (!contextStacks.has(key)) {
537
+ contextStacks.set(key, []);
538
+ }
539
+ const stack = contextStacks.get(key);
540
+ const previousValue = globalState.get(key);
541
+ stack.push(previousValue);
542
+ globalState.set(key, value);
543
+ }
544
+ function createContextProvider(key, value, children) {
545
+ return (renderFunction) => {
546
+ try {
547
+ provideContext(key, value);
548
+ if (renderFunction && typeof renderFunction === "function") {
549
+ return renderFunction(children);
550
+ } else {
551
+ return children;
552
+ }
553
+ } finally {
554
+ restoreContext(key);
555
+ }
556
+ };
557
+ }
558
+ function restoreContext(key) {
559
+ if (!contextStacks.has(key)) return;
560
+ const stack = contextStacks.get(key);
561
+ const previousValue = stack.pop();
562
+ if (stack.length === 0) {
563
+ if (previousValue === void 0) {
564
+ globalState.delete(key);
565
+ } else {
566
+ globalState.set(key, previousValue);
567
+ }
568
+ contextStacks.delete(key);
569
+ } else {
570
+ globalState.set(key, previousValue);
571
+ }
572
+ }
573
+ function clearAllContexts() {
574
+ contextStacks.clear();
575
+ }
576
+ function useContext(key) {
577
+ return globalState.get(key);
578
+ }
579
+
580
+ // src/state-persistence.js
581
+ var LocalStorageAdapter = class {
582
+ constructor() {
583
+ this.available = typeof localStorage !== "undefined";
584
+ }
585
+ async get(key) {
586
+ if (!this.available) return null;
587
+ try {
588
+ return localStorage.getItem(key);
589
+ } catch (error) {
590
+ console.error("LocalStorage get error:", error);
591
+ return null;
592
+ }
593
+ }
594
+ async set(key, value) {
595
+ if (!this.available) return false;
596
+ try {
597
+ localStorage.setItem(key, value);
598
+ return true;
599
+ } catch (error) {
600
+ console.error("LocalStorage set error:", error);
601
+ return false;
602
+ }
603
+ }
604
+ async remove(key) {
605
+ if (!this.available) return false;
606
+ try {
607
+ localStorage.removeItem(key);
608
+ return true;
609
+ } catch (error) {
610
+ console.error("LocalStorage remove error:", error);
611
+ return false;
612
+ }
613
+ }
614
+ async clear() {
615
+ if (!this.available) return false;
616
+ try {
617
+ localStorage.clear();
618
+ return true;
619
+ } catch (error) {
620
+ console.error("LocalStorage clear error:", error);
621
+ return false;
622
+ }
623
+ }
624
+ };
625
+ var SessionStorageAdapter = class {
626
+ constructor() {
627
+ this.available = typeof sessionStorage !== "undefined";
628
+ }
629
+ async get(key) {
630
+ if (!this.available) return null;
631
+ try {
632
+ return sessionStorage.getItem(key);
633
+ } catch (error) {
634
+ console.error("SessionStorage get error:", error);
635
+ return null;
636
+ }
637
+ }
638
+ async set(key, value) {
639
+ if (!this.available) return false;
640
+ try {
641
+ sessionStorage.setItem(key, value);
642
+ return true;
643
+ } catch (error) {
644
+ console.error("SessionStorage set error:", error);
645
+ return false;
646
+ }
647
+ }
648
+ async remove(key) {
649
+ if (!this.available) return false;
650
+ try {
651
+ sessionStorage.removeItem(key);
652
+ return true;
653
+ } catch (error) {
654
+ console.error("SessionStorage remove error:", error);
655
+ return false;
656
+ }
657
+ }
658
+ async clear() {
659
+ if (!this.available) return false;
660
+ try {
661
+ sessionStorage.clear();
662
+ return true;
663
+ } catch (error) {
664
+ console.error("SessionStorage clear error:", error);
665
+ return false;
666
+ }
667
+ }
668
+ };
669
+ var IndexedDBAdapter = class {
670
+ constructor(dbName = "coherent-db", storeName = "state") {
671
+ this.dbName = dbName;
672
+ this.storeName = storeName;
673
+ this.available = typeof indexedDB !== "undefined";
674
+ this.db = null;
675
+ }
676
+ async init() {
677
+ if (!this.available) return false;
678
+ if (this.db) return true;
679
+ return new Promise((resolve, reject) => {
680
+ const request = indexedDB.open(this.dbName, 1);
681
+ request.onerror = () => {
682
+ console.error("IndexedDB open error:", request.error);
683
+ reject(request.error);
684
+ };
685
+ request.onsuccess = () => {
686
+ this.db = request.result;
687
+ resolve(true);
688
+ };
689
+ request.onupgradeneeded = (event) => {
690
+ const db = event.target.result;
691
+ if (!db.objectStoreNames.contains(this.storeName)) {
692
+ db.createObjectStore(this.storeName);
693
+ }
694
+ };
695
+ });
696
+ }
697
+ async get(key) {
698
+ if (!this.available) return null;
699
+ await this.init();
700
+ return new Promise((resolve, reject) => {
701
+ const transaction = this.db.transaction([this.storeName], "readonly");
702
+ const store = transaction.objectStore(this.storeName);
703
+ const request = store.get(key);
704
+ request.onerror = () => {
705
+ console.error("IndexedDB get error:", request.error);
706
+ reject(request.error);
707
+ };
708
+ request.onsuccess = () => {
709
+ resolve(request.result || null);
710
+ };
711
+ });
712
+ }
713
+ async set(key, value) {
714
+ if (!this.available) return false;
715
+ await this.init();
716
+ return new Promise((resolve, reject) => {
717
+ const transaction = this.db.transaction([this.storeName], "readwrite");
718
+ const store = transaction.objectStore(this.storeName);
719
+ const request = store.put(value, key);
720
+ request.onerror = () => {
721
+ console.error("IndexedDB set error:", request.error);
722
+ reject(request.error);
723
+ };
724
+ request.onsuccess = () => {
725
+ resolve(true);
726
+ };
727
+ });
728
+ }
729
+ async remove(key) {
730
+ if (!this.available) return false;
731
+ await this.init();
732
+ return new Promise((resolve, reject) => {
733
+ const transaction = this.db.transaction([this.storeName], "readwrite");
734
+ const store = transaction.objectStore(this.storeName);
735
+ const request = store.delete(key);
736
+ request.onerror = () => {
737
+ console.error("IndexedDB remove error:", request.error);
738
+ reject(request.error);
739
+ };
740
+ request.onsuccess = () => {
741
+ resolve(true);
742
+ };
743
+ });
744
+ }
745
+ async clear() {
746
+ if (!this.available) return false;
747
+ await this.init();
748
+ return new Promise((resolve, reject) => {
749
+ const transaction = this.db.transaction([this.storeName], "readwrite");
750
+ const store = transaction.objectStore(this.storeName);
751
+ const request = store.clear();
752
+ request.onerror = () => {
753
+ console.error("IndexedDB clear error:", request.error);
754
+ reject(request.error);
755
+ };
756
+ request.onsuccess = () => {
757
+ resolve(true);
758
+ };
759
+ });
760
+ }
761
+ };
762
+ var MemoryAdapter = class {
763
+ constructor() {
764
+ this.storage = /* @__PURE__ */ new Map();
765
+ this.available = true;
766
+ }
767
+ async get(key) {
768
+ return this.storage.get(key) || null;
769
+ }
770
+ async set(key, value) {
771
+ this.storage.set(key, value);
772
+ return true;
773
+ }
774
+ async remove(key) {
775
+ return this.storage.delete(key);
776
+ }
777
+ async clear() {
778
+ this.storage.clear();
779
+ return true;
780
+ }
781
+ };
782
+ var SimpleEncryption = class {
783
+ constructor(key) {
784
+ this.key = key || "default-key";
785
+ }
786
+ encrypt(text) {
787
+ let result = "";
788
+ for (let i = 0; i < text.length; i++) {
789
+ result += String.fromCharCode(
790
+ text.charCodeAt(i) ^ this.key.charCodeAt(i % this.key.length)
791
+ );
792
+ }
793
+ return btoa(result);
794
+ }
795
+ decrypt(encrypted) {
796
+ const text = atob(encrypted);
797
+ let result = "";
798
+ for (let i = 0; i < text.length; i++) {
799
+ result += String.fromCharCode(
800
+ text.charCodeAt(i) ^ this.key.charCodeAt(i % this.key.length)
801
+ );
802
+ }
803
+ return result;
804
+ }
805
+ };
806
+ function createStorageAdapter(type) {
807
+ switch (type) {
808
+ case "localStorage":
809
+ return new LocalStorageAdapter();
810
+ case "sessionStorage":
811
+ return new SessionStorageAdapter();
812
+ case "indexedDB":
813
+ return new IndexedDBAdapter();
814
+ case "memory":
815
+ return new MemoryAdapter();
816
+ default:
817
+ return new LocalStorageAdapter();
818
+ }
819
+ }
820
+ function createPersistentState(initialState = {}, options = {}) {
821
+ const opts = {
822
+ storage: "localStorage",
823
+ key: "coherent-state",
824
+ debounce: true,
825
+ debounceDelay: 300,
826
+ serialize: JSON.stringify,
827
+ deserialize: JSON.parse,
828
+ include: null,
829
+ exclude: null,
830
+ encrypt: false,
831
+ encryptionKey: null,
832
+ onSave: null,
833
+ onLoad: null,
834
+ onError: null,
835
+ versioning: false,
836
+ version: "1.0.0",
837
+ migrate: null,
838
+ ttl: null,
839
+ crossTab: false,
840
+ ...options
841
+ };
842
+ const adapter = createStorageAdapter(opts.storage);
843
+ const encryption = opts.encrypt ? new SimpleEncryption(opts.encryptionKey) : null;
844
+ let state = { ...initialState };
845
+ let saveTimeout = null;
846
+ const listeners = /* @__PURE__ */ new Set();
847
+ function filterKeys(obj) {
848
+ if (!obj || typeof obj !== "object") return obj;
849
+ if (opts.include && Array.isArray(opts.include)) {
850
+ const filtered = {};
851
+ opts.include.forEach((key) => {
852
+ if (key in obj) {
853
+ filtered[key] = obj[key];
854
+ }
855
+ });
856
+ return filtered;
857
+ }
858
+ if (opts.exclude && Array.isArray(opts.exclude)) {
859
+ const filtered = { ...obj };
860
+ opts.exclude.forEach((key) => {
861
+ delete filtered[key];
862
+ });
863
+ return filtered;
864
+ }
865
+ return obj;
866
+ }
867
+ async function save(immediate = false) {
868
+ if (opts.debounce && !immediate) {
869
+ clearTimeout(saveTimeout);
870
+ saveTimeout = setTimeout(() => save(true), opts.debounceDelay);
871
+ return;
872
+ }
873
+ try {
874
+ const filteredState = filterKeys(state);
875
+ const serialized = opts.serialize(filteredState);
876
+ const data = {
877
+ state: serialized,
878
+ version: opts.version,
879
+ timestamp: Date.now(),
880
+ ttl: opts.ttl
881
+ };
882
+ let dataString = JSON.stringify(data);
883
+ if (encryption) {
884
+ dataString = encryption.encrypt(dataString);
885
+ }
886
+ await adapter.set(opts.key, dataString);
887
+ if (opts.onSave) {
888
+ opts.onSave(filteredState);
889
+ }
890
+ if (opts.crossTab && typeof BroadcastChannel !== "undefined") {
891
+ const channel = new BroadcastChannel("coherent-state-sync");
892
+ channel.postMessage({ type: "state-update", state: filteredState });
893
+ channel.close();
894
+ }
895
+ } catch (error) {
896
+ console.error("State save error:", error);
897
+ if (opts.onError) {
898
+ opts.onError(error);
899
+ }
900
+ }
901
+ }
902
+ async function load() {
903
+ try {
904
+ let dataString = await adapter.get(opts.key);
905
+ if (!dataString) return null;
906
+ if (encryption) {
907
+ dataString = encryption.decrypt(dataString);
908
+ }
909
+ const data = JSON.parse(dataString);
910
+ if (data.ttl && data.timestamp) {
911
+ const age = Date.now() - data.timestamp;
912
+ if (age > data.ttl) {
913
+ await adapter.remove(opts.key);
914
+ return null;
915
+ }
916
+ }
917
+ if (opts.versioning && data.version !== opts.version) {
918
+ if (opts.migrate) {
919
+ const migrated = opts.migrate(data.state, data.version, opts.version);
920
+ return opts.deserialize(migrated);
921
+ }
922
+ return null;
923
+ }
924
+ const loadedState = opts.deserialize(data.state);
925
+ if (opts.onLoad) {
926
+ opts.onLoad(loadedState);
927
+ }
928
+ return loadedState;
929
+ } catch (error) {
930
+ console.error("State load error:", error);
931
+ if (opts.onError) {
932
+ opts.onError(error);
933
+ }
934
+ return null;
935
+ }
936
+ }
937
+ function subscribe(listener) {
938
+ listeners.add(listener);
939
+ return () => listeners.delete(listener);
940
+ }
941
+ function notifyListeners(oldState, newState) {
942
+ listeners.forEach((listener) => {
943
+ try {
944
+ listener(newState, oldState);
945
+ } catch (error) {
946
+ console.error("Listener error:", error);
947
+ }
948
+ });
949
+ }
950
+ function getState(key) {
951
+ return key ? state[key] : { ...state };
952
+ }
953
+ function setState(updates, persist2 = true) {
954
+ const oldState = { ...state };
955
+ if (typeof updates === "function") {
956
+ updates = updates(oldState);
957
+ }
958
+ state = { ...state, ...updates };
959
+ notifyListeners(oldState, state);
960
+ if (persist2) {
961
+ save();
962
+ }
963
+ }
964
+ function resetState(persist2 = true) {
965
+ const oldState = { ...state };
966
+ state = { ...initialState };
967
+ notifyListeners(oldState, state);
968
+ if (persist2) {
969
+ save(true);
970
+ }
971
+ }
972
+ async function clearStorage() {
973
+ await adapter.remove(opts.key);
974
+ }
975
+ async function persist() {
976
+ await save(true);
977
+ }
978
+ async function restore() {
979
+ const loaded = await load();
980
+ if (loaded) {
981
+ const oldState = { ...state };
982
+ state = { ...state, ...loaded };
983
+ notifyListeners(oldState, state);
984
+ return true;
985
+ }
986
+ return false;
987
+ }
988
+ if (opts.crossTab && typeof BroadcastChannel !== "undefined") {
989
+ const channel = new BroadcastChannel("coherent-state-sync");
990
+ channel.onmessage = (event) => {
991
+ if (event.data.type === "state-update") {
992
+ const oldState = { ...state };
993
+ state = { ...state, ...event.data.state };
994
+ notifyListeners(oldState, state);
995
+ }
996
+ };
997
+ }
998
+ if (opts.storage !== "memory") {
999
+ restore();
1000
+ }
1001
+ return {
1002
+ getState,
1003
+ setState,
1004
+ resetState,
1005
+ subscribe,
1006
+ persist,
1007
+ restore,
1008
+ clearStorage,
1009
+ load,
1010
+ save: () => save(true),
1011
+ get adapter() {
1012
+ return adapter;
1013
+ }
1014
+ };
1015
+ }
1016
+ function withLocalStorage(initialState = {}, key = "coherent-state", options = {}) {
1017
+ return createPersistentState(initialState, {
1018
+ ...options,
1019
+ storage: "localStorage",
1020
+ key
1021
+ });
1022
+ }
1023
+ function withSessionStorage(initialState = {}, key = "coherent-state", options = {}) {
1024
+ return createPersistentState(initialState, {
1025
+ ...options,
1026
+ storage: "sessionStorage",
1027
+ key
1028
+ });
1029
+ }
1030
+ function withIndexedDB(initialState = {}, key = "coherent-state", options = {}) {
1031
+ return createPersistentState(initialState, {
1032
+ ...options,
1033
+ storage: "indexedDB",
1034
+ key
1035
+ });
1036
+ }
1037
+
1038
+ // src/state-validation.js
1039
+ var SchemaValidator = class {
1040
+ constructor(schema, options = {}) {
1041
+ this.schema = schema;
1042
+ this.options = {
1043
+ coerce: false,
1044
+ allowUnknown: true,
1045
+ ...options
1046
+ };
1047
+ }
1048
+ /**
1049
+ * Validate value against schema
1050
+ * @param {*} value - Value to validate
1051
+ * @param {Object} schema - Schema to validate against
1052
+ * @param {string} path - Current path in object
1053
+ * @returns {ValidationResult} Validation result
1054
+ */
1055
+ validate(value, schema = this.schema, path = "") {
1056
+ const errors = [];
1057
+ let coercedValue = value;
1058
+ if (schema.type) {
1059
+ const typeResult = this.validateType(value, schema.type, path);
1060
+ if (!typeResult.valid) {
1061
+ errors.push(...typeResult.errors);
1062
+ if (!this.options.coerce) {
1063
+ return { valid: false, errors, value };
1064
+ }
1065
+ }
1066
+ coercedValue = typeResult.value;
1067
+ }
1068
+ if (schema.enum) {
1069
+ const enumResult = this.validateEnum(coercedValue, schema.enum, path);
1070
+ if (!enumResult.valid) {
1071
+ errors.push(...enumResult.errors);
1072
+ }
1073
+ }
1074
+ if (schema.type === "string") {
1075
+ const stringResult = this.validateString(coercedValue, schema, path);
1076
+ if (!stringResult.valid) {
1077
+ errors.push(...stringResult.errors);
1078
+ }
1079
+ }
1080
+ if (schema.type === "number" || schema.type === "integer") {
1081
+ const numberResult = this.validateNumber(coercedValue, schema, path);
1082
+ if (!numberResult.valid) {
1083
+ errors.push(...numberResult.errors);
1084
+ }
1085
+ }
1086
+ if (schema.type === "array") {
1087
+ const arrayResult = this.validateArray(coercedValue, schema, path);
1088
+ if (!arrayResult.valid) {
1089
+ errors.push(...arrayResult.errors);
1090
+ }
1091
+ coercedValue = arrayResult.value;
1092
+ }
1093
+ if (schema.type === "object") {
1094
+ const objectResult = this.validateObject(coercedValue, schema, path);
1095
+ if (!objectResult.valid) {
1096
+ errors.push(...objectResult.errors);
1097
+ }
1098
+ coercedValue = objectResult.value;
1099
+ }
1100
+ if (schema.validate && typeof schema.validate === "function") {
1101
+ const customResult = schema.validate(coercedValue);
1102
+ if (customResult !== true) {
1103
+ errors.push({
1104
+ path,
1105
+ message: typeof customResult === "string" ? customResult : "Custom validation failed",
1106
+ type: "custom",
1107
+ value: coercedValue
1108
+ });
1109
+ }
1110
+ }
1111
+ return {
1112
+ valid: errors.length === 0,
1113
+ errors,
1114
+ value: coercedValue
1115
+ };
1116
+ }
1117
+ validateType(value, type, path) {
1118
+ const actualType = Array.isArray(value) ? "array" : typeof value;
1119
+ const errors = [];
1120
+ let coercedValue = value;
1121
+ const types = Array.isArray(type) ? type : [type];
1122
+ const isValid = types.some((t) => {
1123
+ if (t === "array") return Array.isArray(value);
1124
+ if (t === "null") return value === null;
1125
+ if (t === "integer") return typeof value === "number" && Number.isInteger(value);
1126
+ return typeof value === t;
1127
+ });
1128
+ if (!isValid) {
1129
+ if (this.options.coerce) {
1130
+ const primaryType = types[0];
1131
+ try {
1132
+ if (primaryType === "string") {
1133
+ coercedValue = String(value);
1134
+ } else if (primaryType === "number") {
1135
+ coercedValue = Number(value);
1136
+ if (isNaN(coercedValue)) {
1137
+ errors.push({
1138
+ path,
1139
+ message: `Cannot coerce "${value}" to number`,
1140
+ type: "type",
1141
+ value,
1142
+ expected: primaryType
1143
+ });
1144
+ }
1145
+ } else if (primaryType === "boolean") {
1146
+ coercedValue = Boolean(value);
1147
+ } else if (primaryType === "integer") {
1148
+ coercedValue = parseInt(value, 10);
1149
+ if (isNaN(coercedValue)) {
1150
+ errors.push({
1151
+ path,
1152
+ message: `Cannot coerce "${value}" to integer`,
1153
+ type: "type",
1154
+ value,
1155
+ expected: primaryType
1156
+ });
1157
+ }
1158
+ }
1159
+ } catch {
1160
+ errors.push({
1161
+ path,
1162
+ message: `Cannot coerce value to ${primaryType}`,
1163
+ type: "type",
1164
+ value,
1165
+ expected: primaryType
1166
+ });
1167
+ }
1168
+ } else {
1169
+ errors.push({
1170
+ path,
1171
+ message: `Expected type ${types.join(" or ")}, got ${actualType}`,
1172
+ type: "type",
1173
+ value,
1174
+ expected: type
1175
+ });
1176
+ }
1177
+ }
1178
+ return {
1179
+ valid: errors.length === 0,
1180
+ errors,
1181
+ value: coercedValue
1182
+ };
1183
+ }
1184
+ validateEnum(value, enumValues, path) {
1185
+ const errors = [];
1186
+ if (!enumValues.includes(value)) {
1187
+ errors.push({
1188
+ path,
1189
+ message: `Value must be one of: ${enumValues.join(", ")}`,
1190
+ type: "enum",
1191
+ value,
1192
+ expected: enumValues
1193
+ });
1194
+ }
1195
+ return { valid: errors.length === 0, errors };
1196
+ }
1197
+ validateString(value, schema, path) {
1198
+ const errors = [];
1199
+ if (schema.minLength !== void 0 && value.length < schema.minLength) {
1200
+ errors.push({
1201
+ path,
1202
+ message: `String length must be >= ${schema.minLength}`,
1203
+ type: "minLength",
1204
+ value
1205
+ });
1206
+ }
1207
+ if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
1208
+ errors.push({
1209
+ path,
1210
+ message: `String length must be <= ${schema.maxLength}`,
1211
+ type: "maxLength",
1212
+ value
1213
+ });
1214
+ }
1215
+ if (schema.pattern) {
1216
+ const regex = new RegExp(schema.pattern);
1217
+ if (!regex.test(value)) {
1218
+ errors.push({
1219
+ path,
1220
+ message: `String does not match pattern: ${schema.pattern}`,
1221
+ type: "pattern",
1222
+ value
1223
+ });
1224
+ }
1225
+ }
1226
+ if (schema.format) {
1227
+ const formatResult = this.validateFormat(value, schema.format, path);
1228
+ if (!formatResult.valid) {
1229
+ errors.push(...formatResult.errors);
1230
+ }
1231
+ }
1232
+ return { valid: errors.length === 0, errors };
1233
+ }
1234
+ validateFormat(value, format, path) {
1235
+ const errors = [];
1236
+ const formats = {
1237
+ email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
1238
+ url: /^https?:\/\/.+/,
1239
+ uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
1240
+ date: /^\d{4}-\d{2}-\d{2}$/,
1241
+ "date-time": /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/
1242
+ };
1243
+ if (formats[format] && !formats[format].test(value)) {
1244
+ errors.push({
1245
+ path,
1246
+ message: `String does not match format: ${format}`,
1247
+ type: "format",
1248
+ value,
1249
+ expected: format
1250
+ });
1251
+ }
1252
+ return { valid: errors.length === 0, errors };
1253
+ }
1254
+ validateNumber(value, schema, path) {
1255
+ const errors = [];
1256
+ if (schema.minimum !== void 0 && value < schema.minimum) {
1257
+ errors.push({
1258
+ path,
1259
+ message: `Number must be >= ${schema.minimum}`,
1260
+ type: "minimum",
1261
+ value
1262
+ });
1263
+ }
1264
+ if (schema.maximum !== void 0 && value > schema.maximum) {
1265
+ errors.push({
1266
+ path,
1267
+ message: `Number must be <= ${schema.maximum}`,
1268
+ type: "maximum",
1269
+ value
1270
+ });
1271
+ }
1272
+ if (schema.exclusiveMinimum !== void 0 && value <= schema.exclusiveMinimum) {
1273
+ errors.push({
1274
+ path,
1275
+ message: `Number must be > ${schema.exclusiveMinimum}`,
1276
+ type: "exclusiveMinimum",
1277
+ value
1278
+ });
1279
+ }
1280
+ if (schema.exclusiveMaximum !== void 0 && value >= schema.exclusiveMaximum) {
1281
+ errors.push({
1282
+ path,
1283
+ message: `Number must be < ${schema.exclusiveMaximum}`,
1284
+ type: "exclusiveMaximum",
1285
+ value
1286
+ });
1287
+ }
1288
+ if (schema.multipleOf !== void 0 && value % schema.multipleOf !== 0) {
1289
+ errors.push({
1290
+ path,
1291
+ message: `Number must be multiple of ${schema.multipleOf}`,
1292
+ type: "multipleOf",
1293
+ value
1294
+ });
1295
+ }
1296
+ return { valid: errors.length === 0, errors };
1297
+ }
1298
+ validateArray(value, schema, path) {
1299
+ const errors = [];
1300
+ const coercedValue = [...value];
1301
+ if (schema.minItems !== void 0 && value.length < schema.minItems) {
1302
+ errors.push({
1303
+ path,
1304
+ message: `Array must have at least ${schema.minItems} items`,
1305
+ type: "minItems",
1306
+ value
1307
+ });
1308
+ }
1309
+ if (schema.maxItems !== void 0 && value.length > schema.maxItems) {
1310
+ errors.push({
1311
+ path,
1312
+ message: `Array must have at most ${schema.maxItems} items`,
1313
+ type: "maxItems",
1314
+ value
1315
+ });
1316
+ }
1317
+ if (schema.uniqueItems) {
1318
+ const seen = /* @__PURE__ */ new Set();
1319
+ const duplicates = [];
1320
+ value.forEach((item, index) => {
1321
+ const key = JSON.stringify(item);
1322
+ if (seen.has(key)) {
1323
+ duplicates.push(index);
1324
+ }
1325
+ seen.add(key);
1326
+ });
1327
+ if (duplicates.length > 0) {
1328
+ errors.push({
1329
+ path,
1330
+ message: "Array items must be unique",
1331
+ type: "uniqueItems",
1332
+ value
1333
+ });
1334
+ }
1335
+ }
1336
+ if (schema.items) {
1337
+ value.forEach((item, index) => {
1338
+ const itemPath = `${path}[${index}]`;
1339
+ const itemResult = this.validate(item, schema.items, itemPath);
1340
+ if (!itemResult.valid) {
1341
+ errors.push(...itemResult.errors);
1342
+ }
1343
+ if (this.options.coerce) {
1344
+ coercedValue[index] = itemResult.value;
1345
+ }
1346
+ });
1347
+ }
1348
+ return {
1349
+ valid: errors.length === 0,
1350
+ errors,
1351
+ value: coercedValue
1352
+ };
1353
+ }
1354
+ validateObject(value, schema, path) {
1355
+ const errors = [];
1356
+ const coercedValue = { ...value };
1357
+ if (schema.required) {
1358
+ schema.required.forEach((prop) => {
1359
+ if (!(prop in value)) {
1360
+ errors.push({
1361
+ path: path ? `${path}.${prop}` : prop,
1362
+ message: `Required property "${prop}" is missing`,
1363
+ type: "required",
1364
+ value: void 0
1365
+ });
1366
+ }
1367
+ });
1368
+ }
1369
+ if (schema.properties) {
1370
+ Object.entries(schema.properties).forEach(([prop, propSchema]) => {
1371
+ if (prop in value) {
1372
+ const propPath = path ? `${path}.${prop}` : prop;
1373
+ const propResult = this.validate(value[prop], propSchema, propPath);
1374
+ if (!propResult.valid) {
1375
+ errors.push(...propResult.errors);
1376
+ }
1377
+ if (this.options.coerce) {
1378
+ coercedValue[prop] = propResult.value;
1379
+ }
1380
+ }
1381
+ });
1382
+ }
1383
+ if (schema.additionalProperties === false && !this.options.allowUnknown) {
1384
+ const allowedProps = new Set(Object.keys(schema.properties || {}));
1385
+ Object.keys(value).forEach((prop) => {
1386
+ if (!allowedProps.has(prop)) {
1387
+ errors.push({
1388
+ path: path ? `${path}.${prop}` : prop,
1389
+ message: `Unknown property "${prop}"`,
1390
+ type: "additionalProperties",
1391
+ value: value[prop]
1392
+ });
1393
+ }
1394
+ });
1395
+ }
1396
+ const propCount = Object.keys(value).length;
1397
+ if (schema.minProperties !== void 0 && propCount < schema.minProperties) {
1398
+ errors.push({
1399
+ path,
1400
+ message: `Object must have at least ${schema.minProperties} properties`,
1401
+ type: "minProperties",
1402
+ value
1403
+ });
1404
+ }
1405
+ if (schema.maxProperties !== void 0 && propCount > schema.maxProperties) {
1406
+ errors.push({
1407
+ path,
1408
+ message: `Object must have at most ${schema.maxProperties} properties`,
1409
+ type: "maxProperties",
1410
+ value
1411
+ });
1412
+ }
1413
+ return {
1414
+ valid: errors.length === 0,
1415
+ errors,
1416
+ value: coercedValue
1417
+ };
1418
+ }
1419
+ };
1420
+ function createValidatedState(initialState = {}, options = {}) {
1421
+ const opts = {
1422
+ schema: null,
1423
+ validators: {},
1424
+ strict: false,
1425
+ coerce: false,
1426
+ onError: null,
1427
+ validateOnSet: true,
1428
+ validateOnGet: false,
1429
+ required: [],
1430
+ allowUnknown: true,
1431
+ ...options
1432
+ };
1433
+ const schemaValidator = opts.schema ? new SchemaValidator(opts.schema, {
1434
+ coerce: opts.coerce,
1435
+ allowUnknown: opts.allowUnknown
1436
+ }) : null;
1437
+ let state = { ...initialState };
1438
+ const listeners = /* @__PURE__ */ new Set();
1439
+ const validationErrors = /* @__PURE__ */ new Map();
1440
+ function validateState(value, key = null) {
1441
+ const errors = [];
1442
+ let validatedValue = value;
1443
+ if (schemaValidator) {
1444
+ const schema = key && opts.schema.properties ? opts.schema.properties[key] : opts.schema;
1445
+ const result = schemaValidator.validate(value, schema, key || "");
1446
+ if (!result.valid) {
1447
+ errors.push(...result.errors);
1448
+ }
1449
+ validatedValue = result.value;
1450
+ }
1451
+ if (key && opts.validators[key]) {
1452
+ const validator = opts.validators[key];
1453
+ const result = validator(value);
1454
+ if (result !== true) {
1455
+ errors.push({
1456
+ path: key,
1457
+ message: typeof result === "string" ? result : "Validation failed",
1458
+ type: "custom",
1459
+ value
1460
+ });
1461
+ }
1462
+ } else if (!key) {
1463
+ Object.entries(opts.validators).forEach(([fieldKey, validator]) => {
1464
+ if (fieldKey in value) {
1465
+ const result = validator(value[fieldKey]);
1466
+ if (result !== true) {
1467
+ errors.push({
1468
+ path: fieldKey,
1469
+ message: typeof result === "string" ? result : "Validation failed",
1470
+ type: "custom",
1471
+ value: value[fieldKey]
1472
+ });
1473
+ }
1474
+ }
1475
+ });
1476
+ }
1477
+ if (opts.required.length > 0 && !key) {
1478
+ opts.required.forEach((field) => {
1479
+ if (!(field in value)) {
1480
+ errors.push({
1481
+ path: field,
1482
+ message: `Required field "${field}" is missing`,
1483
+ type: "required",
1484
+ value: void 0
1485
+ });
1486
+ }
1487
+ });
1488
+ }
1489
+ return {
1490
+ valid: errors.length === 0,
1491
+ errors,
1492
+ value: validatedValue
1493
+ };
1494
+ }
1495
+ function getState(key) {
1496
+ const value = key ? state[key] : { ...state };
1497
+ if (opts.validateOnGet) {
1498
+ const result = validateState(value, key);
1499
+ if (!result.valid) {
1500
+ validationErrors.set(key || "__root__", result.errors);
1501
+ if (opts.onError) {
1502
+ opts.onError(result.errors);
1503
+ }
1504
+ }
1505
+ }
1506
+ return value;
1507
+ }
1508
+ function setState(updates) {
1509
+ const oldState = { ...state };
1510
+ if (typeof updates === "function") {
1511
+ updates = updates(oldState);
1512
+ }
1513
+ const newState = { ...state, ...updates };
1514
+ if (opts.validateOnSet) {
1515
+ const result = validateState(newState);
1516
+ if (!result.valid) {
1517
+ validationErrors.set("__root__", result.errors);
1518
+ if (opts.onError) {
1519
+ opts.onError(result.errors);
1520
+ }
1521
+ if (opts.strict) {
1522
+ const error = new Error("Validation failed");
1523
+ error.validationErrors = result.errors;
1524
+ throw error;
1525
+ }
1526
+ return;
1527
+ }
1528
+ if (opts.coerce) {
1529
+ const updatedKeys = Object.keys(updates);
1530
+ const newUpdates = {};
1531
+ updatedKeys.forEach((key) => {
1532
+ if (result.value[key] !== state[key]) {
1533
+ newUpdates[key] = result.value[key];
1534
+ }
1535
+ });
1536
+ updates = newUpdates;
1537
+ }
1538
+ validationErrors.clear();
1539
+ }
1540
+ state = { ...state, ...updates };
1541
+ listeners.forEach((listener) => {
1542
+ try {
1543
+ listener(state, oldState);
1544
+ } catch (error) {
1545
+ console.error("Listener error:", error);
1546
+ }
1547
+ });
1548
+ }
1549
+ function subscribe(listener) {
1550
+ listeners.add(listener);
1551
+ return () => listeners.delete(listener);
1552
+ }
1553
+ function getErrors(key = "__root__") {
1554
+ return validationErrors.get(key) || [];
1555
+ }
1556
+ function isValid() {
1557
+ const result = validateState(state);
1558
+ if (!result.valid) {
1559
+ validationErrors.set("__root__", result.errors);
1560
+ }
1561
+ return result.valid;
1562
+ }
1563
+ function validateField(key, value) {
1564
+ return validateState(value, key);
1565
+ }
1566
+ return {
1567
+ getState,
1568
+ setState,
1569
+ subscribe,
1570
+ getErrors,
1571
+ isValid,
1572
+ validateField,
1573
+ validate: () => validateState(state)
1574
+ };
1575
+ }
1576
+ var validators = {
1577
+ /**
1578
+ * Email validator
1579
+ * @param {string} value - Email to validate
1580
+ * @returns {boolean|string} True if valid, error message otherwise
1581
+ */
1582
+ email: (value) => {
1583
+ if (typeof value !== "string") return "Email must be a string";
1584
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return "Invalid email format";
1585
+ return true;
1586
+ },
1587
+ /**
1588
+ * URL validator
1589
+ * @param {string} value - URL to validate
1590
+ * @returns {boolean|string} True if valid, error message otherwise
1591
+ */
1592
+ url: (value) => {
1593
+ if (typeof value !== "string") return "URL must be a string";
1594
+ try {
1595
+ new URL(value);
1596
+ return true;
1597
+ } catch {
1598
+ return "Invalid URL format";
1599
+ }
1600
+ },
1601
+ /**
1602
+ * Range validator
1603
+ * @param {number} min - Minimum value
1604
+ * @param {number} max - Maximum value
1605
+ * @returns {Function} Validator function
1606
+ */
1607
+ range: (min, max) => (value) => {
1608
+ if (typeof value !== "number") return "Value must be a number";
1609
+ if (value < min || value > max) return `Value must be between ${min} and ${max}`;
1610
+ return true;
1611
+ },
1612
+ /**
1613
+ * Length validator
1614
+ * @param {number} min - Minimum length
1615
+ * @param {number} max - Maximum length
1616
+ * @returns {Function} Validator function
1617
+ */
1618
+ length: (min, max) => (value) => {
1619
+ if (typeof value !== "string") return "Value must be a string";
1620
+ if (value.length < min || value.length > max) {
1621
+ return `Length must be between ${min} and ${max}`;
1622
+ }
1623
+ return true;
1624
+ },
1625
+ /**
1626
+ * Pattern validator
1627
+ * @param {RegExp|string} pattern - Pattern to match
1628
+ * @returns {Function} Validator function
1629
+ */
1630
+ pattern: (pattern) => (value) => {
1631
+ if (typeof value !== "string") return "Value must be a string";
1632
+ const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
1633
+ if (!regex.test(value)) return `Value does not match pattern: ${pattern}`;
1634
+ return true;
1635
+ },
1636
+ /**
1637
+ * Required validator
1638
+ * @param {*} value - Value to validate
1639
+ * @returns {boolean|string} True if valid, error message otherwise
1640
+ */
1641
+ required: (value) => {
1642
+ if (value === void 0 || value === null || value === "") {
1643
+ return "Value is required";
1644
+ }
1645
+ return true;
1646
+ }
1647
+ };
1648
+
1649
+ // src/index.js
1650
+ var index_default = {
1651
+ // Reactive state utilities
1652
+ createReactiveState,
1653
+ observable,
1654
+ computed,
1655
+ // SSR-compatible state management
1656
+ createState,
1657
+ globalStateManager,
1658
+ provideContext,
1659
+ createContextProvider,
1660
+ restoreContext,
1661
+ clearAllContexts,
1662
+ useContext,
1663
+ // Persistence utilities
1664
+ createPersistentState,
1665
+ withLocalStorage,
1666
+ withSessionStorage,
1667
+ withIndexedDB,
1668
+ // Validation utilities
1669
+ createValidatedState,
1670
+ validators,
1671
+ // State utilities
1672
+ stateUtils
1673
+ };
1674
+ export {
1675
+ Observable,
1676
+ ReactiveState,
1677
+ clearAllContexts,
1678
+ computed,
1679
+ createContextProvider,
1680
+ createPersistentState,
1681
+ createReactiveState,
1682
+ createState,
1683
+ createValidatedState,
1684
+ index_default as default,
1685
+ globalStateManager,
1686
+ observable,
1687
+ provideContext,
1688
+ restoreContext,
1689
+ stateUtils,
1690
+ useContext,
1691
+ validators,
1692
+ withIndexedDB,
1693
+ withLocalStorage,
1694
+ withSessionStorage
1695
+ };
1696
+ //# sourceMappingURL=index.js.map