dharma-core 0.3.0 → 0.5.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.
- package/README.md +32 -1
- package/dist/cjs/lib/createPersistentStore.d.ts +23 -24
- package/dist/cjs/lib/createPersistentStore.js +42 -48
- package/dist/cjs/lib/createPersistentStore.js.map +1 -1
- package/dist/cjs/lib/createStore.d.ts +37 -16
- package/dist/cjs/lib/createStore.js +25 -16
- package/dist/cjs/lib/createStore.js.map +1 -1
- package/dist/cjs/lib/merge.d.ts +6 -7
- package/dist/cjs/lib/merge.js +6 -7
- package/dist/cjs/lib/merge.js.map +1 -1
- package/dist/esm/lib/createPersistentStore.d.ts +23 -24
- package/dist/esm/lib/createPersistentStore.js +42 -48
- package/dist/esm/lib/createPersistentStore.js.map +1 -1
- package/dist/esm/lib/createStore.d.ts +37 -16
- package/dist/esm/lib/createStore.js +25 -16
- package/dist/esm/lib/createStore.js.map +1 -1
- package/dist/esm/lib/merge.d.ts +6 -7
- package/dist/esm/lib/merge.js +6 -7
- package/dist/esm/lib/merge.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1 +1,32 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dharma-core
|
|
2
|
+
|
|
3
|
+
[](https://npmjs.com/package/dharma-core)
|
|
4
|
+
[](https://npmjs.com/package/dharma-core)
|
|
5
|
+
[](https://bundlephobia.com/package/dharma-core)
|
|
6
|
+
|
|
7
|
+
A simple and lightweight state management solution for JavaScript and TypeScript applications.
|
|
8
|
+
|
|
9
|
+
## Basic usage
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { createStore } from "dharma-core";
|
|
13
|
+
|
|
14
|
+
const store = createStore({
|
|
15
|
+
initialState: { count: 0 },
|
|
16
|
+
defineActions: ({ set }) => ({
|
|
17
|
+
increment: () => set((state) => ({ count: state.count + 1 })),
|
|
18
|
+
decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const { increment, decrement } = store.actions;
|
|
23
|
+
|
|
24
|
+
// Subscribe to state changes
|
|
25
|
+
const unsubscribe = store.subscribe((state) => console.log(state.count));
|
|
26
|
+
// Update the state
|
|
27
|
+
increment();
|
|
28
|
+
// { count: 1 }
|
|
29
|
+
decrement();
|
|
30
|
+
// { count: 0 }
|
|
31
|
+
unsubscribe();
|
|
32
|
+
```
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Store, StoreConfiguration } from "./createStore";
|
|
2
2
|
export type StorageAPI = {
|
|
3
3
|
getItem: (key: string) => string | null;
|
|
4
4
|
setItem: (key: string, value: string) => void;
|
|
@@ -8,9 +8,10 @@ export type Serializer<T = any> = {
|
|
|
8
8
|
stringify: (value: T) => string;
|
|
9
9
|
parse: (value: string) => T;
|
|
10
10
|
};
|
|
11
|
-
export type
|
|
12
|
-
|
|
13
|
-
storage
|
|
11
|
+
export type PersistentStoreConfiguration<TState extends object, TActions extends object> = StoreConfiguration<TState, TActions> & {
|
|
12
|
+
key: string;
|
|
13
|
+
/** The storage to use for persisting the state. Defaults to local storage if available. */
|
|
14
|
+
storage?: StorageAPI;
|
|
14
15
|
/** The serializer to use for storing the state. Defaults to JSON. */
|
|
15
16
|
serializer?: Serializer<TState>;
|
|
16
17
|
};
|
|
@@ -21,10 +22,7 @@ export type PersistentStoreOptions<TState extends object> = StoreOptions<TState>
|
|
|
21
22
|
* **Note:** The state needs to be serializable by whatever serializer you use. (JSON by default)
|
|
22
23
|
* If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
|
|
23
24
|
*
|
|
24
|
-
* @param {
|
|
25
|
-
* @param {TState} initialState - The initial state of the store.
|
|
26
|
-
* @param {DefineActions<TState, TActions>} [defineActions] - A function to define actions for the store.
|
|
27
|
-
* @param {PersistentStoreOptions<TState>} [options] - Additional options for the persistent store.
|
|
25
|
+
* @param {PersistentStoreConfiguration<TState, TActions>} config - The configuration for the persistent store.
|
|
28
26
|
*
|
|
29
27
|
* @returns {Store<TState, TActions>} The created store.
|
|
30
28
|
*
|
|
@@ -33,11 +31,15 @@ export type PersistentStoreOptions<TState extends object> = StoreOptions<TState>
|
|
|
33
31
|
* ```ts
|
|
34
32
|
* import { createPersistentStore } from "dharma-core";
|
|
35
33
|
*
|
|
36
|
-
* const store = createPersistentStore(
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* }))
|
|
34
|
+
* const store = createPersistentStore({
|
|
35
|
+
* key: "count",
|
|
36
|
+
* initialState: { count: 0 },
|
|
37
|
+
* defineActions: ({ set }) => ({
|
|
38
|
+
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
39
|
+
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
40
|
+
* reset: () => set({ count: 0 }),
|
|
41
|
+
* }),
|
|
42
|
+
* });
|
|
41
43
|
* ```
|
|
42
44
|
* @example
|
|
43
45
|
* With superjson serialization and session storage:
|
|
@@ -45,20 +47,17 @@ export type PersistentStoreOptions<TState extends object> = StoreOptions<TState>
|
|
|
45
47
|
* import { createPersistentStore } from "dharma-core";
|
|
46
48
|
* import superjson from "superjson";
|
|
47
49
|
*
|
|
48
|
-
* const store = createPersistentStore(
|
|
49
|
-
* "count",
|
|
50
|
-
* { count: 0 },
|
|
51
|
-
* (set) => ({
|
|
50
|
+
* const store = createPersistentStore({
|
|
51
|
+
* key: "count",
|
|
52
|
+
* initialState: { count: 0 },
|
|
53
|
+
* defineActions: ({ set }) => ({
|
|
52
54
|
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
53
55
|
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
54
56
|
* reset: () => set({ count: 0 }),
|
|
55
57
|
* }),
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* },
|
|
60
|
-
* );
|
|
58
|
+
* serializer: superjson,
|
|
59
|
+
* storage: sessionStorage,
|
|
60
|
+
* });
|
|
61
61
|
* ```
|
|
62
|
-
* @group Core
|
|
63
62
|
*/
|
|
64
|
-
export declare const createPersistentStore: <TState extends object, TActions extends object = Record<never, never>>(
|
|
63
|
+
export declare const createPersistentStore: <TState extends object, TActions extends object = Record<never, never>>(config: PersistentStoreConfiguration<TState, TActions>) => Store<TState, TActions>;
|
|
@@ -9,10 +9,7 @@ var createStore = require('./createStore.js');
|
|
|
9
9
|
* **Note:** The state needs to be serializable by whatever serializer you use. (JSON by default)
|
|
10
10
|
* If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
|
|
11
11
|
*
|
|
12
|
-
* @param {
|
|
13
|
-
* @param {TState} initialState - The initial state of the store.
|
|
14
|
-
* @param {DefineActions<TState, TActions>} [defineActions] - A function to define actions for the store.
|
|
15
|
-
* @param {PersistentStoreOptions<TState>} [options] - Additional options for the persistent store.
|
|
12
|
+
* @param {PersistentStoreConfiguration<TState, TActions>} config - The configuration for the persistent store.
|
|
16
13
|
*
|
|
17
14
|
* @returns {Store<TState, TActions>} The created store.
|
|
18
15
|
*
|
|
@@ -21,11 +18,15 @@ var createStore = require('./createStore.js');
|
|
|
21
18
|
* ```ts
|
|
22
19
|
* import { createPersistentStore } from "dharma-core";
|
|
23
20
|
*
|
|
24
|
-
* const store = createPersistentStore(
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* }))
|
|
21
|
+
* const store = createPersistentStore({
|
|
22
|
+
* key: "count",
|
|
23
|
+
* initialState: { count: 0 },
|
|
24
|
+
* defineActions: ({ set }) => ({
|
|
25
|
+
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
26
|
+
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
27
|
+
* reset: () => set({ count: 0 }),
|
|
28
|
+
* }),
|
|
29
|
+
* });
|
|
29
30
|
* ```
|
|
30
31
|
* @example
|
|
31
32
|
* With superjson serialization and session storage:
|
|
@@ -33,77 +34,70 @@ var createStore = require('./createStore.js');
|
|
|
33
34
|
* import { createPersistentStore } from "dharma-core";
|
|
34
35
|
* import superjson from "superjson";
|
|
35
36
|
*
|
|
36
|
-
* const store = createPersistentStore(
|
|
37
|
-
* "count",
|
|
38
|
-
* { count: 0 },
|
|
39
|
-
* (set) => ({
|
|
37
|
+
* const store = createPersistentStore({
|
|
38
|
+
* key: "count",
|
|
39
|
+
* initialState: { count: 0 },
|
|
40
|
+
* defineActions: ({ set }) => ({
|
|
40
41
|
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
41
42
|
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
42
43
|
* reset: () => set({ count: 0 }),
|
|
43
44
|
* }),
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* },
|
|
48
|
-
* );
|
|
45
|
+
* serializer: superjson,
|
|
46
|
+
* storage: sessionStorage,
|
|
47
|
+
* });
|
|
49
48
|
* ```
|
|
50
|
-
* @group Core
|
|
51
49
|
*/
|
|
52
|
-
const createPersistentStore = (
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
const createPersistentStore = (config) => {
|
|
51
|
+
const { key, initialState, storage: customStorage, serializer = JSON, onAttach, onDetach, onChange, ...rest } = config;
|
|
52
|
+
const IS_BROWSER = typeof window !== "undefined";
|
|
53
|
+
const storage = customStorage ?? (IS_BROWSER ? localStorage : undefined);
|
|
54
|
+
if (!storage) {
|
|
55
|
+
return createStore.createStore(config);
|
|
55
56
|
}
|
|
56
|
-
const storage = getStorage(_storage);
|
|
57
|
-
const stateKey = `store_${key}`;
|
|
58
57
|
const initialStateKey = `init_${key}`;
|
|
59
58
|
const initialStateSnapshot = storage.getItem(initialStateKey);
|
|
60
59
|
const initialStateString = serializer.stringify(initialState);
|
|
61
60
|
if (initialStateSnapshot !== initialStateString) {
|
|
62
61
|
storage.setItem(initialStateKey, initialStateString);
|
|
63
|
-
storage.removeItem(
|
|
62
|
+
storage.removeItem(key);
|
|
64
63
|
}
|
|
65
64
|
const updateSnapshot = (newState) => {
|
|
66
|
-
const currentSnapshot = storage.getItem(
|
|
65
|
+
const currentSnapshot = storage.getItem(key);
|
|
67
66
|
const newSnapshot = serializer.stringify(newState);
|
|
68
67
|
if (newSnapshot !== currentSnapshot) {
|
|
69
|
-
storage.setItem(
|
|
68
|
+
storage.setItem(key, newSnapshot);
|
|
70
69
|
}
|
|
71
70
|
};
|
|
72
71
|
const updateState = () => {
|
|
73
|
-
const currentSnapshot = storage.getItem(
|
|
72
|
+
const currentSnapshot = storage.getItem(key);
|
|
74
73
|
if (currentSnapshot &&
|
|
75
74
|
currentSnapshot !== serializer.stringify(store.get())) {
|
|
76
75
|
store.set(serializer.parse(currentSnapshot));
|
|
77
76
|
}
|
|
78
77
|
};
|
|
79
|
-
const store = createStore.createStore(
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
const store = createStore.createStore({
|
|
79
|
+
initialState,
|
|
80
|
+
onAttach: (ctx) => {
|
|
81
|
+
onAttach?.(ctx);
|
|
82
82
|
updateState();
|
|
83
|
-
|
|
83
|
+
if (IS_BROWSER) {
|
|
84
|
+
window.addEventListener("focus", updateState);
|
|
85
|
+
}
|
|
84
86
|
},
|
|
85
|
-
onDetach: (
|
|
86
|
-
onDetach?.(
|
|
87
|
-
|
|
87
|
+
onDetach: (ctx) => {
|
|
88
|
+
onDetach?.(ctx);
|
|
89
|
+
if (IS_BROWSER) {
|
|
90
|
+
window.removeEventListener("focus", updateState);
|
|
91
|
+
}
|
|
88
92
|
},
|
|
89
|
-
onChange: (state, ...
|
|
90
|
-
onChange?.(state, ...
|
|
93
|
+
onChange: ({ state, ...ctx }) => {
|
|
94
|
+
onChange?.({ state, ...ctx });
|
|
91
95
|
updateSnapshot(state);
|
|
92
96
|
},
|
|
93
|
-
...
|
|
97
|
+
...rest,
|
|
94
98
|
});
|
|
95
99
|
return store;
|
|
96
100
|
};
|
|
97
|
-
const getStorage = (storage) => {
|
|
98
|
-
switch (storage) {
|
|
99
|
-
case "local":
|
|
100
|
-
return localStorage;
|
|
101
|
-
case "session":
|
|
102
|
-
return sessionStorage;
|
|
103
|
-
default:
|
|
104
|
-
return storage;
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
101
|
|
|
108
102
|
exports.createPersistentStore = createPersistentStore;
|
|
109
103
|
//# sourceMappingURL=createPersistentStore.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createPersistentStore.js","sources":["../../../src/lib/createPersistentStore.ts"],"sourcesContent":["import { createStore,
|
|
1
|
+
{"version":3,"file":"createPersistentStore.js","sources":["../../../src/lib/createPersistentStore.ts"],"sourcesContent":["import { createStore, Store, StoreConfiguration } from \"./createStore\";\n\nexport type StorageAPI = {\n getItem: (key: string) => string | null;\n setItem: (key: string, value: string) => void;\n removeItem: (key: string) => void;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Serializer<T = any> = {\n stringify: (value: T) => string;\n parse: (value: string) => T;\n};\n\nexport type PersistentStoreConfiguration<\n TState extends object,\n TActions extends object,\n> = StoreConfiguration<TState, TActions> & {\n key: string;\n /** The storage to use for persisting the state. Defaults to local storage if available. */\n storage?: StorageAPI;\n /** The serializer to use for storing the state. Defaults to JSON. */\n serializer?: Serializer<TState>;\n};\n\n/**\n * Creates a store that persists its state in local or session storage.\n * Defaults to local storage but this can be changed in the options.\n *\n * **Note:** The state needs to be serializable by whatever serializer you use. (JSON by default)\n * If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).\n *\n * @param {PersistentStoreConfiguration<TState, TActions>} config - The configuration for the persistent store.\n *\n * @returns {Store<TState, TActions>} The created store.\n *\n * @example\n * Basic usage:\n * ```ts\n * import { createPersistentStore } from \"dharma-core\";\n *\n * const store = createPersistentStore({\n * key: \"count\",\n * initialState: { count: 0 },\n * defineActions: ({ set }) => ({\n * increment: () => set((state) => ({ count: state.count + 1 })),\n * decrement: () => set((state) => ({ count: state.count - 1 })),\n * reset: () => set({ count: 0 }),\n * }),\n * });\n * ```\n * @example\n * With superjson serialization and session storage:\n * ```ts\n * import { createPersistentStore } from \"dharma-core\";\n * import superjson from \"superjson\";\n *\n * const store = createPersistentStore({\n * key: \"count\",\n * initialState: { count: 0 },\n * defineActions: ({ set }) => ({\n * increment: () => set((state) => ({ count: state.count + 1 })),\n * decrement: () => set((state) => ({ count: state.count - 1 })),\n * reset: () => set({ count: 0 }),\n * }),\n * serializer: superjson,\n * storage: sessionStorage,\n * });\n * ```\n */\nexport const createPersistentStore = <\n TState extends object,\n TActions extends object = Record<never, never>,\n>(\n config: PersistentStoreConfiguration<TState, TActions>,\n): Store<TState, TActions> => {\n const {\n key,\n initialState,\n storage: customStorage,\n serializer = JSON,\n onAttach,\n onDetach,\n onChange,\n ...rest\n } = config;\n\n const IS_BROWSER = typeof window !== \"undefined\";\n const storage = customStorage ?? (IS_BROWSER ? localStorage : undefined);\n\n if (!storage) {\n return createStore(config);\n }\n\n const initialStateKey = `init_${key}`;\n\n const initialStateSnapshot = storage.getItem(initialStateKey);\n const initialStateString = serializer.stringify(initialState);\n\n if (initialStateSnapshot !== initialStateString) {\n storage.setItem(initialStateKey, initialStateString);\n storage.removeItem(key);\n }\n\n const updateSnapshot = (newState: TState) => {\n const currentSnapshot = storage.getItem(key);\n const newSnapshot = serializer.stringify(newState);\n\n if (newSnapshot !== currentSnapshot) {\n storage.setItem(key, newSnapshot);\n }\n };\n\n const updateState = () => {\n const currentSnapshot = storage.getItem(key);\n\n if (\n currentSnapshot &&\n currentSnapshot !== serializer.stringify(store.get())\n ) {\n store.set(serializer.parse(currentSnapshot));\n }\n };\n\n const store = createStore({\n initialState,\n onAttach: (ctx) => {\n onAttach?.(ctx);\n updateState();\n if (IS_BROWSER) {\n window.addEventListener(\"focus\", updateState);\n }\n },\n onDetach: (ctx) => {\n onDetach?.(ctx);\n if (IS_BROWSER) {\n window.removeEventListener(\"focus\", updateState);\n }\n },\n onChange: ({ state, ...ctx }) => {\n onChange?.({ state, ...ctx });\n updateSnapshot(state);\n },\n ...rest,\n });\n\n return store;\n};\n"],"names":["createStore"],"mappings":";;;;AAyBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CG;AACI,MAAM,qBAAqB,GAAG,CAInC,MAAsD,KAC3B;IAC3B,MAAM,EACJ,GAAG,EACH,YAAY,EACZ,OAAO,EAAE,aAAa,EACtB,UAAU,GAAG,IAAI,EACjB,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,GAAG,IAAI,EACR,GAAG,MAAM;AAEV,IAAA,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW;AAChD,IAAA,MAAM,OAAO,GAAG,aAAa,KAAK,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC;IAExE,IAAI,CAAC,OAAO,EAAE;AACZ,QAAA,OAAOA,uBAAW,CAAC,MAAM,CAAC;IAC5B;AAEA,IAAA,MAAM,eAAe,GAAG,CAAA,KAAA,EAAQ,GAAG,EAAE;IAErC,MAAM,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC;IAC7D,MAAM,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC;AAE7D,IAAA,IAAI,oBAAoB,KAAK,kBAAkB,EAAE;AAC/C,QAAA,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,kBAAkB,CAAC;AACpD,QAAA,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;IACzB;AAEA,IAAA,MAAM,cAAc,GAAG,CAAC,QAAgB,KAAI;QAC1C,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;QAC5C,MAAM,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,QAAQ,CAAC;AAElD,QAAA,IAAI,WAAW,KAAK,eAAe,EAAE;AACnC,YAAA,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC;QACnC;AACF,IAAA,CAAC;IAED,MAAM,WAAW,GAAG,MAAK;QACvB,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;AAE5C,QAAA,IACE,eAAe;YACf,eAAe,KAAK,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EACrD;YACA,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAC9C;AACF,IAAA,CAAC;IAED,MAAM,KAAK,GAAGA,uBAAW,CAAC;QACxB,YAAY;AACZ,QAAA,QAAQ,EAAE,CAAC,GAAG,KAAI;AAChB,YAAA,QAAQ,GAAG,GAAG,CAAC;AACf,YAAA,WAAW,EAAE;YACb,IAAI,UAAU,EAAE;AACd,gBAAA,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC;YAC/C;QACF,CAAC;AACD,QAAA,QAAQ,EAAE,CAAC,GAAG,KAAI;AAChB,YAAA,QAAQ,GAAG,GAAG,CAAC;YACf,IAAI,UAAU,EAAE;AACd,gBAAA,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC;YAClD;QACF,CAAC;QACD,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,GAAG,EAAE,KAAI;YAC9B,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,EAAE,CAAC;YAC7B,cAAc,CAAC,KAAK,CAAC;QACvB,CAAC;AACD,QAAA,GAAG,IAAI;AACR,KAAA,CAAC;AAEF,IAAA,OAAO,KAAK;AACd;;;;"}
|
|
@@ -1,19 +1,41 @@
|
|
|
1
|
+
type Listener<TState extends object> = (state: TState) => void;
|
|
2
|
+
export type SetState<TState extends object> = (stateModifier: StateModifier<TState>) => TState;
|
|
1
3
|
export type Store<TState extends object, TActions extends object> = {
|
|
2
4
|
/** Returns the current state of the store. */
|
|
3
5
|
get: () => TState;
|
|
4
6
|
/** Sets the state of the store. */
|
|
5
|
-
set:
|
|
7
|
+
set: SetState<TState>;
|
|
8
|
+
/** Resets the state of the store to its initial value. */
|
|
9
|
+
reset: () => TState;
|
|
6
10
|
/** Actions that can modify the state of the store. */
|
|
7
11
|
actions: TActions;
|
|
8
12
|
/** Subscribes to changes in the state of the store. Returns an unsubscribe function. */
|
|
9
13
|
subscribe: (listener: Listener<TState>) => () => void;
|
|
10
14
|
};
|
|
11
|
-
type
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
export type EventHandlerContext<TState extends object> = {
|
|
16
|
+
/** The current state of the store. */
|
|
17
|
+
state: TState;
|
|
18
|
+
/** Sets the state of the store. */
|
|
19
|
+
set: SetState<TState>;
|
|
20
|
+
/** Resets the state of the store to its initial value. */
|
|
21
|
+
reset: () => TState;
|
|
22
|
+
};
|
|
23
|
+
export type StoreEventHandler<TState extends object> = (context: EventHandlerContext<TState>) => void;
|
|
14
24
|
export type StateModifier<TState extends object> = Partial<TState> | ((state: TState) => Partial<TState>);
|
|
15
|
-
export type
|
|
16
|
-
|
|
25
|
+
export type StateFunctions<TState extends object> = {
|
|
26
|
+
/** Returns the current state of the store. */
|
|
27
|
+
get: () => TState;
|
|
28
|
+
/** Sets the state of the store. */
|
|
29
|
+
set: SetState<TState>;
|
|
30
|
+
/** Resets the state of the store to its initial value. */
|
|
31
|
+
reset: () => TState;
|
|
32
|
+
};
|
|
33
|
+
export type DefineActions<TState extends object, TActions> = (stateFunctions: StateFunctions<TState>) => TActions;
|
|
34
|
+
export type StoreConfiguration<TState extends object, TActions extends object> = {
|
|
35
|
+
/** The initial state of the store. */
|
|
36
|
+
initialState: TState;
|
|
37
|
+
/** A function that defines actions that can modify the state. */
|
|
38
|
+
defineActions?: DefineActions<TState, TActions>;
|
|
17
39
|
/** Invoked when the store is created. */
|
|
18
40
|
onLoad?: StoreEventHandler<TState>;
|
|
19
41
|
/** Invoked when the store is subscribed to. */
|
|
@@ -26,9 +48,7 @@ export type StoreOptions<TState extends object> = {
|
|
|
26
48
|
/**
|
|
27
49
|
* Creates a store with an initial state and actions that can modify the state.
|
|
28
50
|
*
|
|
29
|
-
* @param {TState}
|
|
30
|
-
* @param {DefineActions<TState, TActions>} [defineActions] - A function that defines actions that can modify the state.
|
|
31
|
-
* @param {StoreOptions<TState>} [options] - Additional options for the store.
|
|
51
|
+
* @param {StoreConfiguration<TState>} [config] - The configuration for the store.
|
|
32
52
|
*
|
|
33
53
|
* @returns {Store<TState, TActions>} The created store with state management methods.
|
|
34
54
|
*
|
|
@@ -36,13 +56,14 @@ export type StoreOptions<TState extends object> = {
|
|
|
36
56
|
* ```ts
|
|
37
57
|
* import { createStore } from "dharma-core";
|
|
38
58
|
*
|
|
39
|
-
* const store = createStore({
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* }))
|
|
59
|
+
* const store = createStore({
|
|
60
|
+
* initialState: { count: 0 },
|
|
61
|
+
* defineActions: ({ set }) => ({
|
|
62
|
+
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
63
|
+
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
64
|
+
* }),
|
|
65
|
+
* });
|
|
44
66
|
* ```
|
|
45
|
-
* @group Core
|
|
46
67
|
*/
|
|
47
|
-
export declare const createStore: <TState extends object, TActions extends object = Record<never, never>>(
|
|
68
|
+
export declare const createStore: <TState extends object, TActions extends object = Record<never, never>>(config: StoreConfiguration<TState, TActions>) => Store<TState, TActions>;
|
|
48
69
|
export {};
|
|
@@ -5,9 +5,7 @@ var merge = require('./merge.js');
|
|
|
5
5
|
/**
|
|
6
6
|
* Creates a store with an initial state and actions that can modify the state.
|
|
7
7
|
*
|
|
8
|
-
* @param {TState}
|
|
9
|
-
* @param {DefineActions<TState, TActions>} [defineActions] - A function that defines actions that can modify the state.
|
|
10
|
-
* @param {StoreOptions<TState>} [options] - Additional options for the store.
|
|
8
|
+
* @param {StoreConfiguration<TState>} [config] - The configuration for the store.
|
|
11
9
|
*
|
|
12
10
|
* @returns {Store<TState, TActions>} The created store with state management methods.
|
|
13
11
|
*
|
|
@@ -15,15 +13,17 @@ var merge = require('./merge.js');
|
|
|
15
13
|
* ```ts
|
|
16
14
|
* import { createStore } from "dharma-core";
|
|
17
15
|
*
|
|
18
|
-
* const store = createStore({
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* }))
|
|
16
|
+
* const store = createStore({
|
|
17
|
+
* initialState: { count: 0 },
|
|
18
|
+
* defineActions: ({ set }) => ({
|
|
19
|
+
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
20
|
+
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
21
|
+
* }),
|
|
22
|
+
* });
|
|
23
23
|
* ```
|
|
24
|
-
* @group Core
|
|
25
24
|
*/
|
|
26
|
-
const createStore = (
|
|
25
|
+
const createStore = (config) => {
|
|
26
|
+
const { initialState, defineActions, onLoad, onAttach, onDetach, onChange } = config;
|
|
27
27
|
let state = initialState;
|
|
28
28
|
const listeners = new Set();
|
|
29
29
|
const get = () => state;
|
|
@@ -31,8 +31,9 @@ const createStore = (initialState, defineActions, { onLoad, onAttach, onDetach,
|
|
|
31
31
|
state = merge.merge(state, stateModifier);
|
|
32
32
|
return state;
|
|
33
33
|
};
|
|
34
|
+
const resetSilently = () => setSilently(initialState);
|
|
34
35
|
const dispatch = () => {
|
|
35
|
-
onChange?.(state, setSilently);
|
|
36
|
+
onChange?.({ state, set: setSilently, reset: resetSilently });
|
|
36
37
|
listeners.forEach((listener) => listener(state));
|
|
37
38
|
};
|
|
38
39
|
const set = (stateModifier) => {
|
|
@@ -40,24 +41,32 @@ const createStore = (initialState, defineActions, { onLoad, onAttach, onDetach,
|
|
|
40
41
|
dispatch();
|
|
41
42
|
return state;
|
|
42
43
|
};
|
|
44
|
+
const reset = () => {
|
|
45
|
+
resetSilently();
|
|
46
|
+
dispatch();
|
|
47
|
+
return state;
|
|
48
|
+
};
|
|
43
49
|
const subscribe = (listener) => {
|
|
44
|
-
listener(state);
|
|
45
50
|
if (listeners.size === 0) {
|
|
46
|
-
onAttach?.(state, set);
|
|
51
|
+
onAttach?.({ state, set, reset });
|
|
47
52
|
}
|
|
53
|
+
listener(state);
|
|
48
54
|
listeners.add(listener);
|
|
49
55
|
return () => {
|
|
50
56
|
listeners.delete(listener);
|
|
51
57
|
if (listeners.size === 0) {
|
|
52
|
-
onDetach?.(state, set);
|
|
58
|
+
onDetach?.({ state, set, reset });
|
|
53
59
|
}
|
|
54
60
|
};
|
|
55
61
|
};
|
|
56
|
-
const actions = defineActions
|
|
57
|
-
|
|
62
|
+
const actions = defineActions
|
|
63
|
+
? defineActions({ set, get, reset })
|
|
64
|
+
: {};
|
|
65
|
+
onLoad?.({ state, set, reset });
|
|
58
66
|
return {
|
|
59
67
|
get,
|
|
60
68
|
set,
|
|
69
|
+
reset,
|
|
61
70
|
subscribe,
|
|
62
71
|
actions,
|
|
63
72
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { merge } from \"./merge\";\n\nexport type Store<TState extends object, TActions extends object> = {\n /** Returns the current state of the store. */\n get: () => TState;\n /** Sets the state of the store. */\n set:
|
|
1
|
+
{"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { merge } from \"./merge\";\n\ntype Listener<TState extends object> = (state: TState) => void;\n\nexport type SetState<TState extends object> = (\n stateModifier: StateModifier<TState>,\n) => TState;\n\nexport type Store<TState extends object, TActions extends object> = {\n /** Returns the current state of the store. */\n get: () => TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n /** Actions that can modify the state of the store. */\n actions: TActions;\n /** Subscribes to changes in the state of the store. Returns an unsubscribe function. */\n subscribe: (listener: Listener<TState>) => () => void;\n};\n\nexport type EventHandlerContext<TState extends object> = {\n /** The current state of the store. */\n state: TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n};\n\nexport type StoreEventHandler<TState extends object> = (\n context: EventHandlerContext<TState>,\n) => void;\n\nexport type StateModifier<TState extends object> =\n | Partial<TState>\n | ((state: TState) => Partial<TState>);\n\nexport type StateFunctions<TState extends object> = {\n /** Returns the current state of the store. */\n get: () => TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n};\n\nexport type DefineActions<TState extends object, TActions> = (\n stateFunctions: StateFunctions<TState>,\n) => TActions;\n\nexport type StoreConfiguration<\n TState extends object,\n TActions extends object,\n> = {\n /** The initial state of the store. */\n initialState: TState;\n /** A function that defines actions that can modify the state. */\n defineActions?: DefineActions<TState, TActions>;\n /** Invoked when the store is created. */\n onLoad?: StoreEventHandler<TState>;\n /** Invoked when the store is subscribed to. */\n onAttach?: StoreEventHandler<TState>;\n /** Invoked when the store is unsubscribed from. */\n onDetach?: StoreEventHandler<TState>;\n /** Invoked whenever the state changes. */\n onChange?: StoreEventHandler<TState>;\n};\n\n/**\n * Creates a store with an initial state and actions that can modify the state.\n *\n * @param {StoreConfiguration<TState>} [config] - The configuration for the store.\n *\n * @returns {Store<TState, TActions>} The created store with state management methods.\n *\n * @example\n * ```ts\n * import { createStore } from \"dharma-core\";\n *\n * const store = createStore({\n * initialState: { count: 0 },\n * defineActions: ({ set }) => ({\n * increment: () => set((state) => ({ count: state.count + 1 })),\n * decrement: () => set((state) => ({ count: state.count - 1 })),\n * }),\n * });\n * ```\n */\nexport const createStore = <\n TState extends object,\n TActions extends object = Record<never, never>,\n>(\n config: StoreConfiguration<TState, TActions>,\n): Store<TState, TActions> => {\n const { initialState, defineActions, onLoad, onAttach, onDetach, onChange } =\n config;\n\n let state = initialState;\n const listeners = new Set<Listener<TState>>();\n\n const get = () => state;\n\n const setSilently = (stateModifier: StateModifier<TState>) => {\n state = merge(state, stateModifier);\n return state;\n };\n\n const resetSilently = () => setSilently(initialState);\n\n const dispatch = () => {\n onChange?.({ state, set: setSilently, reset: resetSilently });\n listeners.forEach((listener) => listener(state));\n };\n\n const set = (stateModifier: StateModifier<TState>) => {\n setSilently(stateModifier);\n dispatch();\n return state;\n };\n\n const reset = () => {\n resetSilently();\n dispatch();\n return state;\n };\n\n const subscribe = (listener: Listener<TState>) => {\n if (listeners.size === 0) {\n onAttach?.({ state, set, reset });\n }\n\n listener(state);\n listeners.add(listener);\n\n return () => {\n listeners.delete(listener);\n\n if (listeners.size === 0) {\n onDetach?.({ state, set, reset });\n }\n };\n };\n\n const actions = defineActions\n ? defineActions({ set, get, reset })\n : ({} as TActions);\n\n onLoad?.({ state, set, reset });\n\n return {\n get,\n set,\n reset,\n subscribe,\n actions,\n };\n};\n"],"names":["merge"],"mappings":";;;;AAqEA;;;;;;;;;;;;;;;;;;;AAmBG;AACI,MAAM,WAAW,GAAG,CAIzB,MAA4C,KACjB;AAC3B,IAAA,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,GACzE,MAAM;IAER,IAAI,KAAK,GAAG,YAAY;AACxB,IAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB;AAE7C,IAAA,MAAM,GAAG,GAAG,MAAM,KAAK;AAEvB,IAAA,MAAM,WAAW,GAAG,CAAC,aAAoC,KAAI;AAC3D,QAAA,KAAK,GAAGA,WAAK,CAAC,KAAK,EAAE,aAAa,CAAC;AACnC,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC;IAErD,MAAM,QAAQ,GAAG,MAAK;AACpB,QAAA,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;AAC7D,QAAA,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC;AAClD,IAAA,CAAC;AAED,IAAA,MAAM,GAAG,GAAG,CAAC,aAAoC,KAAI;QACnD,WAAW,CAAC,aAAa,CAAC;AAC1B,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;IAED,MAAM,KAAK,GAAG,MAAK;AACjB,QAAA,aAAa,EAAE;AACf,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;AAED,IAAA,MAAM,SAAS,GAAG,CAAC,QAA0B,KAAI;AAC/C,QAAA,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE;YACxB,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;QACnC;QAEA,QAAQ,CAAC,KAAK,CAAC;AACf,QAAA,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC;AAEvB,QAAA,OAAO,MAAK;AACV,YAAA,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC;AAE1B,YAAA,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE;gBACxB,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;YACnC;AACF,QAAA,CAAC;AACH,IAAA,CAAC;IAED,MAAM,OAAO,GAAG;UACZ,aAAa,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE;UAChC,EAAe;IAEpB,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAE/B,OAAO;QACL,GAAG;QACH,GAAG;QACH,KAAK;QACL,SAAS;QACT,OAAO;KACR;AACH;;;;"}
|
package/dist/cjs/lib/merge.d.ts
CHANGED
|
@@ -3,22 +3,22 @@ import { StateModifier } from "./createStore";
|
|
|
3
3
|
* Merges the current state with a state modifier.
|
|
4
4
|
* Useful for creating custom set functions.
|
|
5
5
|
*
|
|
6
|
-
* @param {T}
|
|
7
|
-
* @param {StateModifier<T>} stateModifier -
|
|
6
|
+
* @param {T} currentState - The current state object.
|
|
7
|
+
* @param {StateModifier<T>} stateModifier - A function or object that modifies the state.
|
|
8
8
|
* @returns {T} The new state object, which is a combination of the current state and the state modifier.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```ts
|
|
12
12
|
* import { createStore, merge, StateModifier } from "dharma-core";
|
|
13
13
|
*
|
|
14
|
-
* const store = createStore(
|
|
15
|
-
* {
|
|
14
|
+
* const store = createStore({
|
|
15
|
+
* initialState: {
|
|
16
16
|
* counter: {
|
|
17
17
|
* count: 0,
|
|
18
18
|
* },
|
|
19
19
|
* // ...
|
|
20
20
|
* },
|
|
21
|
-
* (set) => {
|
|
21
|
+
* defineActions: ({ set }) => {
|
|
22
22
|
* const setCounter = (counter: StateModifier<{ count: number }>) =>
|
|
23
23
|
* set((state) => ({
|
|
24
24
|
* counter: merge(state.counter, counter),
|
|
@@ -31,8 +31,7 @@ import { StateModifier } from "./createStore";
|
|
|
31
31
|
* // ...
|
|
32
32
|
* };
|
|
33
33
|
* },
|
|
34
|
-
* );
|
|
34
|
+
* });
|
|
35
35
|
* ```
|
|
36
|
-
* @group Utilities
|
|
37
36
|
*/
|
|
38
37
|
export declare const merge: <T extends object>(currentState: T, stateModifier: StateModifier<T>) => T;
|
package/dist/cjs/lib/merge.js
CHANGED
|
@@ -4,22 +4,22 @@
|
|
|
4
4
|
* Merges the current state with a state modifier.
|
|
5
5
|
* Useful for creating custom set functions.
|
|
6
6
|
*
|
|
7
|
-
* @param {T}
|
|
8
|
-
* @param {StateModifier<T>} stateModifier -
|
|
7
|
+
* @param {T} currentState - The current state object.
|
|
8
|
+
* @param {StateModifier<T>} stateModifier - A function or object that modifies the state.
|
|
9
9
|
* @returns {T} The new state object, which is a combination of the current state and the state modifier.
|
|
10
10
|
*
|
|
11
11
|
* @example
|
|
12
12
|
* ```ts
|
|
13
13
|
* import { createStore, merge, StateModifier } from "dharma-core";
|
|
14
14
|
*
|
|
15
|
-
* const store = createStore(
|
|
16
|
-
* {
|
|
15
|
+
* const store = createStore({
|
|
16
|
+
* initialState: {
|
|
17
17
|
* counter: {
|
|
18
18
|
* count: 0,
|
|
19
19
|
* },
|
|
20
20
|
* // ...
|
|
21
21
|
* },
|
|
22
|
-
* (set) => {
|
|
22
|
+
* defineActions: ({ set }) => {
|
|
23
23
|
* const setCounter = (counter: StateModifier<{ count: number }>) =>
|
|
24
24
|
* set((state) => ({
|
|
25
25
|
* counter: merge(state.counter, counter),
|
|
@@ -32,9 +32,8 @@
|
|
|
32
32
|
* // ...
|
|
33
33
|
* };
|
|
34
34
|
* },
|
|
35
|
-
* );
|
|
35
|
+
* });
|
|
36
36
|
* ```
|
|
37
|
-
* @group Utilities
|
|
38
37
|
*/
|
|
39
38
|
const merge = (currentState, stateModifier) => {
|
|
40
39
|
const newState = typeof stateModifier === "function"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.js","sources":["../../../src/lib/merge.ts"],"sourcesContent":["import { StateModifier } from \"./createStore\";\n\n/**\n * Merges the current state with a state modifier.\n * Useful for creating custom set functions.\n *\n * @param {T}
|
|
1
|
+
{"version":3,"file":"merge.js","sources":["../../../src/lib/merge.ts"],"sourcesContent":["import { StateModifier } from \"./createStore\";\n\n/**\n * Merges the current state with a state modifier.\n * Useful for creating custom set functions.\n *\n * @param {T} currentState - The current state object.\n * @param {StateModifier<T>} stateModifier - A function or object that modifies the state.\n * @returns {T} The new state object, which is a combination of the current state and the state modifier.\n *\n * @example\n * ```ts\n * import { createStore, merge, StateModifier } from \"dharma-core\";\n *\n * const store = createStore({\n * initialState: {\n * counter: {\n * count: 0,\n * },\n * // ...\n * },\n * defineActions: ({ set }) => {\n * const setCounter = (counter: StateModifier<{ count: number }>) =>\n * set((state) => ({\n * counter: merge(state.counter, counter),\n * }));\n *\n * return {\n * increment: () => setCounter((state) => ({ count: state.count + 1 })),\n * decrement: () => setCounter((state) => ({ count: state.count - 1 })),\n * reset: () => setCounter({ count: 0 }),\n * // ...\n * };\n * },\n * });\n * ```\n */\nexport const merge = <T extends object>(\n currentState: T,\n stateModifier: StateModifier<T>,\n): T => {\n const newState =\n typeof stateModifier === \"function\"\n ? stateModifier(currentState)\n : stateModifier;\n return { ...currentState, ...newState };\n};\n"],"names":[],"mappings":";;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCG;MACU,KAAK,GAAG,CACnB,YAAe,EACf,aAA+B,KAC1B;AACL,IAAA,MAAM,QAAQ,GACZ,OAAO,aAAa,KAAK;AACvB,UAAE,aAAa,CAAC,YAAY;UAC1B,aAAa;AACnB,IAAA,OAAO,EAAE,GAAG,YAAY,EAAE,GAAG,QAAQ,EAAE;AACzC;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Store, StoreConfiguration } from "./createStore";
|
|
2
2
|
export type StorageAPI = {
|
|
3
3
|
getItem: (key: string) => string | null;
|
|
4
4
|
setItem: (key: string, value: string) => void;
|
|
@@ -8,9 +8,10 @@ export type Serializer<T = any> = {
|
|
|
8
8
|
stringify: (value: T) => string;
|
|
9
9
|
parse: (value: string) => T;
|
|
10
10
|
};
|
|
11
|
-
export type
|
|
12
|
-
|
|
13
|
-
storage
|
|
11
|
+
export type PersistentStoreConfiguration<TState extends object, TActions extends object> = StoreConfiguration<TState, TActions> & {
|
|
12
|
+
key: string;
|
|
13
|
+
/** The storage to use for persisting the state. Defaults to local storage if available. */
|
|
14
|
+
storage?: StorageAPI;
|
|
14
15
|
/** The serializer to use for storing the state. Defaults to JSON. */
|
|
15
16
|
serializer?: Serializer<TState>;
|
|
16
17
|
};
|
|
@@ -21,10 +22,7 @@ export type PersistentStoreOptions<TState extends object> = StoreOptions<TState>
|
|
|
21
22
|
* **Note:** The state needs to be serializable by whatever serializer you use. (JSON by default)
|
|
22
23
|
* If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
|
|
23
24
|
*
|
|
24
|
-
* @param {
|
|
25
|
-
* @param {TState} initialState - The initial state of the store.
|
|
26
|
-
* @param {DefineActions<TState, TActions>} [defineActions] - A function to define actions for the store.
|
|
27
|
-
* @param {PersistentStoreOptions<TState>} [options] - Additional options for the persistent store.
|
|
25
|
+
* @param {PersistentStoreConfiguration<TState, TActions>} config - The configuration for the persistent store.
|
|
28
26
|
*
|
|
29
27
|
* @returns {Store<TState, TActions>} The created store.
|
|
30
28
|
*
|
|
@@ -33,11 +31,15 @@ export type PersistentStoreOptions<TState extends object> = StoreOptions<TState>
|
|
|
33
31
|
* ```ts
|
|
34
32
|
* import { createPersistentStore } from "dharma-core";
|
|
35
33
|
*
|
|
36
|
-
* const store = createPersistentStore(
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* }))
|
|
34
|
+
* const store = createPersistentStore({
|
|
35
|
+
* key: "count",
|
|
36
|
+
* initialState: { count: 0 },
|
|
37
|
+
* defineActions: ({ set }) => ({
|
|
38
|
+
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
39
|
+
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
40
|
+
* reset: () => set({ count: 0 }),
|
|
41
|
+
* }),
|
|
42
|
+
* });
|
|
41
43
|
* ```
|
|
42
44
|
* @example
|
|
43
45
|
* With superjson serialization and session storage:
|
|
@@ -45,20 +47,17 @@ export type PersistentStoreOptions<TState extends object> = StoreOptions<TState>
|
|
|
45
47
|
* import { createPersistentStore } from "dharma-core";
|
|
46
48
|
* import superjson from "superjson";
|
|
47
49
|
*
|
|
48
|
-
* const store = createPersistentStore(
|
|
49
|
-
* "count",
|
|
50
|
-
* { count: 0 },
|
|
51
|
-
* (set) => ({
|
|
50
|
+
* const store = createPersistentStore({
|
|
51
|
+
* key: "count",
|
|
52
|
+
* initialState: { count: 0 },
|
|
53
|
+
* defineActions: ({ set }) => ({
|
|
52
54
|
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
53
55
|
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
54
56
|
* reset: () => set({ count: 0 }),
|
|
55
57
|
* }),
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* },
|
|
60
|
-
* );
|
|
58
|
+
* serializer: superjson,
|
|
59
|
+
* storage: sessionStorage,
|
|
60
|
+
* });
|
|
61
61
|
* ```
|
|
62
|
-
* @group Core
|
|
63
62
|
*/
|
|
64
|
-
export declare const createPersistentStore: <TState extends object, TActions extends object = Record<never, never>>(
|
|
63
|
+
export declare const createPersistentStore: <TState extends object, TActions extends object = Record<never, never>>(config: PersistentStoreConfiguration<TState, TActions>) => Store<TState, TActions>;
|
|
@@ -7,10 +7,7 @@ import { createStore } from './createStore.js';
|
|
|
7
7
|
* **Note:** The state needs to be serializable by whatever serializer you use. (JSON by default)
|
|
8
8
|
* If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
|
|
9
9
|
*
|
|
10
|
-
* @param {
|
|
11
|
-
* @param {TState} initialState - The initial state of the store.
|
|
12
|
-
* @param {DefineActions<TState, TActions>} [defineActions] - A function to define actions for the store.
|
|
13
|
-
* @param {PersistentStoreOptions<TState>} [options] - Additional options for the persistent store.
|
|
10
|
+
* @param {PersistentStoreConfiguration<TState, TActions>} config - The configuration for the persistent store.
|
|
14
11
|
*
|
|
15
12
|
* @returns {Store<TState, TActions>} The created store.
|
|
16
13
|
*
|
|
@@ -19,11 +16,15 @@ import { createStore } from './createStore.js';
|
|
|
19
16
|
* ```ts
|
|
20
17
|
* import { createPersistentStore } from "dharma-core";
|
|
21
18
|
*
|
|
22
|
-
* const store = createPersistentStore(
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* }))
|
|
19
|
+
* const store = createPersistentStore({
|
|
20
|
+
* key: "count",
|
|
21
|
+
* initialState: { count: 0 },
|
|
22
|
+
* defineActions: ({ set }) => ({
|
|
23
|
+
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
24
|
+
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
25
|
+
* reset: () => set({ count: 0 }),
|
|
26
|
+
* }),
|
|
27
|
+
* });
|
|
27
28
|
* ```
|
|
28
29
|
* @example
|
|
29
30
|
* With superjson serialization and session storage:
|
|
@@ -31,77 +32,70 @@ import { createStore } from './createStore.js';
|
|
|
31
32
|
* import { createPersistentStore } from "dharma-core";
|
|
32
33
|
* import superjson from "superjson";
|
|
33
34
|
*
|
|
34
|
-
* const store = createPersistentStore(
|
|
35
|
-
* "count",
|
|
36
|
-
* { count: 0 },
|
|
37
|
-
* (set) => ({
|
|
35
|
+
* const store = createPersistentStore({
|
|
36
|
+
* key: "count",
|
|
37
|
+
* initialState: { count: 0 },
|
|
38
|
+
* defineActions: ({ set }) => ({
|
|
38
39
|
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
39
40
|
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
40
41
|
* reset: () => set({ count: 0 }),
|
|
41
42
|
* }),
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* },
|
|
46
|
-
* );
|
|
43
|
+
* serializer: superjson,
|
|
44
|
+
* storage: sessionStorage,
|
|
45
|
+
* });
|
|
47
46
|
* ```
|
|
48
|
-
* @group Core
|
|
49
47
|
*/
|
|
50
|
-
const createPersistentStore = (
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
const createPersistentStore = (config) => {
|
|
49
|
+
const { key, initialState, storage: customStorage, serializer = JSON, onAttach, onDetach, onChange, ...rest } = config;
|
|
50
|
+
const IS_BROWSER = typeof window !== "undefined";
|
|
51
|
+
const storage = customStorage ?? (IS_BROWSER ? localStorage : undefined);
|
|
52
|
+
if (!storage) {
|
|
53
|
+
return createStore(config);
|
|
53
54
|
}
|
|
54
|
-
const storage = getStorage(_storage);
|
|
55
|
-
const stateKey = `store_${key}`;
|
|
56
55
|
const initialStateKey = `init_${key}`;
|
|
57
56
|
const initialStateSnapshot = storage.getItem(initialStateKey);
|
|
58
57
|
const initialStateString = serializer.stringify(initialState);
|
|
59
58
|
if (initialStateSnapshot !== initialStateString) {
|
|
60
59
|
storage.setItem(initialStateKey, initialStateString);
|
|
61
|
-
storage.removeItem(
|
|
60
|
+
storage.removeItem(key);
|
|
62
61
|
}
|
|
63
62
|
const updateSnapshot = (newState) => {
|
|
64
|
-
const currentSnapshot = storage.getItem(
|
|
63
|
+
const currentSnapshot = storage.getItem(key);
|
|
65
64
|
const newSnapshot = serializer.stringify(newState);
|
|
66
65
|
if (newSnapshot !== currentSnapshot) {
|
|
67
|
-
storage.setItem(
|
|
66
|
+
storage.setItem(key, newSnapshot);
|
|
68
67
|
}
|
|
69
68
|
};
|
|
70
69
|
const updateState = () => {
|
|
71
|
-
const currentSnapshot = storage.getItem(
|
|
70
|
+
const currentSnapshot = storage.getItem(key);
|
|
72
71
|
if (currentSnapshot &&
|
|
73
72
|
currentSnapshot !== serializer.stringify(store.get())) {
|
|
74
73
|
store.set(serializer.parse(currentSnapshot));
|
|
75
74
|
}
|
|
76
75
|
};
|
|
77
|
-
const store = createStore(
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
const store = createStore({
|
|
77
|
+
initialState,
|
|
78
|
+
onAttach: (ctx) => {
|
|
79
|
+
onAttach?.(ctx);
|
|
80
80
|
updateState();
|
|
81
|
-
|
|
81
|
+
if (IS_BROWSER) {
|
|
82
|
+
window.addEventListener("focus", updateState);
|
|
83
|
+
}
|
|
82
84
|
},
|
|
83
|
-
onDetach: (
|
|
84
|
-
onDetach?.(
|
|
85
|
-
|
|
85
|
+
onDetach: (ctx) => {
|
|
86
|
+
onDetach?.(ctx);
|
|
87
|
+
if (IS_BROWSER) {
|
|
88
|
+
window.removeEventListener("focus", updateState);
|
|
89
|
+
}
|
|
86
90
|
},
|
|
87
|
-
onChange: (state, ...
|
|
88
|
-
onChange?.(state, ...
|
|
91
|
+
onChange: ({ state, ...ctx }) => {
|
|
92
|
+
onChange?.({ state, ...ctx });
|
|
89
93
|
updateSnapshot(state);
|
|
90
94
|
},
|
|
91
|
-
...
|
|
95
|
+
...rest,
|
|
92
96
|
});
|
|
93
97
|
return store;
|
|
94
98
|
};
|
|
95
|
-
const getStorage = (storage) => {
|
|
96
|
-
switch (storage) {
|
|
97
|
-
case "local":
|
|
98
|
-
return localStorage;
|
|
99
|
-
case "session":
|
|
100
|
-
return sessionStorage;
|
|
101
|
-
default:
|
|
102
|
-
return storage;
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
99
|
|
|
106
100
|
export { createPersistentStore };
|
|
107
101
|
//# sourceMappingURL=createPersistentStore.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createPersistentStore.js","sources":["../../../src/lib/createPersistentStore.ts"],"sourcesContent":["import { createStore,
|
|
1
|
+
{"version":3,"file":"createPersistentStore.js","sources":["../../../src/lib/createPersistentStore.ts"],"sourcesContent":["import { createStore, Store, StoreConfiguration } from \"./createStore\";\n\nexport type StorageAPI = {\n getItem: (key: string) => string | null;\n setItem: (key: string, value: string) => void;\n removeItem: (key: string) => void;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Serializer<T = any> = {\n stringify: (value: T) => string;\n parse: (value: string) => T;\n};\n\nexport type PersistentStoreConfiguration<\n TState extends object,\n TActions extends object,\n> = StoreConfiguration<TState, TActions> & {\n key: string;\n /** The storage to use for persisting the state. Defaults to local storage if available. */\n storage?: StorageAPI;\n /** The serializer to use for storing the state. Defaults to JSON. */\n serializer?: Serializer<TState>;\n};\n\n/**\n * Creates a store that persists its state in local or session storage.\n * Defaults to local storage but this can be changed in the options.\n *\n * **Note:** The state needs to be serializable by whatever serializer you use. (JSON by default)\n * If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).\n *\n * @param {PersistentStoreConfiguration<TState, TActions>} config - The configuration for the persistent store.\n *\n * @returns {Store<TState, TActions>} The created store.\n *\n * @example\n * Basic usage:\n * ```ts\n * import { createPersistentStore } from \"dharma-core\";\n *\n * const store = createPersistentStore({\n * key: \"count\",\n * initialState: { count: 0 },\n * defineActions: ({ set }) => ({\n * increment: () => set((state) => ({ count: state.count + 1 })),\n * decrement: () => set((state) => ({ count: state.count - 1 })),\n * reset: () => set({ count: 0 }),\n * }),\n * });\n * ```\n * @example\n * With superjson serialization and session storage:\n * ```ts\n * import { createPersistentStore } from \"dharma-core\";\n * import superjson from \"superjson\";\n *\n * const store = createPersistentStore({\n * key: \"count\",\n * initialState: { count: 0 },\n * defineActions: ({ set }) => ({\n * increment: () => set((state) => ({ count: state.count + 1 })),\n * decrement: () => set((state) => ({ count: state.count - 1 })),\n * reset: () => set({ count: 0 }),\n * }),\n * serializer: superjson,\n * storage: sessionStorage,\n * });\n * ```\n */\nexport const createPersistentStore = <\n TState extends object,\n TActions extends object = Record<never, never>,\n>(\n config: PersistentStoreConfiguration<TState, TActions>,\n): Store<TState, TActions> => {\n const {\n key,\n initialState,\n storage: customStorage,\n serializer = JSON,\n onAttach,\n onDetach,\n onChange,\n ...rest\n } = config;\n\n const IS_BROWSER = typeof window !== \"undefined\";\n const storage = customStorage ?? (IS_BROWSER ? localStorage : undefined);\n\n if (!storage) {\n return createStore(config);\n }\n\n const initialStateKey = `init_${key}`;\n\n const initialStateSnapshot = storage.getItem(initialStateKey);\n const initialStateString = serializer.stringify(initialState);\n\n if (initialStateSnapshot !== initialStateString) {\n storage.setItem(initialStateKey, initialStateString);\n storage.removeItem(key);\n }\n\n const updateSnapshot = (newState: TState) => {\n const currentSnapshot = storage.getItem(key);\n const newSnapshot = serializer.stringify(newState);\n\n if (newSnapshot !== currentSnapshot) {\n storage.setItem(key, newSnapshot);\n }\n };\n\n const updateState = () => {\n const currentSnapshot = storage.getItem(key);\n\n if (\n currentSnapshot &&\n currentSnapshot !== serializer.stringify(store.get())\n ) {\n store.set(serializer.parse(currentSnapshot));\n }\n };\n\n const store = createStore({\n initialState,\n onAttach: (ctx) => {\n onAttach?.(ctx);\n updateState();\n if (IS_BROWSER) {\n window.addEventListener(\"focus\", updateState);\n }\n },\n onDetach: (ctx) => {\n onDetach?.(ctx);\n if (IS_BROWSER) {\n window.removeEventListener(\"focus\", updateState);\n }\n },\n onChange: ({ state, ...ctx }) => {\n onChange?.({ state, ...ctx });\n updateSnapshot(state);\n },\n ...rest,\n });\n\n return store;\n};\n"],"names":[],"mappings":";;AAyBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CG;AACI,MAAM,qBAAqB,GAAG,CAInC,MAAsD,KAC3B;IAC3B,MAAM,EACJ,GAAG,EACH,YAAY,EACZ,OAAO,EAAE,aAAa,EACtB,UAAU,GAAG,IAAI,EACjB,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,GAAG,IAAI,EACR,GAAG,MAAM;AAEV,IAAA,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW;AAChD,IAAA,MAAM,OAAO,GAAG,aAAa,KAAK,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC;IAExE,IAAI,CAAC,OAAO,EAAE;AACZ,QAAA,OAAO,WAAW,CAAC,MAAM,CAAC;IAC5B;AAEA,IAAA,MAAM,eAAe,GAAG,CAAA,KAAA,EAAQ,GAAG,EAAE;IAErC,MAAM,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC;IAC7D,MAAM,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC;AAE7D,IAAA,IAAI,oBAAoB,KAAK,kBAAkB,EAAE;AAC/C,QAAA,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,kBAAkB,CAAC;AACpD,QAAA,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;IACzB;AAEA,IAAA,MAAM,cAAc,GAAG,CAAC,QAAgB,KAAI;QAC1C,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;QAC5C,MAAM,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,QAAQ,CAAC;AAElD,QAAA,IAAI,WAAW,KAAK,eAAe,EAAE;AACnC,YAAA,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC;QACnC;AACF,IAAA,CAAC;IAED,MAAM,WAAW,GAAG,MAAK;QACvB,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;AAE5C,QAAA,IACE,eAAe;YACf,eAAe,KAAK,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EACrD;YACA,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAC9C;AACF,IAAA,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,CAAC;QACxB,YAAY;AACZ,QAAA,QAAQ,EAAE,CAAC,GAAG,KAAI;AAChB,YAAA,QAAQ,GAAG,GAAG,CAAC;AACf,YAAA,WAAW,EAAE;YACb,IAAI,UAAU,EAAE;AACd,gBAAA,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC;YAC/C;QACF,CAAC;AACD,QAAA,QAAQ,EAAE,CAAC,GAAG,KAAI;AAChB,YAAA,QAAQ,GAAG,GAAG,CAAC;YACf,IAAI,UAAU,EAAE;AACd,gBAAA,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC;YAClD;QACF,CAAC;QACD,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,GAAG,EAAE,KAAI;YAC9B,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,EAAE,CAAC;YAC7B,cAAc,CAAC,KAAK,CAAC;QACvB,CAAC;AACD,QAAA,GAAG,IAAI;AACR,KAAA,CAAC;AAEF,IAAA,OAAO,KAAK;AACd;;;;"}
|
|
@@ -1,19 +1,41 @@
|
|
|
1
|
+
type Listener<TState extends object> = (state: TState) => void;
|
|
2
|
+
export type SetState<TState extends object> = (stateModifier: StateModifier<TState>) => TState;
|
|
1
3
|
export type Store<TState extends object, TActions extends object> = {
|
|
2
4
|
/** Returns the current state of the store. */
|
|
3
5
|
get: () => TState;
|
|
4
6
|
/** Sets the state of the store. */
|
|
5
|
-
set:
|
|
7
|
+
set: SetState<TState>;
|
|
8
|
+
/** Resets the state of the store to its initial value. */
|
|
9
|
+
reset: () => TState;
|
|
6
10
|
/** Actions that can modify the state of the store. */
|
|
7
11
|
actions: TActions;
|
|
8
12
|
/** Subscribes to changes in the state of the store. Returns an unsubscribe function. */
|
|
9
13
|
subscribe: (listener: Listener<TState>) => () => void;
|
|
10
14
|
};
|
|
11
|
-
type
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
export type EventHandlerContext<TState extends object> = {
|
|
16
|
+
/** The current state of the store. */
|
|
17
|
+
state: TState;
|
|
18
|
+
/** Sets the state of the store. */
|
|
19
|
+
set: SetState<TState>;
|
|
20
|
+
/** Resets the state of the store to its initial value. */
|
|
21
|
+
reset: () => TState;
|
|
22
|
+
};
|
|
23
|
+
export type StoreEventHandler<TState extends object> = (context: EventHandlerContext<TState>) => void;
|
|
14
24
|
export type StateModifier<TState extends object> = Partial<TState> | ((state: TState) => Partial<TState>);
|
|
15
|
-
export type
|
|
16
|
-
|
|
25
|
+
export type StateFunctions<TState extends object> = {
|
|
26
|
+
/** Returns the current state of the store. */
|
|
27
|
+
get: () => TState;
|
|
28
|
+
/** Sets the state of the store. */
|
|
29
|
+
set: SetState<TState>;
|
|
30
|
+
/** Resets the state of the store to its initial value. */
|
|
31
|
+
reset: () => TState;
|
|
32
|
+
};
|
|
33
|
+
export type DefineActions<TState extends object, TActions> = (stateFunctions: StateFunctions<TState>) => TActions;
|
|
34
|
+
export type StoreConfiguration<TState extends object, TActions extends object> = {
|
|
35
|
+
/** The initial state of the store. */
|
|
36
|
+
initialState: TState;
|
|
37
|
+
/** A function that defines actions that can modify the state. */
|
|
38
|
+
defineActions?: DefineActions<TState, TActions>;
|
|
17
39
|
/** Invoked when the store is created. */
|
|
18
40
|
onLoad?: StoreEventHandler<TState>;
|
|
19
41
|
/** Invoked when the store is subscribed to. */
|
|
@@ -26,9 +48,7 @@ export type StoreOptions<TState extends object> = {
|
|
|
26
48
|
/**
|
|
27
49
|
* Creates a store with an initial state and actions that can modify the state.
|
|
28
50
|
*
|
|
29
|
-
* @param {TState}
|
|
30
|
-
* @param {DefineActions<TState, TActions>} [defineActions] - A function that defines actions that can modify the state.
|
|
31
|
-
* @param {StoreOptions<TState>} [options] - Additional options for the store.
|
|
51
|
+
* @param {StoreConfiguration<TState>} [config] - The configuration for the store.
|
|
32
52
|
*
|
|
33
53
|
* @returns {Store<TState, TActions>} The created store with state management methods.
|
|
34
54
|
*
|
|
@@ -36,13 +56,14 @@ export type StoreOptions<TState extends object> = {
|
|
|
36
56
|
* ```ts
|
|
37
57
|
* import { createStore } from "dharma-core";
|
|
38
58
|
*
|
|
39
|
-
* const store = createStore({
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* }))
|
|
59
|
+
* const store = createStore({
|
|
60
|
+
* initialState: { count: 0 },
|
|
61
|
+
* defineActions: ({ set }) => ({
|
|
62
|
+
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
63
|
+
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
64
|
+
* }),
|
|
65
|
+
* });
|
|
44
66
|
* ```
|
|
45
|
-
* @group Core
|
|
46
67
|
*/
|
|
47
|
-
export declare const createStore: <TState extends object, TActions extends object = Record<never, never>>(
|
|
68
|
+
export declare const createStore: <TState extends object, TActions extends object = Record<never, never>>(config: StoreConfiguration<TState, TActions>) => Store<TState, TActions>;
|
|
48
69
|
export {};
|
|
@@ -3,9 +3,7 @@ import { merge } from './merge.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* Creates a store with an initial state and actions that can modify the state.
|
|
5
5
|
*
|
|
6
|
-
* @param {TState}
|
|
7
|
-
* @param {DefineActions<TState, TActions>} [defineActions] - A function that defines actions that can modify the state.
|
|
8
|
-
* @param {StoreOptions<TState>} [options] - Additional options for the store.
|
|
6
|
+
* @param {StoreConfiguration<TState>} [config] - The configuration for the store.
|
|
9
7
|
*
|
|
10
8
|
* @returns {Store<TState, TActions>} The created store with state management methods.
|
|
11
9
|
*
|
|
@@ -13,15 +11,17 @@ import { merge } from './merge.js';
|
|
|
13
11
|
* ```ts
|
|
14
12
|
* import { createStore } from "dharma-core";
|
|
15
13
|
*
|
|
16
|
-
* const store = createStore({
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* }))
|
|
14
|
+
* const store = createStore({
|
|
15
|
+
* initialState: { count: 0 },
|
|
16
|
+
* defineActions: ({ set }) => ({
|
|
17
|
+
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
18
|
+
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
19
|
+
* }),
|
|
20
|
+
* });
|
|
21
21
|
* ```
|
|
22
|
-
* @group Core
|
|
23
22
|
*/
|
|
24
|
-
const createStore = (
|
|
23
|
+
const createStore = (config) => {
|
|
24
|
+
const { initialState, defineActions, onLoad, onAttach, onDetach, onChange } = config;
|
|
25
25
|
let state = initialState;
|
|
26
26
|
const listeners = new Set();
|
|
27
27
|
const get = () => state;
|
|
@@ -29,8 +29,9 @@ const createStore = (initialState, defineActions, { onLoad, onAttach, onDetach,
|
|
|
29
29
|
state = merge(state, stateModifier);
|
|
30
30
|
return state;
|
|
31
31
|
};
|
|
32
|
+
const resetSilently = () => setSilently(initialState);
|
|
32
33
|
const dispatch = () => {
|
|
33
|
-
onChange?.(state, setSilently);
|
|
34
|
+
onChange?.({ state, set: setSilently, reset: resetSilently });
|
|
34
35
|
listeners.forEach((listener) => listener(state));
|
|
35
36
|
};
|
|
36
37
|
const set = (stateModifier) => {
|
|
@@ -38,24 +39,32 @@ const createStore = (initialState, defineActions, { onLoad, onAttach, onDetach,
|
|
|
38
39
|
dispatch();
|
|
39
40
|
return state;
|
|
40
41
|
};
|
|
42
|
+
const reset = () => {
|
|
43
|
+
resetSilently();
|
|
44
|
+
dispatch();
|
|
45
|
+
return state;
|
|
46
|
+
};
|
|
41
47
|
const subscribe = (listener) => {
|
|
42
|
-
listener(state);
|
|
43
48
|
if (listeners.size === 0) {
|
|
44
|
-
onAttach?.(state, set);
|
|
49
|
+
onAttach?.({ state, set, reset });
|
|
45
50
|
}
|
|
51
|
+
listener(state);
|
|
46
52
|
listeners.add(listener);
|
|
47
53
|
return () => {
|
|
48
54
|
listeners.delete(listener);
|
|
49
55
|
if (listeners.size === 0) {
|
|
50
|
-
onDetach?.(state, set);
|
|
56
|
+
onDetach?.({ state, set, reset });
|
|
51
57
|
}
|
|
52
58
|
};
|
|
53
59
|
};
|
|
54
|
-
const actions = defineActions
|
|
55
|
-
|
|
60
|
+
const actions = defineActions
|
|
61
|
+
? defineActions({ set, get, reset })
|
|
62
|
+
: {};
|
|
63
|
+
onLoad?.({ state, set, reset });
|
|
56
64
|
return {
|
|
57
65
|
get,
|
|
58
66
|
set,
|
|
67
|
+
reset,
|
|
59
68
|
subscribe,
|
|
60
69
|
actions,
|
|
61
70
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { merge } from \"./merge\";\n\nexport type Store<TState extends object, TActions extends object> = {\n /** Returns the current state of the store. */\n get: () => TState;\n /** Sets the state of the store. */\n set:
|
|
1
|
+
{"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { merge } from \"./merge\";\n\ntype Listener<TState extends object> = (state: TState) => void;\n\nexport type SetState<TState extends object> = (\n stateModifier: StateModifier<TState>,\n) => TState;\n\nexport type Store<TState extends object, TActions extends object> = {\n /** Returns the current state of the store. */\n get: () => TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n /** Actions that can modify the state of the store. */\n actions: TActions;\n /** Subscribes to changes in the state of the store. Returns an unsubscribe function. */\n subscribe: (listener: Listener<TState>) => () => void;\n};\n\nexport type EventHandlerContext<TState extends object> = {\n /** The current state of the store. */\n state: TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n};\n\nexport type StoreEventHandler<TState extends object> = (\n context: EventHandlerContext<TState>,\n) => void;\n\nexport type StateModifier<TState extends object> =\n | Partial<TState>\n | ((state: TState) => Partial<TState>);\n\nexport type StateFunctions<TState extends object> = {\n /** Returns the current state of the store. */\n get: () => TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n};\n\nexport type DefineActions<TState extends object, TActions> = (\n stateFunctions: StateFunctions<TState>,\n) => TActions;\n\nexport type StoreConfiguration<\n TState extends object,\n TActions extends object,\n> = {\n /** The initial state of the store. */\n initialState: TState;\n /** A function that defines actions that can modify the state. */\n defineActions?: DefineActions<TState, TActions>;\n /** Invoked when the store is created. */\n onLoad?: StoreEventHandler<TState>;\n /** Invoked when the store is subscribed to. */\n onAttach?: StoreEventHandler<TState>;\n /** Invoked when the store is unsubscribed from. */\n onDetach?: StoreEventHandler<TState>;\n /** Invoked whenever the state changes. */\n onChange?: StoreEventHandler<TState>;\n};\n\n/**\n * Creates a store with an initial state and actions that can modify the state.\n *\n * @param {StoreConfiguration<TState>} [config] - The configuration for the store.\n *\n * @returns {Store<TState, TActions>} The created store with state management methods.\n *\n * @example\n * ```ts\n * import { createStore } from \"dharma-core\";\n *\n * const store = createStore({\n * initialState: { count: 0 },\n * defineActions: ({ set }) => ({\n * increment: () => set((state) => ({ count: state.count + 1 })),\n * decrement: () => set((state) => ({ count: state.count - 1 })),\n * }),\n * });\n * ```\n */\nexport const createStore = <\n TState extends object,\n TActions extends object = Record<never, never>,\n>(\n config: StoreConfiguration<TState, TActions>,\n): Store<TState, TActions> => {\n const { initialState, defineActions, onLoad, onAttach, onDetach, onChange } =\n config;\n\n let state = initialState;\n const listeners = new Set<Listener<TState>>();\n\n const get = () => state;\n\n const setSilently = (stateModifier: StateModifier<TState>) => {\n state = merge(state, stateModifier);\n return state;\n };\n\n const resetSilently = () => setSilently(initialState);\n\n const dispatch = () => {\n onChange?.({ state, set: setSilently, reset: resetSilently });\n listeners.forEach((listener) => listener(state));\n };\n\n const set = (stateModifier: StateModifier<TState>) => {\n setSilently(stateModifier);\n dispatch();\n return state;\n };\n\n const reset = () => {\n resetSilently();\n dispatch();\n return state;\n };\n\n const subscribe = (listener: Listener<TState>) => {\n if (listeners.size === 0) {\n onAttach?.({ state, set, reset });\n }\n\n listener(state);\n listeners.add(listener);\n\n return () => {\n listeners.delete(listener);\n\n if (listeners.size === 0) {\n onDetach?.({ state, set, reset });\n }\n };\n };\n\n const actions = defineActions\n ? defineActions({ set, get, reset })\n : ({} as TActions);\n\n onLoad?.({ state, set, reset });\n\n return {\n get,\n set,\n reset,\n subscribe,\n actions,\n };\n};\n"],"names":[],"mappings":";;AAqEA;;;;;;;;;;;;;;;;;;;AAmBG;AACI,MAAM,WAAW,GAAG,CAIzB,MAA4C,KACjB;AAC3B,IAAA,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,GACzE,MAAM;IAER,IAAI,KAAK,GAAG,YAAY;AACxB,IAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB;AAE7C,IAAA,MAAM,GAAG,GAAG,MAAM,KAAK;AAEvB,IAAA,MAAM,WAAW,GAAG,CAAC,aAAoC,KAAI;AAC3D,QAAA,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,aAAa,CAAC;AACnC,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC;IAErD,MAAM,QAAQ,GAAG,MAAK;AACpB,QAAA,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;AAC7D,QAAA,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC;AAClD,IAAA,CAAC;AAED,IAAA,MAAM,GAAG,GAAG,CAAC,aAAoC,KAAI;QACnD,WAAW,CAAC,aAAa,CAAC;AAC1B,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;IAED,MAAM,KAAK,GAAG,MAAK;AACjB,QAAA,aAAa,EAAE;AACf,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;AAED,IAAA,MAAM,SAAS,GAAG,CAAC,QAA0B,KAAI;AAC/C,QAAA,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE;YACxB,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;QACnC;QAEA,QAAQ,CAAC,KAAK,CAAC;AACf,QAAA,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC;AAEvB,QAAA,OAAO,MAAK;AACV,YAAA,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC;AAE1B,YAAA,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE;gBACxB,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;YACnC;AACF,QAAA,CAAC;AACH,IAAA,CAAC;IAED,MAAM,OAAO,GAAG;UACZ,aAAa,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE;UAChC,EAAe;IAEpB,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAE/B,OAAO;QACL,GAAG;QACH,GAAG;QACH,KAAK;QACL,SAAS;QACT,OAAO;KACR;AACH;;;;"}
|
package/dist/esm/lib/merge.d.ts
CHANGED
|
@@ -3,22 +3,22 @@ import { StateModifier } from "./createStore";
|
|
|
3
3
|
* Merges the current state with a state modifier.
|
|
4
4
|
* Useful for creating custom set functions.
|
|
5
5
|
*
|
|
6
|
-
* @param {T}
|
|
7
|
-
* @param {StateModifier<T>} stateModifier -
|
|
6
|
+
* @param {T} currentState - The current state object.
|
|
7
|
+
* @param {StateModifier<T>} stateModifier - A function or object that modifies the state.
|
|
8
8
|
* @returns {T} The new state object, which is a combination of the current state and the state modifier.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```ts
|
|
12
12
|
* import { createStore, merge, StateModifier } from "dharma-core";
|
|
13
13
|
*
|
|
14
|
-
* const store = createStore(
|
|
15
|
-
* {
|
|
14
|
+
* const store = createStore({
|
|
15
|
+
* initialState: {
|
|
16
16
|
* counter: {
|
|
17
17
|
* count: 0,
|
|
18
18
|
* },
|
|
19
19
|
* // ...
|
|
20
20
|
* },
|
|
21
|
-
* (set) => {
|
|
21
|
+
* defineActions: ({ set }) => {
|
|
22
22
|
* const setCounter = (counter: StateModifier<{ count: number }>) =>
|
|
23
23
|
* set((state) => ({
|
|
24
24
|
* counter: merge(state.counter, counter),
|
|
@@ -31,8 +31,7 @@ import { StateModifier } from "./createStore";
|
|
|
31
31
|
* // ...
|
|
32
32
|
* };
|
|
33
33
|
* },
|
|
34
|
-
* );
|
|
34
|
+
* });
|
|
35
35
|
* ```
|
|
36
|
-
* @group Utilities
|
|
37
36
|
*/
|
|
38
37
|
export declare const merge: <T extends object>(currentState: T, stateModifier: StateModifier<T>) => T;
|
package/dist/esm/lib/merge.js
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
* Merges the current state with a state modifier.
|
|
3
3
|
* Useful for creating custom set functions.
|
|
4
4
|
*
|
|
5
|
-
* @param {T}
|
|
6
|
-
* @param {StateModifier<T>} stateModifier -
|
|
5
|
+
* @param {T} currentState - The current state object.
|
|
6
|
+
* @param {StateModifier<T>} stateModifier - A function or object that modifies the state.
|
|
7
7
|
* @returns {T} The new state object, which is a combination of the current state and the state modifier.
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
10
|
* ```ts
|
|
11
11
|
* import { createStore, merge, StateModifier } from "dharma-core";
|
|
12
12
|
*
|
|
13
|
-
* const store = createStore(
|
|
14
|
-
* {
|
|
13
|
+
* const store = createStore({
|
|
14
|
+
* initialState: {
|
|
15
15
|
* counter: {
|
|
16
16
|
* count: 0,
|
|
17
17
|
* },
|
|
18
18
|
* // ...
|
|
19
19
|
* },
|
|
20
|
-
* (set) => {
|
|
20
|
+
* defineActions: ({ set }) => {
|
|
21
21
|
* const setCounter = (counter: StateModifier<{ count: number }>) =>
|
|
22
22
|
* set((state) => ({
|
|
23
23
|
* counter: merge(state.counter, counter),
|
|
@@ -30,9 +30,8 @@
|
|
|
30
30
|
* // ...
|
|
31
31
|
* };
|
|
32
32
|
* },
|
|
33
|
-
* );
|
|
33
|
+
* });
|
|
34
34
|
* ```
|
|
35
|
-
* @group Utilities
|
|
36
35
|
*/
|
|
37
36
|
const merge = (currentState, stateModifier) => {
|
|
38
37
|
const newState = typeof stateModifier === "function"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.js","sources":["../../../src/lib/merge.ts"],"sourcesContent":["import { StateModifier } from \"./createStore\";\n\n/**\n * Merges the current state with a state modifier.\n * Useful for creating custom set functions.\n *\n * @param {T}
|
|
1
|
+
{"version":3,"file":"merge.js","sources":["../../../src/lib/merge.ts"],"sourcesContent":["import { StateModifier } from \"./createStore\";\n\n/**\n * Merges the current state with a state modifier.\n * Useful for creating custom set functions.\n *\n * @param {T} currentState - The current state object.\n * @param {StateModifier<T>} stateModifier - A function or object that modifies the state.\n * @returns {T} The new state object, which is a combination of the current state and the state modifier.\n *\n * @example\n * ```ts\n * import { createStore, merge, StateModifier } from \"dharma-core\";\n *\n * const store = createStore({\n * initialState: {\n * counter: {\n * count: 0,\n * },\n * // ...\n * },\n * defineActions: ({ set }) => {\n * const setCounter = (counter: StateModifier<{ count: number }>) =>\n * set((state) => ({\n * counter: merge(state.counter, counter),\n * }));\n *\n * return {\n * increment: () => setCounter((state) => ({ count: state.count + 1 })),\n * decrement: () => setCounter((state) => ({ count: state.count - 1 })),\n * reset: () => setCounter({ count: 0 }),\n * // ...\n * };\n * },\n * });\n * ```\n */\nexport const merge = <T extends object>(\n currentState: T,\n stateModifier: StateModifier<T>,\n): T => {\n const newState =\n typeof stateModifier === \"function\"\n ? stateModifier(currentState)\n : stateModifier;\n return { ...currentState, ...newState };\n};\n"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCG;MACU,KAAK,GAAG,CACnB,YAAe,EACf,aAA+B,KAC1B;AACL,IAAA,MAAM,QAAQ,GACZ,OAAO,aAAa,KAAK;AACvB,UAAE,aAAa,CAAC,YAAY;UAC1B,aAAa;AACnB,IAAA,OAAO,EAAE,GAAG,YAAY,EAAE,GAAG,QAAQ,EAAE;AACzC;;;;"}
|