dharma-core 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,3 +30,5 @@ decrement();
30
30
  // { count: 0 }
31
31
  unsubscribe();
32
32
  ```
33
+
34
+ [Read the documentation](https://fransek.github.io/dharma/modules/dharma-core.html)
@@ -1,3 +1,2 @@
1
- export * from "./lib/createPersistentStore";
2
1
  export * from "./lib/createStore";
3
2
  export * from "./lib/merge";
package/dist/cjs/index.js CHANGED
@@ -1,12 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var createPersistentStore = require('./lib/createPersistentStore.js');
4
3
  var createStore = require('./lib/createStore.js');
5
4
  var merge = require('./lib/merge.js');
6
5
 
7
6
 
8
7
 
9
- exports.createPersistentStore = createPersistentStore.createPersistentStore;
10
8
  exports.createStore = createStore.createStore;
11
9
  exports.merge = merge.merge;
12
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
@@ -31,7 +31,7 @@ export type StateFunctions<TState extends object> = {
31
31
  reset: () => TState;
32
32
  };
33
33
  export type DefineActions<TState extends object, TActions> = (stateFunctions: StateFunctions<TState>) => TActions;
34
- export type StoreConfiguration<TState extends object, TActions extends object> = {
34
+ type BaseConfig<TState extends object, TActions extends object> = {
35
35
  /** The initial state of the store. */
36
36
  initialState: TState;
37
37
  /** A function that defines actions that can modify the state. */
@@ -45,18 +45,104 @@ export type StoreConfiguration<TState extends object, TActions extends object> =
45
45
  /** Invoked whenever the state changes. */
46
46
  onChange?: StoreEventHandler<TState>;
47
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>);
48
72
  /**
49
73
  * Creates a store with an initial state and actions that can modify the state.
50
74
  *
51
- * @param {StoreConfiguration<TState>} [config] - The configuration for the store.
75
+ * @param {StoreConfig<TState, TActions>} [config] - The configuration for the store.
52
76
  *
53
77
  * @returns {Store<TState, TActions>} The created store with state management methods.
54
78
  *
55
79
  * @example
80
+ * Basic usage:
81
+ * ```ts
82
+ * import { createStore } from "dharma-core";
83
+ *
84
+ * const store = createStore({
85
+ * initialState: { count: 0 },
86
+ * defineActions: ({ set }) => ({
87
+ * increment: () => set((state) => ({ count: state.count + 1 })),
88
+ * decrement: () => set((state) => ({ count: state.count - 1 })),
89
+ * }),
90
+ * });
91
+ * ```
92
+ *
93
+ * @example
94
+ * With event handlers:
95
+ * ```ts
96
+ * import { createStore } from "dharma-core";
97
+ * import { State } from "./types";
98
+ *
99
+ * const initialState: State = { data: null, loading: true, error: null };
100
+ *
101
+ * const store = createStore({
102
+ * initialState,
103
+ * onAttach: async ({ set }) => {
104
+ * try {
105
+ * const response = await fetch("https://api.example.com/data");
106
+ * const data = await response.json();
107
+ * set({ data, loading: false });
108
+ * } catch (error) {
109
+ * set({ error, loading: false });
110
+ * }
111
+ * },
112
+ * onDetach: ({ reset }) => reset(),
113
+ * });
114
+ * ```
115
+ *
116
+ * @example
117
+ * Persistent store (uses local storage by default):
118
+ * ```ts
119
+ * import { createStore } from "dharma-core";
120
+ *
121
+ * const store = createStore({
122
+ * persist: true,
123
+ * key: "count",
124
+ * initialState: { count: 0 },
125
+ * defineActions: ({ set }) => ({
126
+ * increment: () => set((state) => ({ count: state.count + 1 })),
127
+ * decrement: () => set((state) => ({ count: state.count - 1 })),
128
+ * }),
129
+ * });
130
+ * ```
131
+ *
132
+ * **Note:** The state needs to be serializable by whatever serializer you choose (JSON by default).
133
+ * If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
134
+ *
135
+ * @example
136
+ * With superjson serialization and session storage:
56
137
  * ```ts
57
138
  * import { createStore } from "dharma-core";
139
+ * import superjson from "superjson";
58
140
  *
59
141
  * const store = createStore({
142
+ * persist: true,
143
+ * key: "count",
144
+ * serializer: superjson,
145
+ * storage: sessionStorage,
60
146
  * initialState: { count: 0 },
61
147
  * defineActions: ({ set }) => ({
62
148
  * increment: () => set((state) => ({ count: state.count + 1 })),
@@ -65,5 +151,5 @@ export type StoreConfiguration<TState extends object, TActions extends object> =
65
151
  * });
66
152
  * ```
67
153
  */
68
- export declare const createStore: <TState extends object, TActions extends object = Record<never, never>>(config: StoreConfiguration<TState, TActions>) => Store<TState, TActions>;
154
+ export declare const createStore: <TState extends object, TActions extends object = Record<never, never>>(config: StoreConfig<TState, TActions>) => Store<TState, TActions>;
69
155
  export {};
@@ -1,15 +1,17 @@
1
1
  'use strict';
2
2
 
3
+ var getPersistenceHandlers = require('./getPersistenceHandlers.js');
3
4
  var merge = require('./merge.js');
4
5
 
5
6
  /**
6
7
  * Creates a store with an initial state and actions that can modify the state.
7
8
  *
8
- * @param {StoreConfiguration<TState>} [config] - The configuration for the store.
9
+ * @param {StoreConfig<TState, TActions>} [config] - The configuration for the store.
9
10
  *
10
11
  * @returns {Store<TState, TActions>} The created store with state management methods.
11
12
  *
12
13
  * @example
14
+ * Basic usage:
13
15
  * ```ts
14
16
  * import { createStore } from "dharma-core";
15
17
  *
@@ -21,6 +23,67 @@ var merge = require('./merge.js');
21
23
  * }),
22
24
  * });
