@storve/core 1.0.2 → 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 +0 -16
  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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1013,22 +1013,6 @@ counterStore.increment()
1013
1013
 
1014
1014
  ---
1015
1015
 
1016
- ## Roadmap
1017
-
1018
- | Version | Theme | Status |
1019
- |---|---|---|
1020
- | v0.1–v0.3 | Core store, React adapter, Immer, Actions | ✅ Shipped |
1021
- | v0.4 | Async state — createAsync, TTL, SWR, optimistic | ✅ Shipped |
1022
- | v0.5 | Computed values + Signals | ✅ Shipped |
1023
- | v0.6 | DevTools — Time-travel, Undo/Redo, Snapshots | ✅ Shipped |
1024
- | v0.7 | Cross-tab sync via BroadcastChannel | ✅ Shipped |
1025
- | v1.0 | Stable API, polished README, StackBlitz demo | ✅ Shipped |
1026
- | v1.1 | Purpose-built DevTools browser extension | 📋 Planned |
1027
- | v1.2 | Full docs site (Docusaurus) | 📋 Planned |
1028
- | v1.3 | Middleware system | 📋 Planned |
1029
-
1030
- ---
1031
-
1032
1016
  ## License
1033
1017
 
1034
1018
  MIT © 2026 Storve
@@ -1,2 +1 @@
1
1
  "use strict";exports.indexedDBAdapter=function(e="storve-persist"){const n="keyval",t="undefined"==typeof indexedDB;let o=null;function r(){return t?Promise.resolve(null):(null!==o||(o=new Promise(t=>{try{const o=indexedDB.open(e,1);o.onupgradeneeded=()=>{const e=o.result;e.objectStoreNames.contains(n)||e.createObjectStore(n)},o.onsuccess=()=>{t(o.result)},o.onerror=()=>{console.warn(`[storve] Failed to open IndexedDB "${e}"`),t(null)}}catch(n){console.warn(`[storve] Exception opening IndexedDB "${e}":`,n),t(null)}})),o)}return{async getItem(e){const t=await r();return null===t?null:new Promise(o=>{try{const r=t.transaction(n,"readonly"),s=r.objectStore(n).get(e);s.onsuccess=()=>{const e=s.result;o("string"==typeof e?e:null)},s.onerror=()=>{o(null)}}catch{o(null)}})},async setItem(e,t){const o=await r();if(null!==o)return new Promise(r=>{try{const s=o.transaction(n,"readwrite"),c=s.objectStore(n).put(t,e);c.onsuccess=()=>r(),c.onerror=()=>r()}catch{r()}})},async removeItem(e){const t=await r();if(null!==t)return new Promise(o=>{try{const r=t.transaction(n,"readwrite"),s=r.objectStore(n).delete(e);s.onsuccess=()=>o(),s.onerror=()=>o()}catch{o()}})}}};
2
- //# sourceMappingURL=indexedDB.cjs.map
@@ -1,2 +1 @@
1
1
  function e(e="storve-persist"){const n="keyval",t="undefined"==typeof indexedDB;let o=null;function r(){return t?Promise.resolve(null):(null!==o||(o=new Promise(t=>{try{const o=indexedDB.open(e,1);o.onupgradeneeded=()=>{const e=o.result;e.objectStoreNames.contains(n)||e.createObjectStore(n)},o.onsuccess=()=>{t(o.result)},o.onerror=()=>{console.warn(`[storve] Failed to open IndexedDB "${e}"`),t(null)}}catch(n){console.warn(`[storve] Exception opening IndexedDB "${e}":`,n),t(null)}})),o)}return{async getItem(e){const t=await r();return null===t?null:new Promise(o=>{try{const r=t.transaction(n,"readonly"),s=r.objectStore(n).get(e);s.onsuccess=()=>{const e=s.result;o("string"==typeof e?e:null)},s.onerror=()=>{o(null)}}catch{o(null)}})},async setItem(e,t){const o=await r();if(null!==o)return new Promise(r=>{try{const s=o.transaction(n,"readwrite"),c=s.objectStore(n).put(t,e);c.onsuccess=()=>r(),c.onerror=()=>r()}catch{r()}})},async removeItem(e){const t=await r();if(null!==t)return new Promise(o=>{try{const r=t.transaction(n,"readwrite"),s=r.objectStore(n).delete(e);s.onsuccess=()=>o(),s.onerror=()=>o()}catch{o()}})}}}export{e as indexedDBAdapter};
2
- //# sourceMappingURL=indexedDB.mjs.map
@@ -1,2 +1 @@
1
1
  "use strict";exports.localStorageAdapter=function(){const e="undefined"==typeof window;return{getItem:t=>e?null:window.localStorage.getItem(t),setItem(t,o){e||window.localStorage.setItem(t,o)},removeItem(t){e||window.localStorage.removeItem(t)}}};
2
- //# sourceMappingURL=localStorage.cjs.map
@@ -1,2 +1 @@
1
1
  function e(){const e="undefined"==typeof window;return{getItem:t=>e?null:window.localStorage.getItem(t),setItem(t,o){e||window.localStorage.setItem(t,o)},removeItem(t){e||window.localStorage.removeItem(t)}}}export{e as localStorageAdapter};
2
- //# sourceMappingURL=localStorage.mjs.map
@@ -1,2 +1 @@
1
1
  "use strict";exports.memoryAdapter=function(){const e=new Map;return{getItem(t){const r=e.get(t);return void 0!==r?r:null},setItem(t,r){e.set(t,r)},removeItem(t){e.delete(t)}}};
2
- //# sourceMappingURL=memory.cjs.map
@@ -1,2 +1 @@
1
1
  function e(){const e=new Map;return{getItem(t){const n=e.get(t);return void 0!==n?n:null},setItem(t,n){e.set(t,n)},removeItem(t){e.delete(t)}}}export{e as memoryAdapter};
2
- //# sourceMappingURL=memory.mjs.map
@@ -1,2 +1 @@
1
1
  "use strict";exports.sessionStorageAdapter=function(){const e="undefined"==typeof window;return{getItem:t=>e?null:window.sessionStorage.getItem(t),setItem(t,o){e||window.sessionStorage.setItem(t,o)},removeItem(t){e||window.sessionStorage.removeItem(t)}}};
2
- //# sourceMappingURL=sessionStorage.cjs.map
@@ -1,2 +1 @@
1
1
  function e(){const e="undefined"==typeof window;return{getItem:t=>e?null:window.sessionStorage.getItem(t),setItem(t,o){e||window.sessionStorage.setItem(t,o)},removeItem(t){e||window.sessionStorage.removeItem(t)}}}export{e as sessionStorageAdapter};
2
- //# sourceMappingURL=sessionStorage.mjs.map
@@ -4,4 +4,3 @@
4
4
  */
5
5
  export { createAsync } from './async';
6
6
  export type { AsyncState, AsyncOptions, AsyncStatus } from './types';
