@storve/core 1.0.1 → 1.0.3

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.
Files changed (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +993 -26
  3. package/dist/adapters/indexedDB.cjs +0 -1
  4. package/dist/adapters/indexedDB.mjs +0 -1
  5. package/dist/adapters/localStorage.cjs +0 -1
  6. package/dist/adapters/localStorage.mjs +0 -1
  7. package/dist/adapters/memory.cjs +0 -1
  8. package/dist/adapters/memory.mjs +0 -1
  9. package/dist/adapters/sessionStorage.cjs +0 -1
  10. package/dist/adapters/sessionStorage.mjs +0 -1
  11. package/dist/async-entry.d.ts +0 -1
  12. package/dist/async.cjs +0 -1
  13. package/dist/async.d.ts +0 -1
  14. package/dist/async.mjs +0 -1
  15. package/dist/batch.d.ts +0 -1
  16. package/dist/compose.d.ts +0 -1
  17. package/dist/computed-entry.d.ts +0 -1
  18. package/dist/computed.cjs +0 -1
  19. package/dist/computed.d.ts +0 -1
  20. package/dist/computed.mjs +0 -1
  21. package/dist/devtools/history.d.ts +0 -1
  22. package/dist/devtools/index.d.ts +0 -1
  23. package/dist/devtools/redux-bridge.d.ts +0 -1
  24. package/dist/devtools/snapshots.d.ts +0 -1
  25. package/dist/devtools/withDevtools.d.ts +0 -1
  26. package/dist/devtools.cjs +0 -1
  27. package/dist/devtools.mjs +0 -1
  28. package/dist/extensions/noop.d.ts +0 -1
  29. package/dist/index.cjs +0 -1
  30. package/dist/index.d.ts +0 -1
  31. package/dist/index.mjs +0 -1
  32. package/dist/persist/adapters/indexedDB.d.ts +0 -1
  33. package/dist/persist/adapters/localStorage.d.ts +0 -1
  34. package/dist/persist/adapters/memory.d.ts +0 -1
  35. package/dist/persist/adapters/sessionStorage.d.ts +0 -1
  36. package/dist/persist/debounce.d.ts +0 -1
  37. package/dist/persist/hydrate.d.ts +0 -1
  38. package/dist/persist/index.d.ts +0 -1
  39. package/dist/persist/serialize.d.ts +0 -1
  40. package/dist/persist.cjs +0 -1
  41. package/dist/persist.mjs +0 -1
  42. package/dist/proxy.d.ts +0 -1
  43. package/dist/registry-qtr1UpFU.js +0 -1
  44. package/dist/registry-zaKZ1P-s.js +0 -1
  45. package/dist/registry.d.ts +0 -1
  46. package/dist/signals/createSignal.d.ts +0 -1
  47. package/dist/signals/index.d.ts +0 -1
  48. package/dist/signals/useSignal.d.ts +0 -1
  49. package/dist/signals.cjs +0 -1
  50. package/dist/signals.mjs +0 -1
  51. package/dist/store.d.ts +0 -1
  52. package/dist/sync/channel.d.ts +0 -1
  53. package/dist/sync/index.d.ts +0 -1
  54. package/dist/sync/protocol.d.ts +0 -1
  55. package/dist/sync/withSync.d.ts +0 -1
  56. package/dist/sync.cjs +0 -1
  57. package/dist/sync.mjs +0 -1
  58. package/dist/types.d.ts +0 -1
  59. package/package.json +9 -3
  60. package/CHANGELOG.md +0 -151
  61. package/benchmarks/run.ts +0 -102
  62. package/benchmarks/week2.md +0 -9
  63. package/benchmarks/week2.ts +0 -64
  64. package/benchmarks/week4.md +0 -13
  65. package/benchmarks/week4.ts +0 -178
  66. package/benchmarks/week5.md +0 -15
  67. package/benchmarks/week5.ts +0 -184
  68. package/coverage/coverage-summary.json +0 -31
  69. package/dist/adapters/indexedDB.cjs.map +0 -1
  70. package/dist/adapters/indexedDB.mjs.map +0 -1
  71. package/dist/adapters/localStorage.cjs.map +0 -1
  72. package/dist/adapters/localStorage.mjs.map +0 -1
  73. package/dist/adapters/memory.cjs.map +0 -1
  74. package/dist/adapters/memory.mjs.map +0 -1
  75. package/dist/adapters/sessionStorage.cjs.map +0 -1
  76. package/dist/adapters/sessionStorage.mjs.map +0 -1
  77. package/dist/async-entry.d.ts.map +0 -1
  78. package/dist/async.cjs.map +0 -1
  79. package/dist/async.d.ts.map +0 -1
  80. package/dist/async.mjs.map +0 -1
  81. package/dist/batch.d.ts.map +0 -1
  82. package/dist/compose.d.ts.map +0 -1
  83. package/dist/computed-entry.d.ts.map +0 -1
  84. package/dist/computed.cjs.map +0 -1
  85. package/dist/computed.d.ts.map +0 -1
  86. package/dist/computed.mjs.map +0 -1
  87. package/dist/devtools/history.d.ts.map +0 -1
  88. package/dist/devtools/index.d.ts.map +0 -1
  89. package/dist/devtools/redux-bridge.d.ts.map +0 -1
  90. package/dist/devtools/snapshots.d.ts.map +0 -1
  91. package/dist/devtools/withDevtools.d.ts.map +0 -1
  92. package/dist/devtools.cjs.map +0 -1
  93. package/dist/devtools.mjs.map +0 -1
  94. package/dist/extensions/noop.d.ts.map +0 -1
  95. package/dist/index.cjs.js +0 -118
  96. package/dist/index.cjs.js.map +0 -1
  97. package/dist/index.cjs.map +0 -1
  98. package/dist/index.d.ts.map +0 -1
  99. package/dist/index.esm.js +0 -116
  100. package/dist/index.esm.js.map +0 -1
  101. package/dist/index.mjs.map +0 -1
  102. package/dist/persist/adapters/indexedDB.d.ts.map +0 -1
  103. package/dist/persist/adapters/localStorage.d.ts.map +0 -1
  104. package/dist/persist/adapters/memory.d.ts.map +0 -1
  105. package/dist/persist/adapters/sessionStorage.d.ts.map +0 -1
  106. package/dist/persist/debounce.d.ts.map +0 -1
  107. package/dist/persist/hydrate.d.ts.map +0 -1
  108. package/dist/persist/index.d.ts.map +0 -1
  109. package/dist/persist/serialize.d.ts.map +0 -1
  110. package/dist/persist.cjs.map +0 -1
  111. package/dist/persist.mjs.map +0 -1
  112. package/dist/proxy.d.ts.map +0 -1
  113. package/dist/registry-D3X0HSbl.js +0 -26
  114. package/dist/registry-D3X0HSbl.js.map +0 -1
  115. package/dist/registry-RDjbeJdx.js +0 -29
  116. package/dist/registry-RDjbeJdx.js.map +0 -1
  117. package/dist/registry-qtr1UpFU.js.map +0 -1
  118. package/dist/registry-zaKZ1P-s.js.map +0 -1
  119. package/dist/registry.d.ts.map +0 -1
  120. package/dist/signals/createSignal.d.ts.map +0 -1
  121. package/dist/signals/index.d.ts.map +0 -1
  122. package/dist/signals/useSignal.d.ts.map +0 -1
  123. package/dist/signals.cjs.map +0 -1
  124. package/dist/signals.mjs.map +0 -1
  125. package/dist/stats.html +0 -4949
  126. package/dist/store.d.ts.map +0 -1
  127. package/dist/sync/channel.d.ts.map +0 -1
  128. package/dist/sync/index.d.ts.map +0 -1
  129. package/dist/sync/protocol.d.ts.map +0 -1
  130. package/dist/sync/withSync.d.ts.map +0 -1
  131. package/dist/sync.cjs.map +0 -1
  132. package/dist/sync.mjs.map +0 -1
  133. package/dist/types.d.ts.map +0 -1
  134. package/rollup.config.mjs +0 -44
  135. package/src/async-entry.ts +0 -6
  136. package/src/async.ts +0 -240
  137. package/src/batch.ts +0 -33
  138. package/src/compose.ts +0 -50
  139. package/src/computed-entry.ts +0 -6
  140. package/src/computed.ts +0 -187
  141. package/src/devtools/history.ts +0 -103
  142. package/src/devtools/index.ts +0 -5
  143. package/src/devtools/redux-bridge.ts +0 -70
  144. package/src/devtools/snapshots.ts +0 -54
  145. package/src/devtools/withDevtools.ts +0 -196
  146. package/src/extensions/noop.ts +0 -12
  147. package/src/index.ts +0 -4
  148. package/src/persist/adapters/indexedDB.ts +0 -114
  149. package/src/persist/adapters/localStorage.ts +0 -28
  150. package/src/persist/adapters/memory.ts +0 -26
  151. package/src/persist/adapters/sessionStorage.ts +0 -28
  152. package/src/persist/debounce.ts +0 -28
  153. package/src/persist/hydrate.ts +0 -60
  154. package/src/persist/index.ts +0 -141
  155. package/src/persist/serialize.ts +0 -60
  156. package/src/proxy.ts +0 -87
  157. package/src/registry.ts +0 -67
  158. package/src/signals/createSignal.ts +0 -81
  159. package/src/signals/index.ts +0 -20
  160. package/src/signals/useSignal.ts +0 -18
  161. package/src/store.ts +0 -250
  162. package/src/sync/channel.ts +0 -15
  163. package/src/sync/index.ts +0 -3
  164. package/src/sync/protocol.ts +0 -18
  165. package/src/sync/withSync.ts +0 -147
  166. package/src/types.ts +0 -159
  167. package/tests/async.test.ts +0 -1100
  168. package/tests/batch.test.ts +0 -41
  169. package/tests/compose.test.ts +0 -209
  170. package/tests/computed.test.ts +0 -867
  171. package/tests/devtools.test.ts +0 -1039
  172. package/tests/integration/persist.integration.test.ts +0 -258
  173. package/tests/integration/signals.integration.test.ts +0 -309
  174. package/tests/integration.test.ts +0 -278
  175. package/tests/persist/adapters/indexedDB.adapter.test.ts +0 -185
  176. package/tests/persist/adapters/localStorage.adapter.test.ts +0 -105
  177. package/tests/persist/adapters/memory.adapter.test.ts +0 -112
  178. package/tests/persist/adapters/sessionStorage.adapter.test.ts +0 -128
  179. package/tests/persist/debounce.test.ts +0 -121
  180. package/tests/persist/hydrate.test.ts +0 -120
  181. package/tests/persist/migrate.test.ts +0 -208
  182. package/tests/persist/persist.test.ts +0 -357
  183. package/tests/persist/serialize.test.ts +0 -128
  184. package/tests/proxy.test.ts +0 -473
  185. package/tests/registry.test.ts +0 -67
  186. package/tests/signals/derived.test.ts +0 -244
  187. package/tests/signals/inference.test.ts +0 -108
  188. package/tests/signals/signal.test.ts +0 -348
  189. package/tests/signals/useSignal.test.tsx +0 -275
  190. package/tests/store.test.ts +0 -482
  191. package/tests/stress.test.ts +0 -268
  192. package/tests/sync.test.ts +0 -576
  193. package/tests/types.test.ts +0 -32
  194. package/tests/v0.3.test.ts +0 -813
  195. package/tree-shake-test.js +0 -1
  196. package/tsconfig.json +0 -15
  197. package/vitest.config.ts +0 -22
  198. package/vitest_play.ts +0 -7
@@ -1,103 +0,0 @@
1
- /**
2
- * Internal structure for a history entry in the ring buffer.
3
- */
4
- export interface HistoryEntry<S> {
5
- state: S;
6
- timestamp: number;
7
- actionName: string;
8
- }
9
-
10
- /**
11
- * Ring buffer for time-travel debugging.
12
- * Fixed capacity ensures no unbounded growth.
13
- */
14
- export interface RingBuffer<S> {
15
- entries: HistoryEntry<S>[];
16
- cursor: number;
17
- capacity: number;
18
- }
19
-
20
- /**
21
- * Creates an empty ring buffer with the given capacity.
22
- * @param capacity - Max number of entries (default 50)
23
- */
24
- export function createRingBuffer<S>(capacity: number = 50): RingBuffer<S> {
25
- return {
26
- entries: [],
27
- cursor: -1,
28
- capacity,
29
- };
30
- }
31
-
32
- /**
33
- * Pushes a new state to the ring buffer.
34
- * Discards any redo stack (entries after the cursor).
35
- * Drops oldest entry if capacity is exceeded.
36
- */
37
- export function push<S>(buffer: RingBuffer<S>, state: S, actionName: string): RingBuffer<S> {
38
- const entry: HistoryEntry<S> = {
39
- state,
40
- timestamp: Date.now(),
41
- actionName,
42
- };
43
-
44
- // Discard any entries after the current cursor (redo stack)
45
- const currentEntries = buffer.entries.slice(0, buffer.cursor + 1);
46
-
47
- let newEntries = [...currentEntries, entry];
48
- let newCursor = newEntries.length - 1;
49
-
50
- // Maintain capacity
51
- if (newEntries.length > buffer.capacity) {
52
- newEntries = newEntries.slice(newEntries.length - buffer.capacity);
53
- newCursor = newEntries.length - 1;
54
- }
55
-
56
- return {
57
- ...buffer,
58
- entries: newEntries,
59
- cursor: newCursor,
60
- };
61
- }
62
-
63
- /**
64
- * Moves the cursor back one position and returns the state.
65
- */
66
- export function undo<S>(buffer: RingBuffer<S>): { buffer: RingBuffer<S>; state: S | null } {
67
- if (buffer.cursor > 0) {
68
- const newCursor = buffer.cursor - 1;
69
- return {
70
- buffer: { ...buffer, cursor: newCursor },
71
- state: buffer.entries[newCursor].state,
72
- };
73
- }
74
- return { buffer, state: null };
75
- }
76
-
77
- /**
78
- * Moves the cursor forward one position and returns the state.
79
- */
80
- export function redo<S>(buffer: RingBuffer<S>): { buffer: RingBuffer<S>; state: S | null } {
81
- if (buffer.cursor < buffer.entries.length - 1) {
82
- const newCursor = buffer.cursor + 1;
83
- return {
84
- buffer: { ...buffer, cursor: newCursor },
85
- state: buffer.entries[newCursor].state,
86
- };
87
- }
88
- return { buffer, state: null };
89
- }
90
-
91
- /**
92
- * Returns true if the ring buffer can perform an undo.
93
- */
94
- export function canUndo<S>(buffer: RingBuffer<S>): boolean {
95
- return buffer.cursor > 0;
96
- }
97
-
98
- /**
99
- * Returns true if the ring buffer can perform a redo.
100
- */
101
- export function canRedo<S>(buffer: RingBuffer<S>): boolean {
102
- return buffer.cursor < buffer.entries.length - 1;
103
- }
@@ -1,5 +0,0 @@
1
- /* v8 ignore next 10 */
2
- export { withDevtools } from './withDevtools';
3
- export type { DevtoolsOptions } from './withDevtools';
4
- export type { HistoryEntry } from './history';
5
- export type { SnapshotEntry } from './snapshots';
@@ -1,70 +0,0 @@
1
- import { Store } from '../types';
2
- import { RingBuffer } from './history';
3
-
4
- /** @internal */
5
- export interface DevtoolsInternals<S> {
6
- buffer: RingBuffer<S>;
7
- initialState: S;
8
- snapshots: Map<string, { state: S; timestamp: number }>;
9
- _lastActionName: string | null;
10
- _applySnapshot: (state: S) => void;
11
- _isInternalUpdate: boolean;
12
- }
13
-
14
- interface DevtoolsInstance {
15
- init(state: unknown): void;
16
- send(action: { type: string }, state: unknown): void;
17
- subscribe(listener: (msg: { type: string; payload?: { type: string }; state?: string }) => void): () => void;
18
- }
19
-
20
- interface ReduxDevtoolsExtension {
21
- connect(options: { name: string; maxAge?: number }): DevtoolsInstance;
22
- }
23
-
24
- /**
25
- * Connects a devtools-enabled store to the Redux DevTools browser extension.
26
- */
27
- export function connectReduxDevtools<S extends object>(
28
- store: Store<S> & { __devtools: DevtoolsInternals<S> },
29
- name: string
30
- ): () => void {
31
- if (typeof window === 'undefined') return () => {};
32
-
33
- const extension = (window as unknown as { __REDUX_DEVTOOLS_EXTENSION__?: ReduxDevtoolsExtension }).__REDUX_DEVTOOLS_EXTENSION__;
34
- if (!extension) return () => {};
35
-
36
- const devtools: DevtoolsInstance = extension.connect({
37
- name: `Storve | ${name}`,
38
- maxAge: store.__devtools.buffer.capacity,
39
- });
40
-
41
- devtools.init(store.getState());
42
-
43
- const unsubscribeStore = store.subscribe((state) => {
44
- // Only send updates if they are NOT internal (undo/redo/restore from ourselves)
45
- if (store.__devtools._isInternalUpdate) return;
46
-
47
- devtools.send(
48
- { type: store.__devtools._lastActionName ?? 'setState' },
49
- state
50
- );
51
- });
52
-
53
- const unsubscribeDevtools = devtools.subscribe((message) => {
54
- if (message.type === 'DISPATCH') {
55
- if (message.payload?.type === 'JUMP_TO_STATE' || message.payload?.type === 'JUMP_TO_ACTION') {
56
- if (message.state) {
57
- store.__devtools._applySnapshot(JSON.parse(message.state));
58
- }
59
- } else if (message.payload?.type === 'RESET') {
60
- store.__devtools._applySnapshot(store.__devtools.initialState);
61
- }
62
- }
63
- });
64
-
65
-
66
- return () => {
67
- unsubscribeStore();
68
- unsubscribeDevtools();
69
- };
70
- }
@@ -1,54 +0,0 @@
1
- /**
2
- * Internal structure for a named snapshot entry.
3
- */
4
- export interface SnapshotEntry<S> {
5
- state: S;
6
- timestamp: number;
7
- }
8
-
9
- /**
10
- * A Map of named state checkpoints.
11
- */
12
- export type SnapshotMap<S> = Map<string, SnapshotEntry<S>>;
13
-
14
- /**
15
- * Creates a new empty snapshot map.
16
- */
17
- export function createSnapshotMap<S>(): SnapshotMap<S> {
18
- return new Map<string, SnapshotEntry<S>>();
19
- }
20
-
21
- /**
22
- * Saves a state snapshot under the given name.
23
- */
24
- export function saveSnapshot<S>(map: SnapshotMap<S>, name: string, state: S): SnapshotMap<S> {
25
- const nextMap = new Map(map);
26
- nextMap.set(name, {
27
- state,
28
- timestamp: Date.now(),
29
- });
30
- return nextMap;
31
- }
32
-
33
- /**
34
- * Returns the snapshot entry for the given name, or null if not found.
35
- */
36
- export function getSnapshot<S>(map: SnapshotMap<S>, name: string): SnapshotEntry<S> | null {
37
- return map.get(name) || null;
38
- }
39
-
40
- /**
41
- * Removes the snapshot with the given name.
42
- */
43
- export function deleteSnapshot<S>(map: SnapshotMap<S>, name: string): SnapshotMap<S> {
44
- const nextMap = new Map(map);
45
- nextMap.delete(name);
46
- return nextMap;
47
- }
48
-
49
- /**
50
- * Returns an array of all snapshot names.
51
- */
52
- export function listSnapshots<S>(map: SnapshotMap<S>): string[] {
53
- return Array.from(map.keys());
54
- }
@@ -1,196 +0,0 @@
1
- import { registerExtension } from '../registry';
2
- import { createRingBuffer, push, undo, redo, canUndo, canRedo } from './history';
3
- import { createSnapshotMap, saveSnapshot, getSnapshot, deleteSnapshot, listSnapshots } from './snapshots';
4
- import { connectReduxDevtools, DevtoolsInternals } from './redux-bridge';
5
- import type { Store, StoreState } from '../types';
6
-
7
- /**
8
- * Configuration options for DevTools.
9
- */
10
- export interface DevtoolsOptions {
11
- /** Label shown in Redux DevTools panel */
12
- name: string;
13
- /** Max history entries in ring buffer (default 50) */
14
- maxHistory?: number;
15
- /** Whether devtools are enabled (default true) */
16
- enabled?: boolean;
17
- }
18
-
19
- /** @internal WeakMap to store devtools options for definitions without polluting state */
20
- const DEVTOOLS_OPTIONS = new WeakMap<object, DevtoolsOptions>();
21
-
22
- /**
23
- * Augmented store type with devtools properties.
24
- * @internal
25
- */
26
- type StoreWithDevtools<S extends object> = Store<S> & {
27
- __devtools?: DevtoolsInternals<S>;
28
- undo?: () => void;
29
- redo?: () => void;
30
- canUndo?: boolean;
31
- canRedo?: boolean;
32
- snapshot?: (name: string) => void;
33
- restore?: (name: string) => void;
34
- deleteSnapshot?: (name: string) => void;
35
- clearHistory?: () => void;
36
- history?: readonly S[];
37
- snapshots?: readonly string[];
38
- }
39
-
40
-
41
- /**
42
- * Wraps a store definition with DevTools capabilities.
43
- * Must be imported to register the devtools extension.
44
- */
45
- export function withDevtools<D extends object>(
46
- definition: D,
47
- options: DevtoolsOptions
48
- ): D {
49
- DEVTOOLS_OPTIONS.set(definition, options);
50
- return definition;
51
- }
52
-
53
- // Register the extension via the registry pattern
54
- registerExtension({
55
- key: 'devtools',
56
- processDefinition: (definition) => {
57
- const options = DEVTOOLS_OPTIONS.get(definition);
58
- if (!options || options.enabled === false) return { state: {} };
59
-
60
- return {
61
- state: {},
62
- };
63
- },
64
- extendStore: (context) => {
65
- const { store, definition } = context as { store: Store<object>; definition: object };
66
- const options = DEVTOOLS_OPTIONS.get(definition);
67
- if (!options || options.enabled === false) return {};
68
-
69
- const initialState = store.getState();
70
- const internals: DevtoolsInternals<object> = {
71
- buffer: createRingBuffer<object>(options.maxHistory || 50),
72
- snapshots: createSnapshotMap<object>(),
73
- initialState,
74
- _isInternalUpdate: false,
75
- _lastActionName: null,
76
- _applySnapshot: (state: object) => {
77
- internals._isInternalUpdate = true;
78
- store.setState(state as unknown as Partial<StoreState<object>>);
79
- internals._isInternalUpdate = false;
80
- }
81
- };
82
- const devStore = store as unknown as StoreWithDevtools<object>;
83
- devStore.__devtools = internals;
84
-
85
- // One-shot flag: the very first subscribe callback after withSync broadcasts
86
- // REQUEST_STATE may fire setState back to initialState. We skip that single echo
87
- // so it doesn't appear as a spurious history entry.
88
- let _initEchoPending = true;
89
-
90
- // Use subscribe instead of wrapping setState to capture history.
91
- // This is more robust against extension ordering issues.
92
- store.subscribe(() => {
93
- if (internals._isInternalUpdate) return;
94
-
95
- const currentState = store.getState();
96
-
97
- // On first callback: if there's no explicit action name and state matches
98
- // initialState, this is likely a withSync echo — skip once and clear flag.
99
- if (_initEchoPending && internals._lastActionName === null) {
100
- const init = internals.initialState as Record<string, unknown>;
101
- const curr = currentState as Record<string, unknown>;
102
- const keys = Object.keys(init);
103
- const matchesInit = keys.length === Object.keys(curr).length
104
- && keys.every((k) => curr[k] === init[k]);
105
- if (matchesInit) {
106
- _initEchoPending = false;
107
- return;
108
- }
109
- }
110
- _initEchoPending = false;
111
-
112
- const actionName = internals._lastActionName ?? 'setState';
113
- internals.buffer = push(internals.buffer, currentState, actionName);
114
- internals._lastActionName = null; // Reset after capture
115
- });
116
-
117
- // Bridge actions to capture names.
118
- const rawActions = store.actions as Record<string, (...args: unknown[]) => unknown>;
119
- Object.keys(rawActions).forEach((key) => {
120
- const original = rawActions[key];
121
- const wrapped = (...args: unknown[]) => {
122
- internals._lastActionName = key;
123
- return original(...args);
124
- };
125
- rawActions[key] = wrapped;
126
- // Also update the store property if it was bound directly
127
- const storeAsRecord = store as unknown as Record<string, unknown>;
128
- if (storeAsRecord[key] === original) {
129
- storeAsRecord[key] = wrapped;
130
- }
131
- });
132
-
133
- if (typeof window !== 'undefined') {
134
- connectReduxDevtools(devStore as Required<StoreWithDevtools<object>>, options.name);
135
- }
136
-
137
- return {
138
- undo: () => {
139
- const { buffer, state } = undo(internals.buffer);
140
- if (state) {
141
- internals.buffer = buffer;
142
- internals._applySnapshot(state);
143
- }
144
- },
145
- redo: () => {
146
- const { buffer, state } = redo(internals.buffer);
147
- if (state) {
148
- internals.buffer = buffer;
149
- internals._applySnapshot(state);
150
- }
151
- },
152
- get canUndo() {
153
- return canUndo(internals.buffer);
154
- },
155
- get canRedo() {
156
- return canRedo(internals.buffer);
157
- },
158
- snapshot: (name: string) => {
159
- internals.snapshots = saveSnapshot(internals.snapshots, name, store.getState());
160
- // We use an internal update to trigger subscribers without pushing to history
161
- internals._isInternalUpdate = true;
162
- store.setState({} as unknown as Partial<StoreState<object>>);
163
- internals._isInternalUpdate = false;
164
- },
165
- restore: (name: string) => {
166
- const entry = getSnapshot(internals.snapshots, name);
167
- if (!entry) {
168
- throw new Error(`Storve DevTools: Snapshot "${name}" not found.`);
169
- }
170
-
171
- // restore() DOES push to history
172
- internals._applySnapshot(entry.state);
173
- internals.buffer = push(internals.buffer, entry.state, `restore('${name}')`);
174
- },
175
- deleteSnapshot: (name: string) => {
176
- internals.snapshots = deleteSnapshot(internals.snapshots, name);
177
- internals._isInternalUpdate = true;
178
- store.setState({} as unknown as Partial<StoreState<object>>);
179
- internals._isInternalUpdate = false;
180
- },
181
- clearHistory: () => {
182
- internals.buffer = createRingBuffer(internals.buffer.capacity);
183
- internals._isInternalUpdate = true;
184
- store.setState({} as unknown as Partial<StoreState<object>>);
185
- internals._isInternalUpdate = false;
186
- },
187
- get history() {
188
- return [...internals.buffer.entries];
189
- },
190
- get snapshots() {
191
- return listSnapshots(internals.snapshots);
192
- }
193
- };
194
- }
195
- });
196
-
@@ -1,12 +0,0 @@
1
- /**
2
- * No-op extension for validating the registry pattern.
3
- * @internal
4
- */
5
- import { registerExtension } from '../registry';
6
-
7
- registerExtension({
8
- key: '__noop',
9
- processDefinition: (definition) => ({
10
- state: { ...definition },
11
- }),
12
- });
package/src/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export { createStore } from './store';
2
- export { batch } from './batch';
3
- export { compose } from './compose';
4
- export type { Store, StoreDefinition, Listener, Unsubscribe, StoreOptions, StoreState, StoreActions, AsyncState, AsyncOptions, AsyncStatus, ComputedValue, WritableStoreState, ComputedKeys } from './types';
@@ -1,114 +0,0 @@
1
- import type { PersistAdapter } from '../index.js'
2
-
3
- /**
4
- * Creates an IndexedDB persistence adapter.
5
- * Lazily opens the database on first interaction and caches the Promise.
6
- * Safe for Server-Side Rendering (SSR) — if 'indexedDB' is totally unavailable,
7
- * methods elegantly degrade to returning null / no-op promises.
8
- *
9
- * @param {string} [dbName='storve-persist'] - Optional custom database name.
10
- * @returns {PersistAdapter} The IndexedDB persistence adapter.
11
- */
12
- export function indexedDBAdapter(dbName: string = 'storve-persist'): PersistAdapter {
13
- const STORE_NAME = 'keyval'
14
- const isServer = typeof indexedDB === 'undefined'
15
- let dbPromise: Promise<IDBDatabase | null> | null = null
16
-
17
- function getDB(): Promise<IDBDatabase | null> {
18
- if (isServer) return Promise.resolve(null)
19
- if (dbPromise !== null) return dbPromise
20
-
21
- dbPromise = new Promise((resolve) => {
22
- try {
23
- const request = indexedDB.open(dbName, 1)
24
-
25
- request.onupgradeneeded = () => {
26
- const db = request.result
27
- if (!db.objectStoreNames.contains(STORE_NAME)) {
28
- db.createObjectStore(STORE_NAME)
29
- }
30
- }
31
-
32
- request.onsuccess = () => {
33
- resolve(request.result)
34
- }
35
-
36
- request.onerror = () => {
37
- console.warn(`[storve] Failed to open IndexedDB "${dbName}"`)
38
- resolve(null)
39
- }
40
- } catch (err) {
41
- console.warn(`[storve] Exception opening IndexedDB "${dbName}":`, err)
42
- resolve(null)
43
- }
44
- })
45
-
46
- return dbPromise
47
- }
48
-
49
- return {
50
- async getItem(key: string): Promise<string | null> {
51
- const db = await getDB()
52
- if (db === null) return null
53
-
54
- return new Promise((resolve) => {
55
- try {
56
- const transaction = db.transaction(STORE_NAME, 'readonly')
57
- const store = transaction.objectStore(STORE_NAME)
58
- const request = store.get(key)
59
-
60
- request.onsuccess = () => {
61
- const result = request.result
62
- if (typeof result === 'string') {
63
- resolve(result)
64
- } else {
65
- resolve(null)
66
- }
67
- }
68
-
69
- request.onerror = () => {
70
- resolve(null)
71
- }
72
- } catch {
73
- resolve(null)
74
- }
75
- })
76
- },
77
-
78
- async setItem(key: string, value: string): Promise<void> {
79
- const db = await getDB()
80
- if (db === null) return
81
-
82
- return new Promise((resolve) => {
83
- try {
84
- const transaction = db.transaction(STORE_NAME, 'readwrite')
85
- const store = transaction.objectStore(STORE_NAME)
86
- const request = store.put(value, key)
87
-
88
- request.onsuccess = () => resolve()
89
- request.onerror = () => resolve()
90
- } catch {
91
- resolve()
92
- }
93
- })
94
- },
95
-
96
- async removeItem(key: string): Promise<void> {
97
- const db = await getDB()
98
- if (db === null) return
99
-
100
- return new Promise((resolve) => {
101
- try {
102
- const transaction = db.transaction(STORE_NAME, 'readwrite')
103
- const store = transaction.objectStore(STORE_NAME)
104
- const request = store.delete(key)
105
-
106
- request.onsuccess = () => resolve()
107
- request.onerror = () => resolve()
108
- } catch {
109
- resolve()
110
- }
111
- })
112
- }
113
- }
114
- }
@@ -1,28 +0,0 @@
1
- import type { PersistAdapter } from '../index.js'
2
-
3
- /**
4
- * Creates a localStorage persistence adapter.
5
- * Uses window.localStorage to automatically persist state modifications in the browser.
6
- * Safe for Server-Side Rendering (SSR) — if 'window' is completely undefined,
7
- * methods gracefully return null or perform no-ops.
8
- *
9
- * @returns {PersistAdapter} The localStorage persistence adapter.
10
- */
11
- export function localStorageAdapter(): PersistAdapter {
12
- const isServer = typeof window === 'undefined'
13
-
14
- return {
15
- getItem(key: string): string | null {
16
- if (isServer) return null
17
- return window.localStorage.getItem(key)
18
- },
19
- setItem(key: string, value: string): void {
20
- if (isServer) return
21
- window.localStorage.setItem(key, value)
22
- },
23
- removeItem(key: string): void {
24
- if (isServer) return
25
- window.localStorage.removeItem(key)
26
- }
27
- }
28
- }
@@ -1,26 +0,0 @@
1
- import type { PersistAdapter } from '../index.js'
2
-
3
- /**
4
- * Creates an entirely isolated memory-based persistence adapter.
5
- * This adapter uses a closure-scoped Map to store data, ensuring fully
6
- * segregated instances without any module-level state.
7
- * Ideal for testing or Node/SSR environments where no real storage is available.
8
- *
9
- * @returns {PersistAdapter} An isolated memory adapter instance.
10
- */
11
- export function memoryAdapter(): PersistAdapter {
12
- const store = new Map<string, string>()
13
-
14
- return {
15
- getItem(key: string): string | null {
16
- const value = store.get(key)
17
- return value !== undefined ? value : null
18
- },
19
- setItem(key: string, value: string): void {
20
- store.set(key, value)
21
- },
22
- removeItem(key: string): void {
23
- store.delete(key)
24
- }
25
- }
26
- }
@@ -1,28 +0,0 @@
1
- import type { PersistAdapter } from '../index.js'
2
-
3
- /**
4
- * Creates a sessionStorage persistence adapter.
5
- * Uses window.sessionStorage to persist state for the lifespan of the browser tab.
6
- * Safe for Server-Side Rendering (SSR) — if 'window' is completely undefined,
7
- * methods gracefully return null or perform no-ops.
8
- *
9
- * @returns {PersistAdapter} The sessionStorage persistence adapter.
10
- */
11
- export function sessionStorageAdapter(): PersistAdapter {
12
- const isServer = typeof window === 'undefined'
13
-
14
- return {
15
- getItem(key: string): string | null {
16
- if (isServer) return null
17
- return window.sessionStorage.getItem(key)
18
- },
19
- setItem(key: string, value: string): void {
20
- if (isServer) return
21
- window.sessionStorage.setItem(key, value)
22
- },
23
- removeItem(key: string): void {
24
- if (isServer) return
25
- window.sessionStorage.removeItem(key)
26
- }
27
- }
28
- }
@@ -1,28 +0,0 @@
1
- /**
2
- * Creates a debounced version of a provided function that delays its execution until after
3
- * the specified milliseconds have elapsed since the last time it was called.
4
- * If 'ms' is 0, the function invokes immediately.
5
- *
6
- * @template T - The arguments type array.
7
- * @param {(...args: T) => void} fn - The function to debounce.
8
- * @param {number} ms - The number of milliseconds to wait.
9
- * @returns {(...args: T) => void} The new debounced function.
10
- */
11
- export function createDebounce<T extends unknown[]>(fn: (...args: T) => void, ms: number): (...args: T) => void {
12
- let timerId: ReturnType<typeof setTimeout> | undefined;
13
-
14
- return function(...args: T): void {
15
- if (ms === 0) {
16
- fn(...args)
17
- return
18
- }
19
-
20
- if (timerId !== undefined) {
21
- clearTimeout(timerId)
22
- }
23
-
24
- timerId = setTimeout(() => {
25
- fn(...args)
26
- }, ms)
27
- }
28
- }