23
25
  * ```
26
+ *
27
+ * @example
28
+ * With event handlers:
29
+ * ```ts
30
+ * import { createStore } from "dharma-core";
31
+ * import { State } from "./types";
32
+ *
33
+ * const initialState: State = { data: null, loading: true, error: null };
34
+ *
35
+ * const store = createStore({
36
+ * initialState,
37
+ * onAttach: async ({ set }) => {
38
+ * try {
39
+ * const response = await fetch("https://api.example.com/data");
40
+ * const data = await response.json();
41
+ * set({ data, loading: false });
42
+ * } catch (error) {
43
+ * set({ error, loading: false });
44
+ * }
45
+ * },
46
+ * onDetach: ({ reset }) => reset(),
47
+ * });
48
+ * ```
49
+ *
50
+ * @example
51
+ * Persistent store (uses local storage by default):
52
+ * ```ts
53
+ * import { createStore } from "dharma-core";
54
+ *
55
+ * const store = createStore({
56
+ * persist: true,
57
+ * key: "count",
58
+ * initialState: { count: 0 },
59
+ * defineActions: ({ set }) => ({
60
+ * increment: () => set((state) => ({ count: state.count + 1 })),
61
+ * decrement: () => set((state) => ({ count: state.count - 1 })),
62
+ * }),
63
+ * });
64
+ * ```
65
+ *
66
+ * **Note:** The state needs to be serializable by whatever serializer you choose (JSON by default).
67
+ * If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
68
+ *
69
+ * @example
70
+ * With superjson serialization and session storage:
71
+ * ```ts
72
+ * import { createStore } from "dharma-core";
73
+ * import superjson from "superjson";
74
+ *
75
+ * const store = createStore({
76
+ * persist: true,
77
+ * key: "count",
78
+ * serializer: superjson,
79
+ * storage: sessionStorage,
80
+ * initialState: { count: 0 },
81
+ * defineActions: ({ set }) => ({
82
+ * increment: () => set((state) => ({ count: state.count + 1 })),
83
+ * decrement: () => set((state) => ({ count: state.count - 1 })),
84
+ * }),
85
+ * });
86
+ * ```
24
87
  */
25
88
  const createStore = (config) => {
26
89
  const { initialState, defineActions, onLoad, onAttach, onDetach, onChange } = config;
@@ -34,6 +97,7 @@ const createStore = (config) => {
34
97
  const resetSilently = () => setSilently(initialState);
35
98
  const dispatch = () => {
36
99
  onChange?.({ state, set: setSilently, reset: resetSilently });
100
+ persistenceHandlers?.updateSnapshot(state);
37
101
  listeners.forEach((listener) => listener(state));
38
102
  };
39
103
  const set = (stateModifier) => {
@@ -49,6 +113,10 @@ const createStore = (config) => {
49
113
  const subscribe = (listener) => {
50
114
  if (listeners.size === 0) {
51
115
  onAttach?.({ state, set, reset });
116
+ persistenceHandlers?.updateState();
117
+ if (IS_BROWSER && persistenceHandlers) {
118
+ window.addEventListener("focus", persistenceHandlers.updateState);
119
+ }
52
120
  }
53
121
  listener(state);
54
122
  listeners.add(listener);
@@ -56,13 +124,19 @@ const createStore = (config) => {
56
124
  listeners.delete(listener);
57
125
  if (listeners.size === 0) {
58
126
  onDetach?.({ state, set, reset });
127
+ if (IS_BROWSER && persistenceHandlers) {
128
+ window.removeEventListener("focus", persistenceHandlers.updateState);
129
+ }
59
130
  }
60
131
  };
61
132
  };
62
133
  const actions = defineActions
63
134
  ? defineActions({ set, get, reset })
64
135
  : {};
136
+ const IS_BROWSER = typeof window !== "undefined";
137
+ const persistenceHandlers = getPersistenceHandlers.getPersistenceHandlers(config, IS_BROWSER, get, set);
65
138
  onLoad?.({ state, set, reset });
139
+ persistenceHandlers?.initializeSnapshots();
66
140
  return {
67
141
  get,
68
142
  set,
@@ -1 +1 @@
1
- {"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { merge } from \"./merge\";\n\ntype Listener<TState extends object> = (state: TState) => void;\n\nexport type SetState<TState extends object> = (\n stateModifier: StateModifier<TState>,\n) => TState;\n\nexport type Store<TState extends object, TActions extends object> = {\n /** Returns the current state of the store. */\n get: () => TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n /** Actions that can modify the state of the store. */\n actions: TActions;\n /** Subscribes to changes in the state of the store. Returns an unsubscribe function. */\n subscribe: (listener: Listener<TState>) => () => void;\n};\n\nexport type EventHandlerContext<TState extends object> = {\n /** The current state of the store. */\n state: TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n};\n\nexport type StoreEventHandler<TState extends object> = (\n context: EventHandlerContext<TState>,\n) => void;\n\nexport type StateModifier<TState extends object> =\n | Partial<TState>\n | ((state: TState) => Partial<TState>);\n\nexport type StateFunctions<TState extends object> = {\n /** Returns the current state of the store. */\n get: () => TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n};\n\nexport type DefineActions<TState extends object, TActions> = (\n stateFunctions: StateFunctions<TState>,\n) => TActions;\n\nexport type StoreConfiguration<\n TState extends object,\n TActions extends object,\n> = {\n /** The initial state of the store. */\n initialState: TState;\n /** A function that defines actions that can modify the state. */\n defineActions?: DefineActions<TState, TActions>;\n /** Invoked when the store is created. */\n onLoad?: StoreEventHandler<TState>;\n /** Invoked when the store is subscribed to. */\n onAttach?: StoreEventHandler<TState>;\n /** Invoked when the store is unsubscribed from. */\n onDetach?: StoreEventHandler<TState>;\n /** Invoked whenever the state changes. */\n onChange?: StoreEventHandler<TState>;\n};\n\n/**\n * Creates a store with an initial state and actions that can modify the state.\n *\n * @param {StoreConfiguration<TState>} [config] - The configuration for the store.\n *\n * @returns {Store<TState, TActions>} The created store with state management methods.\n *\n * @example\n * ```ts\n * import { createStore } from \"dharma-core\";\n *\n * const store = createStore({\n * initialState: { count: 0 },\n * defineActions: ({ set }) => ({\n * increment: () => set((state) => ({ count: state.count + 1 })),\n * decrement: () => set((state) => ({ count: state.count - 1 })),\n * }),\n * });\n * ```\n */\nexport const createStore = <\n TState extends object,\n TActions extends object = Record<never, never>,\n>(\n config: StoreConfiguration<TState, TActions>,\n): Store<TState, TActions> => {\n const { initialState, defineActions, onLoad, onAttach, onDetach, onChange } =\n config;\n\n let state = initialState;\n const listeners = new Set<Listener<TState>>();\n\n const get = () => state;\n\n const setSilently = (stateModifier: StateModifier<TState>) => {\n state = merge(state, stateModifier);\n return state;\n };\n\n const resetSilently = () => setSilently(initialState);\n\n const dispatch = () => {\n onChange?.({ state, set: setSilently, reset: resetSilently });\n listeners.forEach((listener) => listener(state));\n };\n\n const set = (stateModifier: StateModifier<TState>) => {\n setSilently(stateModifier);\n dispatch();\n return state;\n };\n\n const reset = () => {\n resetSilently();\n dispatch();\n return state;\n };\n\n const subscribe = (listener: Listener<TState>) => {\n if (listeners.size === 0) {\n onAttach?.({ state, set, reset });\n }\n\n listener(state);\n listeners.add(listener);\n\n return () => {\n listeners.delete(listener);\n\n if (listeners.size === 0) {\n onDetach?.({ state, set, reset });\n }\n };\n };\n\n const actions = defineActions\n ? defineActions({ set, get, reset })\n : ({} as TActions);\n\n onLoad?.({ state, set, reset });\n\n return {\n get,\n set,\n reset,\n subscribe,\n actions,\n };\n};\n"],"names":["merge"],"mappings":";;;;AAqEA;;;;;;;;;;;;;;;;;;;AAmBG;AACI,MAAM,WAAW,GAAG,CAIzB,MAA4C,KACjB;AAC3B,IAAA,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,GACzE,MAAM;IAER,IAAI,KAAK,GAAG,YAAY;AACxB,IAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB;AAE7C,IAAA,MAAM,GAAG,GAAG,MAAM,KAAK;AAEvB,IAAA,MAAM,WAAW,GAAG,CAAC,aAAoC,KAAI;AAC3D,QAAA,KAAK,GAAGA,WAAK,CAAC,KAAK,EAAE,aAAa,CAAC;AACnC,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC;IAErD,MAAM,QAAQ,GAAG,MAAK;AACpB,QAAA,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;AAC7D,QAAA,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC;AAClD,IAAA,CAAC;AAED,IAAA,MAAM,GAAG,GAAG,CAAC,aAAoC,KAAI;QACnD,WAAW,CAAC,aAAa,CAAC;AAC1B,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;IAED,MAAM,KAAK,GAAG,MAAK;AACjB,QAAA,aAAa,EAAE;AACf,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;AAED,IAAA,MAAM,SAAS,GAAG,CAAC,QAA0B,KAAI;AAC/C,QAAA,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE;YACxB,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;QACnC;QAEA,QAAQ,CAAC,KAAK,CAAC;AACf,QAAA,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC;AAEvB,QAAA,OAAO,MAAK;AACV,YAAA,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC;AAE1B,YAAA,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE;gBACxB,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;YACnC;AACF,QAAA,CAAC;AACH,IAAA,CAAC;IAED,MAAM,OAAO,GAAG;UACZ,aAAa,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE;UAChC,EAAe;IAEpB,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAE/B,OAAO;QACL,GAAG;QACH,GAAG;QACH,KAAK;QACL,SAAS;QACT,OAAO;KACR;AACH;;;;"}
1
+ {"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { getPersistenceHandlers } from \"./getPersistenceHandlers\";\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 EventHandlerContext<TState extends object> = {\n /** The current state of the store. */\n state: TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n};\n\nexport type StoreEventHandler<TState extends object> = (\n context: EventHandlerContext<TState>,\n) => void;\n\nexport type StateModifier<TState extends object> =\n | Partial<TState>\n | ((state: TState) => Partial<TState>);\n\nexport type StateFunctions<TState extends object> = {\n /** Returns the current state of the store. */\n get: () => TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n};\n\nexport type DefineActions<TState extends object, TActions> = (\n stateFunctions: StateFunctions<TState>,\n) => TActions;\n\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 const listeners = new Set<Listener<TState>>();\n\n const get = () => state;\n\n const setSilently = (stateModifier: StateModifier<TState>) => {\n state = merge(state, stateModifier);\n return state;\n };\n\n const resetSilently = () => setSilently(initialState);\n\n const dispatch = () => {\n onChange?.({ state, set: setSilently, reset: resetSilently });\n persistenceHandlers?.updateSnapshot(state);\n listeners.forEach((listener) => listener(state));\n };\n\n const set = (stateModifier: StateModifier<TState>) => {\n setSilently(stateModifier);\n dispatch();\n return state;\n };\n\n const reset = () => {\n resetSilently();\n dispatch();\n return state;\n };\n\n const subscribe = (listener: Listener<TState>) => {\n if (listeners.size === 0) {\n onAttach?.({ state, set, reset });\n persistenceHandlers?.updateState();\n\n if (IS_BROWSER && persistenceHandlers) {\n window.addEventListener(\"focus\", persistenceHandlers.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 && persistenceHandlers) {\n window.removeEventListener(\"focus\", persistenceHandlers.updateState);\n }\n }\n };\n };\n\n const actions = defineActions\n ? defineActions({ set, get, reset })\n : ({} as TActions);\n\n const IS_BROWSER = typeof window !== \"undefined\";\n const persistenceHandlers = getPersistenceHandlers<TState, TActions>(\n config,\n IS_BROWSER,\n get,\n set,\n );\n\n onLoad?.({ state, set, reset });\n persistenceHandlers?.initializeSnapshots();\n\n return {\n get,\n set,\n reset,\n subscribe,\n actions,\n };\n};\n"],"names":["merge","getPersistenceHandlers"],"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;AACxB,IAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB;AAE7C,IAAA,MAAM,GAAG,GAAG,MAAM,KAAK;AAEvB,IAAA,MAAM,WAAW,GAAG,CAAC,aAAoC,KAAI;AAC3D,QAAA,KAAK,GAAGA,WAAK,CAAC,KAAK,EAAE,aAAa,CAAC;AACnC,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC;IAErD,MAAM,QAAQ,GAAG,MAAK;AACpB,QAAA,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;AAC7D,QAAA,mBAAmB,EAAE,cAAc,CAAC,KAAK,CAAC;AAC1C,QAAA,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC;AAClD,IAAA,CAAC;AAED,IAAA,MAAM,GAAG,GAAG,CAAC,aAAoC,KAAI;QACnD,WAAW,CAAC,aAAa,CAAC;AAC1B,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;IAED,MAAM,KAAK,GAAG,MAAK;AACjB,QAAA,aAAa,EAAE;AACf,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;AAED,IAAA,MAAM,SAAS,GAAG,CAAC,QAA0B,KAAI;AAC/C,QAAA,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE;YACxB,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;YACjC,mBAAmB,EAAE,WAAW,EAAE;AAElC,YAAA,IAAI,UAAU,IAAI,mBAAmB,EAAE;gBACrC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,CAAC,WAAW,CAAC;YACnE;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,mBAAmB,EAAE;oBACrC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,CAAC,WAAW,CAAC;gBACtE;YACF;AACF,QAAA,CAAC;AACH,IAAA,CAAC;IAED,MAAM,OAAO,GAAG;UACZ,aAAa,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE;UAChC,EAAe;AAEpB,IAAA,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW;AAChD,IAAA,MAAM,mBAAmB,GAAGC,6CAAsB,CAChD,MAAM,EACN,UAAU,EACV,GAAG,EACH,GAAG,CACJ;IAED,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAC/B,mBAAmB,EAAE,mBAAmB,EAAE;IAE1C,OAAO;QACL,GAAG;QACH,GAAG;QACH,KAAK;QACL,SAAS;QACT,OAAO;KACR;AACH;;;;"}
@@ -0,0 +1,6 @@
1
+ import { SetState, StoreConfig } from "./createStore";
2
+ export declare const getPersistenceHandlers: <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;
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ const getPersistenceHandlers = (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.getPersistenceHandlers = getPersistenceHandlers;
42
+ //# sourceMappingURL=getPersistenceHandlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getPersistenceHandlers.js","sources":["../../../src/lib/getPersistenceHandlers.ts"],"sourcesContent":["import { SetState, StoreConfig } from \"./createStore\";\n\nexport const getPersistenceHandlers = <\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,sBAAsB,GAAG,CAIpC,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,3 +1,2 @@
1
- export * from "./lib/createPersistentStore";
2
1
  export * from "./lib/createStore";
3
2
  export * from "./lib/merge";
package/dist/esm/index.js CHANGED
@@ -1,4 +1,3 @@
1
- export { createPersistentStore } from './lib/createPersistentStore.js';
2
1
  export { createStore } from './lib/createStore.js';
3
2
  export { merge } from './lib/merge.js';
4
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -31,7 +31,7 @@ export type StateFunctions<TState extends object> = {
31
31
  reset: () => TState;
32
32
  };
33
33
  export type DefineActions<TState extends object, TActions> = (stateFunctions: StateFunctions<TState>) => TActions;
34
- export type StoreConfiguration<TState extends object, TActions extends object> = {
34
+ type BaseConfig<TState extends object, TActions extends object> = {
35
35
  /** The initial state of the store. */
36
36
  initialState: TState;
37
37
  /** A function that defines actions that can modify the state. */
@@ -45,18 +45,104 @@ export type StoreConfiguration<TState extends object, TActions extends object> =
45
45
  /** Invoked whenever the state changes. */
46
46
  onChange?: StoreEventHandler<TState>;
47
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>);
48
72
  /**
49
73
  * Creates a store with an initial state and actions that can modify the state.
50
74
  *
51
- * @param {StoreConfiguration<TState>} [config] - The configuration for the store.
75
+ * @param {StoreConfig<TState, TActions>} [config] - The configuration for the store.
52
76
  *
53
77
  * @returns {Store<TState, TActions>} The created store with state management methods.
54
78
  *
55
79
  * @example
80
+ * Basic usage:
81
+ * ```ts
82
+ * import { createStore } from "dharma-core";
83
+ *
84
+ * const store = createStore({
85
+ * initialState: { count: 0 },
86
+ * defineActions: ({ set }) => ({
87
+ * increment: () => set((state) => ({ count: state.count + 1 })),
88
+ * decrement: () => set((state) => ({ count: state.count - 1 })),
89
+ * }),
90
+ * });
91
+ * ```
92
+ *
93
+ * @example
94
+ * With event handlers:
95
+ * ```ts
96
+ * import { createStore } from "dharma-core";
97
+ * import { State } from "./types";
98
+ *
99
+ * const initialState: State = { data: null, loading: true, error: null };
100
+ *
101
+ * const store = createStore({
102
+ * initialState,
103
+ * onAttach: async ({ set }) => {
104
+ * try {
105
+ * const response = await fetch("https://api.example.com/data");
106
+ * const data = await response.json();
107
+ * set({ data, loading: false });
108
+ * } catch (error) {
109
+ * set({ error, loading: false });
110
+ * }
111
+ * },
112
+ * onDetach: ({ reset }) => reset(),
113
+ * });
114
+ * ```
115
+ *
116
+ * @example
117
+ * Persistent store (uses local storage by default):
118
+ * ```ts
119
+ * import { createStore } from "dharma-core";
120
+ *
121
+ * const store = createStore({
122
+ * persist: true,
123
+ * key: "count",
124
+ * initialState: { count: 0 },
125
+ * defineActions: ({ set }) => ({
126
+ * increment: () => set((state) => ({ count: state.count + 1 })),
127
+ * decrement: () => set((state) => ({ count: state.count - 1 })),
128
+ * }),
129
+ * });
130
+ * ```
131
+ *
132
+ * **Note:** The state needs to be serializable by whatever serializer you choose (JSON by default).
133
+ * If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
134
+ *
135
+ * @example
136
+ * With superjson serialization and session storage:
56
137
  * ```ts
57
138
  * import { createStore } from "dharma-core";
139
+ * import superjson from "superjson";
58
140
  *
59
141
  * const store = createStore({
142
+ * persist: true,
143
+ * key: "count",
144
+ * serializer: superjson,
145
+ * storage: sessionStorage,
60
146
  * initialState: { count: 0 },
61
147
  * defineActions: ({ set }) => ({
62
148
  * increment: () => set((state) => ({ count: state.count + 1 })),
@@ -65,5 +151,5 @@ export type StoreConfiguration<TState extends object, TActions extends object> =
65
151
  * });
66
152
  * ```
67
153
  */
68
- export declare const createStore: <TState extends object, TActions extends object = Record<never, never>>(config: StoreConfiguration<TState, TActions>) => Store<TState, TActions>;
154
+ export declare const createStore: <TState extends object, TActions extends object = Record<never, never>>(config: StoreConfig<TState, TActions>) => Store<TState, TActions>;
69
155
  export {};
@@ -1,13 +1,15 @@
1
+ import { getPersistenceHandlers } from './getPersistenceHandlers.js';
1
2
  import { merge } from './merge.js';
2
3
 
3
4
  /**
4
5
  * Creates a store with an initial state and actions that can modify the state.
5
6
  *
6
- * @param {StoreConfiguration<TState>} [config] - The configuration for the store.
7
+ * @param {StoreConfig<TState, TActions>} [config] - The configuration for the store.
7
8
  *
8
9
  * @returns {Store<TState, TActions>} The created store with state management methods.
9
10
  *
10
11
  * @example
12
+ * Basic usage:
11
13
  * ```ts
12
14
  * import { createStore } from "dharma-core";
13
15
  *
@@ -19,6 +21,67 @@ import { merge } from './merge.js';
19
21
  * }),
20
22
  * });
21
23
  * ```
24
+ *
25
+ * @example
26
+ * With event handlers:
27
+ * ```ts
28
+ * import { createStore } from "dharma-core";
29
+ * import { State } from "./types";
30
+ *
31
+ * const initialState: State = { data: null, loading: true, error: null };
32
+ *
33
+ * const store = createStore({
34
+ * initialState,
35
+ * onAttach: async ({ set }) => {
36
+ * try {
37
+ * const response = await fetch("https://api.example.com/data");
38
+ * const data = await response.json();
39
+ * set({ data, loading: false });
40
+ * } catch (error) {
41
+ * set({ error, loading: false });
42
+ * }
43
+ * },
44
+ * onDetach: ({ reset }) => reset(),
45
+ * });
46
+ * ```
47
+ *
48
+ * @example
49
+ * Persistent store (uses local storage by default):
50
+ * ```ts
51
+ * import { createStore } from "dharma-core";
52
+ *
53
+ * const store = createStore({
54
+ * persist: true,
55
+ * key: "count",
56
+ * initialState: { count: 0 },
57
+ * defineActions: ({ set }) => ({
58
+ * increment: () => set((state) => ({ count: state.count + 1 })),
59
+ * decrement: () => set((state) => ({ count: state.count - 1 })),
60
+ * }),
61
+ * });
62
+ * ```
63
+ *
64
+ * **Note:** The state needs to be serializable by whatever serializer you choose (JSON by default).
65
+ * If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
66
+ *
67
+ * @example
68
+ * With superjson serialization and session storage:
69
+ * ```ts
70
+ * import { createStore } from "dharma-core";
71
+ * import superjson from "superjson";
72
+ *
73
+ * const store = createStore({
74
+ * persist: true,
75
+ * key: "count",
76
+ * serializer: superjson,
77
+ * storage: sessionStorage,
78
+ * initialState: { count: 0 },
79
+ * defineActions: ({ set }) => ({
80
+ * increment: () => set((state) => ({ count: state.count + 1 })),
81
+ * decrement: () => set((state) => ({ count: state.count - 1 })),
82
+ * }),
83
+ * });
84
+ * ```
22
85
  */
23
86
  const createStore = (config) => {
24
87
  const { initialState, defineActions, onLoad, onAttach, onDetach, onChange } = config;
@@ -32,6 +95,7 @@ const createStore = (config) => {
32
95
  const resetSilently = () => setSilently(initialState);
33
96
  const dispatch = () => {
34
97
  onChange?.({ state, set: setSilently, reset: resetSilently });
98
+ persistenceHandlers?.updateSnapshot(state);
35
99
  listeners.forEach((listener) => listener(state));
36
100
  };
37
101
  const set = (stateModifier) => {
@@ -47,6 +111,10 @@ const createStore = (config) => {
47
111
  const subscribe = (listener) => {
48
112
  if (listeners.size === 0) {
49
113
  onAttach?.({ state, set, reset });
114
+ persistenceHandlers?.updateState();
115
+ if (IS_BROWSER && persistenceHandlers) {
116
+ window.addEventListener("focus", persistenceHandlers.updateState);
117
+ }
50
118
  }
51
119
  listener(state);
52
120
  listeners.add(listener);
@@ -54,13 +122,19 @@ const createStore = (config) => {
54
122
  listeners.delete(listener);
55
123
  if (listeners.size === 0) {
56
124
  onDetach?.({ state, set, reset });
125
+ if (IS_BROWSER && persistenceHandlers) {
126
+ window.removeEventListener("focus", persistenceHandlers.updateState);
127
+ }
57
128
  }
58
129
  };
59
130
  };
60
131
  const actions = defineActions
61
132
  ? defineActions({ set, get, reset })
62
133
  : {};
134
+ const IS_BROWSER = typeof window !== "undefined";
135
+ const persistenceHandlers = getPersistenceHandlers(config, IS_BROWSER, get, set);
63
136
  onLoad?.({ state, set, reset });
137
+ persistenceHandlers?.initializeSnapshots();
64
138
  return {
65
139
  get,
66
140
  set,
@@ -1 +1 @@
1
- {"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { merge } from \"./merge\";\n\ntype Listener<TState extends object> = (state: TState) => void;\n\nexport type SetState<TState extends object> = (\n stateModifier: StateModifier<TState>,\n) => TState;\n\nexport type Store<TState extends object, TActions extends object> = {\n /** Returns the current state of the store. */\n get: () => TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n /** Actions that can modify the state of the store. */\n actions: TActions;\n /** Subscribes to changes in the state of the store. Returns an unsubscribe function. */\n subscribe: (listener: Listener<TState>) => () => void;\n};\n\nexport type EventHandlerContext<TState extends object> = {\n /** The current state of the store. */\n state: TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n};\n\nexport type StoreEventHandler<TState extends object> = (\n context: EventHandlerContext<TState>,\n) => void;\n\nexport type StateModifier<TState extends object> =\n | Partial<TState>\n | ((state: TState) => Partial<TState>);\n\nexport type StateFunctions<TState extends object> = {\n /** Returns the current state of the store. */\n get: () => TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n};\n\nexport type DefineActions<TState extends object, TActions> = (\n stateFunctions: StateFunctions<TState>,\n) => TActions;\n\nexport type StoreConfiguration<\n TState extends object,\n TActions extends object,\n> = {\n /** The initial state of the store. */\n initialState: TState;\n /** A function that defines actions that can modify the state. */\n defineActions?: DefineActions<TState, TActions>;\n /** Invoked when the store is created. */\n onLoad?: StoreEventHandler<TState>;\n /** Invoked when the store is subscribed to. */\n onAttach?: StoreEventHandler<TState>;\n /** Invoked when the store is unsubscribed from. */\n onDetach?: StoreEventHandler<TState>;\n /** Invoked whenever the state changes. */\n onChange?: StoreEventHandler<TState>;\n};\n\n/**\n * Creates a store with an initial state and actions that can modify the state.\n *\n * @param {StoreConfiguration<TState>} [config] - The configuration for the store.\n *\n * @returns {Store<TState, TActions>} The created store with state management methods.\n *\n * @example\n * ```ts\n * import { createStore } from \"dharma-core\";\n *\n * const store = createStore({\n * initialState: { count: 0 },\n * defineActions: ({ set }) => ({\n * increment: () => set((state) => ({ count: state.count + 1 })),\n * decrement: () => set((state) => ({ count: state.count - 1 })),\n * }),\n * });\n * ```\n */\nexport const createStore = <\n TState extends object,\n TActions extends object = Record<never, never>,\n>(\n config: StoreConfiguration<TState, TActions>,\n): Store<TState, TActions> => {\n const { initialState, defineActions, onLoad, onAttach, onDetach, onChange } =\n config;\n\n let state = initialState;\n const listeners = new Set<Listener<TState>>();\n\n const get = () => state;\n\n const setSilently = (stateModifier: StateModifier<TState>) => {\n state = merge(state, stateModifier);\n return state;\n };\n\n const resetSilently = () => setSilently(initialState);\n\n const dispatch = () => {\n onChange?.({ state, set: setSilently, reset: resetSilently });\n listeners.forEach((listener) => listener(state));\n };\n\n const set = (stateModifier: StateModifier<TState>) => {\n setSilently(stateModifier);\n dispatch();\n return state;\n };\n\n const reset = () => {\n resetSilently();\n dispatch();\n return state;\n };\n\n const subscribe = (listener: Listener<TState>) => {\n if (listeners.size === 0) {\n onAttach?.({ state, set, reset });\n }\n\n listener(state);\n listeners.add(listener);\n\n return () => {\n listeners.delete(listener);\n\n if (listeners.size === 0) {\n onDetach?.({ state, set, reset });\n }\n };\n };\n\n const actions = defineActions\n ? defineActions({ set, get, reset })\n : ({} as TActions);\n\n onLoad?.({ state, set, reset });\n\n return {\n get,\n set,\n reset,\n subscribe,\n actions,\n };\n};\n"],"names":[],"mappings":";;AAqEA;;;;;;;;;;;;;;;;;;;AAmBG;AACI,MAAM,WAAW,GAAG,CAIzB,MAA4C,KACjB;AAC3B,IAAA,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,GACzE,MAAM;IAER,IAAI,KAAK,GAAG,YAAY;AACxB,IAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB;AAE7C,IAAA,MAAM,GAAG,GAAG,MAAM,KAAK;AAEvB,IAAA,MAAM,WAAW,GAAG,CAAC,aAAoC,KAAI;AAC3D,QAAA,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,aAAa,CAAC;AACnC,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC;IAErD,MAAM,QAAQ,GAAG,MAAK;AACpB,QAAA,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;AAC7D,QAAA,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC;AAClD,IAAA,CAAC;AAED,IAAA,MAAM,GAAG,GAAG,CAAC,aAAoC,KAAI;QACnD,WAAW,CAAC,aAAa,CAAC;AAC1B,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;IAED,MAAM,KAAK,GAAG,MAAK;AACjB,QAAA,aAAa,EAAE;AACf,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;AAED,IAAA,MAAM,SAAS,GAAG,CAAC,QAA0B,KAAI;AAC/C,QAAA,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE;YACxB,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;QACnC;QAEA,QAAQ,CAAC,KAAK,CAAC;AACf,QAAA,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC;AAEvB,QAAA,OAAO,MAAK;AACV,YAAA,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC;AAE1B,YAAA,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE;gBACxB,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;YACnC;AACF,QAAA,CAAC;AACH,IAAA,CAAC;IAED,MAAM,OAAO,GAAG;UACZ,aAAa,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE;UAChC,EAAe;IAEpB,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAE/B,OAAO;QACL,GAAG;QACH,GAAG;QACH,KAAK;QACL,SAAS;QACT,OAAO;KACR;AACH;;;;"}
1
+ {"version":3,"file":"createStore.js","sources":["../../../src/lib/createStore.ts"],"sourcesContent":["import { getPersistenceHandlers } from \"./getPersistenceHandlers\";\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 EventHandlerContext<TState extends object> = {\n /** The current state of the store. */\n state: TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n};\n\nexport type StoreEventHandler<TState extends object> = (\n context: EventHandlerContext<TState>,\n) => void;\n\nexport type StateModifier<TState extends object> =\n | Partial<TState>\n | ((state: TState) => Partial<TState>);\n\nexport type StateFunctions<TState extends object> = {\n /** Returns the current state of the store. */\n get: () => TState;\n /** Sets the state of the store. */\n set: SetState<TState>;\n /** Resets the state of the store to its initial value. */\n reset: () => TState;\n};\n\nexport type DefineActions<TState extends object, TActions> = (\n stateFunctions: StateFunctions<TState>,\n) => TActions;\n\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 const listeners = new Set<Listener<TState>>();\n\n const get = () => state;\n\n const setSilently = (stateModifier: StateModifier<TState>) => {\n state = merge(state, stateModifier);\n return state;\n };\n\n const resetSilently = () => setSilently(initialState);\n\n const dispatch = () => {\n onChange?.({ state, set: setSilently, reset: resetSilently });\n persistenceHandlers?.updateSnapshot(state);\n listeners.forEach((listener) => listener(state));\n };\n\n const set = (stateModifier: StateModifier<TState>) => {\n setSilently(stateModifier);\n dispatch();\n return state;\n };\n\n const reset = () => {\n resetSilently();\n dispatch();\n return state;\n };\n\n const subscribe = (listener: Listener<TState>) => {\n if (listeners.size === 0) {\n onAttach?.({ state, set, reset });\n persistenceHandlers?.updateState();\n\n if (IS_BROWSER && persistenceHandlers) {\n window.addEventListener(\"focus\", persistenceHandlers.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 && persistenceHandlers) {\n window.removeEventListener(\"focus\", persistenceHandlers.updateState);\n }\n }\n };\n };\n\n const actions = defineActions\n ? defineActions({ set, get, reset })\n : ({} as TActions);\n\n const IS_BROWSER = typeof window !== \"undefined\";\n const persistenceHandlers = getPersistenceHandlers<TState, TActions>(\n config,\n IS_BROWSER,\n get,\n set,\n );\n\n onLoad?.({ state, set, reset });\n persistenceHandlers?.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;AACxB,IAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB;AAE7C,IAAA,MAAM,GAAG,GAAG,MAAM,KAAK;AAEvB,IAAA,MAAM,WAAW,GAAG,CAAC,aAAoC,KAAI;AAC3D,QAAA,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,aAAa,CAAC;AACnC,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC;IAErD,MAAM,QAAQ,GAAG,MAAK;AACpB,QAAA,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;AAC7D,QAAA,mBAAmB,EAAE,cAAc,CAAC,KAAK,CAAC;AAC1C,QAAA,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC;AAClD,IAAA,CAAC;AAED,IAAA,MAAM,GAAG,GAAG,CAAC,aAAoC,KAAI;QACnD,WAAW,CAAC,aAAa,CAAC;AAC1B,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;IAED,MAAM,KAAK,GAAG,MAAK;AACjB,QAAA,aAAa,EAAE;AACf,QAAA,QAAQ,EAAE;AACV,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;AAED,IAAA,MAAM,SAAS,GAAG,CAAC,QAA0B,KAAI;AAC/C,QAAA,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE;YACxB,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;YACjC,mBAAmB,EAAE,WAAW,EAAE;AAElC,YAAA,IAAI,UAAU,IAAI,mBAAmB,EAAE;gBACrC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,CAAC,WAAW,CAAC;YACnE;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,mBAAmB,EAAE;oBACrC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,CAAC,WAAW,CAAC;gBACtE;YACF;AACF,QAAA,CAAC;AACH,IAAA,CAAC;IAED,MAAM,OAAO,GAAG;UACZ,aAAa,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE;UAChC,EAAe;AAEpB,IAAA,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW;AAChD,IAAA,MAAM,mBAAmB,GAAG,sBAAsB,CAChD,MAAM,EACN,UAAU,EACV,GAAG,EACH,GAAG,CACJ;IAED,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAC/B,mBAAmB,EAAE,mBAAmB,EAAE;IAE1C,OAAO;QACL,GAAG;QACH,GAAG;QACH,KAAK;QACL,SAAS;QACT,OAAO;KACR;AACH;;;;"}
@@ -0,0 +1,6 @@
1
+ import { SetState, StoreConfig } from "./createStore";
2
+ export declare const getPersistenceHandlers: <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;
@@ -0,0 +1,40 @@
1
+ const getPersistenceHandlers = (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 { getPersistenceHandlers };
40
+ //# sourceMappingURL=getPersistenceHandlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getPersistenceHandlers.js","sources":["../../../src/lib/getPersistenceHandlers.ts"],"sourcesContent":["import { SetState, StoreConfig } from \"./createStore\";\n\nexport const getPersistenceHandlers = <\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,sBAAsB,GAAG,CAIpC,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;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dharma-core",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "author": "Frans Ekman <fransedvinekman@gmail.com>",
5
5
  "license": "MIT",
6
6
  "description": "My package description",
@@ -1,63 +0,0 @@
1
- import { Store, StoreConfiguration } from "./createStore";
2
- export type StorageAPI = {
3
- getItem: (key: string) => string | null;
4
- setItem: (key: string, value: string) => void;
5
- removeItem: (key: string) => void;
6
- };
7
- export type Serializer<T = any> = {
8
- stringify: (value: T) => string;
9
- parse: (value: string) => T;
10
- };
11
- export type PersistentStoreConfiguration<TState extends object, TActions extends object> = StoreConfiguration<TState, TActions> & {
12
- key: string;
13
- /** The storage to use for persisting the state. Defaults to local storage if available. */
14
- storage?: StorageAPI;
15
- /** The serializer to use for storing the state. Defaults to JSON. */
16
- serializer?: Serializer<TState>;
17
- };
18
- /**
19
- * Creates a store that persists its state in local or session storage.
20
- * Defaults to local storage but this can be changed in the options.
21
- *
22
- * **Note:** The state needs to be serializable by whatever serializer you use. (JSON by default)
23
- * If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
24
- *
25
- * @param {PersistentStoreConfiguration<TState, TActions>} config - The configuration for the persistent store.
26
- *
27
- * @returns {Store<TState, TActions>} The created store.
28
- *
29
- * @example
30
- * Basic usage:
31
- * ```ts
32
- * import { createPersistentStore } from "dharma-core";
33
- *
34
- * const store = createPersistentStore({
35
- * key: "count",
36
- * initialState: { count: 0 },
37
- * defineActions: ({ set }) => ({
38
- * increment: () => set((state) => ({ count: state.count + 1 })),
39
- * decrement: () => set((state) => ({ count: state.count - 1 })),
40
- * reset: () => set({ count: 0 }),
41
- * }),
42
- * });
43
- * ```
44
- * @example
45
- * With superjson serialization and session storage:
46
- * ```ts
47
- * import { createPersistentStore } from "dharma-core";
48
- * import superjson from "superjson";
49
- *
50
- * const store = createPersistentStore({
51
- * key: "count",
52
- * initialState: { count: 0 },
53
- * defineActions: ({ set }) => ({
54
- * increment: () => set((state) => ({ count: state.count + 1 })),
55
- * decrement: () => set((state) => ({ count: state.count - 1 })),
56
- * reset: () => set({ count: 0 }),
57
- * }),
58
- * serializer: superjson,
59
- * storage: sessionStorage,
60
- * });
61
- * ```
62
- */
63
- export declare const createPersistentStore: <TState extends object, TActions extends object = Record<never, never>>(config: PersistentStoreConfiguration<TState, TActions>) => Store<TState, TActions>;
@@ -1,103 +0,0 @@
1
- 'use strict';
2
-
3
- var createStore = require('./createStore.js');
4
-
5
- /**
6
- * Creates a store that persists its state in local or session storage.
7
- * Defaults to local storage but this can be changed in the options.
8
- *
9
- * **Note:** The state needs to be serializable by whatever serializer you use. (JSON by default)
10
- * If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
11
- *
12
- * @param {PersistentStoreConfiguration<TState, TActions>} config - The configuration for the persistent store.
13
- *
14
- * @returns {Store<TState, TActions>} The created store.
15
- *
16
- * @example
17
- * Basic usage:
18
- * ```ts
19
- * import { createPersistentStore } from "dharma-core";
20
- *
21
- * const store = createPersistentStore({
22
- * key: "count",
23
- * initialState: { count: 0 },
24
- * defineActions: ({ set }) => ({
25
- * increment: () => set((state) => ({ count: state.count + 1 })),
26
- * decrement: () => set((state) => ({ count: state.count - 1 })),
27
- * reset: () => set({ count: 0 }),
28
- * }),
29
- * });
30
- * ```
31
- * @example
32
- * With superjson serialization and session storage:
33
- * ```ts
34
- * import { createPersistentStore } from "dharma-core";
35
- * import superjson from "superjson";
36
- *
37
- * const store = createPersistentStore({
38
- * key: "count",
39
- * initialState: { count: 0 },
40
- * defineActions: ({ set }) => ({
41
- * increment: () => set((state) => ({ count: state.count + 1 })),
42
- * decrement: () => set((state) => ({ count: state.count - 1 })),
43
- * reset: () => set({ count: 0 }),
44
- * }),
45
- * serializer: superjson,
46
- * storage: sessionStorage,
47
- * });
48
- * ```
49
- */
50
- const createPersistentStore = (config) => {
51
- const { key, initialState, storage: customStorage, serializer = JSON, onAttach, onDetach, onChange, ...rest } = config;
52
- const IS_BROWSER = typeof window !== "undefined";
53
- const storage = customStorage ?? (IS_BROWSER ? localStorage : undefined);
54
- if (!storage) {
55
- return createStore.createStore(config);
56
- }
57
- const initialStateKey = `init_${key}`;
58
- const initialStateSnapshot = storage.getItem(initialStateKey);
59
- const initialStateString = serializer.stringify(initialState);
60
- if (initialStateSnapshot !== initialStateString) {
61
- storage.setItem(initialStateKey, initialStateString);
62
- storage.removeItem(key);
63
- }
64
- const updateSnapshot = (newState) => {
65
- const currentSnapshot = storage.getItem(key);
66
- const newSnapshot = serializer.stringify(newState);
67
- if (newSnapshot !== currentSnapshot) {
68
- storage.setItem(key, newSnapshot);
69
- }
70
- };
71
- const updateState = () => {
72
- const currentSnapshot = storage.getItem(key);
73
- if (currentSnapshot &&
74
- currentSnapshot !== serializer.stringify(store.get())) {
75
- store.set(serializer.parse(currentSnapshot));
76
- }
77
- };
78
- const store = createStore.createStore({
79
- initialState,
80
- onAttach: (ctx) => {
81
- onAttach?.(ctx);
82
- updateState();
83
- if (IS_BROWSER) {
84
- window.addEventListener("focus", updateState);
85
- }
86
- },
87
- onDetach: (ctx) => {
88
- onDetach?.(ctx);
89
- if (IS_BROWSER) {
90
- window.removeEventListener("focus", updateState);
91
- }
92
- },
93
- onChange: ({ state, ...ctx }) => {
94
- onChange?.({ state, ...ctx });
95
- updateSnapshot(state);
96
- },
97
- ...rest,
98
- });
99
- return store;
100
- };
101
-
102
- exports.createPersistentStore = createPersistentStore;
103
- //# sourceMappingURL=createPersistentStore.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createPersistentStore.js","sources":["../../../src/lib/createPersistentStore.ts"],"sourcesContent":["import { createStore, Store, StoreConfiguration } from \"./createStore\";\n\nexport type StorageAPI = {\n getItem: (key: string) => string | null;\n setItem: (key: string, value: string) => void;\n removeItem: (key: string) => void;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Serializer<T = any> = {\n stringify: (value: T) => string;\n parse: (value: string) => T;\n};\n\nexport type PersistentStoreConfiguration<\n TState extends object,\n TActions extends object,\n> = StoreConfiguration<TState, TActions> & {\n key: string;\n /** The storage to use for persisting the state. Defaults to local storage if available. */\n storage?: StorageAPI;\n /** The serializer to use for storing the state. Defaults to JSON. */\n serializer?: Serializer<TState>;\n};\n\n/**\n * Creates a store that persists its state in local or session storage.\n * Defaults to local storage but this can be changed in the options.\n *\n * **Note:** The state needs to be serializable by whatever serializer you use. (JSON by default)\n * If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).\n *\n * @param {PersistentStoreConfiguration<TState, TActions>} config - The configuration for the persistent store.\n *\n * @returns {Store<TState, TActions>} The created store.\n *\n * @example\n * Basic usage:\n * ```ts\n * import { createPersistentStore } from \"dharma-core\";\n *\n * const store = createPersistentStore({\n * key: \"count\",\n * initialState: { count: 0 },\n * defineActions: ({ set }) => ({\n * increment: () => set((state) => ({ count: state.count + 1 })),\n * decrement: () => set((state) => ({ count: state.count - 1 })),\n * reset: () => set({ count: 0 }),\n * }),\n * });\n * ```\n * @example\n * With superjson serialization and session storage:\n * ```ts\n * import { createPersistentStore } from \"dharma-core\";\n * import superjson from \"superjson\";\n *\n * const store = createPersistentStore({\n * key: \"count\",\n * initialState: { count: 0 },\n * defineActions: ({ set }) => ({\n * increment: () => set((state) => ({ count: state.count + 1 })),\n * decrement: () => set((state) => ({ count: state.count - 1 })),\n * reset: () => set({ count: 0 }),\n * }),\n * serializer: superjson,\n * storage: sessionStorage,\n * });\n * ```\n */\nexport const createPersistentStore = <\n TState extends object,\n TActions extends object = Record<never, never>,\n>(\n config: PersistentStoreConfiguration<TState, TActions>,\n): Store<TState, TActions> => {\n const {\n key,\n initialState,\n storage: customStorage,\n serializer = JSON,\n onAttach,\n onDetach,\n onChange,\n ...rest\n } = config;\n\n const IS_BROWSER = typeof window !== \"undefined\";\n const storage = customStorage ?? (IS_BROWSER ? localStorage : undefined);\n\n if (!storage) {\n return createStore(config);\n }\n\n const initialStateKey = `init_${key}`;\n\n const initialStateSnapshot = storage.getItem(initialStateKey);\n const initialStateString = serializer.stringify(initialState);\n\n if (initialStateSnapshot !== initialStateString) {\n storage.setItem(initialStateKey, initialStateString);\n storage.removeItem(key);\n }\n\n const updateSnapshot = (newState: TState) => {\n const currentSnapshot = storage.getItem(key);\n const newSnapshot = serializer.stringify(newState);\n\n if (newSnapshot !== currentSnapshot) {\n storage.setItem(key, newSnapshot);\n }\n };\n\n const updateState = () => {\n const currentSnapshot = storage.getItem(key);\n\n if (\n currentSnapshot &&\n currentSnapshot !== serializer.stringify(store.get())\n ) {\n store.set(serializer.parse(currentSnapshot));\n }\n };\n\n const store = createStore({\n initialState,\n onAttach: (ctx) => {\n onAttach?.(ctx);\n updateState();\n if (IS_BROWSER) {\n window.addEventListener(\"focus\", updateState);\n }\n },\n onDetach: (ctx) => {\n onDetach?.(ctx);\n if (IS_BROWSER) {\n window.removeEventListener(\"focus\", updateState);\n }\n },\n onChange: ({ state, ...ctx }) => {\n onChange?.({ state, ...ctx });\n updateSnapshot(state);\n },\n ...rest,\n });\n\n return store;\n};\n"],"names":["createStore"],"mappings":";;;;AAyBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CG;AACI,MAAM,qBAAqB,GAAG,CAInC,MAAsD,KAC3B;IAC3B,MAAM,EACJ,GAAG,EACH,YAAY,EACZ,OAAO,EAAE,aAAa,EACtB,UAAU,GAAG,IAAI,EACjB,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,GAAG,IAAI,EACR,GAAG,MAAM;AAEV,IAAA,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW;AAChD,IAAA,MAAM,OAAO,GAAG,aAAa,KAAK,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC;IAExE,IAAI,CAAC,OAAO,EAAE;AACZ,QAAA,OAAOA,uBAAW,CAAC,MAAM,CAAC;IAC5B;AAEA,IAAA,MAAM,eAAe,GAAG,CAAA,KAAA,EAAQ,GAAG,EAAE;IAErC,MAAM,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC;IAC7D,MAAM,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC;AAE7D,IAAA,IAAI,oBAAoB,KAAK,kBAAkB,EAAE;AAC/C,QAAA,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,kBAAkB,CAAC;AACpD,QAAA,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;IACzB;AAEA,IAAA,MAAM,cAAc,GAAG,CAAC,QAAgB,KAAI;QAC1C,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;QAC5C,MAAM,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,QAAQ,CAAC;AAElD,QAAA,IAAI,WAAW,KAAK,eAAe,EAAE;AACnC,YAAA,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC;QACnC;AACF,IAAA,CAAC;IAED,MAAM,WAAW,GAAG,MAAK;QACvB,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;AAE5C,QAAA,IACE,eAAe;YACf,eAAe,KAAK,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EACrD;YACA,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAC9C;AACF,IAAA,CAAC;IAED,MAAM,KAAK,GAAGA,uBAAW,CAAC;QACxB,YAAY;AACZ,QAAA,QAAQ,EAAE,CAAC,GAAG,KAAI;AAChB,YAAA,QAAQ,GAAG,GAAG,CAAC;AACf,YAAA,WAAW,EAAE;YACb,IAAI,UAAU,EAAE;AACd,gBAAA,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC;YAC/C;QACF,CAAC;AACD,QAAA,QAAQ,EAAE,CAAC,GAAG,KAAI;AAChB,YAAA,QAAQ,GAAG,GAAG,CAAC;YACf,IAAI,UAAU,EAAE;AACd,gBAAA,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC;YAClD;QACF,CAAC;QACD,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,GAAG,EAAE,KAAI;YAC9B,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,EAAE,CAAC;YAC7B,cAAc,CAAC,KAAK,CAAC;QACvB,CAAC;AACD,QAAA,GAAG,IAAI;AACR,KAAA,CAAC;AAEF,IAAA,OAAO,KAAK;AACd;;;;"}
@@ -1,63 +0,0 @@
1
- import { Store, StoreConfiguration } from "./createStore";
2
- export type StorageAPI = {
3
- getItem: (key: string) => string | null;
4
- setItem: (key: string, value: string) => void;
5
- removeItem: (key: string) => void;
6
- };
7
- export type Serializer<T = any> = {
8
- stringify: (value: T) => string;
9
- parse: (value: string) => T;
10
- };
11
- export type PersistentStoreConfiguration<TState extends object, TActions extends object> = StoreConfiguration<TState, TActions> & {
12
- key: string;
13
- /** The storage to use for persisting the state. Defaults to local storage if available. */
14
- storage?: StorageAPI;
15
- /** The serializer to use for storing the state. Defaults to JSON. */
16
- serializer?: Serializer<TState>;
17
- };
18
- /**
19
- * Creates a store that persists its state in local or session storage.
20
- * Defaults to local storage but this can be changed in the options.
21
- *
22
- * **Note:** The state needs to be serializable by whatever serializer you use. (JSON by default)
23
- * If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
24
- *
25
- * @param {PersistentStoreConfiguration<TState, TActions>} config - The configuration for the persistent store.
26
- *
27
- * @returns {Store<TState, TActions>} The created store.
28
- *
29
- * @example
30
- * Basic usage:
31
- * ```ts
32
- * import { createPersistentStore } from "dharma-core";
33
- *
34
- * const store = createPersistentStore({
35
- * key: "count",
36
- * initialState: { count: 0 },
37
- * defineActions: ({ set }) => ({
38
- * increment: () => set((state) => ({ count: state.count + 1 })),
39
- * decrement: () => set((state) => ({ count: state.count - 1 })),
40
- * reset: () => set({ count: 0 }),
41
- * }),
42
- * });
43
- * ```
44
- * @example
45
- * With superjson serialization and session storage:
46
- * ```ts
47
- * import { createPersistentStore } from "dharma-core";
48
- * import superjson from "superjson";
49
- *
50
- * const store = createPersistentStore({
51
- * key: "count",
52
- * initialState: { count: 0 },
53
- * defineActions: ({ set }) => ({
54
- * increment: () => set((state) => ({ count: state.count + 1 })),
55
- * decrement: () => set((state) => ({ count: state.count - 1 })),
56
- * reset: () => set({ count: 0 }),
57
- * }),
58
- * serializer: superjson,
59
- * storage: sessionStorage,
60
- * });
61
- * ```
62
- */
63
- export declare const createPersistentStore: <TState extends object, TActions extends object = Record<never, never>>(config: PersistentStoreConfiguration<TState, TActions>) => Store<TState, TActions>;
@@ -1,101 +0,0 @@
1
- import { createStore } from './createStore.js';
2
-
3
- /**
4
- * Creates a store that persists its state in local or session storage.
5
- * Defaults to local storage but this can be changed in the options.
6
- *
7
- * **Note:** The state needs to be serializable by whatever serializer you use. (JSON by default)
8
- * If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).
9
- *
10
- * @param {PersistentStoreConfiguration<TState, TActions>} config - The configuration for the persistent store.
11
- *
12
- * @returns {Store<TState, TActions>} The created store.
13
- *
14
- * @example
15
- * Basic usage:
16
- * ```ts
17
- * import { createPersistentStore } from "dharma-core";
18
- *
19
- * const store = createPersistentStore({
20
- * key: "count",
21
- * initialState: { count: 0 },
22
- * defineActions: ({ set }) => ({
23
- * increment: () => set((state) => ({ count: state.count + 1 })),
24
- * decrement: () => set((state) => ({ count: state.count - 1 })),
25
- * reset: () => set({ count: 0 }),
26
- * }),
27
- * });
28
- * ```
29
- * @example
30
- * With superjson serialization and session storage:
31
- * ```ts
32
- * import { createPersistentStore } from "dharma-core";
33
- * import superjson from "superjson";
34
- *
35
- * const store = createPersistentStore({
36
- * key: "count",
37
- * initialState: { count: 0 },
38
- * defineActions: ({ set }) => ({
39
- * increment: () => set((state) => ({ count: state.count + 1 })),
40
- * decrement: () => set((state) => ({ count: state.count - 1 })),
41
- * reset: () => set({ count: 0 }),
42
- * }),
43
- * serializer: superjson,
44
- * storage: sessionStorage,
45
- * });
46
- * ```
47
- */
48
- const createPersistentStore = (config) => {
49
- const { key, initialState, storage: customStorage, serializer = JSON, onAttach, onDetach, onChange, ...rest } = config;
50
- const IS_BROWSER = typeof window !== "undefined";
51
- const storage = customStorage ?? (IS_BROWSER ? localStorage : undefined);
52
- if (!storage) {
53
- return createStore(config);
54
- }
55
- const initialStateKey = `init_${key}`;
56
- const initialStateSnapshot = storage.getItem(initialStateKey);
57
- const initialStateString = serializer.stringify(initialState);
58
- if (initialStateSnapshot !== initialStateString) {
59
- storage.setItem(initialStateKey, initialStateString);
60
- storage.removeItem(key);
61
- }
62
- const updateSnapshot = (newState) => {
63
- const currentSnapshot = storage.getItem(key);
64
- const newSnapshot = serializer.stringify(newState);
65
- if (newSnapshot !== currentSnapshot) {
66
- storage.setItem(key, newSnapshot);
67
- }
68
- };
69
- const updateState = () => {
70
- const currentSnapshot = storage.getItem(key);
71
- if (currentSnapshot &&
72
- currentSnapshot !== serializer.stringify(store.get())) {
73
- store.set(serializer.parse(currentSnapshot));
74
- }
75
- };
76
- const store = createStore({
77
- initialState,
78
- onAttach: (ctx) => {
79
- onAttach?.(ctx);
80
- updateState();
81
- if (IS_BROWSER) {
82
- window.addEventListener("focus", updateState);
83
- }
84
- },
85
- onDetach: (ctx) => {
86
- onDetach?.(ctx);
87
- if (IS_BROWSER) {
88
- window.removeEventListener("focus", updateState);
89
- }
90
- },
91
- onChange: ({ state, ...ctx }) => {
92
- onChange?.({ state, ...ctx });
93
- updateSnapshot(state);
94
- },
95
- ...rest,
96
- });
97
- return store;
98
- };
99
-
100
- export { createPersistentStore };
101
- //# sourceMappingURL=createPersistentStore.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createPersistentStore.js","sources":["../../../src/lib/createPersistentStore.ts"],"sourcesContent":["import { createStore, Store, StoreConfiguration } from \"./createStore\";\n\nexport type StorageAPI = {\n getItem: (key: string) => string | null;\n setItem: (key: string, value: string) => void;\n removeItem: (key: string) => void;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Serializer<T = any> = {\n stringify: (value: T) => string;\n parse: (value: string) => T;\n};\n\nexport type PersistentStoreConfiguration<\n TState extends object,\n TActions extends object,\n> = StoreConfiguration<TState, TActions> & {\n key: string;\n /** The storage to use for persisting the state. Defaults to local storage if available. */\n storage?: StorageAPI;\n /** The serializer to use for storing the state. Defaults to JSON. */\n serializer?: Serializer<TState>;\n};\n\n/**\n * Creates a store that persists its state in local or session storage.\n * Defaults to local storage but this can be changed in the options.\n *\n * **Note:** The state needs to be serializable by whatever serializer you use. (JSON by default)\n * If you need something more versatile I would recommend using a library like [superjson](https://github.com/flightcontrolhq/superjson).\n *\n * @param {PersistentStoreConfiguration<TState, TActions>} config - The configuration for the persistent store.\n *\n * @returns {Store<TState, TActions>} The created store.\n *\n * @example\n * Basic usage:\n * ```ts\n * import { createPersistentStore } from \"dharma-core\";\n *\n * const store = createPersistentStore({\n * key: \"count\",\n * initialState: { count: 0 },\n * defineActions: ({ set }) => ({\n * increment: () => set((state) => ({ count: state.count + 1 })),\n * decrement: () => set((state) => ({ count: state.count - 1 })),\n * reset: () => set({ count: 0 }),\n * }),\n * });\n * ```\n * @example\n * With superjson serialization and session storage:\n * ```ts\n * import { createPersistentStore } from \"dharma-core\";\n * import superjson from \"superjson\";\n *\n * const store = createPersistentStore({\n * key: \"count\",\n * initialState: { count: 0 },\n * defineActions: ({ set }) => ({\n * increment: () => set((state) => ({ count: state.count + 1 })),\n * decrement: () => set((state) => ({ count: state.count - 1 })),\n * reset: () => set({ count: 0 }),\n * }),\n * serializer: superjson,\n * storage: sessionStorage,\n * });\n * ```\n */\nexport const createPersistentStore = <\n TState extends object,\n TActions extends object = Record<never, never>,\n>(\n config: PersistentStoreConfiguration<TState, TActions>,\n): Store<TState, TActions> => {\n const {\n key,\n initialState,\n storage: customStorage,\n serializer = JSON,\n onAttach,\n onDetach,\n onChange,\n ...rest\n } = config;\n\n const IS_BROWSER = typeof window !== \"undefined\";\n const storage = customStorage ?? (IS_BROWSER ? localStorage : undefined);\n\n if (!storage) {\n return createStore(config);\n }\n\n const initialStateKey = `init_${key}`;\n\n const initialStateSnapshot = storage.getItem(initialStateKey);\n const initialStateString = serializer.stringify(initialState);\n\n if (initialStateSnapshot !== initialStateString) {\n storage.setItem(initialStateKey, initialStateString);\n storage.removeItem(key);\n }\n\n const updateSnapshot = (newState: TState) => {\n const currentSnapshot = storage.getItem(key);\n const newSnapshot = serializer.stringify(newState);\n\n if (newSnapshot !== currentSnapshot) {\n storage.setItem(key, newSnapshot);\n }\n };\n\n const updateState = () => {\n const currentSnapshot = storage.getItem(key);\n\n if (\n currentSnapshot &&\n currentSnapshot !== serializer.stringify(store.get())\n ) {\n store.set(serializer.parse(currentSnapshot));\n }\n };\n\n const store = createStore({\n initialState,\n onAttach: (ctx) => {\n onAttach?.(ctx);\n updateState();\n if (IS_BROWSER) {\n window.addEventListener(\"focus\", updateState);\n }\n },\n onDetach: (ctx) => {\n onDetach?.(ctx);\n if (IS_BROWSER) {\n window.removeEventListener(\"focus\", updateState);\n }\n },\n onChange: ({ state, ...ctx }) => {\n onChange?.({ state, ...ctx });\n updateSnapshot(state);\n },\n ...rest,\n });\n\n return store;\n};\n"],"names":[],"mappings":";;AAyBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CG;AACI,MAAM,qBAAqB,GAAG,CAInC,MAAsD,KAC3B;IAC3B,MAAM,EACJ,GAAG,EACH,YAAY,EACZ,OAAO,EAAE,aAAa,EACtB,UAAU,GAAG,IAAI,EACjB,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,GAAG,IAAI,EACR,GAAG,MAAM;AAEV,IAAA,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW;AAChD,IAAA,MAAM,OAAO,GAAG,aAAa,KAAK,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC;IAExE,IAAI,CAAC,OAAO,EAAE;AACZ,QAAA,OAAO,WAAW,CAAC,MAAM,CAAC;IAC5B;AAEA,IAAA,MAAM,eAAe,GAAG,CAAA,KAAA,EAAQ,GAAG,EAAE;IAErC,MAAM,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC;IAC7D,MAAM,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC;AAE7D,IAAA,IAAI,oBAAoB,KAAK,kBAAkB,EAAE;AAC/C,QAAA,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,kBAAkB,CAAC;AACpD,QAAA,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;IACzB;AAEA,IAAA,MAAM,cAAc,GAAG,CAAC,QAAgB,KAAI;QAC1C,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;QAC5C,MAAM,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,QAAQ,CAAC;AAElD,QAAA,IAAI,WAAW,KAAK,eAAe,EAAE;AACnC,YAAA,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC;QACnC;AACF,IAAA,CAAC;IAED,MAAM,WAAW,GAAG,MAAK;QACvB,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;AAE5C,QAAA,IACE,eAAe;YACf,eAAe,KAAK,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EACrD;YACA,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAC9C;AACF,IAAA,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,CAAC;QACxB,YAAY;AACZ,QAAA,QAAQ,EAAE,CAAC,GAAG,KAAI;AAChB,YAAA,QAAQ,GAAG,GAAG,CAAC;AACf,YAAA,WAAW,EAAE;YACb,IAAI,UAAU,EAAE;AACd,gBAAA,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC;YAC/C;QACF,CAAC;AACD,QAAA,QAAQ,EAAE,CAAC,GAAG,KAAI;AAChB,YAAA,QAAQ,GAAG,GAAG,CAAC;YACf,IAAI,UAAU,EAAE;AACd,gBAAA,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC;YAClD;QACF,CAAC;QACD,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,GAAG,EAAE,KAAI;YAC9B,QAAQ,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,EAAE,CAAC;YAC7B,cAAc,CAAC,KAAK,CAAC;QACvB,CAAC;AACD,QAAA,GAAG,IAAI;AACR,KAAA,CAAC;AAEF,IAAA,OAAO,KAAK;AACd;;;;"}