dharma-core 0.8.0 → 0.9.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 +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/createStorageAdapter.d.ts +1 -1
- package/dist/cjs/lib/createStorageAdapter.js.map +1 -1
- package/dist/cjs/lib/createStore.d.ts +2 -75
- package/dist/cjs/lib/createStore.js +2 -77
- package/dist/cjs/lib/createStore.js.map +1 -1
- package/dist/cjs/lib/deeplyEquals.d.ts +1 -0
- package/dist/cjs/lib/deeplyEquals.js +49 -0
- package/dist/cjs/lib/deeplyEquals.js.map +1 -0
- package/dist/cjs/lib/derive.d.ts +16 -0
- package/dist/cjs/lib/derive.js +81 -0
- package/dist/cjs/lib/derive.js.map +1 -0
- package/dist/cjs/lib/merge.d.ts +2 -26
- package/dist/cjs/lib/merge.js +14 -29
- package/dist/cjs/lib/merge.js.map +1 -1
- package/dist/cjs/lib/types.d.ts +18 -11
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/createStorageAdapter.d.ts +1 -1
- package/dist/esm/lib/createStorageAdapter.js.map +1 -1
- package/dist/esm/lib/createStore.d.ts +2 -75
- package/dist/esm/lib/createStore.js +2 -77
- package/dist/esm/lib/createStore.js.map +1 -1
- package/dist/esm/lib/deeplyEquals.d.ts +1 -0
- package/dist/esm/lib/deeplyEquals.js +47 -0
- package/dist/esm/lib/deeplyEquals.js.map +1 -0
- package/dist/esm/lib/derive.d.ts +16 -0
- package/dist/esm/lib/derive.js +79 -0
- package/dist/esm/lib/derive.js.map +1 -0
- package/dist/esm/lib/merge.d.ts +2 -26
- package/dist/esm/lib/merge.js +14 -29
- package/dist/esm/lib/merge.js.map +1 -1
- package/dist/esm/lib/types.d.ts +18 -11
- package/package.json +6 -6
package/README.md
CHANGED
package/dist/cjs/index.d.ts
CHANGED
package/dist/cjs/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var createStore = require('./lib/createStore.js');
|
|
4
|
+
var derive = require('./lib/derive.js');
|
|
4
5
|
var merge = require('./lib/merge.js');
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
exports.createStore = createStore.createStore;
|
|
10
|
+
exports.derive = derive.derive;
|
|
9
11
|
exports.merge = merge.merge;
|
|
10
12
|
//# sourceMappingURL=index.js.map
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SetState, StoreConfig } from "./types";
|
|
2
|
-
export declare const createStorageAdapter: <TState
|
|
2
|
+
export declare const createStorageAdapter: <TState, TActions>(config: StoreConfig<TState, TActions>, get: () => TState, set: SetState<TState>) => {
|
|
3
3
|
onLoad: () => Promise<void>;
|
|
4
4
|
onChange: (newState: TState) => Promise<void>;
|
|
5
5
|
onAttach: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createStorageAdapter.js","sources":["../../../src/lib/createStorageAdapter.ts"],"sourcesContent":["import { SetState, StorageAPI, StoreConfig } from \"./types\";\nimport { warn } from \"./warn\";\n\nexport const createStorageAdapter =
|
|
1
|
+
{"version":3,"file":"createStorageAdapter.js","sources":["../../../src/lib/createStorageAdapter.ts"],"sourcesContent":["import { SetState, StorageAPI, StoreConfig } from \"./types\";\nimport { warn } from \"./warn\";\n\nexport const createStorageAdapter = <TState, TActions>(\n config: StoreConfig<TState, TActions>,\n get: () => TState,\n set: SetState<TState>,\n) => {\n if (!config.persist) {\n return null;\n }\n\n const IS_BROWSER = typeof window !== \"undefined\";\n\n const storage =\n config.storage ?? (IS_BROWSER ? (localStorage as StorageAPI) : undefined);\n\n if (!storage) {\n warn(\n \"No storage provider found. If this happened during SSR, you can safely ignore this warning.\",\n );\n return null;\n }\n\n const { key, serializer = JSON, initialState } = config;\n const initKey = `init_${key}`;\n\n const onLoad = async () => {\n try {\n let initialStateSnapshot = storage.getItem(initKey);\n const initialStateString = serializer.stringify(initialState);\n\n if (initialStateSnapshot instanceof Promise) {\n initialStateSnapshot = await initialStateSnapshot;\n }\n\n if (initialStateSnapshot !== initialStateString) {\n storage.setItem(initKey, initialStateString);\n storage.removeItem(key);\n }\n } catch {\n warn(\n \"Failed to initialize snapshots. If this happened during SSR, you can safely ignore this warning.\",\n );\n }\n };\n\n const syncWithSnapshot = async () => {\n try {\n let currentSnapshot = storage.getItem(key);\n\n if (currentSnapshot instanceof Promise) {\n currentSnapshot = await currentSnapshot;\n }\n\n if (currentSnapshot && currentSnapshot !== serializer.stringify(get())) {\n set(serializer.parse(currentSnapshot));\n }\n } catch {\n warn(\"Failed to update state from snapshot\");\n }\n };\n\n const onAttach = () => {\n syncWithSnapshot();\n\n if (IS_BROWSER) {\n window.addEventListener(\"focus\", syncWithSnapshot);\n }\n };\n\n const onChange = async (newState: TState) => {\n try {\n let currentSnapshot = storage.getItem(key);\n const newSnapshot = serializer.stringify(newState);\n\n if (currentSnapshot instanceof Promise) {\n currentSnapshot = await currentSnapshot;\n }\n\n if (newSnapshot !== currentSnapshot) {\n storage.setItem(key, newSnapshot);\n }\n } catch {\n warn(\"Failed to update snapshot\");\n }\n };\n\n const onDetach = () => {\n if (IS_BROWSER) {\n window.removeEventListener(\"focus\", syncWithSnapshot);\n }\n };\n\n return {\n onLoad,\n onChange,\n onAttach,\n onDetach,\n };\n};\n"],"names":["warn"],"mappings":";;;;AAGO,MAAM,oBAAoB,GAAG,CAClC,MAAqC,EACrC,GAAiB,EACjB,GAAqB,KACnB;AACF,IAAA,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;AACnB,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW;AAEhD,IAAA,MAAM,OAAO,GACX,MAAM,CAAC,OAAO,KAAK,UAAU,GAAI,YAA2B,GAAG,SAAS,CAAC;IAE3E,IAAI,CAAC,OAAO,EAAE;QACZA,SAAI,CACF,6FAA6F,CAC9F;AACD,QAAA,OAAO,IAAI;IACb;IAEA,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM;AACvD,IAAA,MAAM,OAAO,GAAG,CAAA,KAAA,EAAQ,GAAG,EAAE;AAE7B,IAAA,MAAM,MAAM,GAAG,YAAW;AACxB,QAAA,IAAI;YACF,IAAI,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;YACnD,MAAM,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC;AAE7D,YAAA,IAAI,oBAAoB,YAAY,OAAO,EAAE;gBAC3C,oBAAoB,GAAG,MAAM,oBAAoB;YACnD;AAEA,YAAA,IAAI,oBAAoB,KAAK,kBAAkB,EAAE;AAC/C,gBAAA,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,kBAAkB,CAAC;AAC5C,gBAAA,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YACzB;QACF;AAAE,QAAA,MAAM;YACNA,SAAI,CACF,kGAAkG,CACnG;QACH;AACF,IAAA,CAAC;AAED,IAAA,MAAM,gBAAgB,GAAG,YAAW;AAClC,QAAA,IAAI;YACF,IAAI,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;AAE1C,YAAA,IAAI,eAAe,YAAY,OAAO,EAAE;gBACtC,eAAe,GAAG,MAAM,eAAe;YACzC;AAEA,YAAA,IAAI,eAAe,IAAI,eAAe,KAAK,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE;gBACtE,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;YACxC;QACF;AAAE,QAAA,MAAM;YACNA,SAAI,CAAC,sCAAsC,CAAC;QAC9C;AACF,IAAA,CAAC;IAED,MAAM,QAAQ,GAAG,MAAK;AACpB,QAAA,gBAAgB,EAAE;QAElB,IAAI,UAAU,EAAE;AACd,YAAA,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,CAAC;QACpD;AACF,IAAA,CAAC;AAED,IAAA,MAAM,QAAQ,GAAG,OAAO,QAAgB,KAAI;AAC1C,QAAA,IAAI;YACF,IAAI,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;YAC1C,MAAM,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,QAAQ,CAAC;AAElD,YAAA,IAAI,eAAe,YAAY,OAAO,EAAE;gBACtC,eAAe,GAAG,MAAM,eAAe;YACzC;AAEA,YAAA,IAAI,WAAW,KAAK,eAAe,EAAE;AACnC,gBAAA,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC;YACnC;QACF;AAAE,QAAA,MAAM;YACNA,SAAI,CAAC,2BAA2B,CAAC;QACnC;AACF,IAAA,CAAC;IAED,MAAM,QAAQ,GAAG,MAAK;QACpB,IAAI,UAAU,EAAE;AACd,YAAA,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,CAAC;QACvD;AACF,IAAA,CAAC;IAED,OAAO;QACL,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,QAAQ;KACT;AACH;;;;"}
|
|
@@ -6,79 +6,6 @@ import { Store, StoreConfig } from "./types";
|
|
|
6
6
|
*
|
|
7
7
|
* @returns {Store<TState, TActions>} The created store with state management methods.
|
|
8
8
|
*
|
|
9
|
-
* @
|
|
10
|
-
* Basic usage:
|
|
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
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* Lifecycle hooks:
|
|
25
|
-
* ```ts
|
|
26
|
-
* import { createStore } from "dharma-core";
|
|
27
|
-
* import { State } from "./types";
|
|
28
|
-
*
|
|
29
|
-
* const initialState: State = { data: null, loading: true, error: null };
|
|
30
|
-
*
|
|
31
|
-
* const store = createStore({
|
|
32
|
-
* initialState,
|
|
33
|
-
* onAttach: async ({ set }) => {
|
|
34
|
-
* try {
|
|
35
|
-
* const response = await fetch("https://api.example.com/data");
|
|
36
|
-
* const data = await response.json();
|
|
37
|
-
* set({ data, loading: false });
|
|
38
|
-
* } catch (error) {
|
|
39
|
-
* set({ error, loading: false });
|
|
40
|
-
* }
|
|
41
|
-
* },
|
|
42
|
-
* onDetach: ({ reset }) => reset(),
|
|
43
|
-
* });
|
|
44
|
-
* ```
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* Persisting state:
|
|
48
|
-
* ```ts
|
|
49
|
-
* import { createStore } from "dharma-core";
|
|
50
|
-
*
|
|
51
|
-
* const store = createStore({
|
|
52
|
-
* persist: true,
|
|
53
|
-
* key: "count",
|
|
54
|
-
* initialState: { count: 0 },
|
|
55
|
-
* defineActions: ({ set }) => ({
|
|
56
|
-
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
57
|
-
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
58
|
-
* }),
|
|
59
|
-
* });
|
|
60
|
-
* ```
|
|
61
|
-
*
|
|
62
|
-
* **Note:** The state needs to be serializable by whatever serializer you choose (JSON by default).
|
|
63
|
-
* If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* Custom storage and serializer:
|
|
67
|
-
* ```ts
|
|
68
|
-
* import { createStore } from "dharma-core";
|
|
69
|
-
* import superjson from "superjson";
|
|
70
|
-
*
|
|
71
|
-
* const store = createStore({
|
|
72
|
-
* persist: true,
|
|
73
|
-
* key: "count",
|
|
74
|
-
* storage: sessionStorage,
|
|
75
|
-
* serializer: superjson,
|
|
76
|
-
* initialState: { count: 0 },
|
|
77
|
-
* defineActions: ({ set }) => ({
|
|
78
|
-
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
79
|
-
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
80
|
-
* }),
|
|
81
|
-
* });
|
|
82
|
-
* ```
|
|
9
|
+
* @see {@link https://dharma.fransek.dev/core/createstore/}
|
|
83
10
|
*/
|
|
84
|
-
export declare const createStore: <TState
|
|
11
|
+
export declare const createStore: <TState, TActions = undefined>(config: StoreConfig<TState, TActions>) => Store<TState, TActions>;
|
|
@@ -10,80 +10,7 @@ var merge = require('./merge.js');
|
|
|
10
10
|
*
|
|
11
11
|
* @returns {Store<TState, TActions>} The created store with state management methods.
|
|
12
12
|
*
|
|
13
|
-
* @
|
|
14
|
-
* Basic usage:
|
|
15
|
-
* ```ts
|
|
16
|
-
* import { createStore } from "dharma-core";
|
|
17
|
-
*
|
|
18
|
-
* const store = createStore({
|
|
19
|
-
* initialState: { count: 0 },
|
|
20
|
-
* defineActions: ({ set }) => ({
|
|
21
|
-
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
22
|
-
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
23
|
-
* }),
|
|
24
|
-
* });
|
|
25
|
-
* ```
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* Lifecycle hooks:
|
|
29
|
-
* ```ts
|
|
30
|
-
* import { createStore } from "dharma-core";
|
|
31
|
-
* import { State } from "./types";
|
|
32
|
-
*
|
|
33
|
-
* const initialState: State = { data: null, loading: true, error: null };
|
|
34
|
-
*
|
|
35
|
-
* const store = createStore({
|
|
36
|
-
* initialState,
|
|
37
|
-
* onAttach: async ({ set }) => {
|
|
38
|
-
* try {
|
|
39
|
-
* const response = await fetch("https://api.example.com/data");
|
|
40
|
-
* const data = await response.json();
|
|
41
|
-
* set({ data, loading: false });
|
|
42
|
-
* } catch (error) {
|
|
43
|
-
* set({ error, loading: false });
|
|
44
|
-
* }
|
|
45
|
-
* },
|
|
46
|
-
* onDetach: ({ reset }) => reset(),
|
|
47
|
-
* });
|
|
48
|
-
* ```
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* Persisting state:
|
|
52
|
-
* ```ts
|
|
53
|
-
* import { createStore } from "dharma-core";
|
|
54
|
-
*
|
|
55
|
-
* const store = createStore({
|
|
56
|
-
* persist: true,
|
|
57
|
-
* key: "count",
|
|
58
|
-
* initialState: { count: 0 },
|
|
59
|
-
* defineActions: ({ set }) => ({
|
|
60
|
-
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
61
|
-
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
62
|
-
* }),
|
|
63
|
-
* });
|
|
64
|
-
* ```
|
|
65
|
-
*
|
|
66
|
-
* **Note:** The state needs to be serializable by whatever serializer you choose (JSON by default).
|
|
67
|
-
* If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* Custom storage and serializer:
|
|
71
|
-
* ```ts
|
|
72
|
-
* import { createStore } from "dharma-core";
|
|
73
|
-
* import superjson from "superjson";
|
|
74
|
-
*
|
|
75
|
-
* const store = createStore({
|
|
76
|
-
* persist: true,
|
|
77
|
-
* key: "count",
|
|
78
|
-
* storage: sessionStorage,
|
|
79
|
-
* serializer: superjson,
|
|
80
|
-
* initialState: { count: 0 },
|
|
81
|
-
* defineActions: ({ set }) => ({
|
|
82
|
-
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
83
|
-
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
84
|
-
* }),
|
|
85
|
-
* });
|
|
86
|
-
* ```
|
|
13
|
+
* @see {@link https://dharma.fransek.dev/core/createstore/}
|
|
87
14
|
*/
|
|
88
15
|
const createStore = (config) => {
|
|
89
16
|
const { initialState, defineActions, onLoad, onAttach, onDetach, onChange } = config;
|
|
@@ -105,9 +32,7 @@ const createStore = (config) => {
|
|
|
105
32
|
return state;
|
|
106
33
|
};
|
|
107
34
|
const listeners = new Set();
|
|
108
|
-
const actions = defineActions
|
|
109
|
-
? defineActions({ set, get, reset })
|
|
110
|
-
: {};
|
|
35
|
+
const actions = defineActions?.({ set, get, reset });
|
|
111
36
|
const storageAdapter = createStorageAdapter.createStorageAdapter(config, get, set);
|
|
112
37
|
const subscribe = (listener) => {
|
|
113
38
|
if (listeners.size === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { createStorageAdapter } from \"./createStorageAdapter\";\nimport { merge } from \"./merge\";\nimport { Listener, StateModifier, Store, StoreConfig } from \"./types\";\n\n/**\n * Creates a store with an initial state and actions that can modify the state.\n *\n * @param {StoreConfig<TState, TActions>} [config] - The configuration for the store.\n *\n * @returns {Store<TState, TActions>} The created store with state management methods.\n *\n * @
|
|
1
|
+
{"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { createStorageAdapter } from \"./createStorageAdapter\";\nimport { merge } from \"./merge\";\nimport { Listener, StateModifier, Store, StoreConfig } from \"./types\";\n\n/**\n * Creates a store with an initial state and actions that can modify the state.\n *\n * @param {StoreConfig<TState, TActions>} [config] - The configuration for the store.\n *\n * @returns {Store<TState, TActions>} The created store with state management methods.\n *\n * @see {@link https://dharma.fransek.dev/core/createstore/}\n */\nexport const createStore = <TState, TActions = undefined>(\n config: StoreConfig<TState, TActions>,\n): Store<TState, TActions> => {\n const { initialState, defineActions, onLoad, onAttach, onDetach, onChange } =\n config;\n\n let state = initialState;\n\n const get = () => state;\n\n const setSilently = (stateModifier: StateModifier<TState>) => {\n state = merge(state, stateModifier);\n return state;\n };\n\n const set = (stateModifier: StateModifier<TState>) => {\n setSilently(stateModifier);\n dispatch();\n return state;\n };\n\n const resetSilently = () => setSilently(initialState);\n\n const reset = () => {\n resetSilently();\n dispatch();\n return state;\n };\n\n const listeners = new Set<Listener<TState>>();\n const actions = defineActions?.({ set, get, reset }) as TActions;\n const storageAdapter = createStorageAdapter(config, get, set);\n\n const subscribe = (listener: Listener<TState>) => {\n if (listeners.size === 0) {\n onAttach?.({ state, set, reset });\n storageAdapter?.onAttach();\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 storageAdapter?.onDetach();\n }\n };\n };\n\n const dispatch = () => {\n onChange?.({\n state,\n set: setSilently,\n reset: resetSilently,\n });\n storageAdapter?.onChange(state);\n listeners.forEach((listener) => listener(state));\n };\n\n onLoad?.({ state, set, reset });\n storageAdapter?.onLoad();\n\n return {\n get,\n subscribe,\n actions,\n };\n};\n"],"names":["merge","createStorageAdapter"],"mappings":";;;;;AAIA;;;;;;;;AAQG;AACI,MAAM,WAAW,GAAG,CACzB,MAAqC,KACV;AAC3B,IAAA,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,GACzE,MAAM;IAER,IAAI,KAAK,GAAG,YAAY;AAExB,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;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,aAAa,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC;IAErD,MAAM,KAAK,GAAG,MAAK;AACjB,QAAA,aAAa,EAAE;AACf,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;AAED,IAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB;AAC7C,IAAA,MAAM,OAAO,GAAG,aAAa,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAa;IAChE,MAAM,cAAc,GAAGC,yCAAoB,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC;AAE7D,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;YACjC,cAAc,EAAE,QAAQ,EAAE;QAC5B;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;gBACjC,cAAc,EAAE,QAAQ,EAAE;YAC5B;AACF,QAAA,CAAC;AACH,IAAA,CAAC;IAED,MAAM,QAAQ,GAAG,MAAK;AACpB,QAAA,QAAQ,GAAG;YACT,KAAK;AACL,YAAA,GAAG,EAAE,WAAW;AAChB,YAAA,KAAK,EAAE,aAAa;AACrB,SAAA,CAAC;AACF,QAAA,cAAc,EAAE,QAAQ,CAAC,KAAK,CAAC;AAC/B,QAAA,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC;AAClD,IAAA,CAAC;IAED,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAC/B,cAAc,EAAE,MAAM,EAAE;IAExB,OAAO;QACL,GAAG;QACH,SAAS;QACT,OAAO;KACR;AACH;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const deeplyEquals: (a: unknown, b: unknown) => boolean;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const deeplyEquals = (a, b) => {
|
|
4
|
+
if (a === b) {
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
if (a === null ||
|
|
8
|
+
b === null ||
|
|
9
|
+
typeof a !== "object" ||
|
|
10
|
+
typeof b !== "object") {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const protoA = Object.getPrototypeOf(a);
|
|
14
|
+
const protoB = Object.getPrototypeOf(b);
|
|
15
|
+
if (protoA !== protoB) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (protoA === Array.prototype) {
|
|
19
|
+
const arrA = a;
|
|
20
|
+
const arrB = b;
|
|
21
|
+
if (arrA.length !== arrB.length)
|
|
22
|
+
return false;
|
|
23
|
+
for (let i = 0; i < arrA.length; i++) {
|
|
24
|
+
if (!deeplyEquals(arrA[i], arrB[i]))
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
if (protoA !== Object.prototype) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const keysA = Object.keys(a);
|
|
33
|
+
const keysB = Object.keys(b);
|
|
34
|
+
if (keysA.length !== keysB.length) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
for (const key of keysA) {
|
|
38
|
+
if (!keysB.includes(key)) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (!deeplyEquals(a[key], b[key])) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
exports.deeplyEquals = deeplyEquals;
|
|
49
|
+
//# sourceMappingURL=deeplyEquals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deeplyEquals.js","sources":["../../../src/lib/deeplyEquals.ts"],"sourcesContent":["export const deeplyEquals = (a: unknown, b: unknown): boolean => {\n if (a === b) {\n return true;\n }\n\n if (\n a === null ||\n b === null ||\n typeof a !== \"object\" ||\n typeof b !== \"object\"\n ) {\n return false;\n }\n\n const protoA = Object.getPrototypeOf(a);\n const protoB = Object.getPrototypeOf(b);\n\n if (protoA !== protoB) {\n return false;\n }\n\n if (protoA === Array.prototype) {\n const arrA = a as unknown[];\n const arrB = b as unknown[];\n if (arrA.length !== arrB.length) return false;\n for (let i = 0; i < arrA.length; i++) {\n if (!deeplyEquals(arrA[i], arrB[i])) return false;\n }\n return true;\n }\n\n if (protoA !== Object.prototype) {\n return false;\n }\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) {\n return false;\n }\n\n for (const key of keysA) {\n if (!keysB.includes(key)) {\n return false;\n }\n if (\n !deeplyEquals(\n (a as Record<string, unknown>)[key],\n (b as Record<string, unknown>)[key],\n )\n ) {\n return false;\n }\n }\n\n return true;\n};\n"],"names":[],"mappings":";;MAAa,YAAY,GAAG,CAAC,CAAU,EAAE,CAAU,KAAa;AAC9D,IAAA,IAAI,CAAC,KAAK,CAAC,EAAE;AACX,QAAA,OAAO,IAAI;IACb;IAEA,IACE,CAAC,KAAK,IAAI;AACV,QAAA,CAAC,KAAK,IAAI;QACV,OAAO,CAAC,KAAK,QAAQ;AACrB,QAAA,OAAO,CAAC,KAAK,QAAQ,EACrB;AACA,QAAA,OAAO,KAAK;IACd;IAEA,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;AAEvC,IAAA,IAAI,MAAM,KAAK,MAAM,EAAE;AACrB,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,IAAI,MAAM,KAAK,KAAK,CAAC,SAAS,EAAE;QAC9B,MAAM,IAAI,GAAG,CAAc;QAC3B,MAAM,IAAI,GAAG,CAAc;AAC3B,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;AAAE,YAAA,OAAO,KAAK;AAC7C,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACpC,YAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AAAE,gBAAA,OAAO,KAAK;QACnD;AACA,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,IAAI,MAAM,KAAK,MAAM,CAAC,SAAS,EAAE;AAC/B,QAAA,OAAO,KAAK;IACd;IAEA,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAE5B,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE;AACjC,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE;QACvB,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;AACxB,YAAA,OAAO,KAAK;QACd;AACA,QAAA,IACE,CAAC,YAAY,CACV,CAA6B,CAAC,GAAG,CAAC,EAClC,CAA6B,CAAC,GAAG,CAAC,CACpC,EACD;AACA,YAAA,OAAO,KAAK;QACd;IACF;AAEA,IAAA,OAAO,IAAI;AACb;;;;"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DerivedStore, Store } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a derived store from an existing store.
|
|
4
|
+
*
|
|
5
|
+
* A derived store computes its state based on the original store's state using a derive function.
|
|
6
|
+
* The derived store's value is memoized and only recomputed when requested.
|
|
7
|
+
*
|
|
8
|
+
* @param store - The original store to derive from
|
|
9
|
+
* @param deriveFn - A function that computes the derived state from the original state
|
|
10
|
+
* @param dependencyFn - Optional function that returns an array of dependencies. If provided, the derived state will only be recomputed when these dependencies change
|
|
11
|
+
*
|
|
12
|
+
* @returns A new store containing the derived state
|
|
13
|
+
*
|
|
14
|
+
* @see {@link https://dharma.fransek.dev/core/derive/}
|
|
15
|
+
*/
|
|
16
|
+
export declare const derive: <TState, TActions, TDerived>(store: Store<TState, TActions>, deriveFn: (state: TState) => TDerived, dependencyFn?: (state: TState) => unknown[]) => DerivedStore<TDerived>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var deeplyEquals = require('./deeplyEquals.js');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a derived store from an existing store.
|
|
7
|
+
*
|
|
8
|
+
* A derived store computes its state based on the original store's state using a derive function.
|
|
9
|
+
* The derived store's value is memoized and only recomputed when requested.
|
|
10
|
+
*
|
|
11
|
+
* @param store - The original store to derive from
|
|
12
|
+
* @param deriveFn - A function that computes the derived state from the original state
|
|
13
|
+
* @param dependencyFn - Optional function that returns an array of dependencies. If provided, the derived state will only be recomputed when these dependencies change
|
|
14
|
+
*
|
|
15
|
+
* @returns A new store containing the derived state
|
|
16
|
+
*
|
|
17
|
+
* @see {@link https://dharma.fransek.dev/core/derive/}
|
|
18
|
+
*/
|
|
19
|
+
const derive = (store, deriveFn, dependencyFn) => {
|
|
20
|
+
let memo;
|
|
21
|
+
let prev;
|
|
22
|
+
let isStale = true;
|
|
23
|
+
let unsubscribe;
|
|
24
|
+
const listeners = new Set();
|
|
25
|
+
const compute = () => {
|
|
26
|
+
memo = deriveFn(store.get());
|
|
27
|
+
isStale = false;
|
|
28
|
+
return memo;
|
|
29
|
+
};
|
|
30
|
+
const get = () => {
|
|
31
|
+
if (!dependencyFn) {
|
|
32
|
+
if (isStale) {
|
|
33
|
+
return compute();
|
|
34
|
+
}
|
|
35
|
+
return memo;
|
|
36
|
+
}
|
|
37
|
+
const next = dependencyFn(store.get());
|
|
38
|
+
isStale &&= !prev || !deeplyEquals.deeplyEquals(prev, next);
|
|
39
|
+
if (isStale) {
|
|
40
|
+
prev = next;
|
|
41
|
+
return compute();
|
|
42
|
+
}
|
|
43
|
+
return memo;
|
|
44
|
+
};
|
|
45
|
+
const subscribe = (listener) => {
|
|
46
|
+
if (listeners.size === 0) {
|
|
47
|
+
mount();
|
|
48
|
+
}
|
|
49
|
+
listener(get());
|
|
50
|
+
listeners.add(listener);
|
|
51
|
+
return () => {
|
|
52
|
+
listeners.delete(listener);
|
|
53
|
+
if (listeners.size === 0) {
|
|
54
|
+
unmount();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
const mount = () => {
|
|
59
|
+
if (!unsubscribe) {
|
|
60
|
+
unsubscribe = store.subscribe(() => {
|
|
61
|
+
isStale = true;
|
|
62
|
+
listeners.forEach((listener) => listener(get()));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const unmount = () => {
|
|
67
|
+
unsubscribe?.();
|
|
68
|
+
unsubscribe = undefined;
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
get,
|
|
72
|
+
subscribe,
|
|
73
|
+
actions: {
|
|
74
|
+
mount,
|
|
75
|
+
unmount,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
exports.derive = derive;
|
|
81
|
+
//# sourceMappingURL=derive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.js","sources":["../../../src/lib/derive.ts"],"sourcesContent":["import { deeplyEquals } from \"./deeplyEquals\";\nimport { DerivedStore, Listener, Store } from \"./types\";\n\n/**\n * Creates a derived store from an existing store.\n *\n * A derived store computes its state based on the original store's state using a derive function.\n * The derived store's value is memoized and only recomputed when requested.\n *\n * @param store - The original store to derive from\n * @param deriveFn - A function that computes the derived state from the original state\n * @param dependencyFn - Optional function that returns an array of dependencies. If provided, the derived state will only be recomputed when these dependencies change\n *\n * @returns A new store containing the derived state\n *\n * @see {@link https://dharma.fransek.dev/core/derive/}\n */\nexport const derive = <TState, TActions, TDerived>(\n store: Store<TState, TActions>,\n deriveFn: (state: TState) => TDerived,\n dependencyFn?: (state: TState) => unknown[],\n): DerivedStore<TDerived> => {\n let memo: TDerived | undefined;\n let prev: unknown[] | undefined;\n let isStale = true;\n let unsubscribe: (() => void) | undefined;\n const listeners = new Set<Listener<TDerived>>();\n\n const compute = () => {\n memo = deriveFn(store.get());\n isStale = false;\n return memo;\n };\n\n const get = () => {\n if (!dependencyFn) {\n if (isStale) {\n return compute();\n }\n\n return memo as TDerived;\n }\n\n const next = dependencyFn(store.get());\n isStale &&= !prev || !deeplyEquals(prev, next);\n\n if (isStale) {\n prev = next;\n return compute();\n }\n\n return memo as TDerived;\n };\n\n const subscribe = (listener: Listener<TDerived>) => {\n if (listeners.size === 0) {\n mount();\n }\n\n listener(get());\n listeners.add(listener);\n\n return () => {\n listeners.delete(listener);\n\n if (listeners.size === 0) {\n unmount();\n }\n };\n };\n\n const mount = () => {\n if (!unsubscribe) {\n unsubscribe = store.subscribe(() => {\n isStale = true;\n listeners.forEach((listener) => listener(get()));\n });\n }\n };\n\n const unmount = () => {\n unsubscribe?.();\n unsubscribe = undefined;\n };\n\n return {\n get,\n subscribe,\n actions: {\n mount,\n unmount,\n },\n };\n};\n"],"names":["deeplyEquals"],"mappings":";;;;AAGA;;;;;;;;;;;;;AAaG;AACI,MAAM,MAAM,GAAG,CACpB,KAA8B,EAC9B,QAAqC,EACrC,YAA2C,KACjB;AAC1B,IAAA,IAAI,IAA0B;AAC9B,IAAA,IAAI,IAA2B;IAC/B,IAAI,OAAO,GAAG,IAAI;AAClB,IAAA,IAAI,WAAqC;AACzC,IAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAsB;IAE/C,MAAM,OAAO,GAAG,MAAK;QACnB,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAC5B,OAAO,GAAG,KAAK;AACf,QAAA,OAAO,IAAI;AACb,IAAA,CAAC;IAED,MAAM,GAAG,GAAG,MAAK;QACf,IAAI,CAAC,YAAY,EAAE;YACjB,IAAI,OAAO,EAAE;gBACX,OAAO,OAAO,EAAE;YAClB;AAEA,YAAA,OAAO,IAAgB;QACzB;QAEA,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACtC,OAAO,KAAK,CAAC,IAAI,IAAI,CAACA,yBAAY,CAAC,IAAI,EAAE,IAAI,CAAC;QAE9C,IAAI,OAAO,EAAE;YACX,IAAI,GAAG,IAAI;YACX,OAAO,OAAO,EAAE;QAClB;AAEA,QAAA,OAAO,IAAgB;AACzB,IAAA,CAAC;AAED,IAAA,MAAM,SAAS,GAAG,CAAC,QAA4B,KAAI;AACjD,QAAA,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE;AACxB,YAAA,KAAK,EAAE;QACT;AAEA,QAAA,QAAQ,CAAC,GAAG,EAAE,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;AACxB,gBAAA,OAAO,EAAE;YACX;AACF,QAAA,CAAC;AACH,IAAA,CAAC;IAED,MAAM,KAAK,GAAG,MAAK;QACjB,IAAI,CAAC,WAAW,EAAE;AAChB,YAAA,WAAW,GAAG,KAAK,CAAC,SAAS,CAAC,MAAK;gBACjC,OAAO,GAAG,IAAI;AACd,gBAAA,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;AAClD,YAAA,CAAC,CAAC;QACJ;AACF,IAAA,CAAC;IAED,MAAM,OAAO,GAAG,MAAK;QACnB,WAAW,IAAI;QACf,WAAW,GAAG,SAAS;AACzB,IAAA,CAAC;IAED,OAAO;QACL,GAAG;QACH,SAAS;AACT,QAAA,OAAO,EAAE;YACP,KAAK;YACL,OAAO;AACR,SAAA;KACF;AACH;;;;"}
|
package/dist/cjs/lib/merge.d.ts
CHANGED
|
@@ -6,30 +6,6 @@ import { StateModifier } from "./types";
|
|
|
6
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
|
-
* @
|
|
10
|
-
* ```ts
|
|
11
|
-
* import { createStore, merge, StateModifier } from "dharma-core";
|
|
12
|
-
*
|
|
13
|
-
* const store = createStore({
|
|
14
|
-
* initialState: {
|
|
15
|
-
* counter: {
|
|
16
|
-
* count: 0,
|
|
17
|
-
* },
|
|
18
|
-
* // ...
|
|
19
|
-
* },
|
|
20
|
-
* defineActions: ({ set }) => {
|
|
21
|
-
* const setCounter = (counter: StateModifier<{ count: number }>) =>
|
|
22
|
-
* set((state) => ({
|
|
23
|
-
* counter: merge(state.counter, counter),
|
|
24
|
-
* }));
|
|
25
|
-
*
|
|
26
|
-
* return {
|
|
27
|
-
* increment: () => setCounter((state) => ({ count: state.count + 1 })),
|
|
28
|
-
* decrement: () => setCounter((state) => ({ count: state.count - 1 })),
|
|
29
|
-
* // ...
|
|
30
|
-
* };
|
|
31
|
-
* },
|
|
32
|
-
* });
|
|
33
|
-
* ```
|
|
9
|
+
* @see {@link https://dharma.fransek.dev/core/merge/}
|
|
34
10
|
*/
|
|
35
|
-
export declare const merge: <T
|
|
11
|
+
export declare const merge: <T>(currentState: T, stateModifier: StateModifier<T>) => T;
|
package/dist/cjs/lib/merge.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
|
+
const isObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
3
5
|
/**
|
|
4
6
|
* Merges the current state with a state modifier.
|
|
5
7
|
*
|
|
@@ -7,37 +9,20 @@
|
|
|
7
9
|
* @param {StateModifier<T>} stateModifier - A function or object that modifies the state.
|
|
8
10
|
* @returns {T} The new state object, which is a combination of the current state and the state modifier.
|
|
9
11
|
*
|
|
10
|
-
* @
|
|
11
|
-
* ```ts
|
|
12
|
-
* import { createStore, merge, StateModifier } from "dharma-core";
|
|
13
|
-
*
|
|
14
|
-
* const store = createStore({
|
|
15
|
-
* initialState: {
|
|
16
|
-
* counter: {
|
|
17
|
-
* count: 0,
|
|
18
|
-
* },
|
|
19
|
-
* // ...
|
|
20
|
-
* },
|
|
21
|
-
* defineActions: ({ set }) => {
|
|
22
|
-
* const setCounter = (counter: StateModifier<{ count: number }>) =>
|
|
23
|
-
* set((state) => ({
|
|
24
|
-
* counter: merge(state.counter, counter),
|
|
25
|
-
* }));
|
|
26
|
-
*
|
|
27
|
-
* return {
|
|
28
|
-
* increment: () => setCounter((state) => ({ count: state.count + 1 })),
|
|
29
|
-
* decrement: () => setCounter((state) => ({ count: state.count - 1 })),
|
|
30
|
-
* // ...
|
|
31
|
-
* };
|
|
32
|
-
* },
|
|
33
|
-
* });
|
|
34
|
-
* ```
|
|
12
|
+
* @see {@link https://dharma.fransek.dev/core/merge/}
|
|
35
13
|
*/
|
|
36
14
|
const merge = (currentState, stateModifier) => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
15
|
+
if (isObject(stateModifier)) {
|
|
16
|
+
return { ...currentState, ...stateModifier };
|
|
17
|
+
}
|
|
18
|
+
if (typeof stateModifier === "function") {
|
|
19
|
+
const modifiedState = stateModifier(currentState);
|
|
20
|
+
if (isObject(modifiedState)) {
|
|
21
|
+
return { ...currentState, ...modifiedState };
|
|
22
|
+
}
|
|
23
|
+
return modifiedState;
|
|
24
|
+
}
|
|
25
|
+
return stateModifier;
|
|
41
26
|
};
|
|
42
27
|
|
|
43
28
|
exports.merge = merge;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.js","sources":["../../../src/lib/merge.ts"],"sourcesContent":["import { StateModifier } from \"./types\";\n\n/**\n * Merges the current state with a state modifier.\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 * @
|
|
1
|
+
{"version":3,"file":"merge.js","sources":["../../../src/lib/merge.ts"],"sourcesContent":["import { StateModifier } from \"./types\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst isObject = (value: unknown): value is Record<keyof any, unknown> =>\n !!value && typeof value === \"object\" && !Array.isArray(value);\n\n/**\n * Merges the current state with a state modifier.\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 * @see {@link https://dharma.fransek.dev/core/merge/}\n */\nexport const merge = <T>(\n currentState: T,\n stateModifier: StateModifier<T>,\n): T => {\n if (isObject(stateModifier)) {\n return { ...currentState, ...stateModifier };\n }\n\n if (typeof stateModifier === \"function\") {\n const modifiedState = stateModifier(currentState);\n\n if (isObject(modifiedState)) {\n return { ...currentState, ...modifiedState };\n }\n\n return modifiedState;\n }\n\n return stateModifier;\n};\n"],"names":[],"mappings":";;AAEA;AACA,MAAM,QAAQ,GAAG,CAAC,KAAc,KAC9B,CAAC,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;AAE/D;;;;;;;;AAQG;MACU,KAAK,GAAG,CACnB,YAAe,EACf,aAA+B,KAC1B;AACL,IAAA,IAAI,QAAQ,CAAC,aAAa,CAAC,EAAE;AAC3B,QAAA,OAAO,EAAE,GAAG,YAAY,EAAE,GAAG,aAAa,EAAE;IAC9C;AAEA,IAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;AACvC,QAAA,MAAM,aAAa,GAAG,aAAa,CAAC,YAAY,CAAC;AAEjD,QAAA,IAAI,QAAQ,CAAC,aAAa,CAAC,EAAE;AAC3B,YAAA,OAAO,EAAE,GAAG,YAAY,EAAE,GAAG,aAAa,EAAE;QAC9C;AAEA,QAAA,OAAO,aAAa;IACtB;AAEA,IAAA,OAAO,aAAa;AACtB;;;;"}
|
package/dist/cjs/lib/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export type Listener<TState
|
|
2
|
-
export type SetState<TState
|
|
3
|
-
export type Store<TState
|
|
1
|
+
export type Listener<TState> = (state: TState) => void;
|
|
2
|
+
export type SetState<TState> = (stateModifier: StateModifier<TState>) => TState;
|
|
3
|
+
export type Store<TState, TActions> = {
|
|
4
4
|
/** Returns the current state of the store. */
|
|
5
5
|
get: () => TState;
|
|
6
6
|
/** Actions that can modify the state of the store. */
|
|
@@ -8,7 +8,14 @@ export type Store<TState extends object, TActions extends object> = {
|
|
|
8
8
|
/** Subscribes to changes in the state of the store. Returns an unsubscribe function. */
|
|
9
9
|
subscribe: (listener: Listener<TState>) => () => void;
|
|
10
10
|
};
|
|
11
|
-
export type
|
|
11
|
+
export type DerivedStoreActions = {
|
|
12
|
+
/** Start listening to changes in the original store. Called automatically on the first subscription. */
|
|
13
|
+
mount: () => void;
|
|
14
|
+
/** Stop listening to changes in the original store. Called automatically when there are no more subscribers. */
|
|
15
|
+
unmount: () => void;
|
|
16
|
+
};
|
|
17
|
+
export type DerivedStore<TState> = Store<TState, DerivedStoreActions>;
|
|
18
|
+
export type StoreEventContext<TState> = {
|
|
12
19
|
/** The current state of the store. */
|
|
13
20
|
state: TState;
|
|
14
21
|
/** Sets the state of the store. */
|
|
@@ -16,9 +23,9 @@ export type StoreEventContext<TState extends object> = {
|
|
|
16
23
|
/** Resets the state of the store to its initial value. */
|
|
17
24
|
reset: () => TState;
|
|
18
25
|
};
|
|
19
|
-
export type StoreEventListener<TState
|
|
20
|
-
export type StateModifier<TState
|
|
21
|
-
export type StateHandler<TState
|
|
26
|
+
export type StoreEventListener<TState> = (context: StoreEventContext<TState>) => void;
|
|
27
|
+
export type StateModifier<TState> = Partial<TState> | ((state: TState) => Partial<TState>);
|
|
28
|
+
export type StateHandler<TState> = {
|
|
22
29
|
/** Returns the current state of the store. */
|
|
23
30
|
get: () => TState;
|
|
24
31
|
/** Sets the state of the store. */
|
|
@@ -26,8 +33,8 @@ export type StateHandler<TState extends object> = {
|
|
|
26
33
|
/** Resets the state of the store to its initial value. */
|
|
27
34
|
reset: () => TState;
|
|
28
35
|
};
|
|
29
|
-
export type DefineActions<TState
|
|
30
|
-
export type BaseConfig<TState
|
|
36
|
+
export type DefineActions<TState, TActions> = (stateHandler: StateHandler<TState>) => TActions;
|
|
37
|
+
export type BaseConfig<TState, TActions> = {
|
|
31
38
|
/** The initial state of the store. */
|
|
32
39
|
initialState: TState;
|
|
33
40
|
/** A function that defines actions that can modify the state. */
|
|
@@ -55,7 +62,7 @@ export type NoPersistConfig = {
|
|
|
55
62
|
/** Whether to persist the state of the store. */
|
|
56
63
|
persist?: false;
|
|
57
64
|
};
|
|
58
|
-
export type PersistConfig<TState
|
|
65
|
+
export type PersistConfig<TState> = {
|
|
59
66
|
/** Whether to persist the state of the store. */
|
|
60
67
|
persist: true;
|
|
61
68
|
/** The unique key used to identify this store in storage. Required if `persist` is true. */
|
|
@@ -65,4 +72,4 @@ export type PersistConfig<TState extends object> = {
|
|
|
65
72
|
/** The serializer to use for storing the state. Defaults to JSON. */
|
|
66
73
|
serializer?: Serializer<TState>;
|
|
67
74
|
};
|
|
68
|
-
export type StoreConfig<TState
|
|
75
|
+
export type StoreConfig<TState, TActions> = BaseConfig<TState, TActions> & (PersistConfig<TState> | NoPersistConfig);
|
package/dist/esm/index.d.ts
CHANGED
package/dist/esm/index.js
CHANGED
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SetState, StoreConfig } from "./types";
|
|
2
|
-
export declare const createStorageAdapter: <TState
|
|
2
|
+
export declare const createStorageAdapter: <TState, TActions>(config: StoreConfig<TState, TActions>, get: () => TState, set: SetState<TState>) => {
|
|
3
3
|
onLoad: () => Promise<void>;
|
|
4
4
|
onChange: (newState: TState) => Promise<void>;
|
|
5
5
|
onAttach: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createStorageAdapter.js","sources":["../../../src/lib/createStorageAdapter.ts"],"sourcesContent":["import { SetState, StorageAPI, StoreConfig } from \"./types\";\nimport { warn } from \"./warn\";\n\nexport const createStorageAdapter =
|
|
1
|
+
{"version":3,"file":"createStorageAdapter.js","sources":["../../../src/lib/createStorageAdapter.ts"],"sourcesContent":["import { SetState, StorageAPI, StoreConfig } from \"./types\";\nimport { warn } from \"./warn\";\n\nexport const createStorageAdapter = <TState, TActions>(\n config: StoreConfig<TState, TActions>,\n get: () => TState,\n set: SetState<TState>,\n) => {\n if (!config.persist) {\n return null;\n }\n\n const IS_BROWSER = typeof window !== \"undefined\";\n\n const storage =\n config.storage ?? (IS_BROWSER ? (localStorage as StorageAPI) : undefined);\n\n if (!storage) {\n warn(\n \"No storage provider found. If this happened during SSR, you can safely ignore this warning.\",\n );\n return null;\n }\n\n const { key, serializer = JSON, initialState } = config;\n const initKey = `init_${key}`;\n\n const onLoad = async () => {\n try {\n let initialStateSnapshot = storage.getItem(initKey);\n const initialStateString = serializer.stringify(initialState);\n\n if (initialStateSnapshot instanceof Promise) {\n initialStateSnapshot = await initialStateSnapshot;\n }\n\n if (initialStateSnapshot !== initialStateString) {\n storage.setItem(initKey, initialStateString);\n storage.removeItem(key);\n }\n } catch {\n warn(\n \"Failed to initialize snapshots. If this happened during SSR, you can safely ignore this warning.\",\n );\n }\n };\n\n const syncWithSnapshot = async () => {\n try {\n let currentSnapshot = storage.getItem(key);\n\n if (currentSnapshot instanceof Promise) {\n currentSnapshot = await currentSnapshot;\n }\n\n if (currentSnapshot && currentSnapshot !== serializer.stringify(get())) {\n set(serializer.parse(currentSnapshot));\n }\n } catch {\n warn(\"Failed to update state from snapshot\");\n }\n };\n\n const onAttach = () => {\n syncWithSnapshot();\n\n if (IS_BROWSER) {\n window.addEventListener(\"focus\", syncWithSnapshot);\n }\n };\n\n const onChange = async (newState: TState) => {\n try {\n let currentSnapshot = storage.getItem(key);\n const newSnapshot = serializer.stringify(newState);\n\n if (currentSnapshot instanceof Promise) {\n currentSnapshot = await currentSnapshot;\n }\n\n if (newSnapshot !== currentSnapshot) {\n storage.setItem(key, newSnapshot);\n }\n } catch {\n warn(\"Failed to update snapshot\");\n }\n };\n\n const onDetach = () => {\n if (IS_BROWSER) {\n window.removeEventListener(\"focus\", syncWithSnapshot);\n }\n };\n\n return {\n onLoad,\n onChange,\n onAttach,\n onDetach,\n };\n};\n"],"names":[],"mappings":";;AAGO,MAAM,oBAAoB,GAAG,CAClC,MAAqC,EACrC,GAAiB,EACjB,GAAqB,KACnB;AACF,IAAA,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;AACnB,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW;AAEhD,IAAA,MAAM,OAAO,GACX,MAAM,CAAC,OAAO,KAAK,UAAU,GAAI,YAA2B,GAAG,SAAS,CAAC;IAE3E,IAAI,CAAC,OAAO,EAAE;QACZ,IAAI,CACF,6FAA6F,CAC9F;AACD,QAAA,OAAO,IAAI;IACb;IAEA,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM;AACvD,IAAA,MAAM,OAAO,GAAG,CAAA,KAAA,EAAQ,GAAG,EAAE;AAE7B,IAAA,MAAM,MAAM,GAAG,YAAW;AACxB,QAAA,IAAI;YACF,IAAI,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;YACnD,MAAM,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC;AAE7D,YAAA,IAAI,oBAAoB,YAAY,OAAO,EAAE;gBAC3C,oBAAoB,GAAG,MAAM,oBAAoB;YACnD;AAEA,YAAA,IAAI,oBAAoB,KAAK,kBAAkB,EAAE;AAC/C,gBAAA,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,kBAAkB,CAAC;AAC5C,gBAAA,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YACzB;QACF;AAAE,QAAA,MAAM;YACN,IAAI,CACF,kGAAkG,CACnG;QACH;AACF,IAAA,CAAC;AAED,IAAA,MAAM,gBAAgB,GAAG,YAAW;AAClC,QAAA,IAAI;YACF,IAAI,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;AAE1C,YAAA,IAAI,eAAe,YAAY,OAAO,EAAE;gBACtC,eAAe,GAAG,MAAM,eAAe;YACzC;AAEA,YAAA,IAAI,eAAe,IAAI,eAAe,KAAK,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE;gBACtE,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;YACxC;QACF;AAAE,QAAA,MAAM;YACN,IAAI,CAAC,sCAAsC,CAAC;QAC9C;AACF,IAAA,CAAC;IAED,MAAM,QAAQ,GAAG,MAAK;AACpB,QAAA,gBAAgB,EAAE;QAElB,IAAI,UAAU,EAAE;AACd,YAAA,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,CAAC;QACpD;AACF,IAAA,CAAC;AAED,IAAA,MAAM,QAAQ,GAAG,OAAO,QAAgB,KAAI;AAC1C,QAAA,IAAI;YACF,IAAI,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;YAC1C,MAAM,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,QAAQ,CAAC;AAElD,YAAA,IAAI,eAAe,YAAY,OAAO,EAAE;gBACtC,eAAe,GAAG,MAAM,eAAe;YACzC;AAEA,YAAA,IAAI,WAAW,KAAK,eAAe,EAAE;AACnC,gBAAA,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC;YACnC;QACF;AAAE,QAAA,MAAM;YACN,IAAI,CAAC,2BAA2B,CAAC;QACnC;AACF,IAAA,CAAC;IAED,MAAM,QAAQ,GAAG,MAAK;QACpB,IAAI,UAAU,EAAE;AACd,YAAA,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,CAAC;QACvD;AACF,IAAA,CAAC;IAED,OAAO;QACL,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,QAAQ;KACT;AACH;;;;"}
|
|
@@ -6,79 +6,6 @@ import { Store, StoreConfig } from "./types";
|
|
|
6
6
|
*
|
|
7
7
|
* @returns {Store<TState, TActions>} The created store with state management methods.
|
|
8
8
|
*
|
|
9
|
-
* @
|
|
10
|
-
* Basic usage:
|
|
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
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* Lifecycle hooks:
|
|
25
|
-
* ```ts
|
|
26
|
-
* import { createStore } from "dharma-core";
|
|
27
|
-
* import { State } from "./types";
|
|
28
|
-
*
|
|
29
|
-
* const initialState: State = { data: null, loading: true, error: null };
|
|
30
|
-
*
|
|
31
|
-
* const store = createStore({
|
|
32
|
-
* initialState,
|
|
33
|
-
* onAttach: async ({ set }) => {
|
|
34
|
-
* try {
|
|
35
|
-
* const response = await fetch("https://api.example.com/data");
|
|
36
|
-
* const data = await response.json();
|
|
37
|
-
* set({ data, loading: false });
|
|
38
|
-
* } catch (error) {
|
|
39
|
-
* set({ error, loading: false });
|
|
40
|
-
* }
|
|
41
|
-
* },
|
|
42
|
-
* onDetach: ({ reset }) => reset(),
|
|
43
|
-
* });
|
|
44
|
-
* ```
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* Persisting state:
|
|
48
|
-
* ```ts
|
|
49
|
-
* import { createStore } from "dharma-core";
|
|
50
|
-
*
|
|
51
|
-
* const store = createStore({
|
|
52
|
-
* persist: true,
|
|
53
|
-
* key: "count",
|
|
54
|
-
* initialState: { count: 0 },
|
|
55
|
-
* defineActions: ({ set }) => ({
|
|
56
|
-
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
57
|
-
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
58
|
-
* }),
|
|
59
|
-
* });
|
|
60
|
-
* ```
|
|
61
|
-
*
|
|
62
|
-
* **Note:** The state needs to be serializable by whatever serializer you choose (JSON by default).
|
|
63
|
-
* If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* Custom storage and serializer:
|
|
67
|
-
* ```ts
|
|
68
|
-
* import { createStore } from "dharma-core";
|
|
69
|
-
* import superjson from "superjson";
|
|
70
|
-
*
|
|
71
|
-
* const store = createStore({
|
|
72
|
-
* persist: true,
|
|
73
|
-
* key: "count",
|
|
74
|
-
* storage: sessionStorage,
|
|
75
|
-
* serializer: superjson,
|
|
76
|
-
* initialState: { count: 0 },
|
|
77
|
-
* defineActions: ({ set }) => ({
|
|
78
|
-
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
79
|
-
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
80
|
-
* }),
|
|
81
|
-
* });
|
|
82
|
-
* ```
|
|
9
|
+
* @see {@link https://dharma.fransek.dev/core/createstore/}
|
|
83
10
|
*/
|
|
84
|
-
export declare const createStore: <TState
|
|
11
|
+
export declare const createStore: <TState, TActions = undefined>(config: StoreConfig<TState, TActions>) => Store<TState, TActions>;
|
|
@@ -8,80 +8,7 @@ import { merge } from './merge.js';
|
|
|
8
8
|
*
|
|
9
9
|
* @returns {Store<TState, TActions>} The created store with state management methods.
|
|
10
10
|
*
|
|
11
|
-
* @
|
|
12
|
-
* Basic usage:
|
|
13
|
-
* ```ts
|
|
14
|
-
* import { createStore } from "dharma-core";
|
|
15
|
-
*
|
|
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
|
-
* ```
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* Lifecycle hooks:
|
|
27
|
-
* ```ts
|
|
28
|
-
* import { createStore } from "dharma-core";
|
|
29
|
-
* import { State } from "./types";
|
|
30
|
-
*
|
|
31
|
-
* const initialState: State = { data: null, loading: true, error: null };
|
|
32
|
-
*
|
|
33
|
-
* const store = createStore({
|
|
34
|
-
* initialState,
|
|
35
|
-
* onAttach: async ({ set }) => {
|
|
36
|
-
* try {
|
|
37
|
-
* const response = await fetch("https://api.example.com/data");
|
|
38
|
-
* const data = await response.json();
|
|
39
|
-
* set({ data, loading: false });
|
|
40
|
-
* } catch (error) {
|
|
41
|
-
* set({ error, loading: false });
|
|
42
|
-
* }
|
|
43
|
-
* },
|
|
44
|
-
* onDetach: ({ reset }) => reset(),
|
|
45
|
-
* });
|
|
46
|
-
* ```
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* Persisting state:
|
|
50
|
-
* ```ts
|
|
51
|
-
* import { createStore } from "dharma-core";
|
|
52
|
-
*
|
|
53
|
-
* const store = createStore({
|
|
54
|
-
* persist: true,
|
|
55
|
-
* key: "count",
|
|
56
|
-
* initialState: { count: 0 },
|
|
57
|
-
* defineActions: ({ set }) => ({
|
|
58
|
-
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
59
|
-
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
60
|
-
* }),
|
|
61
|
-
* });
|
|
62
|
-
* ```
|
|
63
|
-
*
|
|
64
|
-
* **Note:** The state needs to be serializable by whatever serializer you choose (JSON by default).
|
|
65
|
-
* If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* Custom storage and serializer:
|
|
69
|
-
* ```ts
|
|
70
|
-
* import { createStore } from "dharma-core";
|
|
71
|
-
* import superjson from "superjson";
|
|
72
|
-
*
|
|
73
|
-
* const store = createStore({
|
|
74
|
-
* persist: true,
|
|
75
|
-
* key: "count",
|
|
76
|
-
* storage: sessionStorage,
|
|
77
|
-
* serializer: superjson,
|
|
78
|
-
* initialState: { count: 0 },
|
|
79
|
-
* defineActions: ({ set }) => ({
|
|
80
|
-
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
81
|
-
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
82
|
-
* }),
|
|
83
|
-
* });
|
|
84
|
-
* ```
|
|
11
|
+
* @see {@link https://dharma.fransek.dev/core/createstore/}
|
|
85
12
|
*/
|
|
86
13
|
const createStore = (config) => {
|
|
87
14
|
const { initialState, defineActions, onLoad, onAttach, onDetach, onChange } = config;
|
|
@@ -103,9 +30,7 @@ const createStore = (config) => {
|
|
|
103
30
|
return state;
|
|
104
31
|
};
|
|
105
32
|
const listeners = new Set();
|
|
106
|
-
const actions = defineActions
|
|
107
|
-
? defineActions({ set, get, reset })
|
|
108
|
-
: {};
|
|
33
|
+
const actions = defineActions?.({ set, get, reset });
|
|
109
34
|
const storageAdapter = createStorageAdapter(config, get, set);
|
|
110
35
|
const subscribe = (listener) => {
|
|
111
36
|
if (listeners.size === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { createStorageAdapter } from \"./createStorageAdapter\";\nimport { merge } from \"./merge\";\nimport { Listener, StateModifier, Store, StoreConfig } from \"./types\";\n\n/**\n * Creates a store with an initial state and actions that can modify the state.\n *\n * @param {StoreConfig<TState, TActions>} [config] - The configuration for the store.\n *\n * @returns {Store<TState, TActions>} The created store with state management methods.\n *\n * @
|
|
1
|
+
{"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { createStorageAdapter } from \"./createStorageAdapter\";\nimport { merge } from \"./merge\";\nimport { Listener, StateModifier, Store, StoreConfig } from \"./types\";\n\n/**\n * Creates a store with an initial state and actions that can modify the state.\n *\n * @param {StoreConfig<TState, TActions>} [config] - The configuration for the store.\n *\n * @returns {Store<TState, TActions>} The created store with state management methods.\n *\n * @see {@link https://dharma.fransek.dev/core/createstore/}\n */\nexport const createStore = <TState, TActions = undefined>(\n config: StoreConfig<TState, TActions>,\n): Store<TState, TActions> => {\n const { initialState, defineActions, onLoad, onAttach, onDetach, onChange } =\n config;\n\n let state = initialState;\n\n const get = () => state;\n\n const setSilently = (stateModifier: StateModifier<TState>) => {\n state = merge(state, stateModifier);\n return state;\n };\n\n const set = (stateModifier: StateModifier<TState>) => {\n setSilently(stateModifier);\n dispatch();\n return state;\n };\n\n const resetSilently = () => setSilently(initialState);\n\n const reset = () => {\n resetSilently();\n dispatch();\n return state;\n };\n\n const listeners = new Set<Listener<TState>>();\n const actions = defineActions?.({ set, get, reset }) as TActions;\n const storageAdapter = createStorageAdapter(config, get, set);\n\n const subscribe = (listener: Listener<TState>) => {\n if (listeners.size === 0) {\n onAttach?.({ state, set, reset });\n storageAdapter?.onAttach();\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 storageAdapter?.onDetach();\n }\n };\n };\n\n const dispatch = () => {\n onChange?.({\n state,\n set: setSilently,\n reset: resetSilently,\n });\n storageAdapter?.onChange(state);\n listeners.forEach((listener) => listener(state));\n };\n\n onLoad?.({ state, set, reset });\n storageAdapter?.onLoad();\n\n return {\n get,\n subscribe,\n actions,\n };\n};\n"],"names":[],"mappings":";;;AAIA;;;;;;;;AAQG;AACI,MAAM,WAAW,GAAG,CACzB,MAAqC,KACV;AAC3B,IAAA,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,GACzE,MAAM;IAER,IAAI,KAAK,GAAG,YAAY;AAExB,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;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,aAAa,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC;IAErD,MAAM,KAAK,GAAG,MAAK;AACjB,QAAA,aAAa,EAAE;AACf,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;AAED,IAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB;AAC7C,IAAA,MAAM,OAAO,GAAG,aAAa,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAa;IAChE,MAAM,cAAc,GAAG,oBAAoB,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC;AAE7D,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;YACjC,cAAc,EAAE,QAAQ,EAAE;QAC5B;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;gBACjC,cAAc,EAAE,QAAQ,EAAE;YAC5B;AACF,QAAA,CAAC;AACH,IAAA,CAAC;IAED,MAAM,QAAQ,GAAG,MAAK;AACpB,QAAA,QAAQ,GAAG;YACT,KAAK;AACL,YAAA,GAAG,EAAE,WAAW;AAChB,YAAA,KAAK,EAAE,aAAa;AACrB,SAAA,CAAC;AACF,QAAA,cAAc,EAAE,QAAQ,CAAC,KAAK,CAAC;AAC/B,QAAA,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC;AAClD,IAAA,CAAC;IAED,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAC/B,cAAc,EAAE,MAAM,EAAE;IAExB,OAAO;QACL,GAAG;QACH,SAAS;QACT,OAAO;KACR;AACH;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const deeplyEquals: (a: unknown, b: unknown) => boolean;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const deeplyEquals = (a, b) => {
|
|
2
|
+
if (a === b) {
|
|
3
|
+
return true;
|
|
4
|
+
}
|
|
5
|
+
if (a === null ||
|
|
6
|
+
b === null ||
|
|
7
|
+
typeof a !== "object" ||
|
|
8
|
+
typeof b !== "object") {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const protoA = Object.getPrototypeOf(a);
|
|
12
|
+
const protoB = Object.getPrototypeOf(b);
|
|
13
|
+
if (protoA !== protoB) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
if (protoA === Array.prototype) {
|
|
17
|
+
const arrA = a;
|
|
18
|
+
const arrB = b;
|
|
19
|
+
if (arrA.length !== arrB.length)
|
|
20
|
+
return false;
|
|
21
|
+
for (let i = 0; i < arrA.length; i++) {
|
|
22
|
+
if (!deeplyEquals(arrA[i], arrB[i]))
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
if (protoA !== Object.prototype) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const keysA = Object.keys(a);
|
|
31
|
+
const keysB = Object.keys(b);
|
|
32
|
+
if (keysA.length !== keysB.length) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
for (const key of keysA) {
|
|
36
|
+
if (!keysB.includes(key)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
if (!deeplyEquals(a[key], b[key])) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export { deeplyEquals };
|
|
47
|
+
//# sourceMappingURL=deeplyEquals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deeplyEquals.js","sources":["../../../src/lib/deeplyEquals.ts"],"sourcesContent":["export const deeplyEquals = (a: unknown, b: unknown): boolean => {\n if (a === b) {\n return true;\n }\n\n if (\n a === null ||\n b === null ||\n typeof a !== \"object\" ||\n typeof b !== \"object\"\n ) {\n return false;\n }\n\n const protoA = Object.getPrototypeOf(a);\n const protoB = Object.getPrototypeOf(b);\n\n if (protoA !== protoB) {\n return false;\n }\n\n if (protoA === Array.prototype) {\n const arrA = a as unknown[];\n const arrB = b as unknown[];\n if (arrA.length !== arrB.length) return false;\n for (let i = 0; i < arrA.length; i++) {\n if (!deeplyEquals(arrA[i], arrB[i])) return false;\n }\n return true;\n }\n\n if (protoA !== Object.prototype) {\n return false;\n }\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) {\n return false;\n }\n\n for (const key of keysA) {\n if (!keysB.includes(key)) {\n return false;\n }\n if (\n !deeplyEquals(\n (a as Record<string, unknown>)[key],\n (b as Record<string, unknown>)[key],\n )\n ) {\n return false;\n }\n }\n\n return true;\n};\n"],"names":[],"mappings":"MAAa,YAAY,GAAG,CAAC,CAAU,EAAE,CAAU,KAAa;AAC9D,IAAA,IAAI,CAAC,KAAK,CAAC,EAAE;AACX,QAAA,OAAO,IAAI;IACb;IAEA,IACE,CAAC,KAAK,IAAI;AACV,QAAA,CAAC,KAAK,IAAI;QACV,OAAO,CAAC,KAAK,QAAQ;AACrB,QAAA,OAAO,CAAC,KAAK,QAAQ,EACrB;AACA,QAAA,OAAO,KAAK;IACd;IAEA,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;AAEvC,IAAA,IAAI,MAAM,KAAK,MAAM,EAAE;AACrB,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,IAAI,MAAM,KAAK,KAAK,CAAC,SAAS,EAAE;QAC9B,MAAM,IAAI,GAAG,CAAc;QAC3B,MAAM,IAAI,GAAG,CAAc;AAC3B,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;AAAE,YAAA,OAAO,KAAK;AAC7C,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACpC,YAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AAAE,gBAAA,OAAO,KAAK;QACnD;AACA,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,IAAI,MAAM,KAAK,MAAM,CAAC,SAAS,EAAE;AAC/B,QAAA,OAAO,KAAK;IACd;IAEA,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAE5B,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE;AACjC,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE;QACvB,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;AACxB,YAAA,OAAO,KAAK;QACd;AACA,QAAA,IACE,CAAC,YAAY,CACV,CAA6B,CAAC,GAAG,CAAC,EAClC,CAA6B,CAAC,GAAG,CAAC,CACpC,EACD;AACA,YAAA,OAAO,KAAK;QACd;IACF;AAEA,IAAA,OAAO,IAAI;AACb;;;;"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DerivedStore, Store } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a derived store from an existing store.
|
|
4
|
+
*
|
|
5
|
+
* A derived store computes its state based on the original store's state using a derive function.
|
|
6
|
+
* The derived store's value is memoized and only recomputed when requested.
|
|
7
|
+
*
|
|
8
|
+
* @param store - The original store to derive from
|
|
9
|
+
* @param deriveFn - A function that computes the derived state from the original state
|
|
10
|
+
* @param dependencyFn - Optional function that returns an array of dependencies. If provided, the derived state will only be recomputed when these dependencies change
|
|
11
|
+
*
|
|
12
|
+
* @returns A new store containing the derived state
|
|
13
|
+
*
|
|
14
|
+
* @see {@link https://dharma.fransek.dev/core/derive/}
|
|
15
|
+
*/
|
|
16
|
+
export declare const derive: <TState, TActions, TDerived>(store: Store<TState, TActions>, deriveFn: (state: TState) => TDerived, dependencyFn?: (state: TState) => unknown[]) => DerivedStore<TDerived>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { deeplyEquals } from './deeplyEquals.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a derived store from an existing store.
|
|
5
|
+
*
|
|
6
|
+
* A derived store computes its state based on the original store's state using a derive function.
|
|
7
|
+
* The derived store's value is memoized and only recomputed when requested.
|
|
8
|
+
*
|
|
9
|
+
* @param store - The original store to derive from
|
|
10
|
+
* @param deriveFn - A function that computes the derived state from the original state
|
|
11
|
+
* @param dependencyFn - Optional function that returns an array of dependencies. If provided, the derived state will only be recomputed when these dependencies change
|
|
12
|
+
*
|
|
13
|
+
* @returns A new store containing the derived state
|
|
14
|
+
*
|
|
15
|
+
* @see {@link https://dharma.fransek.dev/core/derive/}
|
|
16
|
+
*/
|
|
17
|
+
const derive = (store, deriveFn, dependencyFn) => {
|
|
18
|
+
let memo;
|
|
19
|
+
let prev;
|
|
20
|
+
let isStale = true;
|
|
21
|
+
let unsubscribe;
|
|
22
|
+
const listeners = new Set();
|
|
23
|
+
const compute = () => {
|
|
24
|
+
memo = deriveFn(store.get());
|
|
25
|
+
isStale = false;
|
|
26
|
+
return memo;
|
|
27
|
+
};
|
|
28
|
+
const get = () => {
|
|
29
|
+
if (!dependencyFn) {
|
|
30
|
+
if (isStale) {
|
|
31
|
+
return compute();
|
|
32
|
+
}
|
|
33
|
+
return memo;
|
|
34
|
+
}
|
|
35
|
+
const next = dependencyFn(store.get());
|
|
36
|
+
isStale &&= !prev || !deeplyEquals(prev, next);
|
|
37
|
+
if (isStale) {
|
|
38
|
+
prev = next;
|
|
39
|
+
return compute();
|
|
40
|
+
}
|
|
41
|
+
return memo;
|
|
42
|
+
};
|
|
43
|
+
const subscribe = (listener) => {
|
|
44
|
+
if (listeners.size === 0) {
|
|
45
|
+
mount();
|
|
46
|
+
}
|
|
47
|
+
listener(get());
|
|
48
|
+
listeners.add(listener);
|
|
49
|
+
return () => {
|
|
50
|
+
listeners.delete(listener);
|
|
51
|
+
if (listeners.size === 0) {
|
|
52
|
+
unmount();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
const mount = () => {
|
|
57
|
+
if (!unsubscribe) {
|
|
58
|
+
unsubscribe = store.subscribe(() => {
|
|
59
|
+
isStale = true;
|
|
60
|
+
listeners.forEach((listener) => listener(get()));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const unmount = () => {
|
|
65
|
+
unsubscribe?.();
|
|
66
|
+
unsubscribe = undefined;
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
get,
|
|
70
|
+
subscribe,
|
|
71
|
+
actions: {
|
|
72
|
+
mount,
|
|
73
|
+
unmount,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export { derive };
|
|
79
|
+
//# sourceMappingURL=derive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.js","sources":["../../../src/lib/derive.ts"],"sourcesContent":["import { deeplyEquals } from \"./deeplyEquals\";\nimport { DerivedStore, Listener, Store } from \"./types\";\n\n/**\n * Creates a derived store from an existing store.\n *\n * A derived store computes its state based on the original store's state using a derive function.\n * The derived store's value is memoized and only recomputed when requested.\n *\n * @param store - The original store to derive from\n * @param deriveFn - A function that computes the derived state from the original state\n * @param dependencyFn - Optional function that returns an array of dependencies. If provided, the derived state will only be recomputed when these dependencies change\n *\n * @returns A new store containing the derived state\n *\n * @see {@link https://dharma.fransek.dev/core/derive/}\n */\nexport const derive = <TState, TActions, TDerived>(\n store: Store<TState, TActions>,\n deriveFn: (state: TState) => TDerived,\n dependencyFn?: (state: TState) => unknown[],\n): DerivedStore<TDerived> => {\n let memo: TDerived | undefined;\n let prev: unknown[] | undefined;\n let isStale = true;\n let unsubscribe: (() => void) | undefined;\n const listeners = new Set<Listener<TDerived>>();\n\n const compute = () => {\n memo = deriveFn(store.get());\n isStale = false;\n return memo;\n };\n\n const get = () => {\n if (!dependencyFn) {\n if (isStale) {\n return compute();\n }\n\n return memo as TDerived;\n }\n\n const next = dependencyFn(store.get());\n isStale &&= !prev || !deeplyEquals(prev, next);\n\n if (isStale) {\n prev = next;\n return compute();\n }\n\n return memo as TDerived;\n };\n\n const subscribe = (listener: Listener<TDerived>) => {\n if (listeners.size === 0) {\n mount();\n }\n\n listener(get());\n listeners.add(listener);\n\n return () => {\n listeners.delete(listener);\n\n if (listeners.size === 0) {\n unmount();\n }\n };\n };\n\n const mount = () => {\n if (!unsubscribe) {\n unsubscribe = store.subscribe(() => {\n isStale = true;\n listeners.forEach((listener) => listener(get()));\n });\n }\n };\n\n const unmount = () => {\n unsubscribe?.();\n unsubscribe = undefined;\n };\n\n return {\n get,\n subscribe,\n actions: {\n mount,\n unmount,\n },\n };\n};\n"],"names":[],"mappings":";;AAGA;;;;;;;;;;;;;AAaG;AACI,MAAM,MAAM,GAAG,CACpB,KAA8B,EAC9B,QAAqC,EACrC,YAA2C,KACjB;AAC1B,IAAA,IAAI,IAA0B;AAC9B,IAAA,IAAI,IAA2B;IAC/B,IAAI,OAAO,GAAG,IAAI;AAClB,IAAA,IAAI,WAAqC;AACzC,IAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAsB;IAE/C,MAAM,OAAO,GAAG,MAAK;QACnB,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAC5B,OAAO,GAAG,KAAK;AACf,QAAA,OAAO,IAAI;AACb,IAAA,CAAC;IAED,MAAM,GAAG,GAAG,MAAK;QACf,IAAI,CAAC,YAAY,EAAE;YACjB,IAAI,OAAO,EAAE;gBACX,OAAO,OAAO,EAAE;YAClB;AAEA,YAAA,OAAO,IAAgB;QACzB;QAEA,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACtC,OAAO,KAAK,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC;QAE9C,IAAI,OAAO,EAAE;YACX,IAAI,GAAG,IAAI;YACX,OAAO,OAAO,EAAE;QAClB;AAEA,QAAA,OAAO,IAAgB;AACzB,IAAA,CAAC;AAED,IAAA,MAAM,SAAS,GAAG,CAAC,QAA4B,KAAI;AACjD,QAAA,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE;AACxB,YAAA,KAAK,EAAE;QACT;AAEA,QAAA,QAAQ,CAAC,GAAG,EAAE,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;AACxB,gBAAA,OAAO,EAAE;YACX;AACF,QAAA,CAAC;AACH,IAAA,CAAC;IAED,MAAM,KAAK,GAAG,MAAK;QACjB,IAAI,CAAC,WAAW,EAAE;AAChB,YAAA,WAAW,GAAG,KAAK,CAAC,SAAS,CAAC,MAAK;gBACjC,OAAO,GAAG,IAAI;AACd,gBAAA,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;AAClD,YAAA,CAAC,CAAC;QACJ;AACF,IAAA,CAAC;IAED,MAAM,OAAO,GAAG,MAAK;QACnB,WAAW,IAAI;QACf,WAAW,GAAG,SAAS;AACzB,IAAA,CAAC;IAED,OAAO;QACL,GAAG;QACH,SAAS;AACT,QAAA,OAAO,EAAE;YACP,KAAK;YACL,OAAO;AACR,SAAA;KACF;AACH;;;;"}
|
package/dist/esm/lib/merge.d.ts
CHANGED
|
@@ -6,30 +6,6 @@ import { StateModifier } from "./types";
|
|
|
6
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
|
-
* @
|
|
10
|
-
* ```ts
|
|
11
|
-
* import { createStore, merge, StateModifier } from "dharma-core";
|
|
12
|
-
*
|
|
13
|
-
* const store = createStore({
|
|
14
|
-
* initialState: {
|
|
15
|
-
* counter: {
|
|
16
|
-
* count: 0,
|
|
17
|
-
* },
|
|
18
|
-
* // ...
|
|
19
|
-
* },
|
|
20
|
-
* defineActions: ({ set }) => {
|
|
21
|
-
* const setCounter = (counter: StateModifier<{ count: number }>) =>
|
|
22
|
-
* set((state) => ({
|
|
23
|
-
* counter: merge(state.counter, counter),
|
|
24
|
-
* }));
|
|
25
|
-
*
|
|
26
|
-
* return {
|
|
27
|
-
* increment: () => setCounter((state) => ({ count: state.count + 1 })),
|
|
28
|
-
* decrement: () => setCounter((state) => ({ count: state.count - 1 })),
|
|
29
|
-
* // ...
|
|
30
|
-
* };
|
|
31
|
-
* },
|
|
32
|
-
* });
|
|
33
|
-
* ```
|
|
9
|
+
* @see {@link https://dharma.fransek.dev/core/merge/}
|
|
34
10
|
*/
|
|
35
|
-
export declare const merge: <T
|
|
11
|
+
export declare const merge: <T>(currentState: T, stateModifier: StateModifier<T>) => T;
|
package/dist/esm/lib/merge.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2
|
+
const isObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
1
3
|
/**
|
|
2
4
|
* Merges the current state with a state modifier.
|
|
3
5
|
*
|
|
@@ -5,37 +7,20 @@
|
|
|
5
7
|
* @param {StateModifier<T>} stateModifier - A function or object that modifies the state.
|
|
6
8
|
* @returns {T} The new state object, which is a combination of the current state and the state modifier.
|
|
7
9
|
*
|
|
8
|
-
* @
|
|
9
|
-
* ```ts
|
|
10
|
-
* import { createStore, merge, StateModifier } from "dharma-core";
|
|
11
|
-
*
|
|
12
|
-
* const store = createStore({
|
|
13
|
-
* initialState: {
|
|
14
|
-
* counter: {
|
|
15
|
-
* count: 0,
|
|
16
|
-
* },
|
|
17
|
-
* // ...
|
|
18
|
-
* },
|
|
19
|
-
* defineActions: ({ set }) => {
|
|
20
|
-
* const setCounter = (counter: StateModifier<{ count: number }>) =>
|
|
21
|
-
* set((state) => ({
|
|
22
|
-
* counter: merge(state.counter, counter),
|
|
23
|
-
* }));
|
|
24
|
-
*
|
|
25
|
-
* return {
|
|
26
|
-
* increment: () => setCounter((state) => ({ count: state.count + 1 })),
|
|
27
|
-
* decrement: () => setCounter((state) => ({ count: state.count - 1 })),
|
|
28
|
-
* // ...
|
|
29
|
-
* };
|
|
30
|
-
* },
|
|
31
|
-
* });
|
|
32
|
-
* ```
|
|
10
|
+
* @see {@link https://dharma.fransek.dev/core/merge/}
|
|
33
11
|
*/
|
|
34
12
|
const merge = (currentState, stateModifier) => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
13
|
+
if (isObject(stateModifier)) {
|
|
14
|
+
return { ...currentState, ...stateModifier };
|
|
15
|
+
}
|
|
16
|
+
if (typeof stateModifier === "function") {
|
|
17
|
+
const modifiedState = stateModifier(currentState);
|
|
18
|
+
if (isObject(modifiedState)) {
|
|
19
|
+
return { ...currentState, ...modifiedState };
|
|
20
|
+
}
|
|
21
|
+
return modifiedState;
|
|
22
|
+
}
|
|
23
|
+
return stateModifier;
|
|
39
24
|
};
|
|
40
25
|
|
|
41
26
|
export { merge };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.js","sources":["../../../src/lib/merge.ts"],"sourcesContent":["import { StateModifier } from \"./types\";\n\n/**\n * Merges the current state with a state modifier.\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 * @
|
|
1
|
+
{"version":3,"file":"merge.js","sources":["../../../src/lib/merge.ts"],"sourcesContent":["import { StateModifier } from \"./types\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst isObject = (value: unknown): value is Record<keyof any, unknown> =>\n !!value && typeof value === \"object\" && !Array.isArray(value);\n\n/**\n * Merges the current state with a state modifier.\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 * @see {@link https://dharma.fransek.dev/core/merge/}\n */\nexport const merge = <T>(\n currentState: T,\n stateModifier: StateModifier<T>,\n): T => {\n if (isObject(stateModifier)) {\n return { ...currentState, ...stateModifier };\n }\n\n if (typeof stateModifier === \"function\") {\n const modifiedState = stateModifier(currentState);\n\n if (isObject(modifiedState)) {\n return { ...currentState, ...modifiedState };\n }\n\n return modifiedState;\n }\n\n return stateModifier;\n};\n"],"names":[],"mappings":"AAEA;AACA,MAAM,QAAQ,GAAG,CAAC,KAAc,KAC9B,CAAC,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;AAE/D;;;;;;;;AAQG;MACU,KAAK,GAAG,CACnB,YAAe,EACf,aAA+B,KAC1B;AACL,IAAA,IAAI,QAAQ,CAAC,aAAa,CAAC,EAAE;AAC3B,QAAA,OAAO,EAAE,GAAG,YAAY,EAAE,GAAG,aAAa,EAAE;IAC9C;AAEA,IAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;AACvC,QAAA,MAAM,aAAa,GAAG,aAAa,CAAC,YAAY,CAAC;AAEjD,QAAA,IAAI,QAAQ,CAAC,aAAa,CAAC,EAAE;AAC3B,YAAA,OAAO,EAAE,GAAG,YAAY,EAAE,GAAG,aAAa,EAAE;QAC9C;AAEA,QAAA,OAAO,aAAa;IACtB;AAEA,IAAA,OAAO,aAAa;AACtB;;;;"}
|
package/dist/esm/lib/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export type Listener<TState
|
|
2
|
-
export type SetState<TState
|
|
3
|
-
export type Store<TState
|
|
1
|
+
export type Listener<TState> = (state: TState) => void;
|
|
2
|
+
export type SetState<TState> = (stateModifier: StateModifier<TState>) => TState;
|
|
3
|
+
export type Store<TState, TActions> = {
|
|
4
4
|
/** Returns the current state of the store. */
|
|
5
5
|
get: () => TState;
|
|
6
6
|
/** Actions that can modify the state of the store. */
|
|
@@ -8,7 +8,14 @@ export type Store<TState extends object, TActions extends object> = {
|
|
|
8
8
|
/** Subscribes to changes in the state of the store. Returns an unsubscribe function. */
|
|
9
9
|
subscribe: (listener: Listener<TState>) => () => void;
|
|
10
10
|
};
|
|
11
|
-
export type
|
|
11
|
+
export type DerivedStoreActions = {
|
|
12
|
+
/** Start listening to changes in the original store. Called automatically on the first subscription. */
|
|
13
|
+
mount: () => void;
|
|
14
|
+
/** Stop listening to changes in the original store. Called automatically when there are no more subscribers. */
|
|
15
|
+
unmount: () => void;
|
|
16
|
+
};
|
|
17
|
+
export type DerivedStore<TState> = Store<TState, DerivedStoreActions>;
|
|
18
|
+
export type StoreEventContext<TState> = {
|
|
12
19
|
/** The current state of the store. */
|
|
13
20
|
state: TState;
|
|
14
21
|
/** Sets the state of the store. */
|
|
@@ -16,9 +23,9 @@ export type StoreEventContext<TState extends object> = {
|
|
|
16
23
|
/** Resets the state of the store to its initial value. */
|
|
17
24
|
reset: () => TState;
|
|
18
25
|
};
|
|
19
|
-
export type StoreEventListener<TState
|
|
20
|
-
export type StateModifier<TState
|
|
21
|
-
export type StateHandler<TState
|
|
26
|
+
export type StoreEventListener<TState> = (context: StoreEventContext<TState>) => void;
|
|
27
|
+
export type StateModifier<TState> = Partial<TState> | ((state: TState) => Partial<TState>);
|
|
28
|
+
export type StateHandler<TState> = {
|
|
22
29
|
/** Returns the current state of the store. */
|
|
23
30
|
get: () => TState;
|
|
24
31
|
/** Sets the state of the store. */
|
|
@@ -26,8 +33,8 @@ export type StateHandler<TState extends object> = {
|
|
|
26
33
|
/** Resets the state of the store to its initial value. */
|
|
27
34
|
reset: () => TState;
|
|
28
35
|
};
|
|
29
|
-
export type DefineActions<TState
|
|
30
|
-
export type BaseConfig<TState
|
|
36
|
+
export type DefineActions<TState, TActions> = (stateHandler: StateHandler<TState>) => TActions;
|
|
37
|
+
export type BaseConfig<TState, TActions> = {
|
|
31
38
|
/** The initial state of the store. */
|
|
32
39
|
initialState: TState;
|
|
33
40
|
/** A function that defines actions that can modify the state. */
|
|
@@ -55,7 +62,7 @@ export type NoPersistConfig = {
|
|
|
55
62
|
/** Whether to persist the state of the store. */
|
|
56
63
|
persist?: false;
|
|
57
64
|
};
|
|
58
|
-
export type PersistConfig<TState
|
|
65
|
+
export type PersistConfig<TState> = {
|
|
59
66
|
/** Whether to persist the state of the store. */
|
|
60
67
|
persist: true;
|
|
61
68
|
/** The unique key used to identify this store in storage. Required if `persist` is true. */
|
|
@@ -65,4 +72,4 @@ export type PersistConfig<TState extends object> = {
|
|
|
65
72
|
/** The serializer to use for storing the state. Defaults to JSON. */
|
|
66
73
|
serializer?: Serializer<TState>;
|
|
67
74
|
};
|
|
68
|
-
export type StoreConfig<TState
|
|
75
|
+
export type StoreConfig<TState, TActions> = BaseConfig<TState, TActions> & (PersistConfig<TState> | NoPersistConfig);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dharma-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"author": "Frans Ekman <fransedvinekman@gmail.com>",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "My package description",
|
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
"import": "./dist/esm/lib/createStore.js",
|
|
31
31
|
"require": "./dist/cjs/lib/createStore.js"
|
|
32
32
|
},
|
|
33
|
-
"./createPersistentStore": {
|
|
34
|
-
"types": "./dist/index.d.ts",
|
|
35
|
-
"import": "./dist/esm/lib/createPersistentStore.js",
|
|
36
|
-
"require": "./dist/cjs/lib/createPersistentStore.js"
|
|
37
|
-
},
|
|
38
33
|
"./merge": {
|
|
39
34
|
"types": "./dist/index.d.ts",
|
|
40
35
|
"import": "./dist/esm/lib/merge.js",
|
|
41
36
|
"require": "./dist/cjs/lib/merge.js"
|
|
37
|
+
},
|
|
38
|
+
"./deeplyEquals": {
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"import": "./dist/esm/lib/deeplyEquals.js",
|
|
41
|
+
"require": "./dist/cjs/lib/deeplyEquals.js"
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"sideEffects": false,
|