7
- //# sourceMappingURL=async-entry.d.ts.map
package/dist/async.cjs CHANGED
@@ -1,2 +1 @@
1
1
  "use strict";var t=require("./registry-qtr1UpFU.js");const s="__rf_async";class i{fn;options;onUpdate;lastRequestId=0;lastArgs=[];cache=new Map;status="idle";data=null;error=null;previousData=null;previousStatus="idle";hasPrevious=!1;lastState=null;stableRefetch=()=>this.refetch();constructor(t,s,i){this.fn=t,this.options=s,this.onUpdate=i}getState(){return this.lastState||(this.lastState={data:this.data,error:this.error,status:this.status,loading:"loading"===this.status,refetch:this.stableRefetch}),this.lastState}async fetch(...t){let s,i=t;if(t.length>0){const e=t[t.length-1];e&&"object"==typeof e&&"optimistic"in e&&(s=e,i=t.slice(0,-1))}const e=Date.now(),{ttl:a=0,staleWhileRevalidate:n=!1}=this.options,r=JSON.stringify(i),h=this.cache.get(r);let o=!1;if(s?.optimistic)this.previousData=this.data,this.previousStatus=this.status,this.hasPrevious=!0,this.data=s.optimistic.data,this.status=s.optimistic.status||"success",this.lastArgs=i,this.notify(),o=!0;else{if(a>0&&h&&e<h.expiresAt&&"success"===this.status)return void(this.data!==h.data&&(this.data=h.data,this.lastArgs=i,this.notify()));if(a>0&&h&&e>=h.expiresAt&&"success"===this.status&&n)return this.data=h.data,this.lastArgs=i,this.runFetch(i,!0)}return this.lastArgs=i,this.runFetch(i,o)}async refetch(){return this.runFetch(this.lastArgs,!1)}invalidate(){this.cache.clear()}async runFetch(t,s){const i=++this.lastRequestId;s||(this.status="loading",this.notify());try{const s=await this.fn(...t);if(i!==this.lastRequestId)return;this.data=s,this.error=null,this.status="success",this.hasPrevious=!1;const e=this.options.ttl||0;if(e>0){const i=JSON.stringify(t);this.cache.set(i,{data:s,expiresAt:Date.now()+e})}}catch(t){if(i!==this.lastRequestId)return;this.error=t instanceof Error?t.message:String(t),this.hasPrevious?(this.data=this.previousData,this.status=this.previousStatus,this.hasPrevious=!1):(s||(this.data=null),this.status="error")}this.notify()}notify(){this.lastState=null,this.onUpdate(this.getState())}}t.registerExtension({key:"async",order:0,processDefinition:t=>{const i={},e=[];for(const a of Object.keys(t)){const n=t[a];if(n&&"object"==typeof n&&s in n){const t=n;e.push({key:a,init:t.init})}else i[a]=n}return{state:i,asyncInits:e}},extendStore:t=>{const s=t.engines;return{fetch:async(t,...i)=>{if(!s.has(t))throw new Error(`Storve: no async key "${t}" found in store`);const e=s.get(t);e&&await e.fetch(...i)},refetch:async t=>{const i=s.get(t);i&&await i.refetch()},invalidate:t=>{const i=s.get(t);i&&i.invalidate()},invalidateAll:()=>{s.forEach(t=>t.invalidate())},getAsyncState:t=>{const i=s.get(t);return i?i.getState():void 0}}}}),exports.createAsync=function(t,e={}){return{[s]:!0,init:s=>new i(t,e,s)}};
2
- //# sourceMappingURL=async.cjs.map
package/dist/async.d.ts CHANGED
@@ -49,4 +49,3 @@ export declare class AsyncEngine<T> implements IAsyncEngine<T> {
49
49
  private runFetch;
50
50
  private notify;
51
51
  }
52
- //# sourceMappingURL=async.d.ts.map
package/dist/async.mjs CHANGED
@@ -1,2 +1 @@
1
1
  import{r as t}from"./registry-zaKZ1P-s.js";const s="__rf_async";function i(t,i={}){return{[s]:!0,init:s=>new e(t,i,s)}}class e{fn;options;onUpdate;lastRequestId=0;lastArgs=[];cache=new Map;status="idle";data=null;error=null;previousData=null;previousStatus="idle";hasPrevious=!1;lastState=null;stableRefetch=()=>this.refetch();constructor(t,s,i){this.fn=t,this.options=s,this.onUpdate=i}getState(){return this.lastState||(this.lastState={data:this.data,error:this.error,status:this.status,loading:"loading"===this.status,refetch:this.stableRefetch}),this.lastState}async fetch(...t){let s,i=t;if(t.length>0){const e=t[t.length-1];e&&"object"==typeof e&&"optimistic"in e&&(s=e,i=t.slice(0,-1))}const e=Date.now(),{ttl:a=0,staleWhileRevalidate:n=!1}=this.options,r=JSON.stringify(i),h=this.cache.get(r);let o=!1;if(s?.optimistic)this.previousData=this.data,this.previousStatus=this.status,this.hasPrevious=!0,this.data=s.optimistic.data,this.status=s.optimistic.status||"success",this.lastArgs=i,this.notify(),o=!0;else{if(a>0&&h&&e<h.expiresAt&&"success"===this.status)return void(this.data!==h.data&&(this.data=h.data,this.lastArgs=i,this.notify()));if(a>0&&h&&e>=h.expiresAt&&"success"===this.status&&n)return this.data=h.data,this.lastArgs=i,this.runFetch(i,!0)}return this.lastArgs=i,this.runFetch(i,o)}async refetch(){return this.runFetch(this.lastArgs,!1)}invalidate(){this.cache.clear()}async runFetch(t,s){const i=++this.lastRequestId;s||(this.status="loading",this.notify());try{const s=await this.fn(...t);if(i!==this.lastRequestId)return;this.data=s,this.error=null,this.status="success",this.hasPrevious=!1;const e=this.options.ttl||0;if(e>0){const i=JSON.stringify(t);this.cache.set(i,{data:s,expiresAt:Date.now()+e})}}catch(t){if(i!==this.lastRequestId)return;this.error=t instanceof Error?t.message:String(t),this.hasPrevious?(this.data=this.previousData,this.status=this.previousStatus,this.hasPrevious=!1):(s||(this.data=null),this.status="error")}this.notify()}notify(){this.lastState=null,this.onUpdate(this.getState())}}t({key:"async",order:0,processDefinition:t=>{const i={},e=[];for(const a of Object.keys(t)){const n=t[a];if(n&&"object"==typeof n&&s in n){const t=n;e.push({key:a,init:t.init})}else i[a]=n}return{state:i,asyncInits:e}},extendStore:t=>{const s=t.engines;return{fetch:async(t,...i)=>{if(!s.has(t))throw new Error(`Storve: no async key "${t}" found in store`);const e=s.get(t);e&&await e.fetch(...i)},refetch:async t=>{const i=s.get(t);i&&await i.refetch()},invalidate:t=>{const i=s.get(t);i&&i.invalidate()},invalidateAll:()=>{s.forEach(t=>t.invalidate())},getAsyncState:t=>{const i=s.get(t);return i?i.getState():void 0}}}});export{i as createAsync};
2
- //# sourceMappingURL=async.mjs.map
package/dist/batch.d.ts CHANGED
@@ -9,4 +9,3 @@ export declare function batch(fn: () => void): void;
9
9
  export declare function isBatching(): boolean;
10
10
  /** @internal */
11
11
  export declare function subscribeToBatch(cb: () => void): () => void;
