@coherent.js/state 1.0.0-beta.3 → 1.0.0-beta.6
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 +336 -1
- package/package.json +3 -2
- package/types/index.d.ts +364 -39
package/dist/index.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
// src/reactive-state.js
|
|
2
|
-
|
|
2
|
+
var StateError = class extends Error {
|
|
3
|
+
constructor(message, options = {}) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "StateError";
|
|
6
|
+
this.type = options.type || "state";
|
|
7
|
+
this.component = options.component;
|
|
8
|
+
this.context = options.context;
|
|
9
|
+
this.timestamp = Date.now();
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var globalErrorHandler = {
|
|
13
|
+
handle(error, context = {}) {
|
|
14
|
+
console.error("State Error:", error.message, context);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
3
17
|
var Observable = class _Observable {
|
|
4
18
|
constructor(value, options = {}) {
|
|
5
19
|
this._value = value;
|
|
@@ -1646,6 +1660,318 @@ var validators = {
|
|
|
1646
1660
|
}
|
|
1647
1661
|
};
|
|
1648
1662
|
|
|
1663
|
+
// src/enhanced-state-patterns.js
|
|
1664
|
+
var FormState = class {
|
|
1665
|
+
constructor(initialValues = {}, options = {}) {
|
|
1666
|
+
this._state = createReactiveState({
|
|
1667
|
+
values: { ...initialValues },
|
|
1668
|
+
errors: {},
|
|
1669
|
+
touched: {},
|
|
1670
|
+
isSubmitting: false,
|
|
1671
|
+
isValid: true
|
|
1672
|
+
}, options);
|
|
1673
|
+
this._validators = {};
|
|
1674
|
+
this._options = options;
|
|
1675
|
+
}
|
|
1676
|
+
// OOP methods for form management
|
|
1677
|
+
setValue(field, value) {
|
|
1678
|
+
this._state.set("values", {
|
|
1679
|
+
...this._state.get("values"),
|
|
1680
|
+
[field]: value
|
|
1681
|
+
});
|
|
1682
|
+
this._validateField(field);
|
|
1683
|
+
this._state.set("touched", {
|
|
1684
|
+
...this._state.get("touched"),
|
|
1685
|
+
[field]: true
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
getValue(field) {
|
|
1689
|
+
return this._state.get("values")[field];
|
|
1690
|
+
}
|
|
1691
|
+
setError(field, error) {
|
|
1692
|
+
this._state.set("errors", {
|
|
1693
|
+
...this._state.get("errors"),
|
|
1694
|
+
[field]: error
|
|
1695
|
+
});
|
|
1696
|
+
this._updateIsValid();
|
|
1697
|
+
}
|
|
1698
|
+
addValidator(field, validator) {
|
|
1699
|
+
this._validators[field] = validator;
|
|
1700
|
+
}
|
|
1701
|
+
validateAll() {
|
|
1702
|
+
const values = this._state.get("values");
|
|
1703
|
+
const errors = {};
|
|
1704
|
+
Object.entries(this._validators).forEach(([field, validator]) => {
|
|
1705
|
+
const error = validator(values[field], values);
|
|
1706
|
+
if (error) {
|
|
1707
|
+
errors[field] = error;
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
this._state.set("errors", errors);
|
|
1711
|
+
this._updateIsValid();
|
|
1712
|
+
return Object.keys(errors).length === 0;
|
|
1713
|
+
}
|
|
1714
|
+
async submit(onSubmit) {
|
|
1715
|
+
if (!this.validateAll()) return false;
|
|
1716
|
+
this._state.set("isSubmitting", true);
|
|
1717
|
+
try {
|
|
1718
|
+
await onSubmit(this._state.get("values"));
|
|
1719
|
+
return true;
|
|
1720
|
+
} catch (error) {
|
|
1721
|
+
this.setError("_form", error.message);
|
|
1722
|
+
return false;
|
|
1723
|
+
} finally {
|
|
1724
|
+
this._state.set("isSubmitting", false);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
reset() {
|
|
1728
|
+
this._state.set("values", {});
|
|
1729
|
+
this._state.set("errors", {});
|
|
1730
|
+
this._state.set("touched", {});
|
|
1731
|
+
this._state.set("isSubmitting", false);
|
|
1732
|
+
this._state.set("isValid", true);
|
|
1733
|
+
}
|
|
1734
|
+
// Watch methods for FP integration
|
|
1735
|
+
watchValues(callback) {
|
|
1736
|
+
return this._state.watch("values", callback);
|
|
1737
|
+
}
|
|
1738
|
+
watchErrors(callback) {
|
|
1739
|
+
return this._state.watch("errors", callback);
|
|
1740
|
+
}
|
|
1741
|
+
watchSubmitting(callback) {
|
|
1742
|
+
return this._state.watch("isSubmitting", callback);
|
|
1743
|
+
}
|
|
1744
|
+
// Private methods
|
|
1745
|
+
_validateField(field) {
|
|
1746
|
+
const value = this.getValue(field);
|
|
1747
|
+
const validator = this._validators[field];
|
|
1748
|
+
if (validator) {
|
|
1749
|
+
const error = validator(value, this._state.get("values"));
|
|
1750
|
+
this.setError(field, error);
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
_updateIsValid() {
|
|
1754
|
+
const hasErrors = Object.keys(this._state.get("errors")).some(
|
|
1755
|
+
(key) => this._state.get("errors")[key]
|
|
1756
|
+
);
|
|
1757
|
+
this._state.set("isValid", !hasErrors);
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1760
|
+
var ListState = class {
|
|
1761
|
+
constructor(initialItems = [], options = {}) {
|
|
1762
|
+
this._state = createReactiveState({
|
|
1763
|
+
items: [...initialItems],
|
|
1764
|
+
loading: false,
|
|
1765
|
+
error: null,
|
|
1766
|
+
filters: {},
|
|
1767
|
+
sortBy: null,
|
|
1768
|
+
sortOrder: "asc",
|
|
1769
|
+
page: 1,
|
|
1770
|
+
pageSize: options.pageSize || 10
|
|
1771
|
+
}, options);
|
|
1772
|
+
this._options = options;
|
|
1773
|
+
}
|
|
1774
|
+
// OOP methods for list operations
|
|
1775
|
+
addItem(item) {
|
|
1776
|
+
this._state.set("items", [...this._state.get("items"), item]);
|
|
1777
|
+
}
|
|
1778
|
+
removeItem(indexOrPredicate) {
|
|
1779
|
+
const items = this._state.get("items");
|
|
1780
|
+
let newItems;
|
|
1781
|
+
if (typeof indexOrPredicate === "number") {
|
|
1782
|
+
newItems = items.filter((_, i) => i !== indexOrPredicate);
|
|
1783
|
+
} else {
|
|
1784
|
+
newItems = items.filter((item) => !indexOrPredicate(item));
|
|
1785
|
+
}
|
|
1786
|
+
this._state.set("items", newItems);
|
|
1787
|
+
}
|
|
1788
|
+
updateItem(indexOrPredicate, updates) {
|
|
1789
|
+
const items = this._state.get("items");
|
|
1790
|
+
const newItems = items.map((item, i) => {
|
|
1791
|
+
if (typeof indexOrPredicate === "number") {
|
|
1792
|
+
return i === indexOrPredicate ? { ...item, ...updates } : item;
|
|
1793
|
+
} else {
|
|
1794
|
+
return indexOrPredicate(item) ? { ...item, ...updates } : item;
|
|
1795
|
+
}
|
|
1796
|
+
});
|
|
1797
|
+
this._state.set("items", newItems);
|
|
1798
|
+
}
|
|
1799
|
+
filter(filters) {
|
|
1800
|
+
this._state.set("filters", filters);
|
|
1801
|
+
this._state.set("page", 1);
|
|
1802
|
+
}
|
|
1803
|
+
sort(sortBy, order = "asc") {
|
|
1804
|
+
this._state.set("sortBy", sortBy);
|
|
1805
|
+
this._state.set("sortOrder", order);
|
|
1806
|
+
}
|
|
1807
|
+
setPage(page) {
|
|
1808
|
+
this._state.set("page", Math.max(1, page));
|
|
1809
|
+
}
|
|
1810
|
+
async load(loader) {
|
|
1811
|
+
this._state.set("loading", true);
|
|
1812
|
+
this._state.set("error", null);
|
|
1813
|
+
try {
|
|
1814
|
+
const items = await loader(this._state.get("filters"));
|
|
1815
|
+
this._state.set("items", items);
|
|
1816
|
+
return items;
|
|
1817
|
+
} catch (error) {
|
|
1818
|
+
this._state.set("error", error.message);
|
|
1819
|
+
return [];
|
|
1820
|
+
} finally {
|
|
1821
|
+
this._state.set("loading", false);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
// Computed properties
|
|
1825
|
+
get filteredItems() {
|
|
1826
|
+
const items = this._state.get("items");
|
|
1827
|
+
const filters = this._state.get("filters");
|
|
1828
|
+
return items.filter((item) => {
|
|
1829
|
+
return Object.entries(filters).every(([key, value]) => {
|
|
1830
|
+
if (!value) return true;
|
|
1831
|
+
return String(item[key] || "").toLowerCase().includes(String(value).toLowerCase());
|
|
1832
|
+
});
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
get sortedItems() {
|
|
1836
|
+
const items = this.filteredItems;
|
|
1837
|
+
const sortBy = this._state.get("sortBy");
|
|
1838
|
+
const sortOrder = this._state.get("sortOrder");
|
|
1839
|
+
if (!sortBy) return items;
|
|
1840
|
+
return [...items].sort((a, b) => {
|
|
1841
|
+
const aVal = a[sortBy];
|
|
1842
|
+
const bVal = b[sortBy];
|
|
1843
|
+
if (aVal === bVal) return 0;
|
|
1844
|
+
const comparison = aVal < bVal ? -1 : 1;
|
|
1845
|
+
return sortOrder === "desc" ? -comparison : comparison;
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
get paginatedItems() {
|
|
1849
|
+
const items = this.sortedItems;
|
|
1850
|
+
const page = this._state.get("page");
|
|
1851
|
+
const pageSize = this._state.get("pageSize");
|
|
1852
|
+
const start = (page - 1) * pageSize;
|
|
1853
|
+
const end = start + pageSize;
|
|
1854
|
+
return items.slice(start, end);
|
|
1855
|
+
}
|
|
1856
|
+
get totalPages() {
|
|
1857
|
+
return Math.ceil(this.sortedItems.length / this._state.get("pageSize"));
|
|
1858
|
+
}
|
|
1859
|
+
// Watch methods
|
|
1860
|
+
watchItems(callback) {
|
|
1861
|
+
return this._state.watch("items", callback);
|
|
1862
|
+
}
|
|
1863
|
+
watchLoading(callback) {
|
|
1864
|
+
return this._state.watch("loading", callback);
|
|
1865
|
+
}
|
|
1866
|
+
};
|
|
1867
|
+
var ModalState = class {
|
|
1868
|
+
constructor(_initialState = {}) {
|
|
1869
|
+
this._state = createReactiveState({
|
|
1870
|
+
isOpen: false,
|
|
1871
|
+
data: null,
|
|
1872
|
+
loading: false,
|
|
1873
|
+
error: null
|
|
1874
|
+
});
|
|
1875
|
+
this._resolvers = /* @__PURE__ */ new Map();
|
|
1876
|
+
this._currentId = 0;
|
|
1877
|
+
}
|
|
1878
|
+
// OOP methods for modal control
|
|
1879
|
+
async open(data) {
|
|
1880
|
+
return new Promise((resolve) => {
|
|
1881
|
+
const id = ++this._currentId;
|
|
1882
|
+
this._resolvers.set(id, resolve);
|
|
1883
|
+
this._state.set("data", data);
|
|
1884
|
+
this._state.set("isOpen", true);
|
|
1885
|
+
this._state.set("error", null);
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
close(result = null) {
|
|
1889
|
+
const currentId = this._currentId;
|
|
1890
|
+
const resolver = this._resolvers.get(currentId);
|
|
1891
|
+
if (resolver) {
|
|
1892
|
+
resolver(result);
|
|
1893
|
+
this._resolvers.delete(currentId);
|
|
1894
|
+
}
|
|
1895
|
+
this._state.set("isOpen", false);
|
|
1896
|
+
this._state.set("data", null);
|
|
1897
|
+
}
|
|
1898
|
+
setLoading(loading) {
|
|
1899
|
+
this._state.set("loading", loading);
|
|
1900
|
+
}
|
|
1901
|
+
setError(error) {
|
|
1902
|
+
this._state.set("error", error);
|
|
1903
|
+
}
|
|
1904
|
+
// Watch methods
|
|
1905
|
+
watchOpen(callback) {
|
|
1906
|
+
return this._state.watch("isOpen", callback);
|
|
1907
|
+
}
|
|
1908
|
+
watchData(callback) {
|
|
1909
|
+
return this._state.watch("data", callback);
|
|
1910
|
+
}
|
|
1911
|
+
};
|
|
1912
|
+
var RouterState = class {
|
|
1913
|
+
constructor(initialRoute = "/", options = {}) {
|
|
1914
|
+
this._state = createReactiveState({
|
|
1915
|
+
current: initialRoute,
|
|
1916
|
+
params: {},
|
|
1917
|
+
query: {},
|
|
1918
|
+
history: [initialRoute],
|
|
1919
|
+
canGoBack: false,
|
|
1920
|
+
canGoForward: false
|
|
1921
|
+
}, options);
|
|
1922
|
+
this._routes = /* @__PURE__ */ new Map();
|
|
1923
|
+
this._options = options;
|
|
1924
|
+
}
|
|
1925
|
+
// OOP methods for routing
|
|
1926
|
+
addRoute(path, handler) {
|
|
1927
|
+
this._routes.set(path, handler);
|
|
1928
|
+
}
|
|
1929
|
+
navigate(path, params = {}, query = {}) {
|
|
1930
|
+
this._state.set("history", [...this._state.get("history"), path]);
|
|
1931
|
+
this._state.set("current", path);
|
|
1932
|
+
this._state.set("params", params);
|
|
1933
|
+
this._state.set("query", query);
|
|
1934
|
+
this._updateNavigationState();
|
|
1935
|
+
}
|
|
1936
|
+
back() {
|
|
1937
|
+
const history = this._state.get("history");
|
|
1938
|
+
if (history.length > 1) {
|
|
1939
|
+
const newHistory = history.slice(0, -1);
|
|
1940
|
+
const previousRoute = newHistory[newHistory.length - 1];
|
|
1941
|
+
this._state.set("history", newHistory);
|
|
1942
|
+
this._state.set("current", previousRoute);
|
|
1943
|
+
this._updateNavigationState();
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
forward() {
|
|
1947
|
+
}
|
|
1948
|
+
// Watch methods
|
|
1949
|
+
watchRoute(callback) {
|
|
1950
|
+
return this._state.watch("current", callback);
|
|
1951
|
+
}
|
|
1952
|
+
watchParams(callback) {
|
|
1953
|
+
return this._state.watch("params", callback);
|
|
1954
|
+
}
|
|
1955
|
+
// Private methods
|
|
1956
|
+
_updateNavigationState() {
|
|
1957
|
+
const history = this._state.get("history");
|
|
1958
|
+
this._state.set("canGoBack", history.length > 1);
|
|
1959
|
+
this._state.set("canGoForward", false);
|
|
1960
|
+
}
|
|
1961
|
+
};
|
|
1962
|
+
function createFormState(initialValues, options) {
|
|
1963
|
+
return new FormState(initialValues, options);
|
|
1964
|
+
}
|
|
1965
|
+
function createListState(initialItems, options) {
|
|
1966
|
+
return new ListState(initialItems, options);
|
|
1967
|
+
}
|
|
1968
|
+
function createModalState(initialState) {
|
|
1969
|
+
return new ModalState(initialState);
|
|
1970
|
+
}
|
|
1971
|
+
function createRouterState(initialRoute, options) {
|
|
1972
|
+
return new RouterState(initialRoute, options);
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1649
1975
|
// src/index.js
|
|
1650
1976
|
var index_default = {
|
|
1651
1977
|
// Reactive state utilities
|
|
@@ -1668,6 +1994,11 @@ var index_default = {
|
|
|
1668
1994
|
// Validation utilities
|
|
1669
1995
|
createValidatedState,
|
|
1670
1996
|
validators,
|
|
1997
|
+
// Enhanced state patterns
|
|
1998
|
+
createFormState,
|
|
1999
|
+
createListState,
|
|
2000
|
+
createModalState,
|
|
2001
|
+
createRouterState,
|
|
1671
2002
|
// State utilities
|
|
1672
2003
|
stateUtils
|
|
1673
2004
|
};
|
|
@@ -1677,8 +2008,12 @@ export {
|
|
|
1677
2008
|
clearAllContexts,
|
|
1678
2009
|
computed,
|
|
1679
2010
|
createContextProvider,
|
|
2011
|
+
createFormState,
|
|
2012
|
+
createListState,
|
|
2013
|
+
createModalState,
|
|
1680
2014
|
createPersistentState,
|
|
1681
2015
|
createReactiveState,
|
|
2016
|
+
createRouterState,
|
|
1682
2017
|
createState,
|
|
1683
2018
|
createValidatedState,
|
|
1684
2019
|
index_default as default,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coherent.js/state",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.6",
|
|
4
4
|
"description": "Reactive state management for Coherent.js applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"@coherent.js/core": "1.0.0-beta.
|
|
32
|
+
"@coherent.js/core": "1.0.0-beta.6"
|
|
33
33
|
},
|
|
34
34
|
"types": "./types/index.d.ts",
|
|
35
35
|
"files": [
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"README.md",
|
|
38
38
|
"types/"
|
|
39
39
|
],
|
|
40
|
+
"sideEffects": false,
|
|
40
41
|
"scripts": {
|
|
41
42
|
"build": "node build.mjs",
|
|
42
43
|
"clean": "rm -rf dist"
|
package/types/index.d.ts
CHANGED
|
@@ -3,138 +3,463 @@
|
|
|
3
3
|
* @module @coherent.js/state
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import type { CoherentNode, ComponentState } from '@coherent.js/core';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Core Store Types
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Typed state store with subscribe and update capabilities
|
|
14
|
+
* @template T - The shape of the state object
|
|
15
|
+
*/
|
|
16
|
+
export interface Store<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
17
|
+
/**
|
|
18
|
+
* Get the current state
|
|
19
|
+
*/
|
|
20
|
+
getState(): T;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Update state with partial object or updater function
|
|
24
|
+
*/
|
|
25
|
+
setState(partial: Partial<T> | ((state: T) => Partial<T>)): void;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Subscribe to state changes
|
|
29
|
+
* @returns Unsubscribe function
|
|
30
|
+
*/
|
|
31
|
+
subscribe(listener: (state: T, prevState: T) => void): () => void;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Destroy the store and clean up subscriptions
|
|
35
|
+
*/
|
|
36
|
+
destroy(): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Store creation options
|
|
41
|
+
* @template T - The shape of the state object
|
|
42
|
+
*/
|
|
43
|
+
export interface StoreOptions<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
44
|
+
/** Initial state */
|
|
45
|
+
initialState: T;
|
|
46
|
+
/** Persistence configuration */
|
|
47
|
+
persist?: {
|
|
48
|
+
/** Storage key */
|
|
49
|
+
key: string;
|
|
50
|
+
/** Storage adapter (localStorage, sessionStorage, or custom) */
|
|
51
|
+
storage?: Storage;
|
|
52
|
+
/** Custom serialization */
|
|
53
|
+
serialize?: (state: T) => string;
|
|
54
|
+
/** Custom deserialization */
|
|
55
|
+
deserialize?: (value: string) => T;
|
|
56
|
+
/** Debounce persistence writes (ms) */
|
|
57
|
+
debounce?: number;
|
|
58
|
+
};
|
|
59
|
+
/** Enable devtools integration */
|
|
60
|
+
devtools?: boolean;
|
|
61
|
+
/** Store name for debugging */
|
|
62
|
+
name?: string;
|
|
63
|
+
/** Middleware functions */
|
|
64
|
+
middleware?: Array<StoreMiddleware<T>>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Store middleware function
|
|
69
|
+
*/
|
|
70
|
+
export type StoreMiddleware<T> = (
|
|
71
|
+
state: T,
|
|
72
|
+
action: string,
|
|
73
|
+
payload?: unknown
|
|
74
|
+
) => T | void;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Create a typed state store
|
|
78
|
+
*/
|
|
79
|
+
export function createStore<T extends Record<string, unknown>>(
|
|
80
|
+
options: StoreOptions<T>
|
|
81
|
+
): Store<T>;
|
|
82
|
+
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Selector Types
|
|
85
|
+
// ============================================================================
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Create a derived selector from store state
|
|
89
|
+
* @template T - Store state type
|
|
90
|
+
* @template R - Return type of selector
|
|
91
|
+
*/
|
|
92
|
+
export function createSelector<T extends Record<string, unknown>, R>(
|
|
93
|
+
store: Store<T>,
|
|
94
|
+
selector: (state: T) => R
|
|
95
|
+
): () => R;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Create a memoized selector with dependencies
|
|
99
|
+
*/
|
|
100
|
+
export function createMemoizedSelector<T extends Record<string, unknown>, D extends unknown[], R>(
|
|
101
|
+
store: Store<T>,
|
|
102
|
+
dependencies: (...args: D) => unknown[],
|
|
103
|
+
selector: (state: T, ...deps: D) => R
|
|
104
|
+
): (...args: D) => R;
|
|
105
|
+
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// Action Types
|
|
108
|
+
// ============================================================================
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Action type helper for type-safe actions
|
|
112
|
+
* @template T - Store state type
|
|
113
|
+
* @template P - Payload type (void for no payload)
|
|
114
|
+
*/
|
|
115
|
+
export type Action<T, P = void> = P extends void
|
|
116
|
+
? () => Partial<T>
|
|
117
|
+
: (payload: P) => Partial<T>;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Create typed action creators
|
|
121
|
+
*/
|
|
122
|
+
export function createActions<
|
|
123
|
+
T extends Record<string, unknown>,
|
|
124
|
+
A extends Record<string, Action<T, unknown>>
|
|
125
|
+
>(
|
|
126
|
+
store: Store<T>,
|
|
127
|
+
actions: A
|
|
128
|
+
): {
|
|
129
|
+
[K in keyof A]: A[K] extends Action<T, infer P>
|
|
130
|
+
? P extends void
|
|
131
|
+
? () => void
|
|
132
|
+
: (payload: P) => void
|
|
133
|
+
: never;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// Reactive State Types
|
|
138
|
+
// ============================================================================
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Observer callback type
|
|
142
|
+
*/
|
|
143
|
+
export interface Observer<T = unknown> {
|
|
9
144
|
(value: T, oldValue: T): void;
|
|
10
145
|
}
|
|
11
146
|
|
|
12
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Observable value wrapper
|
|
149
|
+
*/
|
|
150
|
+
export class Observable<T = unknown> {
|
|
13
151
|
constructor(initialValue: T);
|
|
152
|
+
|
|
153
|
+
/** Get current value */
|
|
14
154
|
get(): T;
|
|
155
|
+
|
|
156
|
+
/** Set new value */
|
|
15
157
|
set(value: T): void;
|
|
158
|
+
|
|
159
|
+
/** Subscribe to changes */
|
|
16
160
|
subscribe(observer: Observer<T>): () => void;
|
|
161
|
+
|
|
162
|
+
/** Unsubscribe observer */
|
|
17
163
|
unsubscribe(observer: Observer<T>): void;
|
|
164
|
+
|
|
165
|
+
/** Notify all observers */
|
|
18
166
|
notify(): void;
|
|
19
167
|
}
|
|
20
168
|
|
|
21
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Reactive state options
|
|
171
|
+
*/
|
|
172
|
+
export interface ReactiveStateOptions<T = unknown> {
|
|
173
|
+
/** Initial state value */
|
|
22
174
|
initialValue: T;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
175
|
+
/** Computed properties */
|
|
176
|
+
computed?: Record<string, (state: T) => unknown>;
|
|
177
|
+
/** Property watchers */
|
|
178
|
+
watchers?: Record<string, Observer<unknown>>;
|
|
179
|
+
/** Middleware functions */
|
|
180
|
+
middleware?: Array<(state: T, action: string, payload?: unknown) => T | void>;
|
|
26
181
|
}
|
|
27
182
|
|
|
28
|
-
|
|
183
|
+
/**
|
|
184
|
+
* Reactive state class with computed properties and watchers
|
|
185
|
+
*/
|
|
186
|
+
export class ReactiveState<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
29
187
|
constructor(options: ReactiveStateOptions<T>);
|
|
188
|
+
|
|
189
|
+
/** Get a property value */
|
|
30
190
|
get<K extends keyof T>(key: K): T[K];
|
|
191
|
+
|
|
192
|
+
/** Set a property value */
|
|
31
193
|
set<K extends keyof T>(key: K, value: T[K]): void;
|
|
194
|
+
|
|
195
|
+
/** Update multiple properties */
|
|
32
196
|
update(partial: Partial<T>): void;
|
|
197
|
+
|
|
198
|
+
/** Subscribe to all changes */
|
|
33
199
|
subscribe(observer: Observer<T>): () => void;
|
|
200
|
+
|
|
201
|
+
/** Watch a specific property */
|
|
34
202
|
watch<K extends keyof T>(key: K, observer: Observer<T[K]>): () => void;
|
|
203
|
+
|
|
204
|
+
/** Get full state */
|
|
35
205
|
getState(): T;
|
|
206
|
+
|
|
207
|
+
/** Reset to initial state */
|
|
36
208
|
reset(): void;
|
|
37
209
|
}
|
|
38
210
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Create a reactive state instance
|
|
213
|
+
*/
|
|
214
|
+
export function createReactiveState<T extends Record<string, unknown> = Record<string, unknown>>(
|
|
215
|
+
options: ReactiveStateOptions<T>
|
|
216
|
+
): ReactiveState<T>;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Create a simple observable value
|
|
220
|
+
*/
|
|
221
|
+
export function observable<T = unknown>(initialValue: T): Observable<T>;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Create a computed observable
|
|
225
|
+
*/
|
|
226
|
+
export function computed<T = unknown>(
|
|
227
|
+
fn: () => T,
|
|
228
|
+
dependencies: Observable<unknown>[]
|
|
229
|
+
): Observable<T>;
|
|
42
230
|
|
|
231
|
+
/**
|
|
232
|
+
* State utility functions
|
|
233
|
+
*/
|
|
43
234
|
export const stateUtils: {
|
|
235
|
+
/** Batch multiple state updates */
|
|
44
236
|
batch<T>(fn: () => T): T;
|
|
237
|
+
/** Run updates in a transaction */
|
|
45
238
|
transaction<T>(fn: () => T): T;
|
|
239
|
+
/** Freeze state (make immutable) */
|
|
46
240
|
freeze<T>(state: T): Readonly<T>;
|
|
241
|
+
/** Deep clone state */
|
|
47
242
|
clone<T>(state: T): T;
|
|
48
243
|
};
|
|
49
244
|
|
|
50
|
-
//
|
|
245
|
+
// ============================================================================
|
|
246
|
+
// SSR-Compatible State Manager
|
|
247
|
+
// ============================================================================
|
|
51
248
|
|
|
52
|
-
|
|
249
|
+
/**
|
|
250
|
+
* State manager options
|
|
251
|
+
*/
|
|
252
|
+
export interface StateManagerOptions<T = unknown> {
|
|
253
|
+
/** Initial state */
|
|
53
254
|
initialState?: T;
|
|
255
|
+
/** Enable persistence */
|
|
54
256
|
persist?: boolean;
|
|
257
|
+
/** Persistence key */
|
|
55
258
|
key?: string;
|
|
259
|
+
/** Middleware functions */
|
|
56
260
|
middleware?: Array<(state: T, action: string) => T | void>;
|
|
57
261
|
}
|
|
58
262
|
|
|
59
|
-
|
|
263
|
+
/**
|
|
264
|
+
* Simple state interface
|
|
265
|
+
*/
|
|
266
|
+
export interface State<T = unknown> {
|
|
267
|
+
/** Get current state */
|
|
60
268
|
get(): T;
|
|
269
|
+
/** Set new state */
|
|
61
270
|
set(value: T): void;
|
|
271
|
+
/** Update with partial */
|
|
62
272
|
update(partial: Partial<T>): void;
|
|
273
|
+
/** Subscribe to changes */
|
|
63
274
|
subscribe(listener: (state: T) => void): () => void;
|
|
275
|
+
/** Reset to initial state */
|
|
64
276
|
reset(): void;
|
|
65
277
|
}
|
|
66
278
|
|
|
67
|
-
|
|
279
|
+
/**
|
|
280
|
+
* Create a simple state container
|
|
281
|
+
*/
|
|
282
|
+
export function createState<T = unknown>(
|
|
283
|
+
initialState: T,
|
|
284
|
+
options?: StateManagerOptions<T>
|
|
285
|
+
): State<T>;
|
|
68
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Global state manager for SSR
|
|
289
|
+
*/
|
|
69
290
|
export const globalStateManager: {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
291
|
+
/** Get state by key */
|
|
292
|
+
getState<T = unknown>(key: string): T | undefined;
|
|
293
|
+
/** Set state by key */
|
|
294
|
+
setState<T = unknown>(key: string, value: T): void;
|
|
295
|
+
/** Subscribe to state key */
|
|
296
|
+
subscribe<T = unknown>(key: string, listener: (state: T) => void): () => void;
|
|
297
|
+
/** Clear state (optionally by key) */
|
|
73
298
|
clear(key?: string): void;
|
|
74
299
|
};
|
|
75
300
|
|
|
76
|
-
//
|
|
301
|
+
// ============================================================================
|
|
302
|
+
// Context API
|
|
303
|
+
// ============================================================================
|
|
77
304
|
|
|
78
|
-
|
|
305
|
+
/**
|
|
306
|
+
* Context value wrapper
|
|
307
|
+
*/
|
|
308
|
+
export interface ContextValue<T = unknown> {
|
|
79
309
|
value: T;
|
|
80
310
|
subscribers: Set<(value: T) => void>;
|
|
81
311
|
}
|
|
82
312
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
export function
|
|
313
|
+
/**
|
|
314
|
+
* Provide a context value
|
|
315
|
+
*/
|
|
316
|
+
export function provideContext<T = unknown>(key: string, value: T): void;
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Create a context provider
|
|
320
|
+
*/
|
|
321
|
+
export function createContextProvider<T = unknown>(
|
|
322
|
+
key: string,
|
|
323
|
+
value: T
|
|
324
|
+
): { key: string; value: T };
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Use/consume a context value
|
|
328
|
+
*/
|
|
329
|
+
export function useContext<T = unknown>(key: string, defaultValue?: T): T;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Restore context from saved state
|
|
333
|
+
*/
|
|
334
|
+
export function restoreContext(contexts: Record<string, unknown>): void;
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Clear all context stacks
|
|
338
|
+
*/
|
|
87
339
|
export function clearAllContexts(): void;
|
|
88
340
|
|
|
89
|
-
//
|
|
341
|
+
// ============================================================================
|
|
342
|
+
// Persistent State
|
|
343
|
+
// ============================================================================
|
|
90
344
|
|
|
345
|
+
/**
|
|
346
|
+
* Persistence adapter interface
|
|
347
|
+
*/
|
|
91
348
|
export interface PersistenceAdapter {
|
|
349
|
+
/** Get item from storage */
|
|
92
350
|
getItem(key: string): Promise<string | null> | string | null;
|
|
351
|
+
/** Set item in storage */
|
|
93
352
|
setItem(key: string, value: string): Promise<void> | void;
|
|
353
|
+
/** Remove item from storage */
|
|
94
354
|
removeItem(key: string): Promise<void> | void;
|
|
95
355
|
}
|
|
96
356
|
|
|
97
|
-
|
|
357
|
+
/**
|
|
358
|
+
* Persistent state options
|
|
359
|
+
*/
|
|
360
|
+
export interface PersistentStateOptions<T = unknown> extends StateManagerOptions<T> {
|
|
361
|
+
/** Required: storage key */
|
|
98
362
|
key: string;
|
|
363
|
+
/** Storage adapter */
|
|
99
364
|
storage?: PersistenceAdapter;
|
|
365
|
+
/** Custom serialization */
|
|
100
366
|
serialize?: (state: T) => string;
|
|
367
|
+
/** Custom deserialization */
|
|
101
368
|
deserialize?: (data: string) => T;
|
|
369
|
+
/** Debounce writes (ms) */
|
|
102
370
|
debounce?: number;
|
|
103
371
|
}
|
|
104
372
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
export function
|
|
373
|
+
/**
|
|
374
|
+
* Create a persistent state
|
|
375
|
+
*/
|
|
376
|
+
export function createPersistentState<T = unknown>(
|
|
377
|
+
options: PersistentStateOptions<T>
|
|
378
|
+
): State<T>;
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Wrap state with localStorage persistence
|
|
382
|
+
*/
|
|
383
|
+
export function withLocalStorage<T = unknown>(state: State<T>, key: string): State<T>;
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Wrap state with sessionStorage persistence
|
|
387
|
+
*/
|
|
388
|
+
export function withSessionStorage<T = unknown>(state: State<T>, key: string): State<T>;
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Wrap state with IndexedDB persistence
|
|
392
|
+
*/
|
|
393
|
+
export function withIndexedDB<T = unknown>(
|
|
394
|
+
state: State<T>,
|
|
395
|
+
key: string,
|
|
396
|
+
dbName?: string
|
|
397
|
+
): Promise<State<T>>;
|
|
109
398
|
|
|
110
|
-
//
|
|
399
|
+
// ============================================================================
|
|
400
|
+
// Validated State
|
|
401
|
+
// ============================================================================
|
|
111
402
|
|
|
112
|
-
|
|
403
|
+
/**
|
|
404
|
+
* Validation rule function
|
|
405
|
+
*/
|
|
406
|
+
export interface ValidationRule<T = unknown> {
|
|
113
407
|
(value: T): boolean | string;
|
|
114
408
|
}
|
|
115
409
|
|
|
116
|
-
|
|
117
|
-
|
|
410
|
+
/**
|
|
411
|
+
* Validated state options
|
|
412
|
+
*/
|
|
413
|
+
export interface ValidatedStateOptions<T extends Record<string, unknown> = Record<string, unknown>>
|
|
414
|
+
extends StateManagerOptions<T> {
|
|
415
|
+
/** Validation rules by property */
|
|
416
|
+
validators: { [K in keyof T]?: ValidationRule<T[K]>[] };
|
|
417
|
+
/** Validate on every change */
|
|
118
418
|
validateOnChange?: boolean;
|
|
419
|
+
/** Throw on validation failure */
|
|
119
420
|
strict?: boolean;
|
|
120
421
|
}
|
|
121
422
|
|
|
122
|
-
|
|
123
|
-
|
|
423
|
+
/**
|
|
424
|
+
* Validated state interface
|
|
425
|
+
*/
|
|
426
|
+
export interface ValidatedState<T extends Record<string, unknown> = Record<string, unknown>>
|
|
427
|
+
extends State<T> {
|
|
428
|
+
/** Validate current state */
|
|
429
|
+
validate(): { valid: boolean; errors: { [K in keyof T]?: string[] } };
|
|
430
|
+
/** Check if state is valid */
|
|
124
431
|
isValid(): boolean;
|
|
125
|
-
|
|
432
|
+
/** Get validation errors */
|
|
433
|
+
getErrors(): { [K in keyof T]?: string[] };
|
|
126
434
|
}
|
|
127
435
|
|
|
128
|
-
|
|
436
|
+
/**
|
|
437
|
+
* Create a validated state
|
|
438
|
+
*/
|
|
439
|
+
export function createValidatedState<T extends Record<string, unknown> = Record<string, unknown>>(
|
|
440
|
+
options: ValidatedStateOptions<T>
|
|
441
|
+
): ValidatedState<T>;
|
|
129
442
|
|
|
443
|
+
/**
|
|
444
|
+
* Built-in validators
|
|
445
|
+
*/
|
|
130
446
|
export const validators: {
|
|
447
|
+
/** Required value */
|
|
131
448
|
required(message?: string): ValidationRule;
|
|
449
|
+
/** Minimum length */
|
|
132
450
|
minLength(length: number, message?: string): ValidationRule;
|
|
451
|
+
/** Maximum length */
|
|
133
452
|
maxLength(length: number, message?: string): ValidationRule;
|
|
453
|
+
/** Minimum value */
|
|
134
454
|
min(value: number, message?: string): ValidationRule;
|
|
455
|
+
/** Maximum value */
|
|
135
456
|
max(value: number, message?: string): ValidationRule;
|
|
457
|
+
/** Pattern matching */
|
|
136
458
|
pattern(regex: RegExp, message?: string): ValidationRule;
|
|
459
|
+
/** Email format */
|
|
137
460
|
email(message?: string): ValidationRule;
|
|
461
|
+
/** URL format */
|
|
138
462
|
url(message?: string): ValidationRule;
|
|
139
|
-
|
|
463
|
+
/** Custom validator */
|
|
464
|
+
custom(fn: (value: unknown) => boolean | string): ValidationRule;
|
|
140
465
|
};
|