dharma-core 0.6.1 → 0.7.1
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/lib/createStorageAdapter.d.ts +7 -0
- package/dist/cjs/lib/createStorageAdapter.js +82 -0
- package/dist/cjs/lib/createStorageAdapter.js.map +1 -0
- package/dist/cjs/lib/createStore.d.ts +5 -76
- package/dist/cjs/lib/createStore.js +10 -16
- package/dist/cjs/lib/createStore.js.map +1 -1
- package/dist/cjs/lib/merge.d.ts +1 -3
- package/dist/cjs/lib/merge.js +0 -2
- package/dist/cjs/lib/merge.js.map +1 -1
- package/dist/cjs/lib/types.d.ts +72 -0
- package/dist/cjs/lib/warn.d.ts +1 -0
- package/dist/cjs/lib/warn.js +6 -0
- package/dist/cjs/lib/warn.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/lib/createStorageAdapter.d.ts +7 -0
- package/dist/esm/lib/createStorageAdapter.js +80 -0
- package/dist/esm/lib/createStorageAdapter.js.map +1 -0
- package/dist/esm/lib/createStore.d.ts +5 -76
- package/dist/esm/lib/createStore.js +10 -16
- package/dist/esm/lib/createStore.js.map +1 -1
- package/dist/esm/lib/merge.d.ts +1 -3
- package/dist/esm/lib/merge.js +0 -2
- package/dist/esm/lib/merge.js.map +1 -1
- package/dist/esm/lib/types.d.ts +72 -0
- package/dist/esm/lib/warn.d.ts +1 -0
- package/dist/esm/lib/warn.js +4 -0
- package/dist/esm/lib/warn.js.map +1 -0
- package/package.json +4 -1
- package/dist/cjs/lib/getPersistHandler.d.ts +0 -6
- package/dist/cjs/lib/getPersistHandler.js +0 -42
- package/dist/cjs/lib/getPersistHandler.js.map +0 -1
- package/dist/esm/lib/getPersistHandler.d.ts +0 -6
- package/dist/esm/lib/getPersistHandler.js +0 -40
- package/dist/esm/lib/getPersistHandler.js.map +0 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ const store = createStore({
|
|
|
22
22
|
const { increment, decrement } = store.actions;
|
|
23
23
|
|
|
24
24
|
// Subscribe to state changes
|
|
25
|
-
const unsubscribe = store.subscribe((state) => console.log(state
|
|
25
|
+
const unsubscribe = store.subscribe((state) => console.log(state));
|
|
26
26
|
// Update the state
|
|
27
27
|
increment();
|
|
28
28
|
// { count: 1 }
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SetState, StoreConfig } from "./types";
|
|
2
|
+
export declare const createStorageAdapter: <TState extends object, TActions extends object = Record<never, never>>(config: StoreConfig<TState, TActions>, get: () => TState, set: SetState<TState>) => {
|
|
3
|
+
onLoad: () => Promise<void>;
|
|
4
|
+
onChange: (newState: TState) => Promise<void>;
|
|
5
|
+
onAttach: () => void;
|
|
6
|
+
onDetach: () => void;
|
|
7
|
+
} | null;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var warn = require('./warn.js');
|
|
4
|
+
|
|
5
|
+
const createStorageAdapter = (config, get, set) => {
|
|
6
|
+
if (!config.persist) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const IS_BROWSER = typeof window !== "undefined";
|
|
10
|
+
const storage = config.storage ?? (IS_BROWSER ? localStorage : undefined);
|
|
11
|
+
if (!storage) {
|
|
12
|
+
warn.warn("No storage provider found. If this happened during SSR, you can safely ignore this warning.");
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const { key, serializer = JSON, initialState } = config;
|
|
16
|
+
const initKey = `init_${key}`;
|
|
17
|
+
const onLoad = async () => {
|
|
18
|
+
try {
|
|
19
|
+
let initialStateSnapshot = storage.getItem(initKey);
|
|
20
|
+
const initialStateString = serializer.stringify(initialState);
|
|
21
|
+
if (initialStateSnapshot instanceof Promise) {
|
|
22
|
+
initialStateSnapshot = await initialStateSnapshot;
|
|
23
|
+
}
|
|
24
|
+
if (initialStateSnapshot !== initialStateString) {
|
|
25
|
+
storage.setItem(initKey, initialStateString);
|
|
26
|
+
storage.removeItem(key);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
warn.warn("Failed to initialize snapshots. If this happened during SSR, you can safely ignore this warning.");
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const syncWithSnapshot = async () => {
|
|
34
|
+
try {
|
|
35
|
+
let currentSnapshot = storage.getItem(key);
|
|
36
|
+
if (currentSnapshot instanceof Promise) {
|
|
37
|
+
currentSnapshot = await currentSnapshot;
|
|
38
|
+
}
|
|
39
|
+
if (currentSnapshot && currentSnapshot !== serializer.stringify(get())) {
|
|
40
|
+
set(serializer.parse(currentSnapshot));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
warn.warn("Failed to update state from snapshot");
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const onAttach = () => {
|
|
48
|
+
syncWithSnapshot();
|
|
49
|
+
if (IS_BROWSER) {
|
|
50
|
+
window.addEventListener("focus", syncWithSnapshot);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const onChange = async (newState) => {
|
|
54
|
+
try {
|
|
55
|
+
let currentSnapshot = storage.getItem(key);
|
|
56
|
+
const newSnapshot = serializer.stringify(newState);
|
|
57
|
+
if (currentSnapshot instanceof Promise) {
|
|
58
|
+
currentSnapshot = await currentSnapshot;
|
|
59
|
+
}
|
|
60
|
+
if (newSnapshot !== currentSnapshot) {
|
|
61
|
+
storage.setItem(key, newSnapshot);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
warn.warn("Failed to update snapshot");
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const onDetach = () => {
|
|
69
|
+
if (IS_BROWSER) {
|
|
70
|
+
window.removeEventListener("focus", syncWithSnapshot);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
onLoad,
|
|
75
|
+
onChange,
|
|
76
|
+
onAttach,
|
|
77
|
+
onDetach,
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
exports.createStorageAdapter = createStorageAdapter;
|
|
82
|
+
//# sourceMappingURL=createStorageAdapter.js.map
|
|
@@ -0,0 +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 = <\n TState extends object,\n TActions extends object = Record<never, never>,\n>(\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,CAIlC,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;;;;"}
|
|
@@ -1,74 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export type SetState<TState extends object> = (stateModifier: StateModifier<TState>) => TState;
|
|
3
|
-
export type Store<TState extends object, TActions extends object> = {
|
|
4
|
-
/** Returns the current state of the store. */
|
|
5
|
-
get: () => TState;
|
|
6
|
-
/** Sets the state of the store. */
|
|
7
|
-
set: SetState<TState>;
|
|
8
|
-
/** Resets the state of the store to its initial value. */
|
|
9
|
-
reset: () => TState;
|
|
10
|
-
/** Actions that can modify the state of the store. */
|
|
11
|
-
actions: TActions;
|
|
12
|
-
/** Subscribes to changes in the state of the store. Returns an unsubscribe function. */
|
|
13
|
-
subscribe: (listener: Listener<TState>) => () => void;
|
|
14
|
-
};
|
|
15
|
-
export type StoreEventContext<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: StoreEventContext<TState>) => void;
|
|
24
|
-
export type StateModifier<TState extends object> = Partial<TState> | ((state: TState) => Partial<TState>);
|
|
25
|
-
export type StateHandler<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> = (stateHandler: StateHandler<TState>) => TActions;
|
|
34
|
-
type BaseConfig<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>;
|
|
39
|
-
/** Invoked when the store is created. */
|
|
40
|
-
onLoad?: StoreEventHandler<TState>;
|
|
41
|
-
/** Invoked when the store is subscribed to. */
|
|
42
|
-
onAttach?: StoreEventHandler<TState>;
|
|
43
|
-
/** Invoked when the store is unsubscribed from. */
|
|
44
|
-
onDetach?: StoreEventHandler<TState>;
|
|
45
|
-
/** Invoked whenever the state changes. */
|
|
46
|
-
onChange?: StoreEventHandler<TState>;
|
|
47
|
-
};
|
|
48
|
-
export type StorageAPI = {
|
|
49
|
-
getItem: (key: string) => string | null;
|
|
50
|
-
setItem: (key: string, value: string) => void;
|
|
51
|
-
removeItem: (key: string) => void;
|
|
52
|
-
};
|
|
53
|
-
export type Serializer<T = any> = {
|
|
54
|
-
stringify: (value: T) => string;
|
|
55
|
-
parse: (value: string) => T;
|
|
56
|
-
};
|
|
57
|
-
type NonPersistentConfig = {
|
|
58
|
-
/** Whether to persist the state of the store. */
|
|
59
|
-
persist?: false;
|
|
60
|
-
};
|
|
61
|
-
type PersistentConfig<TState extends object> = {
|
|
62
|
-
/** Whether to persist the state of the store. */
|
|
63
|
-
persist: true;
|
|
64
|
-
/** The unique key used to identify this store in storage. */
|
|
65
|
-
key: string;
|
|
66
|
-
/** The storage to use for persisting the state. Defaults to local storage if available. */
|
|
67
|
-
storage?: StorageAPI;
|
|
68
|
-
/** The serializer to use for storing the state. Defaults to JSON. */
|
|
69
|
-
serializer?: Serializer<TState>;
|
|
70
|
-
};
|
|
71
|
-
export type StoreConfig<TState extends object, TActions extends object> = BaseConfig<TState, TActions> & (NonPersistentConfig | PersistentConfig<TState>);
|
|
1
|
+
import { Store, StoreConfig } from "./types";
|
|
72
2
|
/**
|
|
73
3
|
* Creates a store with an initial state and actions that can modify the state.
|
|
74
4
|
*
|
|
@@ -91,7 +21,7 @@ export type StoreConfig<TState extends object, TActions extends object> = BaseCo
|
|
|
91
21
|
* ```
|
|
92
22
|
*
|
|
93
23
|
* @example
|
|
94
|
-
*
|
|
24
|
+
* Lifecycle hooks:
|
|
95
25
|
* ```ts
|
|
96
26
|
* import { createStore } from "dharma-core";
|
|
97
27
|
* import { State } from "./types";
|
|
@@ -114,7 +44,7 @@ export type StoreConfig<TState extends object, TActions extends object> = BaseCo
|
|
|
114
44
|
* ```
|
|
115
45
|
*
|
|
116
46
|
* @example
|
|
117
|
-
*
|
|
47
|
+
* Persisting state:
|
|
118
48
|
* ```ts
|
|
119
49
|
* import { createStore } from "dharma-core";
|
|
120
50
|
*
|
|
@@ -133,7 +63,7 @@ export type StoreConfig<TState extends object, TActions extends object> = BaseCo
|
|
|
133
63
|
* If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
|
|
134
64
|
*
|
|
135
65
|
* @example
|
|
136
|
-
*
|
|
66
|
+
* Custom storage and serializer:
|
|
137
67
|
* ```ts
|
|
138
68
|
* import { createStore } from "dharma-core";
|
|
139
69
|
* import superjson from "superjson";
|
|
@@ -141,8 +71,8 @@ export type StoreConfig<TState extends object, TActions extends object> = BaseCo
|
|
|
141
71
|
* const store = createStore({
|
|
142
72
|
* persist: true,
|
|
143
73
|
* key: "count",
|
|
144
|
-
* serializer: superjson,
|
|
145
74
|
* storage: sessionStorage,
|
|
75
|
+
* serializer: superjson,
|
|
146
76
|
* initialState: { count: 0 },
|
|
147
77
|
* defineActions: ({ set }) => ({
|
|
148
78
|
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
@@ -152,4 +82,3 @@ export type StoreConfig<TState extends object, TActions extends object> = BaseCo
|
|
|
152
82
|
* ```
|
|
153
83
|
*/
|
|
154
84
|
export declare const createStore: <TState extends object, TActions extends object = Record<never, never>>(config: StoreConfig<TState, TActions>) => Store<TState, TActions>;
|
|
155
|
-
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var createStorageAdapter = require('./createStorageAdapter.js');
|
|
4
4
|
var merge = require('./merge.js');
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -25,7 +25,7 @@ var merge = require('./merge.js');
|
|
|
25
25
|
* ```
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
28
|
-
*
|
|
28
|
+
* Lifecycle hooks:
|
|
29
29
|
* ```ts
|
|
30
30
|
* import { createStore } from "dharma-core";
|
|
31
31
|
* import { State } from "./types";
|
|
@@ -48,7 +48,7 @@ var merge = require('./merge.js');
|
|
|
48
48
|
* ```
|
|
49
49
|
*
|
|
50
50
|
* @example
|
|
51
|
-
*
|
|
51
|
+
* Persisting state:
|
|
52
52
|
* ```ts
|
|
53
53
|
* import { createStore } from "dharma-core";
|
|
54
54
|
*
|
|
@@ -67,7 +67,7 @@ var merge = require('./merge.js');
|
|
|
67
67
|
* If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
|
|
68
68
|
*
|
|
69
69
|
* @example
|
|
70
|
-
*
|
|
70
|
+
* Custom storage and serializer:
|
|
71
71
|
* ```ts
|
|
72
72
|
* import { createStore } from "dharma-core";
|
|
73
73
|
* import superjson from "superjson";
|
|
@@ -75,8 +75,8 @@ var merge = require('./merge.js');
|
|
|
75
75
|
* const store = createStore({
|
|
76
76
|
* persist: true,
|
|
77
77
|
* key: "count",
|
|
78
|
-
* serializer: superjson,
|
|
79
78
|
* storage: sessionStorage,
|
|
79
|
+
* serializer: superjson,
|
|
80
80
|
* initialState: { count: 0 },
|
|
81
81
|
* defineActions: ({ set }) => ({
|
|
82
82
|
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
@@ -108,15 +108,11 @@ const createStore = (config) => {
|
|
|
108
108
|
const actions = defineActions
|
|
109
109
|
? defineActions({ set, get, reset })
|
|
110
110
|
: {};
|
|
111
|
-
const
|
|
112
|
-
const persistHandler = getPersistHandler.getPersistHandler(config, IS_BROWSER, get, set);
|
|
111
|
+
const storageAdapter = createStorageAdapter.createStorageAdapter(config, get, set);
|
|
113
112
|
const subscribe = (listener) => {
|
|
114
113
|
if (listeners.size === 0) {
|
|
115
114
|
onAttach?.({ state, set, reset });
|
|
116
|
-
|
|
117
|
-
if (IS_BROWSER && persistHandler) {
|
|
118
|
-
window.addEventListener("focus", persistHandler.updateState);
|
|
119
|
-
}
|
|
115
|
+
storageAdapter?.onAttach();
|
|
120
116
|
}
|
|
121
117
|
listener(state);
|
|
122
118
|
listeners.add(listener);
|
|
@@ -124,9 +120,7 @@ const createStore = (config) => {
|
|
|
124
120
|
listeners.delete(listener);
|
|
125
121
|
if (listeners.size === 0) {
|
|
126
122
|
onDetach?.({ state, set, reset });
|
|
127
|
-
|
|
128
|
-
window.removeEventListener("focus", persistHandler.updateState);
|
|
129
|
-
}
|
|
123
|
+
storageAdapter?.onDetach();
|
|
130
124
|
}
|
|
131
125
|
};
|
|
132
126
|
};
|
|
@@ -136,11 +130,11 @@ const createStore = (config) => {
|
|
|
136
130
|
set: setSilently,
|
|
137
131
|
reset: resetSilently,
|
|
138
132
|
});
|
|
139
|
-
|
|
133
|
+
storageAdapter?.onChange(state);
|
|
140
134
|
listeners.forEach((listener) => listener(state));
|
|
141
135
|
};
|
|
142
136
|
onLoad?.({ state, set, reset });
|
|
143
|
-
|
|
137
|
+
storageAdapter?.onLoad();
|
|
144
138
|
return {
|
|
145
139
|
get,
|
|
146
140
|
set,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { getPersistHandler } from \"./getPersistHandler\";\nimport { 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 StoreEventContext<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: StoreEventContext<TState>,\n) => void;\n\nexport type StateModifier<TState extends object> =\n | Partial<TState>\n | ((state: TState) => Partial<TState>);\n\nexport type StateHandler<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 stateHandler: StateHandler<TState>,\n) => TActions;\n\ntype BaseConfig<TState extends object, TActions extends object> = {\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\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\ntype NonPersistentConfig = {\n /** Whether to persist the state of the store. */\n persist?: false;\n};\n\ntype PersistentConfig<TState extends object> = {\n /** Whether to persist the state of the store. */\n persist: true;\n /** The unique key used to identify this store in storage. */\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\nexport type StoreConfig<\n TState extends object,\n TActions extends object,\n> = BaseConfig<TState, TActions> &\n (NonPersistentConfig | PersistentConfig<TState>);\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 * @example\n * Basic usage:\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 *\n * @example\n * With event handlers:\n * ```ts\n * import { createStore } from \"dharma-core\";\n * import { State } from \"./types\";\n *\n * const initialState: State = { data: null, loading: true, error: null };\n *\n * const store = createStore({\n * initialState,\n * onAttach: async ({ set }) => {\n * try {\n * const response = await fetch(\"https://api.example.com/data\");\n * const data = await response.json();\n * set({ data, loading: false });\n * } catch (error) {\n * set({ error, loading: false });\n * }\n * },\n * onDetach: ({ reset }) => reset(),\n * });\n * ```\n *\n * @example\n * Persistent store (uses local storage by default):\n * ```ts\n * import { createStore } from \"dharma-core\";\n *\n * const store = createStore({\n * persist: true,\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 * }),\n * });\n * ```\n *\n * **Note:** The state needs to be serializable by whatever serializer you choose (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 * @example\n * With superjson serialization and session storage:\n * ```ts\n * import { createStore } from \"dharma-core\";\n * import superjson from \"superjson\";\n *\n * const store = createStore({\n * persist: true,\n * key: \"count\",\n * serializer: superjson,\n * storage: sessionStorage,\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: 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\n ? defineActions({ set, get, reset })\n : ({} as TActions);\n const IS_BROWSER = typeof window !== \"undefined\";\n const persistHandler = getPersistHandler(config, IS_BROWSER, get, set);\n\n const subscribe = (listener: Listener<TState>) => {\n if (listeners.size === 0) {\n onAttach?.({ state, set, reset });\n persistHandler?.updateState();\n\n if (IS_BROWSER && persistHandler) {\n window.addEventListener(\"focus\", persistHandler.updateState);\n }\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 if (IS_BROWSER && persistHandler) {\n window.removeEventListener(\"focus\", persistHandler.updateState);\n }\n }\n };\n };\n\n const dispatch = () => {\n onChange?.({\n state,\n set: setSilently,\n reset: resetSilently,\n });\n persistHandler?.updateSnapshot(state);\n listeners.forEach((listener) => listener(state));\n };\n\n onLoad?.({ state, set, reset });\n persistHandler?.initializeSnapshots();\n\n return {\n get,\n set,\n reset,\n subscribe,\n actions,\n };\n};\n"],"names":["merge","getPersistHandler"],"mappings":";;;;;AAqGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiFG;AACI,MAAM,WAAW,GAAG,CAIzB,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;IAC7C,MAAM,OAAO,GAAG;UACZ,aAAa,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE;UAChC,EAAe;AACpB,IAAA,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW;AAChD,IAAA,MAAM,cAAc,GAAGC,mCAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,CAAC;AAEtE,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,WAAW,EAAE;AAE7B,YAAA,IAAI,UAAU,IAAI,cAAc,EAAE;gBAChC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,cAAc,CAAC,WAAW,CAAC;YAC9D;QACF;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;AAEjC,gBAAA,IAAI,UAAU,IAAI,cAAc,EAAE;oBAChC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,cAAc,CAAC,WAAW,CAAC;gBACjE;YACF;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,cAAc,CAAC,KAAK,CAAC;AACrC,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,mBAAmB,EAAE;IAErC,OAAO;QACL,GAAG;QACH,GAAG;QACH,KAAK;QACL,SAAS;QACT,OAAO;KACR;AACH;;;;"}
|
|
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 * @example\n * Basic usage:\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 *\n * @example\n * Lifecycle hooks:\n * ```ts\n * import { createStore } from \"dharma-core\";\n * import { State } from \"./types\";\n *\n * const initialState: State = { data: null, loading: true, error: null };\n *\n * const store = createStore({\n * initialState,\n * onAttach: async ({ set }) => {\n * try {\n * const response = await fetch(\"https://api.example.com/data\");\n * const data = await response.json();\n * set({ data, loading: false });\n * } catch (error) {\n * set({ error, loading: false });\n * }\n * },\n * onDetach: ({ reset }) => reset(),\n * });\n * ```\n *\n * @example\n * Persisting state:\n * ```ts\n * import { createStore } from \"dharma-core\";\n *\n * const store = createStore({\n * persist: true,\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 * }),\n * });\n * ```\n *\n * **Note:** The state needs to be serializable by whatever serializer you choose (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 * @example\n * Custom storage and serializer:\n * ```ts\n * import { createStore } from \"dharma-core\";\n * import superjson from \"superjson\";\n *\n * const store = createStore({\n * persist: true,\n * key: \"count\",\n * storage: sessionStorage,\n * serializer: superjson,\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: 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\n ? defineActions({ set, get, reset })\n : ({} 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 set,\n reset,\n subscribe,\n actions,\n };\n};\n"],"names":["merge","createStorageAdapter"],"mappings":";;;;;AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiFG;AACI,MAAM,WAAW,GAAG,CAIzB,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;IAC7C,MAAM,OAAO,GAAG;UACZ,aAAa,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE;UAChC,EAAe;IACpB,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,GAAG;QACH,KAAK;QACL,SAAS;QACT,OAAO;KACR;AACH;;;;"}
|
package/dist/cjs/lib/merge.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { StateModifier } from "./
|
|
1
|
+
import { StateModifier } from "./types";
|
|
2
2
|
/**
|
|
3
3
|
* Merges the current state with a state modifier.
|
|
4
|
-
* Useful for creating custom set functions.
|
|
5
4
|
*
|
|
6
5
|
* @param {T} currentState - The current state object.
|
|
7
6
|
* @param {StateModifier<T>} stateModifier - A function or object that modifies the state.
|
|
@@ -27,7 +26,6 @@ import { StateModifier } from "./createStore";
|
|
|
27
26
|
* return {
|
|
28
27
|
* increment: () => setCounter((state) => ({ count: state.count + 1 })),
|
|
29
28
|
* decrement: () => setCounter((state) => ({ count: state.count - 1 })),
|
|
30
|
-
* reset: () => setCounter({ count: 0 }),
|
|
31
29
|
* // ...
|
|
32
30
|
* };
|
|
33
31
|
* },
|
package/dist/cjs/lib/merge.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Merges the current state with a state modifier.
|
|
5
|
-
* Useful for creating custom set functions.
|
|
6
5
|
*
|
|
7
6
|
* @param {T} currentState - The current state object.
|
|
8
7
|
* @param {StateModifier<T>} stateModifier - A function or object that modifies the state.
|
|
@@ -28,7 +27,6 @@
|
|
|
28
27
|
* return {
|
|
29
28
|
* increment: () => setCounter((state) => ({ count: state.count + 1 })),
|
|
30
29
|
* decrement: () => setCounter((state) => ({ count: state.count - 1 })),
|
|
31
|
-
* reset: () => setCounter({ count: 0 }),
|
|
32
30
|
* // ...
|
|
33
31
|
* };
|
|
34
32
|
* },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.js","sources":["../../../src/lib/merge.ts"],"sourcesContent":["import { StateModifier } from \"./
|
|
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 * @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 * // ...\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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCG;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;;;;"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export type Listener<TState extends object> = (state: TState) => void;
|
|
2
|
+
export type SetState<TState extends object> = (stateModifier: StateModifier<TState>) => TState;
|
|
3
|
+
export type Store<TState extends object, TActions extends object> = {
|
|
4
|
+
/** Returns the current state of the store. */
|
|
5
|
+
get: () => TState;
|
|
6
|
+
/** Sets the state of the store. */
|
|
7
|
+
set: SetState<TState>;
|
|
8
|
+
/** Resets the state of the store to its initial value. */
|
|
9
|
+
reset: () => TState;
|
|
10
|
+
/** Actions that can modify the state of the store. */
|
|
11
|
+
actions: TActions;
|
|
12
|
+
/** Subscribes to changes in the state of the store. Returns an unsubscribe function. */
|
|
13
|
+
subscribe: (listener: Listener<TState>) => () => void;
|
|
14
|
+
};
|
|
15
|
+
export type StoreEventContext<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 StoreEventListener<TState extends object> = (context: StoreEventContext<TState>) => void;
|
|
24
|
+
export type StateModifier<TState extends object> = Partial<TState> | ((state: TState) => Partial<TState>);
|
|
25
|
+
export type StateHandler<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> = (stateHandler: StateHandler<TState>) => TActions;
|
|
34
|
+
export type BaseConfig<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>;
|
|
39
|
+
/** Invoked when the store is created. */
|
|
40
|
+
onLoad?: StoreEventListener<TState>;
|
|
41
|
+
/** Invoked when the store is subscribed to. */
|
|
42
|
+
onAttach?: StoreEventListener<TState>;
|
|
43
|
+
/** Invoked when the store is unsubscribed from. */
|
|
44
|
+
onDetach?: StoreEventListener<TState>;
|
|
45
|
+
/** Invoked whenever the state changes. */
|
|
46
|
+
onChange?: StoreEventListener<TState>;
|
|
47
|
+
};
|
|
48
|
+
export type MaybePromise<T> = T | Promise<T>;
|
|
49
|
+
export type StorageAPI = {
|
|
50
|
+
getItem: (key: string) => MaybePromise<string | null>;
|
|
51
|
+
setItem: (key: string, value: string) => MaybePromise<void>;
|
|
52
|
+
removeItem: (key: string) => MaybePromise<void>;
|
|
53
|
+
};
|
|
54
|
+
export type Serializer<T = any> = {
|
|
55
|
+
stringify: (value: T) => string;
|
|
56
|
+
parse: (value: string) => T;
|
|
57
|
+
};
|
|
58
|
+
export type NoPersistConfig = {
|
|
59
|
+
/** Whether to persist the state of the store. */
|
|
60
|
+
persist?: false;
|
|
61
|
+
};
|
|
62
|
+
export type PersistConfig<TState extends object> = {
|
|
63
|
+
/** Whether to persist the state of the store. */
|
|
64
|
+
persist: true;
|
|
65
|
+
/** The unique key used to identify this store in storage. Required if `persist` is true. */
|
|
66
|
+
key: string;
|
|
67
|
+
/** The storage to use for persisting the state. Defaults to local storage if available. */
|
|
68
|
+
storage?: StorageAPI;
|
|
69
|
+
/** The serializer to use for storing the state. Defaults to JSON. */
|
|
70
|
+
serializer?: Serializer<TState>;
|
|
71
|
+
};
|
|
72
|
+
export type StoreConfig<TState extends object, TActions extends object> = BaseConfig<TState, TActions> & (PersistConfig<TState> | NoPersistConfig);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const warn: (message: string) => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warn.js","sources":["../../../src/lib/warn.ts"],"sourcesContent":["export const warn = (message: string) =>\n console.warn(`[dharma-core] ${message}`);\n"],"names":[],"mappings":";;AAAO,MAAM,IAAI,GAAG,CAAC,OAAe,KAClC,OAAO,CAAC,IAAI,CAAC,iBAAiB,OAAO,CAAA,CAAE;;;;"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SetState, StoreConfig } from "./types";
|
|
2
|
+
export declare const createStorageAdapter: <TState extends object, TActions extends object = Record<never, never>>(config: StoreConfig<TState, TActions>, get: () => TState, set: SetState<TState>) => {
|
|
3
|
+
onLoad: () => Promise<void>;
|
|
4
|
+
onChange: (newState: TState) => Promise<void>;
|
|
5
|
+
onAttach: () => void;
|
|
6
|
+
onDetach: () => void;
|
|
7
|
+
} | null;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { warn } from './warn.js';
|
|
2
|
+
|
|
3
|
+
const createStorageAdapter = (config, get, set) => {
|
|
4
|
+
if (!config.persist) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const IS_BROWSER = typeof window !== "undefined";
|
|
8
|
+
const storage = config.storage ?? (IS_BROWSER ? localStorage : undefined);
|
|
9
|
+
if (!storage) {
|
|
10
|
+
warn("No storage provider found. If this happened during SSR, you can safely ignore this warning.");
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const { key, serializer = JSON, initialState } = config;
|
|
14
|
+
const initKey = `init_${key}`;
|
|
15
|
+
const onLoad = async () => {
|
|
16
|
+
try {
|
|
17
|
+
let initialStateSnapshot = storage.getItem(initKey);
|
|
18
|
+
const initialStateString = serializer.stringify(initialState);
|
|
19
|
+
if (initialStateSnapshot instanceof Promise) {
|
|
20
|
+
initialStateSnapshot = await initialStateSnapshot;
|
|
21
|
+
}
|
|
22
|
+
if (initialStateSnapshot !== initialStateString) {
|
|
23
|
+
storage.setItem(initKey, initialStateString);
|
|
24
|
+
storage.removeItem(key);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
warn("Failed to initialize snapshots. If this happened during SSR, you can safely ignore this warning.");
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const syncWithSnapshot = async () => {
|
|
32
|
+
try {
|
|
33
|
+
let currentSnapshot = storage.getItem(key);
|
|
34
|
+
if (currentSnapshot instanceof Promise) {
|
|
35
|
+
currentSnapshot = await currentSnapshot;
|
|
36
|
+
}
|
|
37
|
+
if (currentSnapshot && currentSnapshot !== serializer.stringify(get())) {
|
|
38
|
+
set(serializer.parse(currentSnapshot));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
warn("Failed to update state from snapshot");
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const onAttach = () => {
|
|
46
|
+
syncWithSnapshot();
|
|
47
|
+
if (IS_BROWSER) {
|
|
48
|
+
window.addEventListener("focus", syncWithSnapshot);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const onChange = async (newState) => {
|
|
52
|
+
try {
|
|
53
|
+
let currentSnapshot = storage.getItem(key);
|
|
54
|
+
const newSnapshot = serializer.stringify(newState);
|
|
55
|
+
if (currentSnapshot instanceof Promise) {
|
|
56
|
+
currentSnapshot = await currentSnapshot;
|
|
57
|
+
}
|
|
58
|
+
if (newSnapshot !== currentSnapshot) {
|
|
59
|
+
storage.setItem(key, newSnapshot);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
warn("Failed to update snapshot");
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const onDetach = () => {
|
|
67
|
+
if (IS_BROWSER) {
|
|
68
|
+
window.removeEventListener("focus", syncWithSnapshot);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
onLoad,
|
|
73
|
+
onChange,
|
|
74
|
+
onAttach,
|
|
75
|
+
onDetach,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export { createStorageAdapter };
|
|
80
|
+
//# sourceMappingURL=createStorageAdapter.js.map
|
|
@@ -0,0 +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 = <\n TState extends object,\n TActions extends object = Record<never, never>,\n>(\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,CAIlC,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;;;;"}
|
|
@@ -1,74 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export type SetState<TState extends object> = (stateModifier: StateModifier<TState>) => TState;
|
|
3
|
-
export type Store<TState extends object, TActions extends object> = {
|
|
4
|
-
/** Returns the current state of the store. */
|
|
5
|
-
get: () => TState;
|
|
6
|
-
/** Sets the state of the store. */
|
|
7
|
-
set: SetState<TState>;
|
|
8
|
-
/** Resets the state of the store to its initial value. */
|
|
9
|
-
reset: () => TState;
|
|
10
|
-
/** Actions that can modify the state of the store. */
|
|
11
|
-
actions: TActions;
|
|
12
|
-
/** Subscribes to changes in the state of the store. Returns an unsubscribe function. */
|
|
13
|
-
subscribe: (listener: Listener<TState>) => () => void;
|
|
14
|
-
};
|
|
15
|
-
export type StoreEventContext<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: StoreEventContext<TState>) => void;
|
|
24
|
-
export type StateModifier<TState extends object> = Partial<TState> | ((state: TState) => Partial<TState>);
|
|
25
|
-
export type StateHandler<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> = (stateHandler: StateHandler<TState>) => TActions;
|
|
34
|
-
type BaseConfig<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>;
|
|
39
|
-
/** Invoked when the store is created. */
|
|
40
|
-
onLoad?: StoreEventHandler<TState>;
|
|
41
|
-
/** Invoked when the store is subscribed to. */
|
|
42
|
-
onAttach?: StoreEventHandler<TState>;
|
|
43
|
-
/** Invoked when the store is unsubscribed from. */
|
|
44
|
-
onDetach?: StoreEventHandler<TState>;
|
|
45
|
-
/** Invoked whenever the state changes. */
|
|
46
|
-
onChange?: StoreEventHandler<TState>;
|
|
47
|
-
};
|
|
48
|
-
export type StorageAPI = {
|
|
49
|
-
getItem: (key: string) => string | null;
|
|
50
|
-
setItem: (key: string, value: string) => void;
|
|
51
|
-
removeItem: (key: string) => void;
|
|
52
|
-
};
|
|
53
|
-
export type Serializer<T = any> = {
|
|
54
|
-
stringify: (value: T) => string;
|
|
55
|
-
parse: (value: string) => T;
|
|
56
|
-
};
|
|
57
|
-
type NonPersistentConfig = {
|
|
58
|
-
/** Whether to persist the state of the store. */
|
|
59
|
-
persist?: false;
|
|
60
|
-
};
|
|
61
|
-
type PersistentConfig<TState extends object> = {
|
|
62
|
-
/** Whether to persist the state of the store. */
|
|
63
|
-
persist: true;
|
|
64
|
-
/** The unique key used to identify this store in storage. */
|
|
65
|
-
key: string;
|
|
66
|
-
/** The storage to use for persisting the state. Defaults to local storage if available. */
|
|
67
|
-
storage?: StorageAPI;
|
|
68
|
-
/** The serializer to use for storing the state. Defaults to JSON. */
|
|
69
|
-
serializer?: Serializer<TState>;
|
|
70
|
-
};
|
|
71
|
-
export type StoreConfig<TState extends object, TActions extends object> = BaseConfig<TState, TActions> & (NonPersistentConfig | PersistentConfig<TState>);
|
|
1
|
+
import { Store, StoreConfig } from "./types";
|
|
72
2
|
/**
|
|
73
3
|
* Creates a store with an initial state and actions that can modify the state.
|
|
74
4
|
*
|
|
@@ -91,7 +21,7 @@ export type StoreConfig<TState extends object, TActions extends object> = BaseCo
|
|
|
91
21
|
* ```
|
|
92
22
|
*
|
|
93
23
|
* @example
|
|
94
|
-
*
|
|
24
|
+
* Lifecycle hooks:
|
|
95
25
|
* ```ts
|
|
96
26
|
* import { createStore } from "dharma-core";
|
|
97
27
|
* import { State } from "./types";
|
|
@@ -114,7 +44,7 @@ export type StoreConfig<TState extends object, TActions extends object> = BaseCo
|
|
|
114
44
|
* ```
|
|
115
45
|
*
|
|
116
46
|
* @example
|
|
117
|
-
*
|
|
47
|
+
* Persisting state:
|
|
118
48
|
* ```ts
|
|
119
49
|
* import { createStore } from "dharma-core";
|
|
120
50
|
*
|
|
@@ -133,7 +63,7 @@ export type StoreConfig<TState extends object, TActions extends object> = BaseCo
|
|
|
133
63
|
* If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
|
|
134
64
|
*
|
|
135
65
|
* @example
|
|
136
|
-
*
|
|
66
|
+
* Custom storage and serializer:
|
|
137
67
|
* ```ts
|
|
138
68
|
* import { createStore } from "dharma-core";
|
|
139
69
|
* import superjson from "superjson";
|
|
@@ -141,8 +71,8 @@ export type StoreConfig<TState extends object, TActions extends object> = BaseCo
|
|
|
141
71
|
* const store = createStore({
|
|
142
72
|
* persist: true,
|
|
143
73
|
* key: "count",
|
|
144
|
-
* serializer: superjson,
|
|
145
74
|
* storage: sessionStorage,
|
|
75
|
+
* serializer: superjson,
|
|
146
76
|
* initialState: { count: 0 },
|
|
147
77
|
* defineActions: ({ set }) => ({
|
|
148
78
|
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
@@ -152,4 +82,3 @@ export type StoreConfig<TState extends object, TActions extends object> = BaseCo
|
|
|
152
82
|
* ```
|
|
153
83
|
*/
|
|
154
84
|
export declare const createStore: <TState extends object, TActions extends object = Record<never, never>>(config: StoreConfig<TState, TActions>) => Store<TState, TActions>;
|
|
155
|
-
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createStorageAdapter } from './createStorageAdapter.js';
|
|
2
2
|
import { merge } from './merge.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -23,7 +23,7 @@ import { merge } from './merge.js';
|
|
|
23
23
|
* ```
|
|
24
24
|
*
|
|
25
25
|
* @example
|
|
26
|
-
*
|
|
26
|
+
* Lifecycle hooks:
|
|
27
27
|
* ```ts
|
|
28
28
|
* import { createStore } from "dharma-core";
|
|
29
29
|
* import { State } from "./types";
|
|
@@ -46,7 +46,7 @@ import { merge } from './merge.js';
|
|
|
46
46
|
* ```
|
|
47
47
|
*
|
|
48
48
|
* @example
|
|
49
|
-
*
|
|
49
|
+
* Persisting state:
|
|
50
50
|
* ```ts
|
|
51
51
|
* import { createStore } from "dharma-core";
|
|
52
52
|
*
|
|
@@ -65,7 +65,7 @@ import { merge } from './merge.js';
|
|
|
65
65
|
* If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
|
|
66
66
|
*
|
|
67
67
|
* @example
|
|
68
|
-
*
|
|
68
|
+
* Custom storage and serializer:
|
|
69
69
|
* ```ts
|
|
70
70
|
* import { createStore } from "dharma-core";
|
|
71
71
|
* import superjson from "superjson";
|
|
@@ -73,8 +73,8 @@ import { merge } from './merge.js';
|
|
|
73
73
|
* const store = createStore({
|
|
74
74
|
* persist: true,
|
|
75
75
|
* key: "count",
|
|
76
|
-
* serializer: superjson,
|
|
77
76
|
* storage: sessionStorage,
|
|
77
|
+
* serializer: superjson,
|
|
78
78
|
* initialState: { count: 0 },
|
|
79
79
|
* defineActions: ({ set }) => ({
|
|
80
80
|
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
@@ -106,15 +106,11 @@ const createStore = (config) => {
|
|
|
106
106
|
const actions = defineActions
|
|
107
107
|
? defineActions({ set, get, reset })
|
|
108
108
|
: {};
|
|
109
|
-
const
|
|
110
|
-
const persistHandler = getPersistHandler(config, IS_BROWSER, get, set);
|
|
109
|
+
const storageAdapter = createStorageAdapter(config, get, set);
|
|
111
110
|
const subscribe = (listener) => {
|
|
112
111
|
if (listeners.size === 0) {
|
|
113
112
|
onAttach?.({ state, set, reset });
|
|
114
|
-
|
|
115
|
-
if (IS_BROWSER && persistHandler) {
|
|
116
|
-
window.addEventListener("focus", persistHandler.updateState);
|
|
117
|
-
}
|
|
113
|
+
storageAdapter?.onAttach();
|
|
118
114
|
}
|
|
119
115
|
listener(state);
|
|
120
116
|
listeners.add(listener);
|
|
@@ -122,9 +118,7 @@ const createStore = (config) => {
|
|
|
122
118
|
listeners.delete(listener);
|
|
123
119
|
if (listeners.size === 0) {
|
|
124
120
|
onDetach?.({ state, set, reset });
|
|
125
|
-
|
|
126
|
-
window.removeEventListener("focus", persistHandler.updateState);
|
|
127
|
-
}
|
|
121
|
+
storageAdapter?.onDetach();
|
|
128
122
|
}
|
|
129
123
|
};
|
|
130
124
|
};
|
|
@@ -134,11 +128,11 @@ const createStore = (config) => {
|
|
|
134
128
|
set: setSilently,
|
|
135
129
|
reset: resetSilently,
|
|
136
130
|
});
|
|
137
|
-
|
|
131
|
+
storageAdapter?.onChange(state);
|
|
138
132
|
listeners.forEach((listener) => listener(state));
|
|
139
133
|
};
|
|
140
134
|
onLoad?.({ state, set, reset });
|
|
141
|
-
|
|
135
|
+
storageAdapter?.onLoad();
|
|
142
136
|
return {
|
|
143
137
|
get,
|
|
144
138
|
set,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { getPersistHandler } from \"./getPersistHandler\";\nimport { 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 StoreEventContext<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: StoreEventContext<TState>,\n) => void;\n\nexport type StateModifier<TState extends object> =\n | Partial<TState>\n | ((state: TState) => Partial<TState>);\n\nexport type StateHandler<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 stateHandler: StateHandler<TState>,\n) => TActions;\n\ntype BaseConfig<TState extends object, TActions extends object> = {\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\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\ntype NonPersistentConfig = {\n /** Whether to persist the state of the store. */\n persist?: false;\n};\n\ntype PersistentConfig<TState extends object> = {\n /** Whether to persist the state of the store. */\n persist: true;\n /** The unique key used to identify this store in storage. */\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\nexport type StoreConfig<\n TState extends object,\n TActions extends object,\n> = BaseConfig<TState, TActions> &\n (NonPersistentConfig | PersistentConfig<TState>);\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 * @example\n * Basic usage:\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 *\n * @example\n * With event handlers:\n * ```ts\n * import { createStore } from \"dharma-core\";\n * import { State } from \"./types\";\n *\n * const initialState: State = { data: null, loading: true, error: null };\n *\n * const store = createStore({\n * initialState,\n * onAttach: async ({ set }) => {\n * try {\n * const response = await fetch(\"https://api.example.com/data\");\n * const data = await response.json();\n * set({ data, loading: false });\n * } catch (error) {\n * set({ error, loading: false });\n * }\n * },\n * onDetach: ({ reset }) => reset(),\n * });\n * ```\n *\n * @example\n * Persistent store (uses local storage by default):\n * ```ts\n * import { createStore } from \"dharma-core\";\n *\n * const store = createStore({\n * persist: true,\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 * }),\n * });\n * ```\n *\n * **Note:** The state needs to be serializable by whatever serializer you choose (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 * @example\n * With superjson serialization and session storage:\n * ```ts\n * import { createStore } from \"dharma-core\";\n * import superjson from \"superjson\";\n *\n * const store = createStore({\n * persist: true,\n * key: \"count\",\n * serializer: superjson,\n * storage: sessionStorage,\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: 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\n ? defineActions({ set, get, reset })\n : ({} as TActions);\n const IS_BROWSER = typeof window !== \"undefined\";\n const persistHandler = getPersistHandler(config, IS_BROWSER, get, set);\n\n const subscribe = (listener: Listener<TState>) => {\n if (listeners.size === 0) {\n onAttach?.({ state, set, reset });\n persistHandler?.updateState();\n\n if (IS_BROWSER && persistHandler) {\n window.addEventListener(\"focus\", persistHandler.updateState);\n }\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 if (IS_BROWSER && persistHandler) {\n window.removeEventListener(\"focus\", persistHandler.updateState);\n }\n }\n };\n };\n\n const dispatch = () => {\n onChange?.({\n state,\n set: setSilently,\n reset: resetSilently,\n });\n persistHandler?.updateSnapshot(state);\n listeners.forEach((listener) => listener(state));\n };\n\n onLoad?.({ state, set, reset });\n persistHandler?.initializeSnapshots();\n\n return {\n get,\n set,\n reset,\n subscribe,\n actions,\n };\n};\n"],"names":[],"mappings":";;;AAqGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiFG;AACI,MAAM,WAAW,GAAG,CAIzB,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;IAC7C,MAAM,OAAO,GAAG;UACZ,aAAa,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE;UAChC,EAAe;AACpB,IAAA,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW;AAChD,IAAA,MAAM,cAAc,GAAG,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,CAAC;AAEtE,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,WAAW,EAAE;AAE7B,YAAA,IAAI,UAAU,IAAI,cAAc,EAAE;gBAChC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,cAAc,CAAC,WAAW,CAAC;YAC9D;QACF;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;AAEjC,gBAAA,IAAI,UAAU,IAAI,cAAc,EAAE;oBAChC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,cAAc,CAAC,WAAW,CAAC;gBACjE;YACF;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,cAAc,CAAC,KAAK,CAAC;AACrC,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,mBAAmB,EAAE;IAErC,OAAO;QACL,GAAG;QACH,GAAG;QACH,KAAK;QACL,SAAS;QACT,OAAO;KACR;AACH;;;;"}
|
|
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 * @example\n * Basic usage:\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 *\n * @example\n * Lifecycle hooks:\n * ```ts\n * import { createStore } from \"dharma-core\";\n * import { State } from \"./types\";\n *\n * const initialState: State = { data: null, loading: true, error: null };\n *\n * const store = createStore({\n * initialState,\n * onAttach: async ({ set }) => {\n * try {\n * const response = await fetch(\"https://api.example.com/data\");\n * const data = await response.json();\n * set({ data, loading: false });\n * } catch (error) {\n * set({ error, loading: false });\n * }\n * },\n * onDetach: ({ reset }) => reset(),\n * });\n * ```\n *\n * @example\n * Persisting state:\n * ```ts\n * import { createStore } from \"dharma-core\";\n *\n * const store = createStore({\n * persist: true,\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 * }),\n * });\n * ```\n *\n * **Note:** The state needs to be serializable by whatever serializer you choose (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 * @example\n * Custom storage and serializer:\n * ```ts\n * import { createStore } from \"dharma-core\";\n * import superjson from \"superjson\";\n *\n * const store = createStore({\n * persist: true,\n * key: \"count\",\n * storage: sessionStorage,\n * serializer: superjson,\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: 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\n ? defineActions({ set, get, reset })\n : ({} 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 set,\n reset,\n subscribe,\n actions,\n };\n};\n"],"names":[],"mappings":";;;AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiFG;AACI,MAAM,WAAW,GAAG,CAIzB,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;IAC7C,MAAM,OAAO,GAAG;UACZ,aAAa,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE;UAChC,EAAe;IACpB,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,GAAG;QACH,KAAK;QACL,SAAS;QACT,OAAO;KACR;AACH;;;;"}
|
package/dist/esm/lib/merge.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { StateModifier } from "./
|
|
1
|
+
import { StateModifier } from "./types";
|
|
2
2
|
/**
|
|
3
3
|
* Merges the current state with a state modifier.
|
|
4
|
-
* Useful for creating custom set functions.
|
|
5
4
|
*
|
|
6
5
|
* @param {T} currentState - The current state object.
|
|
7
6
|
* @param {StateModifier<T>} stateModifier - A function or object that modifies the state.
|
|
@@ -27,7 +26,6 @@ import { StateModifier } from "./createStore";
|
|
|
27
26
|
* return {
|
|
28
27
|
* increment: () => setCounter((state) => ({ count: state.count + 1 })),
|
|
29
28
|
* decrement: () => setCounter((state) => ({ count: state.count - 1 })),
|
|
30
|
-
* reset: () => setCounter({ count: 0 }),
|
|
31
29
|
* // ...
|
|
32
30
|
* };
|
|
33
31
|
* },
|
package/dist/esm/lib/merge.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Merges the current state with a state modifier.
|
|
3
|
-
* Useful for creating custom set functions.
|
|
4
3
|
*
|
|
5
4
|
* @param {T} currentState - The current state object.
|
|
6
5
|
* @param {StateModifier<T>} stateModifier - A function or object that modifies the state.
|
|
@@ -26,7 +25,6 @@
|
|
|
26
25
|
* return {
|
|
27
26
|
* increment: () => setCounter((state) => ({ count: state.count + 1 })),
|
|
28
27
|
* decrement: () => setCounter((state) => ({ count: state.count - 1 })),
|
|
29
|
-
* reset: () => setCounter({ count: 0 }),
|
|
30
28
|
* // ...
|
|
31
29
|
* };
|
|
32
30
|
* },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.js","sources":["../../../src/lib/merge.ts"],"sourcesContent":["import { StateModifier } from \"./
|
|
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 * @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 * // ...\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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCG;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;;;;"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export type Listener<TState extends object> = (state: TState) => void;
|
|
2
|
+
export type SetState<TState extends object> = (stateModifier: StateModifier<TState>) => TState;
|
|
3
|
+
export type Store<TState extends object, TActions extends object> = {
|
|
4
|
+
/** Returns the current state of the store. */
|
|
5
|
+
get: () => TState;
|
|
6
|
+
/** Sets the state of the store. */
|
|
7
|
+
set: SetState<TState>;
|
|
8
|
+
/** Resets the state of the store to its initial value. */
|
|
9
|
+
reset: () => TState;
|
|
10
|
+
/** Actions that can modify the state of the store. */
|
|
11
|
+
actions: TActions;
|
|
12
|
+
/** Subscribes to changes in the state of the store. Returns an unsubscribe function. */
|
|
13
|
+
subscribe: (listener: Listener<TState>) => () => void;
|
|
14
|
+
};
|
|
15
|
+
export type StoreEventContext<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 StoreEventListener<TState extends object> = (context: StoreEventContext<TState>) => void;
|
|
24
|
+
export type StateModifier<TState extends object> = Partial<TState> | ((state: TState) => Partial<TState>);
|
|
25
|
+
export type StateHandler<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> = (stateHandler: StateHandler<TState>) => TActions;
|
|
34
|
+
export type BaseConfig<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>;
|
|
39
|
+
/** Invoked when the store is created. */
|
|
40
|
+
onLoad?: StoreEventListener<TState>;
|
|
41
|
+
/** Invoked when the store is subscribed to. */
|
|
42
|
+
onAttach?: StoreEventListener<TState>;
|
|
43
|
+
/** Invoked when the store is unsubscribed from. */
|
|
44
|
+
onDetach?: StoreEventListener<TState>;
|
|
45
|
+
/** Invoked whenever the state changes. */
|
|
46
|
+
onChange?: StoreEventListener<TState>;
|
|
47
|
+
};
|
|
48
|
+
export type MaybePromise<T> = T | Promise<T>;
|
|
49
|
+
export type StorageAPI = {
|
|
50
|
+
getItem: (key: string) => MaybePromise<string | null>;
|
|
51
|
+
setItem: (key: string, value: string) => MaybePromise<void>;
|
|
52
|
+
removeItem: (key: string) => MaybePromise<void>;
|
|
53
|
+
};
|
|
54
|
+
export type Serializer<T = any> = {
|
|
55
|
+
stringify: (value: T) => string;
|
|
56
|
+
parse: (value: string) => T;
|
|
57
|
+
};
|
|
58
|
+
export type NoPersistConfig = {
|
|
59
|
+
/** Whether to persist the state of the store. */
|
|
60
|
+
persist?: false;
|
|
61
|
+
};
|
|
62
|
+
export type PersistConfig<TState extends object> = {
|
|
63
|
+
/** Whether to persist the state of the store. */
|
|
64
|
+
persist: true;
|
|
65
|
+
/** The unique key used to identify this store in storage. Required if `persist` is true. */
|
|
66
|
+
key: string;
|
|
67
|
+
/** The storage to use for persisting the state. Defaults to local storage if available. */
|
|
68
|
+
storage?: StorageAPI;
|
|
69
|
+
/** The serializer to use for storing the state. Defaults to JSON. */
|
|
70
|
+
serializer?: Serializer<TState>;
|
|
71
|
+
};
|
|
72
|
+
export type StoreConfig<TState extends object, TActions extends object> = BaseConfig<TState, TActions> & (PersistConfig<TState> | NoPersistConfig);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const warn: (message: string) => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warn.js","sources":["../../../src/lib/warn.ts"],"sourcesContent":["export const warn = (message: string) =>\n console.warn(`[dharma-core] ${message}`);\n"],"names":[],"mappings":"AAAO,MAAM,IAAI,GAAG,CAAC,OAAe,KAClC,OAAO,CAAC,IAAI,CAAC,iBAAiB,OAAO,CAAA,CAAE;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dharma-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"author": "Frans Ekman <fransedvinekman@gmail.com>",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "My package description",
|
|
@@ -45,6 +45,9 @@
|
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"provenance": true
|
|
47
47
|
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@react-native-async-storage/async-storage": "^2.2.0"
|
|
50
|
+
},
|
|
48
51
|
"scripts": {
|
|
49
52
|
"build": "rm -rf dist && rollup -c",
|
|
50
53
|
"dev": "rollup -c -w",
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { SetState, StoreConfig } from "./createStore";
|
|
2
|
-
export declare const getPersistHandler: <TState extends object, TActions extends object = Record<never, never>>(config: StoreConfig<TState, TActions>, isBrowser: boolean, get: () => TState, set: SetState<TState>) => {
|
|
3
|
-
initializeSnapshots: () => void;
|
|
4
|
-
updateSnapshot: (newState: TState) => void;
|
|
5
|
-
updateState: () => void;
|
|
6
|
-
} | null;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const getPersistHandler = (config, isBrowser, get, set) => {
|
|
4
|
-
if (!config.persist) {
|
|
5
|
-
return null;
|
|
6
|
-
}
|
|
7
|
-
const storage = config.storage ?? (isBrowser ? localStorage : undefined);
|
|
8
|
-
if (!storage) {
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
const { key, serializer = JSON, initialState } = config;
|
|
12
|
-
const initKey = `init_${key}`;
|
|
13
|
-
const initializeSnapshots = () => {
|
|
14
|
-
const initialStateSnapshot = storage.getItem(initKey);
|
|
15
|
-
const initialStateString = serializer.stringify(initialState);
|
|
16
|
-
if (initialStateSnapshot !== initialStateString) {
|
|
17
|
-
storage.setItem(initKey, initialStateString);
|
|
18
|
-
storage.removeItem(key);
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
const updateSnapshot = (newState) => {
|
|
22
|
-
const currentSnapshot = storage.getItem(key);
|
|
23
|
-
const newSnapshot = serializer.stringify(newState);
|
|
24
|
-
if (newSnapshot !== currentSnapshot) {
|
|
25
|
-
storage.setItem(key, newSnapshot);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
const updateState = () => {
|
|
29
|
-
const currentSnapshot = storage.getItem(key);
|
|
30
|
-
if (currentSnapshot && currentSnapshot !== serializer.stringify(get())) {
|
|
31
|
-
set(serializer.parse(currentSnapshot));
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
return {
|
|
35
|
-
initializeSnapshots,
|
|
36
|
-
updateSnapshot,
|
|
37
|
-
updateState,
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
exports.getPersistHandler = getPersistHandler;
|
|
42
|
-
//# sourceMappingURL=getPersistHandler.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getPersistHandler.js","sources":["../../../src/lib/getPersistHandler.ts"],"sourcesContent":["import { SetState, StoreConfig } from \"./createStore\";\n\nexport const getPersistHandler = <\n TState extends object,\n TActions extends object = Record<never, never>,\n>(\n config: StoreConfig<TState, TActions>,\n isBrowser: boolean,\n get: () => TState,\n set: SetState<TState>,\n) => {\n if (!config.persist) {\n return null;\n }\n\n const storage = config.storage ?? (isBrowser ? localStorage : undefined);\n\n if (!storage) {\n return null;\n }\n\n const { key, serializer = JSON, initialState } = config;\n const initKey = `init_${key}`;\n\n const initializeSnapshots = () => {\n const initialStateSnapshot = storage.getItem(initKey);\n const initialStateString = serializer.stringify(initialState);\n\n if (initialStateSnapshot !== initialStateString) {\n storage.setItem(initKey, initialStateString);\n storage.removeItem(key);\n }\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 (currentSnapshot && currentSnapshot !== serializer.stringify(get())) {\n set(serializer.parse(currentSnapshot));\n }\n };\n\n return {\n initializeSnapshots,\n updateSnapshot,\n updateState,\n };\n};\n"],"names":[],"mappings":";;AAEO,MAAM,iBAAiB,GAAG,CAI/B,MAAqC,EACrC,SAAkB,EAClB,GAAiB,EACjB,GAAqB,KACnB;AACF,IAAA,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;AACnB,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,KAAK,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;IAExE,IAAI,CAAC,OAAO,EAAE;AACZ,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;IAE7B,MAAM,mBAAmB,GAAG,MAAK;QAC/B,MAAM,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QACrD,MAAM,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC;AAE7D,QAAA,IAAI,oBAAoB,KAAK,kBAAkB,EAAE;AAC/C,YAAA,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,kBAAkB,CAAC;AAC5C,YAAA,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QACzB;AACF,IAAA,CAAC;AAED,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,IAAI,eAAe,IAAI,eAAe,KAAK,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE;YACtE,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QACxC;AACF,IAAA,CAAC;IAED,OAAO;QACL,mBAAmB;QACnB,cAAc;QACd,WAAW;KACZ;AACH;;;;"}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { SetState, StoreConfig } from "./createStore";
|
|
2
|
-
export declare const getPersistHandler: <TState extends object, TActions extends object = Record<never, never>>(config: StoreConfig<TState, TActions>, isBrowser: boolean, get: () => TState, set: SetState<TState>) => {
|
|
3
|
-
initializeSnapshots: () => void;
|
|
4
|
-
updateSnapshot: (newState: TState) => void;
|
|
5
|
-
updateState: () => void;
|
|
6
|
-
} | null;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
const getPersistHandler = (config, isBrowser, get, set) => {
|
|
2
|
-
if (!config.persist) {
|
|
3
|
-
return null;
|
|
4
|
-
}
|
|
5
|
-
const storage = config.storage ?? (isBrowser ? localStorage : undefined);
|
|
6
|
-
if (!storage) {
|
|
7
|
-
return null;
|
|
8
|
-
}
|
|
9
|
-
const { key, serializer = JSON, initialState } = config;
|
|
10
|
-
const initKey = `init_${key}`;
|
|
11
|
-
const initializeSnapshots = () => {
|
|
12
|
-
const initialStateSnapshot = storage.getItem(initKey);
|
|
13
|
-
const initialStateString = serializer.stringify(initialState);
|
|
14
|
-
if (initialStateSnapshot !== initialStateString) {
|
|
15
|
-
storage.setItem(initKey, initialStateString);
|
|
16
|
-
storage.removeItem(key);
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
const updateSnapshot = (newState) => {
|
|
20
|
-
const currentSnapshot = storage.getItem(key);
|
|
21
|
-
const newSnapshot = serializer.stringify(newState);
|
|
22
|
-
if (newSnapshot !== currentSnapshot) {
|
|
23
|
-
storage.setItem(key, newSnapshot);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
const updateState = () => {
|
|
27
|
-
const currentSnapshot = storage.getItem(key);
|
|
28
|
-
if (currentSnapshot && currentSnapshot !== serializer.stringify(get())) {
|
|
29
|
-
set(serializer.parse(currentSnapshot));
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
return {
|
|
33
|
-
initializeSnapshots,
|
|
34
|
-
updateSnapshot,
|
|
35
|
-
updateState,
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export { getPersistHandler };
|
|
40
|
-
//# sourceMappingURL=getPersistHandler.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getPersistHandler.js","sources":["../../../src/lib/getPersistHandler.ts"],"sourcesContent":["import { SetState, StoreConfig } from \"./createStore\";\n\nexport const getPersistHandler = <\n TState extends object,\n TActions extends object = Record<never, never>,\n>(\n config: StoreConfig<TState, TActions>,\n isBrowser: boolean,\n get: () => TState,\n set: SetState<TState>,\n) => {\n if (!config.persist) {\n return null;\n }\n\n const storage = config.storage ?? (isBrowser ? localStorage : undefined);\n\n if (!storage) {\n return null;\n }\n\n const { key, serializer = JSON, initialState } = config;\n const initKey = `init_${key}`;\n\n const initializeSnapshots = () => {\n const initialStateSnapshot = storage.getItem(initKey);\n const initialStateString = serializer.stringify(initialState);\n\n if (initialStateSnapshot !== initialStateString) {\n storage.setItem(initKey, initialStateString);\n storage.removeItem(key);\n }\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 (currentSnapshot && currentSnapshot !== serializer.stringify(get())) {\n set(serializer.parse(currentSnapshot));\n }\n };\n\n return {\n initializeSnapshots,\n updateSnapshot,\n updateState,\n };\n};\n"],"names":[],"mappings":"AAEO,MAAM,iBAAiB,GAAG,CAI/B,MAAqC,EACrC,SAAkB,EAClB,GAAiB,EACjB,GAAqB,KACnB;AACF,IAAA,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;AACnB,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,KAAK,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;IAExE,IAAI,CAAC,OAAO,EAAE;AACZ,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;IAE7B,MAAM,mBAAmB,GAAG,MAAK;QAC/B,MAAM,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QACrD,MAAM,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC;AAE7D,QAAA,IAAI,oBAAoB,KAAK,kBAAkB,EAAE;AAC/C,YAAA,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,kBAAkB,CAAC;AAC5C,YAAA,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QACzB;AACF,IAAA,CAAC;AAED,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,IAAI,eAAe,IAAI,eAAe,KAAK,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE;YACtE,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QACxC;AACF,IAAA,CAAC;IAED,OAAO;QACL,mBAAmB;QACnB,cAAc;QACd,WAAW;KACZ;AACH;;;;"}
|