12
- //# sourceMappingURL=batch.d.ts.map
package/dist/compose.d.ts CHANGED
@@ -4,4 +4,3 @@ export declare function compose<D extends object, S extends Store<D>, R1>(store:
4
4
  export declare function compose<D extends object, S extends Store<D>, R1, R2>(store: S, f1: (store: S) => R1, f2: (store: R1) => R2): R2;
5
5
  export declare function compose<D extends object, S extends Store<D>, R1, R2, R3>(store: S, f1: (store: S) => R1, f2: (store: R1) => R2, f3: (store: R2) => R3): R3;
6
6
  export declare function compose<D extends object>(store: Store<D>, ...enhancers: Array<(store: unknown) => unknown>): unknown;
7
- //# sourceMappingURL=compose.d.ts.map
@@ -4,4 +4,3 @@
4
4
  */
5
5
  export { computed, COMPUTED_MARKER } from './computed';
6
6
  export type { ComputedValue } from './types';
7
- //# sourceMappingURL=computed-entry.d.ts.map
package/dist/computed.cjs CHANGED
@@ -1,2 +1 @@
1
1
  "use strict";var e=require("./registry-qtr1UpFU.js");const t="__rf_computed";function n(e,t){const n=new Set;return{result:t(new Proxy(e,{get:(e,t)=>(n.add(t),e[t])})),deps:n}}e.registerExtension({key:"computed",order:1,processDefinition:e=>{const o={...e},s=new Set,r=new Map;for(const c of Object.keys(e)){const f=e[c];if(f&&"object"==typeof f&&t in f){const e=f;s.add(c),delete o[c];const{result:t,deps:d}=n(o,e.fn);r.set(c,{fn:e.fn,value:t,deps:d,dirty:!1})}}const c=Array.from(s);let f=[];if(c.length>0){const e=e=>r.get(e).deps;!function(e,t){const n=new Set(e),o=[],s=new Set;function r(e){if(o.includes(e))throw new Error(`Storve: circular dependency in computed values: ${[...o,e].join(" → ")}`);if(!s.has(e)){o.push(e);for(const o of t(e))n.has(o)&&r(o);o.pop(),s.add(e)}}for(const t of e)s.has(t)||r(t)}(c,e),f=function(e,t){const n=[],o=new Set,s=new Set(e);function r(e){if(!o.has(e)){o.add(e);for(const n of t(e))s.has(n)&&r(n);n.push(e)}}for(const t of e)r(t);return n}(c,e);for(const e of f){const t=r.get(e),{result:s,deps:c}=n(o,t.fn);t.value=s,t.deps=c,o[e]=s}}return{state:o,readOnlyKeys:s,onStateChanged:e=>{return t=e.changedKeys,o=e.getState,s=e.setComputed,f.forEach(e=>{const n=r.get(e);for(const e of n.deps)if(t.has(e)){n.dirty=!0;break}}),void f.forEach(e=>{const t=r.get(e);if(!t.dirty)return;const c=o(),{result:d,deps:i}=n(c,t.fn);t.value=d,t.deps=i,t.dirty=!1,s(e,d),f.forEach(t=>{t!==e&&r.get(t).deps.has(e)&&(r.get(t).dirty=!0)})});var t,o,s}}}}),exports.COMPUTED_MARKER=t,exports.computed=function(e){return{[t]:!0,fn:e}};
2
- //# sourceMappingURL=computed.cjs.map
@@ -53,4 +53,3 @@ export declare function trackDependencies<S extends object, T>(state: S, fn: (st
53
53
  result: T;
54
54
  deps: Set<string>;
55
55
  };
56
- //# sourceMappingURL=computed.d.ts.map
package/dist/computed.mjs CHANGED
@@ -1,2 +1 @@
1
1
  import{r as e}from"./registry-zaKZ1P-s.js";const t="__rf_computed";function n(e){return{[t]:!0,fn:e}}function o(e,t){const n=new Set;return{result:t(new Proxy(e,{get:(e,t)=>(n.add(t),e[t])})),deps:n}}e({key:"computed",order:1,processDefinition:e=>{const n={...e},s=new Set,r=new Map;for(const c of Object.keys(e)){const f=e[c];if(f&&"object"==typeof f&&t in f){const e=f;s.add(c),delete n[c];const{result:t,deps:d}=o(n,e.fn);r.set(c,{fn:e.fn,value:t,deps:d,dirty:!1})}}const c=Array.from(s);let f=[];if(c.length>0){const e=e=>r.get(e).deps;!function(e,t){const n=new Set(e),o=[],s=new Set;function r(e){if(o.includes(e))throw new Error(`Storve: circular dependency in computed values: ${[...o,e].join(" → ")}`);if(!s.has(e)){o.push(e);for(const o of t(e))n.has(o)&&r(o);o.pop(),s.add(e)}}for(const t of e)s.has(t)||r(t)}(c,e),f=function(e,t){const n=[],o=new Set,s=new Set(e);function r(e){if(!o.has(e)){o.add(e);for(const n of t(e))s.has(n)&&r(n);n.push(e)}}for(const t of e)r(t);return n}(c,e);for(const e of f){const t=r.get(e),{result:s,deps:c}=o(n,t.fn);t.value=s,t.deps=c,n[e]=s}}return{state:n,readOnlyKeys:s,onStateChanged:e=>{return t=e.changedKeys,n=e.getState,s=e.setComputed,f.forEach(e=>{const n=r.get(e);for(const e of n.deps)if(t.has(e)){n.dirty=!0;break}}),void f.forEach(e=>{const t=r.get(e);if(!t.dirty)return;const c=n(),{result:d,deps:a}=o(c,t.fn);t.value=d,t.deps=a,t.dirty=!1,s(e,d),f.forEach(t=>{t!==e&&r.get(t).deps.has(e)&&(r.get(t).dirty=!0)})});var t,n,s}}}});export{t as COMPUTED_MARKER,n as computed};
2
- //# sourceMappingURL=computed.mjs.map
@@ -48,4 +48,3 @@ export declare function canUndo<S>(buffer: RingBuffer<S>): boolean;
48
48
  * Returns true if the ring buffer can perform a redo.
49
49
  */
50
50
  export declare function canRedo<S>(buffer: RingBuffer<S>): boolean;
51
- //# sourceMappingURL=history.d.ts.map
@@ -2,4 +2,3 @@ export { withDevtools } from './withDevtools';
2
2
  export type { DevtoolsOptions } from './withDevtools';
3
3
  export type { HistoryEntry } from './history';
4
4
  export type { SnapshotEntry } from './snapshots';
5
- //# sourceMappingURL=index.d.ts.map
@@ -18,4 +18,3 @@ export interface DevtoolsInternals<S> {
18
18
  export declare function connectReduxDevtools<S extends object>(store: Store<S> & {
19
19
  __devtools: DevtoolsInternals<S>;
20
20
  }, name: string): () => void;
21
- //# sourceMappingURL=redux-bridge.d.ts.map
@@ -29,4 +29,3 @@ export declare function deleteSnapshot<S>(map: SnapshotMap<S>, name: string): Sn
29
29
  * Returns an array of all snapshot names.
30
30
  */
31
31
  export declare function listSnapshots<S>(map: SnapshotMap<S>): string[];
32
- //# sourceMappingURL=snapshots.d.ts.map
@@ -14,4 +14,3 @@ export interface DevtoolsOptions {
14
14
  * Must be imported to register the devtools extension.
15
15
  */
16
16
  export declare function withDevtools<D extends object>(definition: D, options: DevtoolsOptions): D;
17
- //# sourceMappingURL=withDevtools.d.ts.map
package/dist/devtools.cjs CHANGED
@@ -1,2 +1 @@
1
1
  "use strict";var t=require("./registry-qtr1UpFU.js");function e(t=50){return{entries:[],cursor:-1,capacity:t}}function n(t,e,n){const s={state:e,timestamp:Date.now(),actionName:n};let r=[...t.entries.slice(0,t.cursor+1),s],a=r.length-1;return r.length>t.capacity&&(r=r.slice(r.length-t.capacity),a=r.length-1),{...t,entries:r,cursor:a}}const s=new WeakMap;t.registerExtension({key:"devtools",processDefinition:t=>{const e=s.get(t);return e&&e.enabled,{state:{}}},extendStore:t=>{const{store:r,definition:a}=t,o=s.get(a);if(!o||!1===o.enabled)return{};const i=r.getState(),u={buffer:e(o.maxHistory||50),snapshots:new Map,initialState:i,_isInternalUpdate:!1,_lastActionName:null,_applySnapshot:t=>{u._isInternalUpdate=!0,r.setState(t),u._isInternalUpdate=!1}},c=r;c.__devtools=u;let f=!0;r.subscribe(()=>{if(u._isInternalUpdate)return;const t=r.getState();if(f&&null===u._lastActionName){const e=u.initialState,n=t,s=Object.keys(e);if(s.length===Object.keys(n).length&&s.every(t=>n[t]===e[t]))return void(f=!1)}f=!1;const e=u._lastActionName??"setState";u.buffer=n(u.buffer,t,e),u._lastActionName=null});const l=r.actions;return Object.keys(l).forEach(t=>{const e=l[t],n=(...n)=>(u._lastActionName=t,e(...n));l[t]=n;const s=r;s[t]===e&&(s[t]=n)}),"undefined"!=typeof window&&function(t,e){if("undefined"==typeof window)return()=>{};const n=window.__REDUX_DEVTOOLS_EXTENSION__;if(!n)return()=>{};const s=n.connect({name:`Storve | ${e}`,maxAge:t.__devtools.buffer.capacity});s.init(t.getState());const r=t.subscribe(e=>{t.__devtools._isInternalUpdate||s.send({type:t.__devtools._lastActionName??"setState"},e)}),a=s.subscribe(e=>{"DISPATCH"===e.type&&("JUMP_TO_STATE"===e.payload?.type||"JUMP_TO_ACTION"===e.payload?.type?e.state&&t.__devtools._applySnapshot(JSON.parse(e.state)):"RESET"===e.payload?.type&&t.__devtools._applySnapshot(t.__devtools.initialState))})}(c,o.name),{undo:()=>{const{buffer:t,state:e}=function(t){if(t.cursor>0){const e=t.cursor-1;return{buffer:{...t,cursor:e},state:t.entries[e].state}}return{buffer:t,state:null}}(u.buffer);e&&(u.buffer=t,u._applySnapshot(e))},redo:()=>{const{buffer:t,state:e}=function(t){if(t.cursor<t.entries.length-1){const e=t.cursor+1;return{buffer:{...t,cursor:e},state:t.entries[e].state}}return{buffer:t,state:null}}(u.buffer);e&&(u.buffer=t,u._applySnapshot(e))},get canUndo(){return u.buffer.cursor>0},get canRedo(){return(t=u.buffer).cursor<t.entries.length-1;var t},snapshot:t=>{u.snapshots=function(t,e,n){const s=new Map(t);return s.set(e,{state:n,timestamp:Date.now()}),s}(u.snapshots,t,r.getState()),u._isInternalUpdate=!0,r.setState({}),u._isInternalUpdate=!1},restore:t=>{const e=function(t,e){return t.get(e)||null}(u.snapshots,t);if(!e)throw new Error(`Storve DevTools: Snapshot "${t}" not found.`);u._applySnapshot(e.state),u.buffer=n(u.buffer,e.state,`restore('${t}')`)},deleteSnapshot:t=>{u.snapshots=function(t,e){const n=new Map(t);return n.delete(e),n}(u.snapshots,t),u._isInternalUpdate=!0,r.setState({}),u._isInternalUpdate=!1},clearHistory:()=>{u.buffer=e(u.buffer.capacity),u._isInternalUpdate=!0,r.setState({}),u._isInternalUpdate=!1},get history(){return[...u.buffer.entries]},get snapshots(){return t=u.snapshots,Array.from(t.keys());var t}}}}),exports.withDevtools=function(t,e){return s.set(t,e),t};
2
- //# sourceMappingURL=devtools.cjs.map
package/dist/devtools.mjs CHANGED
@@ -1,2 +1 @@
1
1
  import{r as t}from"./registry-zaKZ1P-s.js";function e(t=50){return{entries:[],cursor:-1,capacity:t}}function n(t,e,n){const s={state:e,timestamp:Date.now(),actionName:n};let r=[...t.entries.slice(0,t.cursor+1),s],a=r.length-1;return r.length>t.capacity&&(r=r.slice(r.length-t.capacity),a=r.length-1),{...t,entries:r,cursor:a}}const s=new WeakMap;function r(t,e){return s.set(t,e),t}t({key:"devtools",processDefinition:t=>{const e=s.get(t);return e&&e.enabled,{state:{}}},extendStore:t=>{const{store:r,definition:a}=t,o=s.get(a);if(!o||!1===o.enabled)return{};const i=r.getState(),u={buffer:e(o.maxHistory||50),snapshots:new Map,initialState:i,_isInternalUpdate:!1,_lastActionName:null,_applySnapshot:t=>{u._isInternalUpdate=!0,r.setState(t),u._isInternalUpdate=!1}},c=r;c.__devtools=u;let f=!0;r.subscribe(()=>{if(u._isInternalUpdate)return;const t=r.getState();if(f&&null===u._lastActionName){const e=u.initialState,n=t,s=Object.keys(e);if(s.length===Object.keys(n).length&&s.every(t=>n[t]===e[t]))return void(f=!1)}f=!1;const e=u._lastActionName??"setState";u.buffer=n(u.buffer,t,e),u._lastActionName=null});const l=r.actions;return Object.keys(l).forEach(t=>{const e=l[t],n=(...n)=>(u._lastActionName=t,e(...n));l[t]=n;const s=r;s[t]===e&&(s[t]=n)}),"undefined"!=typeof window&&function(t,e){if("undefined"==typeof window)return()=>{};const n=window.__REDUX_DEVTOOLS_EXTENSION__;if(!n)return()=>{};const s=n.connect({name:`Storve | ${e}`,maxAge:t.__devtools.buffer.capacity});s.init(t.getState());const r=t.subscribe(e=>{t.__devtools._isInternalUpdate||s.send({type:t.__devtools._lastActionName??"setState"},e)}),a=s.subscribe(e=>{"DISPATCH"===e.type&&("JUMP_TO_STATE"===e.payload?.type||"JUMP_TO_ACTION"===e.payload?.type?e.state&&t.__devtools._applySnapshot(JSON.parse(e.state)):"RESET"===e.payload?.type&&t.__devtools._applySnapshot(t.__devtools.initialState))})}(c,o.name),{undo:()=>{const{buffer:t,state:e}=function(t){if(t.cursor>0){const e=t.cursor-1;return{buffer:{...t,cursor:e},state:t.entries[e].state}}return{buffer:t,state:null}}(u.buffer);e&&(u.buffer=t,u._applySnapshot(e))},redo:()=>{const{buffer:t,state:e}=function(t){if(t.cursor<t.entries.length-1){const e=t.cursor+1;return{buffer:{...t,cursor:e},state:t.entries[e].state}}return{buffer:t,state:null}}(u.buffer);e&&(u.buffer=t,u._applySnapshot(e))},get canUndo(){return u.buffer.cursor>0},get canRedo(){return(t=u.buffer).cursor<t.entries.length-1;var t},snapshot:t=>{u.snapshots=function(t,e,n){const s=new Map(t);return s.set(e,{state:n,timestamp:Date.now()}),s}(u.snapshots,t,r.getState()),u._isInternalUpdate=!0,r.setState({}),u._isInternalUpdate=!1},restore:t=>{const e=function(t,e){return t.get(e)||null}(u.snapshots,t);if(!e)throw new Error(`Storve DevTools: Snapshot "${t}" not found.`);u._applySnapshot(e.state),u.buffer=n(u.buffer,e.state,`restore('${t}')`)},deleteSnapshot:t=>{u.snapshots=function(t,e){const n=new Map(t);return n.delete(e),n}(u.snapshots,t),u._isInternalUpdate=!0,r.setState({}),u._isInternalUpdate=!1},clearHistory:()=>{u.buffer=e(u.buffer.capacity),u._isInternalUpdate=!0,r.setState({}),u._isInternalUpdate=!1},get history(){return[...u.buffer.entries]},get snapshots(){return t=u.snapshots,Array.from(t.keys());var t}}}});export{r as withDevtools};
2
- //# sourceMappingURL=devtools.mjs.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=noop.d.ts.map
package/dist/index.cjs CHANGED
@@ -1,2 +1 @@
1
1
  "use strict";var e=require("immer"),t=require("./registry-qtr1UpFU.js");const n=new WeakMap,r=new WeakMap;let o=!1;function s(e){if(null===e||"object"!=typeof e)return!1;return Object.getPrototypeOf(e)===Object.prototype||Array.isArray(e)}function c(e,t){if(!s(e))return e;if(n.has(e))return n.get(e);if(r.has(e))return e;const i=new Proxy(e,{get(e,r,c){if(Array.isArray(e)&&"string"==typeof r&&["push","pop","shift","unshift","splice","sort","reverse"].includes(r))return(...n)=>{const s=o;o=!0;const i=Reflect.get(e,r,c),a=Reflect.apply(i,c,n);return o=s,o||t(),a};const i=Reflect.get(e,r,c);return s(i)&&n.has(i)?n.get(i):i},set(e,n,i,a){const f=r.has(i)?r.get(i):i;s(f)&&c(f,t);const l=Reflect.set(e,n,f,a);return o||t(),l},deleteProperty(e,n){const r=Reflect.deleteProperty(e,n);return o||t(),r}});n.set(e,i),r.set(i,e);for(const n in e)if(Object.prototype.hasOwnProperty.call(e,n)){const r=e[n];s(r)&&c(r,t)}return i}let i=0;const a=new Set;function f(){return i>0}exports.batch=function(e){i++;try{e()}finally{i--,0===i&&a.forEach(e=>e())}},exports.compose=function(e,...t){if(0===t.length)return e;let n=e;for(let e=0;e<t.length;e++)n=t[e](n);return n},exports.createStore=function(n,r={}){const{actions:o={},...s}=n;let i={...s};const l=[],u=new Set,y=[];for(const e of t.getExtensions())if(e.processDefinition){const t=e.processDefinition(i);i={...i,...t.state},t.asyncInits&&l.push(...t.asyncInits),t.readOnlyKeys&&t.readOnlyKeys.forEach(e=>u.add(e)),t.onStateChanged&&y.push(t.onStateChanged)}const p={current:null},d=new Map;for(const{key:e,init:t}of l){const n=t(t=>{p.current?.({[e]:t})});d.set(e,n),i[e]=n.getState()}const h=i,g=new Set;let S=h,w=0,O=!1,b=null,j=new Set,E=null,x=null;const P=()=>{w>0||f()?O=!0:(O=!1,g.forEach(e=>e(S)))},k=e=>{const t=(e,t)=>{S[e]=t};for(const n of y)n({changedKeys:e,getState:()=>S,setComputed:t,store:A})},m=c(h,P),v=t=>{let n;if(n="function"==typeof t?r.immer?e.produce(S,t):{...S,...t(S)}:{...S,...t},n===S)return;const o={...n};u.forEach(e=>delete o[e]);const s=S,c=new Set(Object.keys(o).filter(e=>s[e]!==o[e]));S={...S,...o},w>0||f()?(c.forEach(e=>j.add(e)),O=!0):k(c),E=null,x=null,w++;try{for(const e in S)Object.prototype.hasOwnProperty.call(S,e)&&S[e]!==s[e]&&(m[e]=S[e])}finally{w--}w>0?O=!0:P()};p.current=v;const A={getState:()=>{if(null!==E&&x===S)return E;const e={...S};return E=e,x=S,e},setState:v,subscribe:e=>{var t;return g.add(e),1===g.size&&(t=()=>{O&&(O=!1,k(j),j=new Set,P())},a.add(t),b=()=>{a.delete(t)},O&&(O=!1,k(j),j=new Set,P())),()=>{g.delete(e),0===g.size&&(b?.(),b=null)}},batch:e=>{w++;try{e()}finally{w--,0===w&&O&&(O=!1,k(j),j=new Set,P())}},actions:{},fetch:async e=>{throw new Error(`Storve: no async key "${e}" found in store. Import "storve/async" to use createAsync.`)},refetch:async()=>{},invalidate:()=>{},invalidateAll:()=>{},getAsyncState:()=>{}};for(const e of t.getExtensions())if(e.extendStore){const t=e.extendStore({engines:d,store:A,definition:n});Object.defineProperties(A,Object.getOwnPropertyDescriptors(t))}k(new Set(Object.keys(S)));const R={};return Object.keys(o).forEach(e=>{R[e]=(...t)=>o[e](...t)}),Object.assign(A,R),A.actions=R,A};
2
- //# sourceMappingURL=index.cjs.map
package/dist/index.d.ts CHANGED
@@ -2,4 +2,3 @@ export { createStore } from './store';
2
2
  export { batch } from './batch';
3
3
  export { compose } from './compose';
4
4
  export type { Store, StoreDefinition, Listener, Unsubscribe, StoreOptions, StoreState, StoreActions, AsyncState, AsyncOptions, AsyncStatus, ComputedValue, WritableStoreState, ComputedKeys } from './types';
5
- //# sourceMappingURL=index.d.ts.map
package/dist/index.mjs CHANGED
@@ -1,2 +1 @@
1
1
  import{produce as t}from"immer";import{g as e}from"./registry-zaKZ1P-s.js";const n=new WeakMap,r=new WeakMap;let o=!1;function s(t){if(null===t||"object"!=typeof t)return!1;return Object.getPrototypeOf(t)===Object.prototype||Array.isArray(t)}function c(t,e){if(!s(t))return t;if(n.has(t))return n.get(t);if(r.has(t))return t;const i=new Proxy(t,{get(t,r,c){if(Array.isArray(t)&&"string"==typeof r&&["push","pop","shift","unshift","splice","sort","reverse"].includes(r))return(...n)=>{const s=o;o=!0;const i=Reflect.get(t,r,c),a=Reflect.apply(i,c,n);return o=s,o||e(),a};const i=Reflect.get(t,r,c);return s(i)&&n.has(i)?n.get(i):i},set(t,n,i,a){const f=r.has(i)?r.get(i):i;s(f)&&c(f,e);const l=Reflect.set(t,n,f,a);return o||e(),l},deleteProperty(t,n){const r=Reflect.deleteProperty(t,n);return o||e(),r}});n.set(t,i),r.set(i,t);for(const n in t)if(Object.prototype.hasOwnProperty.call(t,n)){const r=t[n];s(r)&&c(r,e)}return i}let i=0;const a=new Set;function f(t){i++;try{t()}finally{i--,0===i&&a.forEach(t=>t())}}function l(){return i>0}function u(n,r={}){const{actions:o={},...s}=n;let i={...s};const f=[],u=new Set,y=[];for(const t of e())if(t.processDefinition){const e=t.processDefinition(i);i={...i,...e.state},e.asyncInits&&f.push(...e.asyncInits),e.readOnlyKeys&&e.readOnlyKeys.forEach(t=>u.add(t)),e.onStateChanged&&y.push(e.onStateChanged)}const p={current:null},d=new Map;for(const{key:t,init:e}of f){const n=e(e=>{p.current?.({[t]:e})});d.set(t,n),i[t]=n.getState()}const h=i,g=new Set;let S=h,w=0,O=!1,b=null,j=new Set,m=null,P=null;const k=()=>{w>0||l()?O=!0:(O=!1,g.forEach(t=>t(S)))},A=t=>{const e=(t,e)=>{S[t]=e};for(const n of y)n({changedKeys:t,getState:()=>S,setComputed:e,store:R})},E=c(h,k),v=e=>{let n;if(n="function"==typeof e?r.immer?t(S,e):{...S,...e(S)}:{...S,...e},n===S)return;const o={...n};u.forEach(t=>delete o[t]);const s=S,c=new Set(Object.keys(o).filter(t=>s[t]!==o[t]));S={...S,...o},w>0||l()?(c.forEach(t=>j.add(t)),O=!0):A(c),m=null,P=null,w++;try{for(const t in S)Object.prototype.hasOwnProperty.call(S,t)&&S[t]!==s[t]&&(E[t]=S[t])}finally{w--}w>0?O=!0:k()};p.current=v;const R={getState:()=>{if(null!==m&&P===S)return m;const t={...S};return m=t,P=S,t},setState:v,subscribe:t=>{var e;return g.add(t),1===g.size&&(e=()=>{O&&(O=!1,A(j),j=new Set,k())},a.add(e),b=()=>{a.delete(e)},O&&(O=!1,A(j),j=new Set,k())),()=>{g.delete(t),0===g.size&&(b?.(),b=null)}},batch:t=>{w++;try{t()}finally{w--,0===w&&O&&(O=!1,A(j),j=new Set,k())}},actions:{},fetch:async t=>{throw new Error(`Storve: no async key "${t}" found in store. Import "storve/async" to use createAsync.`)},refetch:async()=>{},invalidate:()=>{},invalidateAll:()=>{},getAsyncState:()=>{}};for(const t of e())if(t.extendStore){const e=t.extendStore({engines:d,store:R,definition:n});Object.defineProperties(R,Object.getOwnPropertyDescriptors(e))}A(new Set(Object.keys(S)));const x={};return Object.keys(o).forEach(t=>{x[t]=(...e)=>o[t](...e)}),Object.assign(R,x),R.actions=x,R}function y(t,...e){if(0===e.length)return t;let n=t;for(let t=0;t<e.length;t++)n=e[t](n);return n}export{f as batch,y as compose,u as createStore};
2
- //# sourceMappingURL=index.mjs.map
@@ -9,4 +9,3 @@ import type { PersistAdapter } from '../index.js';
9
9
  * @returns {PersistAdapter} The IndexedDB persistence adapter.
10
10
  */
11
11
  export declare function indexedDBAdapter(dbName?: string): PersistAdapter;
12
- //# sourceMappingURL=indexedDB.d.ts.map
@@ -8,4 +8,3 @@ import type { PersistAdapter } from '../index.js';
8
8
  * @returns {PersistAdapter} The localStorage persistence adapter.
9
9
  */
10
10
  export declare function localStorageAdapter(): PersistAdapter;
11
- //# sourceMappingURL=localStorage.d.ts.map
@@ -8,4 +8,3 @@ import type { PersistAdapter } from '../index.js';
8
8
  * @returns {PersistAdapter} An isolated memory adapter instance.
9
9
  */
10
10
  export declare function memoryAdapter(): PersistAdapter;
11
- //# sourceMappingURL=memory.d.ts.map
@@ -8,4 +8,3 @@ import type { PersistAdapter } from '../index.js';
8
8
  * @returns {PersistAdapter} The sessionStorage persistence adapter.
9
9
  */
10
10
  export declare function sessionStorageAdapter(): PersistAdapter;
11
- //# sourceMappingURL=sessionStorage.d.ts.map
@@ -9,4 +9,3 @@
9
9
  * @returns {(...args: T) => void} The new debounced function.
10
10
  */
11
11
  export declare function createDebounce<T extends unknown[]>(fn: (...args: T) => void, ms: number): (...args: T) => void;
12
- //# sourceMappingURL=debounce.d.ts.map
@@ -12,4 +12,3 @@ import type { PersistAdapter } from './index.js';
12
12
  * @returns {Promise<Partial<T>>} A promise that resolves to the hydrated partial state (or an empty object).
13
13
  */
14
14
  export declare function hydrate<T extends object>(adapter: PersistAdapter, key: string, currentState: T, version: number, migrate?: (persisted: Partial<T>, version: number) => Partial<T>): Promise<Partial<T>>;
15
- //# sourceMappingURL=hydrate.d.ts.map
@@ -31,4 +31,3 @@ export declare function withPersist<D extends object>(store: Store<D>, options:
31
31
  export declare function withPersist<D extends object>(options: PersistOptions<StoreState<D>>): (store: Store<D>) => Store<D> & {
32
32
  hydrated: Promise<void>;
33
33
  };
34
- //# sourceMappingURL=index.d.ts.map
@@ -25,4 +25,3 @@ export declare function toJSON(value: unknown): string;
25
25
  * @throws {Error} Throws if 'raw' is empty or if JSON.parse fails.
26
26
  */
27
27
  export declare function fromJSON<T>(raw: string): T;
28
- //# sourceMappingURL=serialize.d.ts.map
package/dist/persist.cjs CHANGED
@@ -1,2 +1 @@
1
1
  "use strict";function t(t,e){if(void 0===e||0===e.length)return{...t};const r={};for(let n=0;n<e.length;n++){const o=e[n];o in t&&(r[o]=t[o])}return r}function e(t){try{return JSON.stringify(t)}catch(t){throw new Error(`[storve] Failed to serialize state to JSON: ${t instanceof Error?t.message:String(t)}`)}}async function r(t,e,r,n,o){const i=await t.getItem(e);if(!i)return{};let s;try{s=function(t){if(!t)throw new Error("[storve] Cannot parse empty or null/undefined JSON string.");try{return JSON.parse(t)}catch(t){throw new Error(`[storve] Failed to parse JSON state: ${t instanceof Error?t.message:String(t)}`)}}(i)}catch(t){return console.warn(`[storve] Hydration failed for key "${e}":`,t),{}}const c=void 0!==s.__version?s.__version:0;let a;if(c!==n){if(void 0===o)return console.warn(`Storve: persisted state version mismatch (stored: ${c}, expected: ${n}). No migrate function provided — falling back to default state.`),{};a=o(s,c)}else a=s;const u={...a};return delete u.__version,u}function n(t){return null!==t&&"object"==typeof t&&"adapter"in t&&"key"in t}function o(n,o){let i;const s=new Promise(t=>{i=t}),c=void 0!==o.version?o.version:1,a=void 0!==o.debounce?o.debounce:100;r(o.adapter,o.key,n.getState(),c,o.migrate).then(t=>{n.setState(t),i()}).catch(t=>{console.warn(`[storve] withPersist hydrate error for key "${o.key}":`,t),i()});const u=function(t,e){let r;return function(...n){0!==e?(void 0!==r&&clearTimeout(r),r=setTimeout(()=>{t(...n)},e)):t(...n)}}(t=>{const e=o.adapter.setItem(o.key,t);e&&"function"==typeof e.catch&&e.catch(t=>{console.warn(`[storve] Failed to persist state for key "${o.key}":`,t)})},a);let f=e({...o.pick&&o.pick.length>0?t(n.getState(),o.pick):{...n.getState()},__version:c});return n.subscribe(r=>{const n=e({...o.pick&&o.pick.length>0?t(r,o.pick):{...r},__version:c});n!==f&&(f=n,u(n))}),{...n,hydrated:s}}exports.withPersist=function(t,e){if(void 0!==e&&!n(t))return o(t,e);if(n(t))return e=>o(e,t);throw new Error("[storve] Invalid withPersist arguments")};
2
- //# sourceMappingURL=persist.cjs.map
package/dist/persist.mjs CHANGED
@@ -1,2 +1 @@
1
1
  function t(t,e){if(void 0===e||0===e.length)return{...t};const r={};for(let n=0;n<e.length;n++){const o=e[n];o in t&&(r[o]=t[o])}return r}function e(t){try{return JSON.stringify(t)}catch(t){throw new Error(`[storve] Failed to serialize state to JSON: ${t instanceof Error?t.message:String(t)}`)}}async function r(t,e,r,n,o){const i=await t.getItem(e);if(!i)return{};let s;try{s=function(t){if(!t)throw new Error("[storve] Cannot parse empty or null/undefined JSON string.");try{return JSON.parse(t)}catch(t){throw new Error(`[storve] Failed to parse JSON state: ${t instanceof Error?t.message:String(t)}`)}}(i)}catch(t){return console.warn(`[storve] Hydration failed for key "${e}":`,t),{}}const a=void 0!==s.__version?s.__version:0;let c;if(a!==n){if(void 0===o)return console.warn(`Storve: persisted state version mismatch (stored: ${a}, expected: ${n}). No migrate function provided — falling back to default state.`),{};c=o(s,a)}else c=s;const u={...c};return delete u.__version,u}function n(t){return null!==t&&"object"==typeof t&&"adapter"in t&&"key"in t}function o(n,o){let i;const s=new Promise(t=>{i=t}),a=void 0!==o.version?o.version:1,c=void 0!==o.debounce?o.debounce:100;r(o.adapter,o.key,n.getState(),a,o.migrate).then(t=>{n.setState(t),i()}).catch(t=>{console.warn(`[storve] withPersist hydrate error for key "${o.key}":`,t),i()});const u=function(t,e){let r;return function(...n){0!==e?(void 0!==r&&clearTimeout(r),r=setTimeout(()=>{t(...n)},e)):t(...n)}}(t=>{const e=o.adapter.setItem(o.key,t);e&&"function"==typeof e.catch&&e.catch(t=>{console.warn(`[storve] Failed to persist state for key "${o.key}":`,t)})},c);let f=e({...o.pick&&o.pick.length>0?t(n.getState(),o.pick):{...n.getState()},__version:a});return n.subscribe(r=>{const n=e({...o.pick&&o.pick.length>0?t(r,o.pick):{...r},__version:a});n!==f&&(f=n,u(n))}),{...n,hydrated:s}}function i(t,e){if(void 0!==e&&!n(t))return o(t,e);if(n(t))return e=>o(e,t);throw new Error("[storve] Invalid withPersist arguments")}export{i as withPersist};
2
- //# sourceMappingURL=persist.mjs.map
package/dist/proxy.d.ts CHANGED
@@ -1,2 +1 @@
1
1
  export declare function createStateProxy<T extends object>(state: T, onChange: () => void): T;
2
- //# sourceMappingURL=proxy.d.ts.map
@@ -1,2 +1 @@
1
1
  "use strict";const e=[];exports.getExtensions=function(){return e},exports.registerExtension=function(t){e.some(e=>e.key===t.key)||(e.push(t),e.sort((e,t)=>(e.order??99)-(t.order??99)))};
2
- //# sourceMappingURL=registry-qtr1UpFU.js.map
@@ -1,2 +1 @@
1
1
  const r=[];function o(o){r.some(r=>r.key===o.key)||(r.push(o),r.sort((r,o)=>(r.order??99)-(o.order??99)))}function e(){return r}export{e as g,o as r};
2
- //# sourceMappingURL=registry-zaKZ1P-s.js.map
@@ -51,4 +51,3 @@ export declare function getExtensions(): readonly StoreExtension[];
51
51
  * @internal
52
52
  */
53
53
  export declare function __testingOnlyClearExtensions(): void;
54
- //# sourceMappingURL=registry.d.ts.map
@@ -16,4 +16,3 @@ export declare function signal<D extends object, K extends keyof StoreState<D>>(
16
16
  * const doubleSignal = signal(store, 'count', v => v * 2);
17
17
  */
18
18
  export declare function signal<D extends object, K extends keyof StoreState<D>, R>(store: Store<D>, key: K, transform: (value: StoreState<D>[K]) => R): Signal<R>;
19
- //# sourceMappingURL=createSignal.d.ts.map
@@ -17,4 +17,3 @@ export interface Signal<T> {
17
17
  }
18
18
  export { signal } from './createSignal';
19
19
  export { useSignal } from './useSignal';
20
- //# sourceMappingURL=index.d.ts.map
@@ -8,4 +8,3 @@ import type { Signal } from './index';
8
8
  * const count = useSignal(countSignal) // re-renders only when count changes
9
9
  */
10
10
  export declare function useSignal<T>(signal: Signal<T>): T;
11
- //# sourceMappingURL=useSignal.d.ts.map
package/dist/signals.cjs CHANGED
@@ -1,2 +1 @@
1
1
  "use strict";var e=require("react");exports.signal=function(e,t,r){const n=!!r;return new Proxy({get:()=>{const n=e.getState()[t];return r?r(n):n},set(r){if(n)throw new Error("Storve: cannot call set() on a derived signal. Derived signals are read-only.");const s="function"==typeof r?r(e.getState()[t]):r;e.setState({[t]:s})},subscribe(n){let s=r?r(e.getState()[t]):e.getState()[t];return e.subscribe(()=>{const a=r?r(e.getState()[t]):e.getState()[t];Object.is(s,a)||(s=a,n(a))})},_derived:n},{set:(e,t,r)=>"_derived"===t||Reflect.set(e,t,r)})},exports.useSignal=function(t){return e.useSyncExternalStore(e=>t.subscribe(e),()=>t.get(),()=>t.get())};
2
- //# sourceMappingURL=signals.cjs.map
package/dist/signals.mjs CHANGED
@@ -1,2 +1 @@
1
1
  import{useSyncExternalStore as t}from"react";function e(t,e,r){const n=!!r;return new Proxy({get:()=>{const n=t.getState()[e];return r?r(n):n},set(r){if(n)throw new Error("Storve: cannot call set() on a derived signal. Derived signals are read-only.");const s="function"==typeof r?r(t.getState()[e]):r;t.setState({[e]:s})},subscribe(n){let s=r?r(t.getState()[e]):t.getState()[e];return t.subscribe(()=>{const o=r?r(t.getState()[e]):t.getState()[e];Object.is(s,o)||(s=o,n(o))})},_derived:n},{set:(t,e,r)=>"_derived"===e||Reflect.set(t,e,r)})}function r(e){return t(t=>e.subscribe(t),()=>e.get(),()=>e.get())}export{e as signal,r as useSignal};
2
- //# sourceMappingURL=signals.mjs.map
package/dist/store.d.ts CHANGED
@@ -9,4 +9,3 @@ import { Store, StoreDefinition, StoreOptions } from './types';
9
9
  * @returns A generic store instance with getState, setState, subscribe, batch, and actions.
10
10
  */
11
11
  export declare function createStore<D extends object>(definition: StoreDefinition<D>, options?: StoreOptions): Store<D>;
12
- //# sourceMappingURL=store.d.ts.map
@@ -4,4 +4,3 @@
4
4
  * @internal
5
5
  */
6
6
  export declare function openChannel(name: string): BroadcastChannel | null;
7
- //# sourceMappingURL=channel.d.ts.map
@@ -1,3 +1,2 @@
1
1
  export { withSync } from './withSync';
2
2
  export type { SyncOptions } from './withSync';
3
- //# sourceMappingURL=index.d.ts.map
@@ -19,4 +19,3 @@ export type SyncMessage<S> = {
19
19
  targetTabId: string;
20
20
  tabId: string;
21
21
  };
22
- //# sourceMappingURL=protocol.d.ts.map
@@ -14,4 +14,3 @@ export interface SyncOptions {
14
14
  * Updates to the store will be broadcast to other tabs via BroadcastChannel.
15
15
  */
16
16
  export declare function withSync<D extends object>(definition: D, options: SyncOptions): D;
17
- //# sourceMappingURL=withSync.d.ts.map
package/dist/sync.cjs CHANGED
@@ -1,2 +1 @@
1
1
  "use strict";var t=require("./registry-qtr1UpFU.js");const e="undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():Math.random().toString(36).substring(2,11),n=Symbol("storve_sync_options");t.registerExtension({key:"sync",extendStore:t=>{const{store:o,definition:a}=t,s=a[n];if(!s||!1===s.enabled)return{};const r=function(t){if("undefined"==typeof window)return null;if("undefined"==typeof BroadcastChannel)return null;try{return new BroadcastChannel(t)}catch{return null}}(s.channel);if(!r)return{};let c=!1,y=!1;const i=o.setState.bind(o);return o.setState=t=>{const n={...o.getState()};if(i(t),!c){const t=((t,e)=>{const n=s.keys||Object.keys(t),o={};let a=!1;for(const s of n)"symbol"!=typeof s&&t[s]!==e[s]&&(o[s]=t[s],a=!0);return a?o:null})(o.getState(),n);t&&r.postMessage({type:"STATE_UPDATE",payload:t,tabId:e})}},r.onmessage=t=>{const n=t.data;if(n.tabId!==e)switch(n.type){case"STATE_UPDATE":c=!0,o.setState(n.payload),c=!1;break;case"REQUEST_STATE":{const t=o.getState(),a={},c=s.keys||Object.keys(t);for(const e of c)"symbol"!=typeof e&&e in t&&(a[e]=t[e]);r.postMessage({type:"PROVIDE_STATE",payload:a,targetTabId:n.tabId,tabId:e});break}case"PROVIDE_STATE":n.targetTabId!==e||y||(y=!0,c=!0,o.setState(n.payload),c=!1)}},r.postMessage({type:"REQUEST_STATE",tabId:e}),{__sync_channel:r}}}),exports.withSync=function(t,e){return Object.defineProperty(t,n,{value:e,enumerable:!1,configurable:!0}),t};
2
- //# sourceMappingURL=sync.cjs.map
package/dist/sync.mjs CHANGED
@@ -1,2 +1 @@
1
1
  import{r as t}from"./registry-zaKZ1P-s.js";const e="undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():Math.random().toString(36).substring(2,11),n=Symbol("storve_sync_options");function o(t,e){return Object.defineProperty(t,n,{value:e,enumerable:!1,configurable:!0}),t}t({key:"sync",extendStore:t=>{const{store:o,definition:a}=t,s=a[n];if(!s||!1===s.enabled)return{};const r=function(t){if("undefined"==typeof window)return null;if("undefined"==typeof BroadcastChannel)return null;try{return new BroadcastChannel(t)}catch{return null}}(s.channel);if(!r)return{};let c=!1,y=!1;const d=o.setState.bind(o);return o.setState=t=>{const n={...o.getState()};if(d(t),!c){const t=((t,e)=>{const n=s.keys||Object.keys(t),o={};let a=!1;for(const s of n)"symbol"!=typeof s&&t[s]!==e[s]&&(o[s]=t[s],a=!0);return a?o:null})(o.getState(),n);t&&r.postMessage({type:"STATE_UPDATE",payload:t,tabId:e})}},r.onmessage=t=>{const n=t.data;if(n.tabId!==e)switch(n.type){case"STATE_UPDATE":c=!0,o.setState(n.payload),c=!1;break;case"REQUEST_STATE":{const t=o.getState(),a={},c=s.keys||Object.keys(t);for(const e of c)"symbol"!=typeof e&&e in t&&(a[e]=t[e]);r.postMessage({type:"PROVIDE_STATE",payload:a,targetTabId:n.tabId,tabId:e});break}case"PROVIDE_STATE":n.targetTabId!==e||y||(y=!0,c=!0,o.setState(n.payload),c=!1)}},r.postMessage({type:"REQUEST_STATE",tabId:e}),{__sync_channel:r}}});export{o as withSync};
2
- //# sourceMappingURL=sync.mjs.map
package/dist/types.d.ts CHANGED
@@ -131,4 +131,3 @@ export type Store<D extends object> = {
131
131
  */
132
132
  getAsyncState: (key: keyof StoreState<D>) => AsyncState<unknown> | undefined;
133
133
  } & StoreActions<D>;
134
- //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -4,7 +4,12 @@
4
4
  "type": "git",
5
5
  "url": "https://github.com/Nam1001/React-Flux.git"
6
6
  },
7
- "version": "1.0.2",
7
+ "version": "1.0.3",
8
+ "files": [
9
+ "dist",
10
+ "README.md",
11
+ "LICENSE"
12
+ ],
8
13
  "main": "dist/index.cjs",
9
14
  "module": "dist/index.mjs",
10
15
  "types": "dist/index.d.ts",
@@ -74,7 +79,8 @@
74
79
  "bench:week5": "npx tsx benchmarks/week5.ts",
75
80
  "lint": "eslint src --ext .ts",
76
81
  "build": "rollup -c",
77
- "build:analyze": "rollup -c --environment ANALYZE:true"
82
+ "build:analyze": "rollup -c --environment ANALYZE:true",
83
+ "prepack": "rm -f dist/index.cjs.js dist/index.cjs.js.map dist/index.esm.js dist/index.esm.js.map dist/stats.html"
78
84
  },
79
85
  "devDependencies": {
80
86
  "@rollup/plugin-terser": "^0.4.4",
@@ -92,4 +98,4 @@
92
98
  "immer": ">=10.0.0",
93
99
  "react": ">=18.0.0"
94
100
  }
95
- }
101
+ }
package/CHANGELOG.md DELETED
@@ -1,151 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project will be documented in this file.
4
-
5
- ## [1.0.0] - 2026-03-14
6
-
7
- ### Added
8
- - Getting Started in 5 minutes section in README
9
- - Migration guides — Redux → Storve, Zustand → Storve
10
- - StackBlitz interactive demo (Counter, Todo, Async)
11
- - Bundle size and test count badges
12
-
13
- ### Changed
14
- - Roadmap updated to reflect shipped features and planned v1.1–v1.3
15
- - Test count updated to 937 across 29 test files
16
- - Coverage badge updated to 99%
17
-
18
- ### Notes
19
- - Stable API — no breaking changes planned until v2.0
20
- - All subpath imports locked: storve, storve/async, storve/computed,
21
- storve/persist, storve/signals, storve/devtools, storve/sync
22
-
23
- ---
24
-
25
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
26
-
27
- ---
28
-
29
- ## [0.7.0] - 2026-03-14
30
-
31
- ### Added — `storve/sync`
32
- - `withSync` — cross-tab state synchronization using `BroadcastChannel`.
33
- - Automatic state rehydration across tabs on initialization.
34
- - Selective key synchronization via `SyncOptions`.
35
- - Tree-shakable subpath import: `storve/sync`.
36
- - Full integration with existing extensions (DevTools, Persist).
37
- - 100% test coverage for sync logic.
38
-
39
- ## [0.6.0] - 2026-03-14
40
-
41
- ### Added
42
- - `storve/devtools` entry point for time-travel debugging.
43
- - `withDevtools` store enhancer for ring-buffer history and Redux DevTools integration.
44
- - `undo()` and `redo()` API on stores.
45
- - `canUndo` and `canRedo` flags.
46
- - `snapshot()` and `restore()` for named state checkpoints.
47
- - `useDevtools` React hook for reactive access to devtools state.
48
- - Redux DevTools extension support (JUMP_TO_STATE, RESET).
49
-
50
- ### Fixed
51
- - Internal: extensions now receive the store instance and original definition.
52
- - Internal: added `Object.defineProperties` support for extension method merging.
53
- - Internal: triggered initial state change notification for early extension initialization.
54
-
55
- ## [0.5.0] - 2026-03-14
56
-
57
- ### Added — `storve/signals`
58
- - `signal(store, key)` — fine-grained reactivity for individual store keys
59
- - `signal(store, key, transform)` — read-only derived signals with automatic dependency filtering
60
- - `useSignal(signal)` — React hook for zero-overhead subscription to specific state slices
61
- - `Object.is` value filtering — zero re-renders unless the specific signal value actually changes
62
- - 100% test coverage for signals package
63
- - Tree-shakable subpath import: `storve/signals`
64
- - Bundle size: < 0.6KB for signals entry point
65
-
66
- ## [0.4.0] - 2026-03-12
67
-
68
- ### Added — `storve/persist`
69
- - `withPersist` — persist store state with pluggable adapters
70
- - `localStorageAdapter`, `sessionStorageAdapter`, `memoryAdapter`, `indexedDBAdapter`.
71
- - `compose()` — compose multiple store enhancers cleanly.
72
- - Version + migration support for schema changes.
73
- - Configurable debounce on writes.
74
- - SSR safe — all adapters guard against missing window/indexedDB.
75
-
76
- ### Added — `storve/async`
77
- - `createAsync(fetcher, options)` — core engine for async state management.
78
- - Lifecycle management — automatic tracking of `idle`, `loading`, `success`, and `error` states.
79
- - Advanced caching — TTL (Time-To-Live) and SWR (Stale-While-Revalidate) support.
80
- - Optimistic updates — immediate UI feedback with automatic rollback on failure.
81
- - Argument-aware fetching — separate cache entries based on fetcher arguments.
82
-
83
- ### Changed — BREAKING
84
- - **Barrel import removed.** Use subpath imports:
85
- - `import { createStore, batch } from 'storve'` — core only (~1.4KB gzipped)
86
- - `import { createAsync } from 'storve/async'` — async support (~1.1KB gzipped)
87
- - `import { computed } from 'storve/computed'` — computed support (~0.8KB gzipped)
88
- - Store without async extension: `fetch()` throws with message to import `storve/async`
89
-
90
- ### Fixed
91
- - Bundle sizes: core 1.4KB, async 1.1KB, computed 0.8KB (gzipped, with Terser).
92
- - ESLint: Replaced `@ts-ignore` with `@ts-expect-error` across benchmarks.
93
-
94
- ---
95
-
96
- ## [0.3.0] - 2026-03-01
97
- ### Added — `storve-core`
98
- - `actions` support inside `createStore()` — define methods inline, auto-bound, excluded from state
99
- - Async action support — actions can be `async` and notify subscribers on completion
100
- - Immer integration — `setState(draft => { ... })` mutation style via `{ immer: true }` store option
101
- - `store.batch(fn)` — multiple `setState()` calls inside a batch fire exactly one subscriber notification
102
- - Nested batch support via internal `batchCount` counter
103
- - `batchDirty` flag — empty batch fires zero notifications
104
- - `store.actions` stable reference — same object across renders, safe to spread in hooks
105
- - Added types: `StoreState<D>`, `StoreActions<D>`, `StoreOptions`
106
- - Updated `Store<D>` interface — `getState()` returns state only, actions excluded from state type
107
- - Added `immer` to `peerDependencies` — not bundled, tree-shaken when unused
108
- - 85+ new tests across `actions.test.ts`, `immer.test.ts`, `batch.test.ts`
109
- - Week 4 performance benchmarks: action calls, Immer mutations, batch notification overhead
110
-
111
- ### Fixed — `storve-react`
112
- - `useStore` regression — hook was merging actions into result even when a selector was provided, causing React to throw "Objects are not valid as a React child" for primitive selector returns
113
- - Moved selector logic into `getSnapshot` so React correctly detects value changes
114
- - Selector path now returns selected value directly — no action merging
115
- - No-selector path merges actions at return time only, after `useSyncExternalStore`
116
- - Shallow copy in no-selector snapshot fixes Proxy same-reference issue
117
- - `shallowEqual` on object selector results prevents unnecessary re-renders
118
- - Store reference change correctly invalidates snapshot cache
119
- - Confirmed zero hook rule violations — no conditional hook calls
120
-
121
- ### Fixed — `storve-react` (ESLint)
122
- - Removed unused `useState` import in `lifecycle.test.tsx`
123
- - Replaced `any` with typed alternatives across `selector.test.tsx` and `useStore.test.tsx`
124
- - Fixed `no-unused-vars` in `benchmarks/week3.ts` — replaced `const _ = ...` with `void`
125
-
126
- ---
127
-
128
- ## [0.2.0] - 2026-02-28
129
- ### Added — `storve-react`
130
- - `useStore()` hook using `useSyncExternalStore` (React 18)
131
- - Selector support as optional second argument to `useStore()`
132
- - Concurrent mode safety — verified with React 18 concurrent test suite
133
- - Auto-cleanup of subscriptions on component unmount
134
- - Integration tests with React Testing Library
135
- - Performance benchmarks for subscription setup, cleanup, selector execution
136
- - 55 tests passing across 6 test files
137
-
138
- ---
139
-
140
- ## [0.1.0] - 2026-02-28
141
- ### Added — `storve-core`
142
- - Initial `createStore()` implementation
143
- - Proxy-based auto-tracking for state mutations with eager wrapping for deep paths
144
- - Proper interception of arrays (`push`, `pop`, `splice`, index sets)
145
- - Store subscription mechanism via `subscribe()`
146
- - `getState()` and `setState()` — plain object and updater function forms
147
- - Added types: `StoreDefinition`, `Listener`, `Unsubscribe`, `Store`
148
- - Full test coverage for core store (100% functions, 95%+ statements)
149
- - Week 3 performance benchmarks and limits verification
150
- (100% functions, 95%+ statements)
151
- - Week 3 performance benchmarks and limits verification