stroid 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,11 +2,51 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## 0.0.1
6
- - Initial release: tsup-minified ESM bundles with subpath outputs; shared chunk noted; CJS omitted.
7
- - Packaging: `sideEffects: false`, subpath exports (`./react`, `./async`, `./testing`), React peer dependency `>=18`.
8
- - DX: `useStore` selector overload with dev warning on broad subscriptions; hooks split into core/async/form modules; usage docs with quick start, gotchas, limitations.
9
- - Async: focus/online revalidation helper; dev warning when no AbortSignal is provided.
10
- - Safety: path/type guard warnings dev-only; persist key collision warning; circular-friendly `produceClone` error message; Map/Set/Date warnings.
11
- - Utils: CRC table lazy init.
12
- - Docs: testing subpath guidance, LWW clock-skew note, semver policy.
5
+ ## Unreleased
6
+
7
+ ## 0.0.4 - 2026-03-06
8
+ ### Added
9
+ - Persistence recovery hooks: `persist.onMigrationFail` and `persist.onStorageCleared`.
10
+ - Sync hardening options and behaviors: `sync.maxPayloadBytes` plus snapshot requests for reconnecting tabs.
11
+ ### Changed
12
+ - Docs: converted the repo docs into the chapter-based handbook, then aligned README and API chapters with the real package surface so examples now use `setStore(name, path, value)`, `getStore(name, path)`, `useStore(name, path)`, `subscribeWithSelector(name, selector, equalityFn, listener)`, and `useFormStore(name, field)`.
13
+ - Packaging: rebuilt `dist` from the current `src`, and fixed the `stroid/react` build entry so the published subpath exports `useAsyncStore` and `useFormStore` consistently with the root package and docs.
14
+ ### Fixed
15
+ - `hydrateStores` now rejects invalid schema payloads without leaving broken store shells behind.
16
+ - Middleware throws no longer poison later notifications.
17
+ - Critical persistence failures in production now surface through `onError`.
18
+ - Async lifecycle cleanup is hardened: inflight metadata clears on store deletion, lifecycle-owned requests abort on delete, and internal cleanup registrations are removed correctly.
19
+ - Persisted version/schema mismatches can recover through `onMigrationFail`, and cleared storage keys can be detected through `onStorageCleared`.
20
+ - Sync now reports unavailable `BroadcastChannel` transport, rejects oversized payloads safely, orders conflicts with monotonic clocks, and requests fresh snapshots on reconnect/focus/online.
21
+
22
+ ## 0.0.3 - 2026-03-04
23
+ ### Fixed
24
+ - Persistence now catches `localStorage.setItem` / driver `setItem` errors (e.g., `QuotaExceededError`) and routes them to `onError` instead of letting them bubble and crash. State updates still apply while persistence failures surface to the app.
25
+ - Async fetch metadata (inflight, cache, registry) now cleans up when a store is deleted, avoiding stale entries and refetch surprises.
26
+ - `enableRevalidateOnFocus` now removes its focus/online listeners when the store is deleted, preventing event-listener leaks.
27
+ - React `useSelector` now memoizes selected values with shallow equality, preventing endless re-renders when selectors return new array/object references for unchanged data.
28
+ - Store schemas are now enforced on write (`setStore`/`mergeStore`), blocking invalid shapes at runtime instead of silently accepting them.
29
+ - `createStore` no longer overwrites an existing store name; it warns and keeps the original state.
30
+ - `setStore` path updates now respect existing structure: array paths no longer auto-create missing indices and array shapes are preserved instead of converting to objects.
31
+ - Docs: README updated with the pre-v1 bundle-size promise so the published package README matches the repo.
32
+
33
+ ## 0.0.2 - 2026-03-03
34
+ ### Added
35
+ - SSR helpers: `createStoreForRequest` and `hydrateStores` for request-scoped stores and snapshot hydration.
36
+ - Store helpers: `createEntityStore`, `createListStore`, and `createCounterStore` for common patterns.
37
+ - Observability: `getHistory`/`clearHistory` (with `historyLimit`) and `getMetrics` for notify timing.
38
+ - Sync tuning: optional `channel` and `conflictResolver`; warnings when BroadcastChannel is unavailable.
39
+ - Branding & DX: new logo/favicons, Next.js docs site with theme switcher, compact prev/next pager, robots.txt + sitemap + OG/Twitter metadata for SEO.
40
+ ### Changed
41
+ - Docs now reflect actual APIs (persist driver/serialize/encrypt; top-level version/migrations; DevTools boolean + historyLimit).
42
+ ### Fixed
43
+ - Hydration safety around theme toggles to prevent client/server mismatch warnings.
44
+
45
+ ## 0.0.1
46
+ - Initial release: tsup-minified ESM bundles with subpath outputs; shared chunk noted; CJS omitted.
47
+ - Packaging: `sideEffects: false`, subpath exports (`./react`, `./async`, `./testing`), React peer dependency `>=18`.
48
+ - DX: `useStore` selector overload with dev warning on broad subscriptions; hooks split into core/async/form modules; usage docs with quick start, gotchas, limitations.
49
+ - Async: focus/online revalidation helper; dev warning when no AbortSignal is provided.
50
+ - Safety: path/type guard warnings dev-only; persist key collision warning; circular-friendly `produceClone` error message; Map/Set/Date warnings.
51
+ - Utils: CRC table lazy init.
52
+ - Docs: testing subpath guidance, LWW clock-skew note, semver policy.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Himesh Bhattarai
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.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Himeshchanchal Bhattarai
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
@@ -1,46 +1,82 @@
1
- # stroid
1
+ # Stroid
2
2
 
3
- Compact state management for JavaScript/React with batteries included: mutable-friendly updates, selectors, persistence, async caching, sync, and drop-in presets. ESM-only; import subpaths like `stroid/core`, `stroid/react`, `stroid/async`, `stroid/testing` as needed. For non-React/Node usage, prefer `stroid/core`.
3
+ Compact, batteries-included state management for JavaScript and React.
4
4
 
5
- ## Quick start
5
+ Stroid keeps the API small: create a store, update it, read it. Persistence, async caching, cross-tab sync, middleware, schema validation, history, and React hooks are configured per store instead of bolted on later.
6
+
7
+ This package is ESM-only, tree-shakeable, side-effect free, and ships with zero runtime dependencies.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install stroid
13
+ ```
14
+
15
+ ## Quick Example
6
16
 
7
17
  ```js
8
- import { createStore, setStore, useStore } from "stroid";
18
+ import { createStore, setStore } from "stroid/core"
19
+ import { useStore } from "stroid/react"
9
20
 
10
- createStore("user", { name: "Alex", theme: "dark" }, { devtools: true, persist: true });
11
- setStore("user", (draft) => { draft.name = "Jordan"; });
21
+ createStore("user", { name: "Eli", theme: "dark" })
12
22
 
13
23
  function Profile() {
14
- const name = useStore("user", "name");
15
- return <div>{name}</div>;
24
+ const name = useStore("user", "name")
25
+ return <h1>Hello, {name}</h1>
16
26
  }
27
+
28
+ setStore("user", "theme", "light")
17
29
  ```
18
30
 
19
- Install: `npm install stroid`
31
+ The core path API uses `storeName` and `path` separately:
32
+
33
+ - `setStore("user", "name", "Jo")`
34
+ - `getStore("user", "name")`
35
+ - `useStore("user", "name")`
36
+
37
+ ## What Ships
38
+
39
+ - Core store primitives: `createStore`, `setStore`, `getStore`, `mergeStore`, `resetStore`
40
+ - React hooks: `useStore`, `useSelector`, `useStoreStatic`, `useAsyncStore`, `useFormStore`
41
+ - Async helpers: `fetchStore`, `refetchStore`, `enableRevalidateOnFocus`
42
+ - Per-store features: `persist`, `sync`, middleware, validator/schema, devtools, history
43
+ - Utility helpers: selectors, metrics, testing helpers, entity/list/counter presets, SSR hydrate helpers
20
44
 
21
45
  ## Highlights
22
- - Mutator-friendly updates and batched notifications.
23
- - Selectors (`createSelector`, `useSelector`) and presets (counter/list/entity).
24
- - Persistence adapters with checksum + migrations; sync via BroadcastChannel.
25
- - Async helper with SWR, TTL, dedupe, retries, abort, focus/online revalidate; metrics.
26
- - React hooks (`useStore`, `useStoreField`, `useSelector`, `useAsyncStore`, `useFormStore`, `useStoreStatic`); `useStore` warns in dev when subscribing to the whole store.
27
- - DevTools bridge (Redux DevTools), middleware hooks, schema validation.
28
- - Subpath imports share a common internal chunk today; true per-feature isolation is planned for v1.1.
29
-
30
- ## Testing
31
- Import testing helpers without bundling them into apps:
32
- ```js
33
- import { createMockStore, resetAllStoresForTest } from "stroid/testing";
34
- ```
35
46
 
36
- ## More docs
37
- - SSR/RSC patterns, sync conflict resolution, and the full demo: `docs/DETAILS.md`.
38
- - LWW sync uses `Date.now()`; significant clock skew between tabs/devices can reorder updates.
47
+ - Dot-path reads and writes with path validation
48
+ - Draft-style mutator updates
49
+ - Persistence with custom drivers, migrations, and recovery hooks
50
+ - Async caching with TTL, dedupe, retries, and focus/online revalidation
51
+ - BroadcastChannel sync with conflict resolution and payload size guardrails
52
+ - No Provider required for React usage
53
+
54
+ ## Docs
55
+
56
+ - [The Book](./docs/README.md)
57
+ - [Getting Started](./docs/02-getting-started.md)
58
+ - [Core API](./docs/04-createStore.md)
59
+ - [React](./docs/12-react.md)
60
+ - [Async](./docs/13-async.md)
61
+ - [Persistence](./docs/14-persist.md)
62
+ - [Sync](./docs/15-sync.md)
63
+ - [Testing](./docs/20-testing.md)
64
+ - [Roadmap](./docs/24-roadmap.md)
65
+
66
+ ## Package Entry Points
67
+
68
+ - `stroid` exports the full public API
69
+ - `stroid/core` exports the core store APIs
70
+ - `stroid/react` exports the React hooks
71
+ - `stroid/async` exports async helpers
72
+ - `stroid/testing` exports test helpers
73
+
74
+ ## Notes
75
+
76
+ - React is a peer dependency (`>=18`)
77
+ - Node `>=18` is required
78
+ - Planned or not-yet-implemented ideas belong in the roadmap, not the API docs
39
79
 
40
- ## Roadmap (packaging)
41
- - Phase 1 (done): sideEffects flag, testing subpath, dev-only verbose warnings, lazy CRC init.
42
- - Phase 2 (done): hooks split, subpath exports, focus/online revalidate helper, useStore selector overload.
43
- - Phase 3 (planned): modularize persistence/history/devtools/sync into opt-in chunks.
80
+ ## License
44
81
 
45
- ## Versioning / Semver
46
- Follows semver. Breaking changes bump MAJOR; features MINOR; fixes PATCH. See CHANGELOG.md.
82
+ MIT
package/dist/async.js CHANGED
@@ -1 +1 @@
1
- export{c as enableRevalidateOnFocus,a as fetchStore,d as getAsyncMetrics,b as refetchStore}from'./chunk-TB5U3OFY.js';import'./chunk-6V676XCZ.js';
1
+ export{c as enableRevalidateOnFocus,a as fetchStore,d as getAsyncMetrics,b as refetchStore}from'./chunk-G6JMMJYH.js';import'./chunk-5F2FD6DX.js';
@@ -0,0 +1,17 @@
1
+ var Mt=typeof process<"u"&&typeof process.env?.NODE_ENV=="string"?process.env.NODE_ENV:void 0,zt=typeof import.meta<"u"&&import.meta?.env?.MODE?import.meta.env.MODE:void 0,bt=typeof globalThis<"u"&&typeof globalThis.__STROID_DEV__=="boolean"?globalThis.__STROID_DEV__:void 0,Ft=typeof process<"u"?"development":"production",qt=Mt??zt??Ft,H=typeof bt=="boolean"?bt:qt!=="production",w=()=>H,p=H?t=>{console.warn(`[stroid] ${t}`);}:()=>{},R=H?t=>{console.error(`[stroid] ${t}`);}:()=>{},j=H?t=>{console.log(`[stroid] ${t}`);}:()=>{},st=null,Lt=()=>{if(st)return st;let t,e=[];for(let n=0;n<256;n++){t=n;for(let r=0;r<8;r++)t=t&1?3988292384^t>>>1:t>>>1;e[n]=t>>>0;}return st=e,e},vt=t=>{let e=Lt(),n=-1;for(let r=0;r<t.length;r++)n=n>>>0,n=n>>>8^e[(n^t.charCodeAt(r))&255];return (n^-1)>>>0},G=t=>{try{return vt(JSON.stringify(t))}catch{return vt(String(t))}},Ut=typeof globalThis<"u"&&typeof globalThis.structuredClone=="function",h=t=>{try{return Ut?structuredClone(t):JSON.parse(JSON.stringify(t))}catch{return Array.isArray(t)?[...t]:t&&typeof t=="object"?{...t}:t}};var it=(t,e)=>{try{let n=h(t);return e(n),n}catch(n){throw new Error(`produceClone failed (possible circular reference or unserializable data): ${n?.message??n}`)}},at=(t,e)=>{if(!t)return {ok:true};try{if(typeof t.safeParse=="function"){let n=t.safeParse(e);return n.success?{ok:!0,data:n.data}:{ok:!1,error:n.error}}if(typeof t.parse=="function")return t.parse(e),{ok:!0,data:e};if(typeof t.validateSync=="function")return t.validateSync(e),{ok:!0,data:e};if(typeof t.isValidSync=="function")return t.isValidSync(e)?{ok:!0,data:e}:{ok:!1,error:"Schema validation failed"};if(typeof t.validate=="function")return t.validate(e)?{ok:!0,data:e}:{ok:!1,error:t.errors||"Schema validation failed"};if(typeof t=="function"){let n=t(e);return n===!1?{ok:!1,error:"Schema validation failed"}:{ok:!0,data:n===!0?e:n}}return {ok:!0,data:e}}catch(n){return {ok:false,error:n?.message??n}}};var C=t=>t===null?"null":Array.isArray(t)?"array":t instanceof Map?"map":t instanceof Set?"set":t instanceof Date?"date":typeof t=="function"?"function":typeof t,F=t=>{let e=C(t);return e==="function"?(R(`Functions cannot be stored in stroid.
2
+ Store data only - handle functions outside the store.`),false):e==="map"||e==="set"?(p(`Map/Set detected. stroid converts these to plain objects.
3
+ Use arrays or plain objects for best results.`),true):(e==="date"&&p(`Date object detected. stroid stores it as ISO string.
4
+ Use new Date(value) to convert back when reading.`),true)},O=t=>{let e=C(t);if(e==="date")return w()&&p("Date detected; stored as ISO string. Use new Date(value) when reading."),t.toISOString();if(e==="map")return w()&&p("Map detected; converting to plain object."),Object.fromEntries(t);if(e==="set")return w()&&p("Set detected; converting to array."),Array.from(t);if(e==="object"){let n={};for(let r in t)n[r]=O(t[r]);return n}return e==="array"?t.map(O):t},kt=10,Kt=5,q=t=>Array.isArray(t)?[...t]:typeof t=="string"&&!t.includes(".")?[t]:typeof t=="string"?t.split("."):[String(t)],ct=t=>{let e=q(t),n=e.length;return n>kt?(R(`Path depth of ${n} exceeded maximum of ${kt}.
5
+ "${e.join(".")}"
6
+ This is a data design issue. Split into separate stores:
7
+ createStore("${e[0]}", ...) and createStore("${e[1]}", ...)`),false):(n>Kt&&p(`Deep nesting detected (${n} levels): "${e.join(".")}"
8
+ Consider splitting into separate stores for better readability.`),true)},wt=(t,e)=>{let n=q(e),r=t;for(let o of n){if(r==null){p(`Path "${n.join(".")}" not found - reached null at "${o}"`);return}if(typeof r!="object"){p(`Cannot go deeper at "${o}" - value is not an object`);return}r=r[o];}return r},xt=(t,e,n)=>{let r=q(e);if(r.length===0)return t;let o=(s,a)=>{let l=r[a],f=a===r.length-1;if(Array.isArray(s)){let i=Number(l);if(!Number.isInteger(i))return s;let d=[...s];return f?(d[i]=n,d):(d[i]=o(d[i],a+1),d)}if(s&&typeof s=="object"){let i={...s};return f?(i[l]=n,i):(i[l]=o(i[l],a+1),i)}return s};return o(t,0)},$t=t=>typeof t!="string"||t.trim()===""?(R(`Store name must be a non-empty string. Got: ${JSON.stringify(t)}`),false):t.includes(" ")?(R(`Store name "${t}" contains spaces.
9
+ Use camelCase or kebab-case: "userName" or "user-name"`),false):true,Tt=(t,e)=>{let n=e.find(r=>{let o=r.toLowerCase(),s=t.toLowerCase();return o.includes(s)||s.includes(o)||Bt(o,s)<=2});n?p(`Store "${t}" not found. Did you mean "${n}"?`):R(`Store "${t}" not found.
10
+ Available stores: [${e.join(", ")}]
11
+ Call createStore("${t}", data) first.`);},Bt=(t,e)=>{let n=Array.from({length:e.length+1},(r,o)=>Array.from({length:t.length+1},(s,a)=>o===0?a:a===0?o:0));for(let r=1;r<=e.length;r++)for(let o=1;o<=t.length;o++)n[r][o]=e[r-1]===t[o-1]?n[r-1][o-1]:Math.min(n[r-1][o-1],n[r][o-1],n[r-1][o])+1;return n[e.length][t.length]};var L=t=>{if(typeof t!="object"||t===null)return t;Object.freeze(t);for(let e of Object.keys(t)){let n=t[e];typeof n=="object"&&n!==null&&!Object.isFrozen(n)&&L(n);}return t};var u=Object.create(null),_=Object.create(null),D=Object.create(null),c=Object.create(null),x=Object.create(null),B=Object.create(null),V=Object.create(null),Y=Object.create(null),Z=new Set,lt=false,U=0,et=`stroid_${Math.random().toString(16).slice(2)}`,ut={},Q=Object.create(null),N=Object.create(null),_t=false,Rt=t=>typeof t=="string"?t:t.name,K,X=(()=>{let t=new Map;return {getItem:e=>t.has(e)?t.get(e):null,setItem:(e,n)=>{t.set(e,n);},removeItem:e=>{t.delete(e);},type:"memory"}})(),Ot=()=>{if(lt)return;lt=true;let t=()=>{lt=false,Z.forEach(e=>{let n=_[e];if(!n||n.length===0)return;let r=typeof performance<"u"&&performance.now?performance.now():Date.now(),o=h(u[e]);n.forEach(f=>{try{f(o);}catch(i){p(`Subscriber for "${e}" threw: ${i?.message??i}`);}});let a=(typeof performance<"u"&&performance.now?performance.now():Date.now())-r,l=c[e]?.metrics||{notifyCount:0,totalNotifyMs:0,lastNotifyMs:0};l.notifyCount+=1,l.totalNotifyMs+=a,l.lastNotifyMs=a,c[e]&&(c[e].metrics=l);}),Z.clear();};typeof queueMicrotask=="function"?queueMicrotask(t):Promise.resolve().then(t);},J=t=>{Z.add(t),U===0&&Ot();},M=t=>u[t]!==void 0?true:(Tt(t,Object.keys(u)),false),Jt=(t,e,n,r)=>{let o=q(n);if(o.length===0)return {ok:true};if(e==null){let a=`Cannot set "${o.join(".")}" on "${t}" because the store value is ${e===null?"null":"undefined"}.`;return p(a),{ok:false,reason:a}}let s=e;for(let a=0;a<o.length;a++){let l=o[a],f=a===o.length-1;if(s==null||typeof s!="object"){let d=`Path "${o.join(".")}" is invalid for "${t}" - "${o.slice(0,a).join(".")||"root"}" is not an object.`;return p(d),{ok:false,reason:d}}if(Array.isArray(s)){let d=Number(l);if(!Number.isInteger(d)||d<0){let g=`Path "${o.join(".")}" targets non-numeric index "${l}" on an array in "${t}".`;return p(g),{ok:false,reason:g}}let y=s;if(d>=y.length){let g=`Path "${o.join(".")}" is invalid for "${t}" - index ${d} is out of bounds (length ${y.length}).`;return p(g),{ok:false,reason:g}}if(f){let g=y[d];if(g!==void 0){let k=C(g),T=C(r);if(k!==T){let P=`Type mismatch setting "${o.join(".")}" on "${t}": expected ${k}, received ${T}.`;return p(P),{ok:false,reason:P}}}return {ok:true}}s=y[d];continue}if(!Object.prototype.hasOwnProperty.call(s,l)){let d=`Path "${o.join(".")}" does not exist on store "${t}" (missing "${l}").`;return p(d),{ok:false,reason:d}}if(f){let d=s[l];if(d!==void 0){let y=C(d),g=C(r);if(y!==g){let k=`Type mismatch setting "${o.join(".")}" on "${t}": expected ${y}, received ${g}.`;return p(k),{ok:false,reason:k}}}return {ok:true}}s=s[l];}return {ok:true}},dt=t=>{try{return typeof window>"u"?X:t==="session"||t==="sessionStorage"?window.sessionStorage??X:window.localStorage??X}catch{return X}},Wt=(t,e)=>{N[t]&&(N[t].lastPresent=e);},Ht=(t,e)=>{if(!t)return null;let n={key:`stroid_${e}`,serialize:JSON.stringify,deserialize:JSON.parse,encrypt:r=>r,decrypt:r=>r,onMigrationFail:"reset"};return t===true?{driver:dt("localStorage"),...n}:typeof t=="string"?{driver:dt(t),...n}:{driver:t.driver||t.storage||dt("localStorage"),key:t.key||n.key,serialize:t.serialize||n.serialize,deserialize:t.deserialize||n.deserialize,encrypt:t.encrypt||n.encrypt,decrypt:t.decrypt||n.decrypt,onMigrationFail:t.onMigrationFail||"reset",onStorageCleared:t.onStorageCleared}},Gt=t=>{let e=c[t]?.options?.persist,n=e?.onStorageCleared;if(!e||typeof n!="function"||typeof window>"u"||typeof window.addEventListener!="function")return;N[t]?.dispose();let r=window,o=()=>{try{return e.driver.getItem?.(e.key)!=null}catch{return false}},s=f=>{let i=N[t],d=o();if(i){if(!i.lastPresent||d){i.lastPresent=d;return}i.lastPresent=false,n({name:t,key:e.key,reason:f});}},a=f=>{if(f.key===null){s("clear");return}f.key===e.key&&f.newValue===null&&s("remove");},l=()=>{s("missing");};r.addEventListener("storage",a),r.addEventListener("focus",l),N[t]={lastPresent:o(),dispose:()=>{r.removeEventListener("storage",a),r.removeEventListener("focus",l);}};},Xt=t=>{if(!c[t]?.options?.devtools||typeof window>"u")return;let n=window.__REDUX_DEVTOOLS_EXTENSION__||window.__REDUX_DEVTOOLS_EXTENSION__;if(!n||typeof n.connect!="function"){p(`DevTools requested for "${t}" but Redux DevTools extension not found.`);return}K||(K=n.connect({name:"stroid"}),K.init(u));},tt=(t,e)=>{let n=c[t]?.options?.redactor;if(typeof n=="function")try{return n(h(e))}catch{return e}return e},Yt=t=>typeof TextEncoder<"u"?new TextEncoder().encode(t).length:typeof Buffer<"u"?Buffer.byteLength(t):t.length,v=(t,e)=>{c[t]?.options?.onError?.(e),p(e);},ft=(t,e,n)=>{v(t,n);let r=c[t]?.options?.persist?.onMigrationFail??"reset";if(r==="keep")return {state:e,requiresValidation:false};if(typeof r=="function")try{let o=r(h(e));if(o!==void 0)return {state:O(o),requiresValidation:!0};v(t,`onMigrationFail for "${t}" returned undefined. Falling back to initial state.`);}catch(o){v(t,`onMigrationFail for "${t}" failed: ${o?.message??o}`);}return {state:h(D[t]),requiresValidation:false}},Zt=(t,e)=>{if(typeof t!="object"||typeof e!="object"||t===null||e===null)return null;let n=t,r=e,o=[],s=[],a=[],l=new Set(Object.keys(n)),f=new Set(Object.keys(r));return f.forEach(i=>{l.has(i)?Object.is(n[i],r[i])||a.push(i):o.push(i);}),l.forEach(i=>{f.has(i)||s.push(i);}),{added:o,removed:s,changed:a}},nt=(t,e,n,r)=>{let o=c[t]?.options?.historyLimit??50;if(o===0)return;x[t]||(x[t]=[]);let s={ts:Date.now(),action:e,prev:tt(t,n),next:tt(t,r),diff:Zt(n,r)};x[t].push(s),x[t].length>o&&x[t].splice(0,x[t].length-o);},rt=(t,e,n=false)=>{if(!(!K||!n&&!c[t]?.options?.devtools))try{let r={...u,[t]:tt(t,u[t])};K.send({type:`${t}/${e}`},r);}catch{}},Vt=(t,e)=>{let n=c[t]?.options?.middleware||[];if(!Array.isArray(n))return e.next;let r=e.next;for(let o of n){if(typeof o!="function")continue;let s;try{s=o({action:e.action,name:t,prev:e.prev,next:r,path:e.path});}catch(a){let l=`Middleware for "${t}" failed: ${a?.message??a}`;c[t]?.options?.onError?.(l),p(l);continue}s!==void 0&&(r=s);}return r},A=(t,e)=>{let n=c[t]?.options?.schema;if(!n)return {ok:true};let r=at(n,e);return r.ok||v(t,`Schema validation failed for "${t}": ${r.error}`),r},pt=t=>{let e=c[t]?.options?.persist;e&&(ut[t]&&clearTimeout(ut[t]),ut[t]=setTimeout(()=>{try{let n=e.serialize(u[t]),r=G(n),o=JSON.stringify({v:c[t]?.version??1,checksum:r,data:n}),s=e.encrypt(o);e.driver.setItem?.(e.key,s),Wt(t,!0);}catch(n){v(t,`Could not persist store "${t}" (${n?.message||n})`);}},0));},Qt=(t,{silent:e}={silent:false})=>{let n=c[t]?.options?.persist;if(n)try{let r=n.driver.getItem?.(n.key)??null;if(!r)return;let o=n.decrypt(r),s=JSON.parse(o),{v:a=1,checksum:l,data:f}=s||{};if(!f)return;if(l!==G(f)){v(t,`Checksum mismatch loading store "${t}". Falling back to initial state.`),u[t]=h(D[t]);return}let i=n.deserialize(f),d=c[t]?.version??1;if(a!==d){let g=c[t]?.options?.migrations||{},k=Object.keys(g).map(S=>Number(S)).filter(S=>S>a&&S<=d).sort((S,m)=>S-m);if(k.length===0){let S=ft(t,i,`No migration path from v${a} to v${d} for "${t}". Applying onMigrationFail strategy.`);if(i=S.state,!S.requiresValidation){u[t]=i;return}}let T=!1,P=!0;if(k.forEach(S=>{if(!T)try{let m=g[S](i);m!==void 0&&(i=m);}catch(m){let z=ft(t,i,`Migration to v${S} failed for "${t}": ${m?.message||m}`);i=z.state,P=z.requiresValidation,T=!0;}}),T){if(!P){u[t]=i;return}if(!A(t,i).ok){u[t]=h(D[t]);return}u[t]=i;return}}if(!A(t,i).ok){if(a!==d){let g=ft(t,i,`Persisted state for "${t}" failed schema after version change. Applying onMigrationFail strategy.`);if(!g.requiresValidation){u[t]=g.state;return}if(A(t,g.state).ok){u[t]=g.state;return}}v(t,`Persisted state for "${t}" failed schema; resetting to initial.`),u[t]=h(D[t]);return}u[t]=i,e||j(`Store "${t}" loaded from persistence`);}catch(r){v(t,`Could not load store "${t}" (${r?.message||r})`);}},W=(t,e,n={})=>{if(!$t(t)||!F(e))return;let r=typeof window>"u",o=typeof process<"u"?process.env?.NODE_ENV:void 0,s=r&&o==="production",a=n.allowSSRGlobalStore??false;if(s&&!a){w()&&R(`createStore("${t}") is blocked on the server in production to prevent cross-request memory leaks.
12
+ Call createStoreForRequest(...) inside each request scope or pass { allowSSRGlobalStore: true } to opt in.`);return}if(u[t]!==void 0){let b=`Store "${t}" already exists. Call setStore("${t}", data) to update instead.`;return p(b),c[t]?.options?.onError?.(b),{name:t}}let{persist:l=false,devtools:f=false,middleware:i=[],onSet:d,onReset:y,onDelete:g,onCreate:k,onError:T,validator:P,schema:S,migrations:m={},version:z=1,redactor:Ct,historyLimit:Nt=50,sync:At,allowSSRGlobalStore:St=a}=n;r&&!St&&!_t&&w()&&(_t=true,p(`createStore("${t}") called in a server environment. Use createStoreForRequest(...) per request to avoid cross-request leaks or pass { allowSSRGlobalStore: true } if you really want a global store on the server.`));let I=Ht(l,t);if(I?.key){let b=Q[I.key];b&&b!==t&&w()?p(`Persist key collision: "${I.key}" already used by store "${b}". Store "${t}" will overwrite the same storage key.`):Q[I.key]=t;}let E=O(e),It={persist:I,devtools:!!f,middleware:i??[],onSet:d,onReset:y,onDelete:g,onCreate:k,onError:T,validator:P,schema:S,migrations:m,version:z,redactor:Ct,historyLimit:Nt,sync:At??false,allowSSRGlobalStore:St},ht=S?at(S,E):{ok:true};if(!ht.ok){let b=`Schema validation failed for "${t}": ${ht.error}`;T?.(b),p(b);return}if(P&&P(E)===false){let b=`Validator blocked initial state for "${t}"`;T?.(b),p(b);return}return u[t]=E,_[t]=_[t]||[],D[t]=h(E),c[t]={createdAt:new Date().toISOString(),updatedAt:new Date().toISOString(),updateCount:0,version:z,metrics:{notifyCount:0,totalNotifyMs:0,lastNotifyMs:0},options:It},I&&(pt(t),Qt(t,{silent:true}),Gt(t)),c[t].options.onCreate?.(E),Xt(t),ne(t),nt(t,"create",null,E),j(`Store "${t}" created -> ${JSON.stringify(E)}`),{name:t}};function $(t,e,n){let r=Rt(t);if(!M(r))return;let o,s=u[r];if(typeof e=="function"&&n===void 0)o=it(s,e);else if(typeof e=="object"&&!Array.isArray(e)&&n===void 0){if(!F(e))return;o={...s,...O(e)};}else if(typeof e=="string"||Array.isArray(e)){if(!ct(e))return;let d=O(n),y=Jt(r,s,e,d);if(!y.ok){w()&&c[r]?.options?.onError?.(y.reason??`Invalid path for "${r}".`);return}o=xt(s,e,d);}else {R(`setStore("${r}") - invalid arguments.
13
+ Usage:
14
+ setStore("${r}", "field", value)
15
+ setStore("${r}", "nested.field", value)
16
+ setStore("${r}", { field: value })`);return}if(!F(o))return;let a=O(o);if(!A(r,a).ok)return;let f=c[r]?.options?.validator;if(f&&f(a)===false){c[r]?.options?.onError?.(`Validator blocked update for "${r}"`);return}let i=Vt(r,{action:"set",prev:s,next:a,path:e});u[r]=w()?L(i):i,c[r].updatedAt=new Date().toISOString(),c[r].updateCount++,yt(r),c[r].options?.persist&&pt(r),c[r].options.onSet?.(s,i),nt(r,"set",s,i),rt(r,"set"),ot(r),J(r),j(`Store "${r}" updated`);}var ue=t=>{U++;try{t();}finally{U=Math.max(0,U-1),U===0&&Z.size>0&&Ot();}};function gt(t,e){let n=Rt(t);if(!M(n))return null;let r=u[n];return e===void 0?Array.isArray(r)?[...r]:r&&typeof r=="object"?{...r}:r:ct(e)?wt(r,e):null}var Dt=t=>{if(!M(t))return;_[t]?.forEach(o=>o(null)),c[t].options.onDelete?.(u[t]);let n=c[t].options.persist,r=c[t].options.devtools;delete u[t],delete _[t],delete D[t],delete c[t];try{n?.driver?.removeItem&&n.driver.removeItem(n.key);}catch{}n?.key&&Q[n.key]===t&&delete Q[n.key],N[t]?.dispose(),delete N[t],B[t]?.close(),delete B[t],Y[t]?.(),delete Y[t],delete V[t],r&&rt(t,"delete",true),j(`Store "${t}" deleted`);},Et=t=>{if(!M(t)||!D[t])return;let e=u[t],n=h(D[t]);u[t]=w()?L(n):n,c[t].updatedAt=new Date().toISOString(),yt(t),c[t].options.onReset?.(e,n),nt(t,"reset",e,n),rt(t,"reset"),ot(t),J(t),j(`Store "${t}" reset to initial state/value`);},de=(t,e)=>{if(!M(t)||!F(e))return;let n=u[t];if(typeof n!="object"||Array.isArray(n)||n===null){R(`mergeStore("${t}") only works on object stores.
17
+ Use setStore("${t}", value) instead.`);return}let r={...n,...O(e)};if(!A(t,r).ok)return;let s=c[t]?.options?.validator;if(s&&s(r)===false){c[t]?.options?.onError?.(`Validator blocked update for "${t}"`);return}let a=Vt(t,{action:"merge",prev:n,next:r,path:null});u[t]=w()?L(a):a,c[t].updatedAt=new Date().toISOString(),c[t].updateCount++,yt(t),c[t].options?.persist&&pt(t),c[t].options.onSet?.(n,a),nt(t,"merge",n,a),rt(t,"merge"),ot(t),J(t),j(`Store "${t}" merged with data`);},fe=()=>{let t=Object.keys(u);t.forEach(Dt),p(`All stores cleared (${t.length} stores removed)`);},te=t=>u[t]!==void 0,pe=()=>Object.keys(u),ge=t=>M(t)?{...c[t]}:null,jt=(t,e)=>(_[t]||(_[t]=[]),_[t].push(e),()=>{_[t]=_[t].filter(n=>n!==e);}),ye=t=>u[t]??null,yt=t=>(V[t]=(V[t]??0)+1,V[t]),Pt=(t,e)=>(V[t]=Math.max(V[t]??0,e)+1,V[t]),ee=(t,e)=>{let n=V[t]??0,r=typeof e.clock=="number"?e.clock:0;if(r!==n)return r-n;let o=new Date(c[t]?.updatedAt||0).getTime(),s=typeof e.updatedAt=="number"?e.updatedAt:0;if(s!==o)return s-o;let a=et;return (e.source??"").localeCompare(a)},mt=t=>{let e=B[t];if(e)try{e.postMessage({type:"sync-request",source:et,name:t,requestedAt:Date.now()});}catch(n){v(t,`Failed to request sync snapshot for "${t}": ${n?.message??n}`);}},ne=t=>{let e=c[t]?.options?.sync;if(!e)return;if(typeof window>"u"||typeof BroadcastChannel>"u"){v(t,`Sync enabled for "${t}" but BroadcastChannel not available in this environment.`);return}let n=typeof e=="object"&&e.channel?e.channel:`stroid_sync_${t}`;try{let r=new BroadcastChannel(n);if(B[t]=r,r.onmessage=o=>{let s=o.data;if(!s||s.source===et||s.name!==t)return;if(s.type==="sync-request"){ot(t);return}let a=typeof e=="object"?e.conflictResolver:null;if(ee(t,{clock:s.clock,updatedAt:s.updatedAt,source:s.source})<=0){let i=new Date(c[t]?.updatedAt||0).getTime(),d=s.updatedAt;if(a){let y=a({local:u[t],incoming:s.data,localUpdated:i,incomingUpdated:d});if(y!==void 0){if(!A(t,y).ok)return;u[t]=y,c[t].updatedAt=new Date(Math.max(i,d)).toISOString(),c[t].updateCount++,Pt(t,typeof s.clock=="number"?s.clock:0),J(t);}}return}A(t,s.data).ok&&(u[t]=s.data,c[t].updatedAt=new Date(s.updatedAt).toISOString(),c[t].updateCount++,Pt(t,typeof s.clock=="number"?s.clock:0),J(t));},typeof window<"u"&&typeof window.addEventListener=="function"){Y[t]?.();let o=window,s=()=>{mt(t);};o.addEventListener("focus",s),o.addEventListener("online",s),Y[t]=()=>{o.removeEventListener("focus",s),o.removeEventListener("online",s);};}queueMicrotask(()=>{mt(t);});}catch(r){p(`Failed to setup sync for "${t}": ${r?.message||r}`);}},ot=t=>{let e=B[t];if(e)try{let n=c[t]?.options?.sync,r={type:"sync-state",source:et,name:t,clock:V[t]??0,updatedAt:Date.parse(c[t]?.updatedAt||new Date().toISOString()),data:tt(t,u[t]),checksum:G(u[t])},o=typeof n=="object"&&typeof n.maxPayloadBytes=="number"?n.maxPayloadBytes:64*1024,s=Yt(JSON.stringify(r));if(s>o){v(t,`Sync payload for "${t}" exceeds ${o} bytes (${s} bytes). Skipping BroadcastChannel sync.`);return}e.postMessage(r);}catch(n){v(t,`Failed to broadcast sync for "${t}": ${n?.message??n}`);}},Se=(t,e)=>{let n,r;return ()=>{let o=u[t];return o===void 0?null:(o===n||(n=o,r=e(o)),r??null)}},re=(t,e,n=Object.is,r)=>{if(typeof e!="function"||typeof r!="function")return p(`subscribeWithSelector("${t}") requires selector and listener functions.`),()=>{};let o=e(u[t]);return jt(t,a=>{let l=e(a);if(!n(l,o)){let f=o;o=l,r(l,f);}})},he=(t,e=0,n={})=>(W(t,{value:e},n),{inc:(r=1)=>$(t,o=>{o.value+=r;}),dec:(r=1)=>$(t,o=>{o.value-=r;}),set:r=>$(t,"value",r),reset:()=>Et(t),get:()=>gt(t,"value")}),be=(t,e=[],n={})=>(W(t,{items:e},n),{push:r=>$(t,o=>{o.items.push(r);}),removeAt:r=>$(t,o=>{o.items.splice(r,1);}),clear:()=>$(t,{items:[]}),replace:r=>$(t,{items:r}),all:()=>gt(t,"items")}),ve=(t,e={})=>(W(t,{entities:{},ids:[]},e),{upsert:n=>$(t,r=>{let o=n.id??n._id??(typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():`${Date.now()}_${Math.random().toString(16).slice(2)}`);r.ids.includes(o)||r.ids.push(o),r.entities[o]=n;}),remove:n=>$(t,r=>{r.ids=r.ids.filter(o=>o!==n),delete r.entities[n];}),all:()=>{let n=u[t];return n?n.ids.map(r=>n.entities[r]):[]},get:n=>gt(t,`entities.${n}`),clear:()=>Et(t)}),ke=()=>h(u),we=(t,e)=>{if(!x[t])return [];let n=x[t];return e&&e>0?n.slice(-e):[...n]},xe=t=>{t?delete x[t]:Object.keys(x).forEach(e=>delete x[e]);},$e=t=>{let e=c[t];return e?.metrics?{...e.metrics}:null},Te=t=>{let e={};return typeof t=="function"&&t({create:(r,o,s={})=>(e[r]=h(o),e[r]),set:(r,o)=>{if(e[r])return e[r]=typeof o=="function"?it(e[r],o):o,e[r]},get:r=>h(e[r])}),{snapshot:()=>h(e),hydrate:(r={})=>oe(e,r)}},oe=(t,e={})=>{!t||typeof t!="object"||Object.entries(t).forEach(([n,r])=>{te(n)?$(n,r):W(n,r,e[n]||e.default||{});});},_e=(t,e={})=>{let n=e.name||`zstore_${Date.now()}`,r=(l,f=false)=>{let i=u[n]??{},d=typeof l=="function"?l(i):l,y=f?d:{...i,...d};$(n,y);},o=()=>u[n],s={setState:r,getState:o,subscribe:l=>jt(n,l),subscribeWithSelector:(l,f=Object.is,i)=>re(n,l,f,i??(()=>{})),destroy:()=>Dt(n)},a=t(r,o,s);return W(n,a,e),s};export{oe as A,_e as B,w as a,p as b,R as c,W as d,$ as e,ue as f,gt as g,Dt as h,Et as i,de as j,fe as k,te as l,pe as m,ge as n,jt as o,ye as p,Se as q,re as r,he as s,be as t,ve as u,ke as v,we as w,xe as x,$e as y,Te as z};
@@ -0,0 +1,5 @@
1
+ import {c,a as a$1,b as b$1,l,d,e,o}from'./chunk-5F2FD6DX.js';var R={},u={},f={},b={},x=new Set,_={},a={},Y=e=>new Promise(t=>setTimeout(t,e)),Z=(e,t)=>{if(!t)return false;let r=b[e];return r?Date.now()-r.timestamp<t:false},n={cacheHits:0,cacheMisses:0,dedupes:0,requests:0,failures:0,avgMs:0,lastMs:0},ee=e=>{delete R[e],x.delete(e);let t=r=>r===e||r.startsWith(`${e}:`);Object.keys(u).forEach(r=>{t(r)&&delete u[r];}),Object.keys(f).forEach(r=>{t(r)&&delete f[r];}),Object.keys(b).forEach(r=>{t(r)&&delete b[r];});},D=(e,t)=>{a[e]||(a[e]=new Set),a[e].add(t),T(e);},L=(e,t)=>{let r=a[e];r&&(r.delete(t),r.size===0&&delete a[e]);},T=e=>{_[e]||(_[e]=o(e,t=>{if(t!==null)return;let r=a[e];r&&(r.forEach(o=>{try{o();}catch{}}),delete a[e]),_[e]?.(),delete _[e],ee(e);}));},te=async(e$1,t,r={})=>{if(!e$1||typeof e$1!="string"){c("fetchStore requires a store name as first argument");return}if(!t){c(`fetchStore("${e$1}") requires a URL or Promise as second argument`);return}let{transform:o,onSuccess:w,onError:W,method:H,headers:U,body:K,ttl:B,staleWhileRevalidate:I=false,dedupe:N=true,retry:V=0,retryDelay:z=400,retryBackoff:J=1.7,signal:E,cacheKey:M}=r;!E&&a$1()&&!x.has(e$1)&&(x.add(e$1),b$1(`fetchStore("${e$1}") called without an AbortSignal. Provide "signal" to enable cancellation (recommended).`));let s=M?`${e$1}:${M}`:e$1;if(l(e$1)||d(e$1,{data:null,loading:false,error:null,status:"idle"}),T(e$1),Z(s,B)){n.cacheHits+=1;let g=b[s].data;if(e(e$1,{data:g,loading:false,error:null,status:"success",cached:true}),!I)return g}else n.cacheMisses+=1;if(N&&u[s])return n.dedupes+=1,u[s];let $=(f[s]??0)+1;f[s]=$,e(e$1,{loading:true,error:null,status:"loading"}),n.requests+=1;let G=Date.now(),v=!E&&typeof AbortController<"u"?new AbortController:null,Q=E||v?.signal,S=v?()=>{v.signal.aborted||v.abort();}:null;S&&D(e$1,S);let q=(async()=>{let g=0,F=z??400;for(;;)try{let i;if(typeof t=="string"){let X=re({method:H,headers:U,body:K,signal:Q,...r}),l=await fetch(t,X);if(!l.ok)throw new Error(`HTTP ${l.status}: ${l.statusText}`);(l.headers.get("content-type")||"").includes("application/json")?i=await l.json():i=await l.text();}else if(typeof t.then=="function")i=await t;else return c(`fetchStore("${e$1}") - second argument must be a URL string or Promise.
2
+ Examples:
3
+ fetchStore("users", "https://api.example.com/users")
4
+ fetchStore("users", axios.get("/users"))`),null;let h=o?o(i):i;if(f[s]!==$)return null;b[s]={timestamp:Date.now(),data:h},l(e$1)&&e(e$1,{data:h,loading:!1,error:null,status:"success"}),w?.(h);let c$1=Date.now()-G;return n.lastMs=c$1,n.avgMs=(n.avgMs*(n.requests-1)+c$1)/n.requests,h}catch(i){if(g+=1,i?.name==="AbortError")return b$1(`fetchStore("${e$1}") aborted`),l(e$1)&&e(e$1,{loading:false,error:"aborted",status:"aborted"}),null;if(g<=(V??0)){await Y(F),F*=J??1.7;continue}if(f[s]!==$)return null;let c=i?.message||"Something went wrong";return l(e$1)&&e(e$1,{data:null,loading:false,error:c,status:"error"}),W?.(c),n.failures+=1,b$1(`fetchStore("${e$1}") failed: ${c}`),null}})().finally(()=>{delete u[s],S&&L(e$1,S);});return u[s]=q,R[e$1]={url:t,options:{...r,cacheKey:M}},q},j=async e=>{let t=R[e];if(!t){a$1()&&b$1(`refetchStore("${e}") - no previous fetch found.
5
+ Call fetchStore("${e}", url) first.`);return}return te(e,t.url,t.options)},A=new Set,C={},ae=e=>{if(typeof window>"u"||typeof window.addEventListener!="function")return ()=>{};let t=e??"*";if(A.has(t))return C[t]??(()=>{});let r=()=>{t==="*"?Object.keys(R).forEach(w=>{j(w);}):j(t);};window.addEventListener("focus",r),window.addEventListener("online",r),A.add(t);let o=()=>{window.removeEventListener("focus",r),window.removeEventListener("online",r),A.delete(t),delete C[t],t!=="*"&&L(t,o);};return C[t]=o,t!=="*"&&D(t,o),o},re=e=>{let t={};return e.method&&(t.method=e.method.toUpperCase()),e.headers?t.headers=e.headers:t.headers={"Content-Type":"application/json"},e.body&&(t.body=typeof e.body=="string"?e.body:JSON.stringify(e.body)),e.signal&&(t.signal=e.signal),t},ce=()=>({...n});export{te as a,j as b,ae as c,ce as d};
@@ -0,0 +1,3 @@
1
+ import {r,o,p,l,b,a,e}from'./chunk-5F2FD6DX.js';import {useCallback,useSyncExternalStore,useEffect,useRef}from'react';var m=(t,e)=>{if(!e)return t;let o=e.split("."),n=t;for(let r of o){if(n==null)return null;n=n[r];}return n??null},h=new Set,w=(t,e)=>{if(Object.is(t,e))return true;if(typeof t!="object"||typeof e!="object"||t===null||e===null)return false;let o=Object.keys(t),n=Object.keys(e);if(o.length!==n.length)return false;for(let r of o)if(!Object.prototype.hasOwnProperty.call(e,r)||!Object.is(t[r],e[r]))return false;return true};function c(t,e,o$1=Object.is){let n=typeof e=="function",r$1=typeof e=="string"?e:void 0,u=n?e:void 0,p$1=useCallback(s=>n?r(t,i=>u(i),o$1,s):o(t,()=>s()),[t,n,u,o$1]),S=useCallback(()=>{let s=p(t);return n?s==null?null:u(s):m(s,r$1)},[t,n,u,r$1]),l$1=useSyncExternalStore(p$1,S,S);return useEffect(()=>{l(t)||b(`useStore("${t}") - store not found yet.
2
+ Component will update automatically when createStore("${t}") is called.`),a()&&!n&&!r$1&&!h.has(t)&&(h.add(t),b(`useStore("${t}") without a selector/path subscribes to the entire store and may re-render on every change.
3
+ Prefer useSelector/useStoreField for better performance.`));},[t,n,r$1]),l$1}var v=(t,e)=>c(t,e),C=(t,e,o=w)=>{let n=useRef(null),r$1=useCallback(l=>{if(l==null)return null;let s=e(l),i=n.current;return i!==null&&o(s,i)?i:(n.current=s,s)},[e,o]),u=useCallback(()=>{let l=p(t);return r$1(l)},[t,r$1]),p$1=useCallback(l=>r(t,s=>r$1(s),o,()=>l()),[t,r$1,o]);return useSyncExternalStore(p$1,u,u)},E=(t,e)=>{let o=p(t);return o==null?null:m(o,e)};var A=t=>{let e=c(t);return {data:e?.data??null,loading:e?.loading??false,error:e?.error??null,status:e?.status??"idle",isEmpty:!e?.data&&!e?.loading&&!e?.error}};var _=(t,e$1)=>{let o=c(t,e$1),n=useCallback(r=>{let u=r?.target?r.target.value:r;e(t,e$1,u);},[t,e$1]);return {value:o,onChange:n}};export{c as a,v as b,C as c,E as d,A as e,_ as f};
@@ -1 +1 @@
1
- import {c,g,e,b,l as l$1}from'./chunk-6V676XCZ.js';var l=class r{constructor(t,e=[]){this._storeName=t,this._path=e;}nested(t){return typeof t!="string"||t.trim()===""?(c(`nested() expects a string key. Got: ${JSON.stringify(t)}`),this):new r(this._storeName,[...this._path,t])}target(t){return typeof t!="string"||t.trim()===""?(c(`target() expects a string key. Got: ${JSON.stringify(t)}`),new a(this._storeName,this._path,null)):new a(this._storeName,this._path,t)}get value(){return this._path.length===0?g(this._storeName):g(this._storeName,this._path.join("."))}set(t){this._path.length===0?e(this._storeName,t):e(this._storeName,this._path.join("."),t);}},a=class{constructor(t,e,o){this._storeName=t,this._parentPath=e,this._key=o,this._fullPath=o?[...e,o].join("."):e.join(".");}get value(){return this._key?g(this._storeName,this._fullPath):(b("target() was called without a key - returning null"),null)}set(t){if(!this._key){b("target() was called without a key - cannot set value");return}e(this._storeName,this._fullPath,t);}},p=r=>(l$1(r)||b(`chain("${r}") called before store exists; operations will no-op until created.`),new l(r));export{p as a};
1
+ import {c,g,e,b,l as l$1}from'./chunk-5F2FD6DX.js';var l=class r{constructor(t,e=[]){this._storeName=t,this._path=e;}nested(t){return typeof t!="string"||t.trim()===""?(c(`nested() expects a string key. Got: ${JSON.stringify(t)}`),this):new r(this._storeName,[...this._path,t])}target(t){return typeof t!="string"||t.trim()===""?(c(`target() expects a string key. Got: ${JSON.stringify(t)}`),new a(this._storeName,this._path,null)):new a(this._storeName,this._path,t)}get value(){return this._path.length===0?g(this._storeName):g(this._storeName,this._path.join("."))}set(t){this._path.length===0?e(this._storeName,t):e(this._storeName,this._path.join("."),t);}},a=class{constructor(t,e,o){this._storeName=t,this._parentPath=e,this._key=o,this._fullPath=o?[...e,o].join("."):e.join(".");}get value(){return this._key?g(this._storeName,this._fullPath):(b("target() was called without a key - returning null"),null)}set(t){if(!this._key){b("target() was called without a key - cannot set value");return}e(this._storeName,this._fullPath,t);}},p=r=>(l$1(r)||b(`chain("${r}") called before store exists; operations will no-op until created.`),new l(r));export{p as a};
@@ -27,6 +27,12 @@ interface PersistConfig {
27
27
  deserialize: (v: string) => unknown;
28
28
  encrypt: (v: string) => string;
29
29
  decrypt: (v: string) => string;
30
+ onMigrationFail?: "reset" | "keep" | ((state: unknown) => unknown);
31
+ onStorageCleared?: (info: {
32
+ name: string;
33
+ key: string;
34
+ reason: "clear" | "remove" | "missing";
35
+ }) => void;
30
36
  }
31
37
  interface MiddlewareCtx {
32
38
  action: string;
@@ -53,6 +59,7 @@ interface StoreOptions<State = StoreValue> {
53
59
  allowSSRGlobalStore?: boolean;
54
60
  sync?: boolean | {
55
61
  channel?: string;
62
+ maxPayloadBytes?: number;
56
63
  conflictResolver?: (args: {
57
64
  local: StoreValue;
58
65
  incoming: StoreValue;
@@ -79,6 +86,7 @@ type NormalizedOptions = {
79
86
  allowSSRGlobalStore?: boolean;
80
87
  sync?: boolean | {
81
88
  channel?: string;
89
+ maxPayloadBytes?: number;
82
90
  conflictResolver?: (args: {
83
91
  local: StoreValue;
84
92
  incoming: StoreValue;
package/dist/core.d.ts CHANGED
@@ -1 +1 @@
1
- export { c as chain, a as clearAllStores, b as clearHistory, h as createStore, i as createStoreForRequest, k as deleteStore, l as getHistory, n as getMetrics, o as getStore, p as getStoreMeta, q as hasStore, r as hydrateStores, s as listStores, t as mergeStore, u as resetStore, v as setStore, w as setStoreBatch, x as subscribeWithSelector } from './core-CLIi-SLm.js';
1
+ export { c as chain, a as clearAllStores, b as clearHistory, h as createStore, i as createStoreForRequest, k as deleteStore, l as getHistory, n as getMetrics, o as getStore, p as getStoreMeta, q as hasStore, r as hydrateStores, s as listStores, t as mergeStore, u as resetStore, v as setStore, w as setStoreBatch, x as subscribeWithSelector } from './core-CKzRwVaY.js';
package/dist/core.js CHANGED
@@ -1 +1 @@
1
- export{a as chain}from'./chunk-SKZAS43O.js';export{k as clearAllStores,x as clearHistory,d as createStore,z as createStoreForRequest,h as deleteStore,w as getHistory,y as getMetrics,g as getStore,n as getStoreMeta,l as hasStore,A as hydrateStores,m as listStores,j as mergeStore,i as resetStore,e as setStore,f as setStoreBatch,r as subscribeWithSelector}from'./chunk-6V676XCZ.js';
1
+ export{a as chain}from'./chunk-K6QIWMMW.js';export{k as clearAllStores,x as clearHistory,d as createStore,z as createStoreForRequest,h as deleteStore,w as getHistory,y as getMetrics,g as getStore,n as getStoreMeta,l as hasStore,A as hydrateStores,m as listStores,j as mergeStore,i as resetStore,e as setStore,f as setStoreBatch,r as subscribeWithSelector}from'./chunk-5F2FD6DX.js';
package/dist/index.d.ts CHANGED
@@ -1,18 +1,3 @@
1
- export { c as chain, a as clearAllStores, b as clearHistory, d as createCounterStore, e as createEntityStore, f as createListStore, g as createSelector, h as createStore, i as createStoreForRequest, j as createZustandCompatStore, k as deleteStore, l as getHistory, m as getInitialState, n as getMetrics, o as getStore, p as getStoreMeta, q as hasStore, r as hydrateStores, s as listStores, t as mergeStore, u as resetStore, v as setStore, w as setStoreBatch, x as subscribeWithSelector } from './core-CLIi-SLm.js';
1
+ export { c as chain, a as clearAllStores, b as clearHistory, d as createCounterStore, e as createEntityStore, f as createListStore, g as createSelector, h as createStore, i as createStoreForRequest, j as createZustandCompatStore, k as deleteStore, l as getHistory, m as getInitialState, n as getMetrics, o as getStore, p as getStoreMeta, q as hasStore, r as hydrateStores, s as listStores, t as mergeStore, u as resetStore, v as setStore, w as setStoreBatch, x as subscribeWithSelector } from './core-CKzRwVaY.js';
2
2
  export { enableRevalidateOnFocus, fetchStore, getAsyncMetrics, refetchStore } from './async.js';
3
- export { useSelector, useStore, useStoreField, useStoreStatic } from './react.js';
4
-
5
- declare const useAsyncStore: (name: string) => {
6
- data: any;
7
- loading: any;
8
- error: any;
9
- status: any;
10
- isEmpty: boolean;
11
- };
12
-
13
- declare const useFormStore: <T = any>(storeName: string, field: string) => {
14
- value: T | null;
15
- onChange: (eOrValue: any) => void;
16
- };
17
-
18
- export { useAsyncStore, useFormStore };
3
+ export { useAsyncStore, useFormStore, useSelector, useStore, useStoreField, useStoreStatic } from './react.js';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export{a as chain}from'./chunk-SKZAS43O.js';export{c as enableRevalidateOnFocus,a as fetchStore,d as getAsyncMetrics,b as refetchStore}from'./chunk-TB5U3OFY.js';import {a}from'./chunk-CZARAS3I.js';export{c as useSelector,a as useStore,b as useStoreField,d as useStoreStatic}from'./chunk-CZARAS3I.js';import {e}from'./chunk-6V676XCZ.js';export{k as clearAllStores,x as clearHistory,s as createCounterStore,u as createEntityStore,t as createListStore,q as createSelector,d as createStore,z as createStoreForRequest,B as createZustandCompatStore,h as deleteStore,w as getHistory,v as getInitialState,y as getMetrics,g as getStore,n as getStoreMeta,l as hasStore,A as hydrateStores,m as listStores,j as mergeStore,i as resetStore,e as setStore,f as setStoreBatch,r as subscribeWithSelector}from'./chunk-6V676XCZ.js';import {useCallback}from'react';var n=r=>{let e=a(r);return {data:e?.data??null,loading:e?.loading??false,error:e?.error??null,status:e?.status??"idle",isEmpty:!e?.data&&!e?.loading&&!e?.error}};var u=(r,e$1)=>{let i=a(r,e$1),l=useCallback(o=>{let m=o?.target?o.target.value:o;e(r,e$1,m);},[r,e$1]);return {value:i,onChange:l}};export{n as useAsyncStore,u as useFormStore};
1
+ export{a as chain}from'./chunk-K6QIWMMW.js';export{c as enableRevalidateOnFocus,a as fetchStore,d as getAsyncMetrics,b as refetchStore}from'./chunk-G6JMMJYH.js';export{e as useAsyncStore,f as useFormStore,c as useSelector,a as useStore,b as useStoreField,d as useStoreStatic}from'./chunk-JBYLHJKN.js';export{k as clearAllStores,x as clearHistory,s as createCounterStore,u as createEntityStore,t as createListStore,q as createSelector,d as createStore,z as createStoreForRequest,B as createZustandCompatStore,h as deleteStore,w as getHistory,v as getInitialState,y as getMetrics,g as getStore,n as getStoreMeta,l as hasStore,A as hydrateStores,m as listStores,j as mergeStore,i as resetStore,e as setStore,f as setStoreBatch,r as subscribeWithSelector}from'./chunk-5F2FD6DX.js';
package/dist/react.d.ts CHANGED
@@ -4,4 +4,17 @@ declare const useStoreField: <T = any>(storeName: string, field: string) => T |
4
4
  declare const useSelector: <T = any, R = any>(storeName: string, selectorFn: (state: T) => R, equalityFn?: (a: R, b: R) => boolean) => R | null;
5
5
  declare const useStoreStatic: <T = any>(name: string, path?: string) => T | null;
6
6
 
7
- export { useSelector, useStore, useStoreField, useStoreStatic };
7
+ declare const useAsyncStore: (name: string) => {
8
+ data: any;
9
+ loading: any;
10
+ error: any;
11
+ status: any;
12
+ isEmpty: boolean;
13
+ };
14
+
15
+ declare const useFormStore: <T = any>(storeName: string, field: string) => {
16
+ value: T | null;
17
+ onChange: (eOrValue: any) => void;
18
+ };
19
+
20
+ export { useAsyncStore, useFormStore, useSelector, useStore, useStoreField, useStoreStatic };
package/dist/react.js CHANGED
@@ -1 +1 @@
1
- export{c as useSelector,a as useStore,b as useStoreField,d as useStoreStatic}from'./chunk-CZARAS3I.js';import'./chunk-6V676XCZ.js';
1
+ export{e as useAsyncStore,f as useFormStore,c as useSelector,a as useStore,b as useStoreField,d as useStoreStatic}from'./chunk-JBYLHJKN.js';import'./chunk-5F2FD6DX.js';
package/dist/testing.js CHANGED
@@ -1 +1 @@
1
- import {d,i as i$1,e,k}from'./chunk-6V676XCZ.js';var l=(e$1="mock",o={})=>(d(e$1,o),{set:r=>e(e$1,r),reset:()=>i$1(e$1),use:()=>({name:e$1})}),u=(e,o)=>{let r=Date.now;Date.now=()=>e;try{return o()}finally{Date.now=r;}},i=()=>k(),m=(e$1,o=1e3,r=t=>({value:t}))=>{let t=typeof performance<"u"&&performance.now?performance.now():Date.now();for(let n=0;n<o;n++)e(e$1,r(n));let s=(typeof performance<"u"&&performance.now?performance.now():Date.now())-t;return {iterations:o,totalMs:s,avgMs:s/o}};export{m as benchmarkStoreSet,l as createMockStore,i as resetAllStoresForTest,u as withMockedTime};
1
+ import {d,i as i$1,e,k}from'./chunk-5F2FD6DX.js';var l=(e$1="mock",o={})=>(d(e$1,o),{set:r=>e(e$1,r),reset:()=>i$1(e$1),use:()=>({name:e$1})}),u=(e,o)=>{let r=Date.now;Date.now=()=>e;try{return o()}finally{Date.now=r;}},i=()=>k(),m=(e$1,o=1e3,r=t=>({value:t}))=>{let t=typeof performance<"u"&&performance.now?performance.now():Date.now();for(let n=0;n<o;n++)e(e$1,r(n));let s=(typeof performance<"u"&&performance.now?performance.now():Date.now())-t;return {iterations:o,totalMs:s,avgMs:s/o}};export{m as benchmarkStoreSet,l as createMockStore,i as resetAllStoresForTest,u as withMockedTime};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stroid",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Compact state management library with persistence, async caching, sync, and React hooks.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,18 +0,0 @@
1
- var Rt=typeof process<"u"&&typeof process.env?.NODE_ENV=="string"?process.env.NODE_ENV:void 0,_t=typeof import.meta<"u"&&import.meta?.env?.MODE?import.meta.env.MODE:void 0,it=typeof globalThis<"u"&&typeof globalThis.__STROID_DEV__=="boolean"?globalThis.__STROID_DEV__:void 0,Ot=typeof process<"u"?"development":"production",Pt=Rt??_t??Ot,z=typeof it=="boolean"?it:Pt!=="production",S=()=>z,l=z?t=>{console.warn(`[stroid] ${t}`);}:()=>{},m=z?t=>{console.error(`[stroid] ${t}`);}:()=>{},R=z?t=>{console.log(`[stroid] ${t}`);}:()=>{},H=null,Dt=()=>{if(H)return H;let t,n=[];for(let e=0;e<256;e++){t=e;for(let r=0;r<8;r++)t=t&1?3988292384^t>>>1:t>>>1;n[e]=t>>>0;}return H=n,n},at=t=>{let n=Dt(),e=-1;for(let r=0;r<t.length;r++)e=e>>>0,e=e>>>8^n[(e^t.charCodeAt(r))&255];return (e^-1)>>>0},J=t=>{try{return at(JSON.stringify(t))}catch{return at(String(t))}},Vt=typeof globalThis<"u"&&typeof globalThis.structuredClone=="function",h=t=>{try{return Vt?structuredClone(t):JSON.parse(JSON.stringify(t))}catch{return Array.isArray(t)?[...t]:t&&typeof t=="object"?{...t}:t}};var G=(t,n)=>{try{let e=h(t);return n(e),e}catch(e){throw new Error(`produceClone failed (possible circular reference or unserializable data): ${e?.message??e}`)}},ut=(t,n)=>{if(!t)return {ok:true};try{if(typeof t.safeParse=="function"){let e=t.safeParse(n);return e.success?{ok:!0,data:e.data}:{ok:!1,error:e.error}}if(typeof t.parse=="function")return t.parse(n),{ok:!0,data:n};if(typeof t.validateSync=="function")return t.validateSync(n),{ok:!0,data:n};if(typeof t.isValidSync=="function")return t.isValidSync(n)?{ok:!0,data:n}:{ok:!1,error:"Schema validation failed"};if(typeof t.validate=="function")return t.validate(n)?{ok:!0,data:n}:{ok:!1,error:t.errors||"Schema validation failed"};if(typeof t=="function"){let e=t(n);return e===!1?{ok:!1,error:"Schema validation failed"}:{ok:!0,data:e===!0?n:e}}return {ok:!0,data:n}}catch(e){return {ok:false,error:e?.message??e}}};var _=t=>t===null?"null":Array.isArray(t)?"array":t instanceof Map?"map":t instanceof Set?"set":t instanceof Date?"date":typeof t=="function"?"function":typeof t,j=t=>{let n=_(t);return n==="function"?(m(`Functions cannot be stored in stroid.
2
- Store data only - handle functions outside the store.`),false):n==="map"||n==="set"?(l(`Map/Set detected. stroid converts these to plain objects.
3
- Use arrays or plain objects for best results.`),true):(n==="date"&&l(`Date object detected. stroid stores it as ISO string.
4
- Use new Date(value) to convert back when reading.`),true)},$=t=>{let n=_(t);if(n==="date")return S()&&l("Date detected; stored as ISO string. Use new Date(value) when reading."),t.toISOString();if(n==="map")return S()&&l("Map detected; converting to plain object."),Object.fromEntries(t);if(n==="set")return S()&&l("Set detected; converting to array."),Array.from(t);if(n==="object"){let e={};for(let r in t)e[r]=$(t[r]);return e}return n==="array"?t.map($):t},ct=10,jt=5,N=t=>Array.isArray(t)?[...t]:typeof t=="string"&&!t.includes(".")?[t]:typeof t=="string"?t.split("."):[String(t)],X=t=>{let n=N(t),e=n.length;return e>ct?(m(`Path depth of ${e} exceeded maximum of ${ct}.
5
- "${n.join(".")}"
6
- This is a data design issue. Split into separate stores:
7
- createStore("${n[0]}", ...) and createStore("${n[1]}", ...)`),false):(e>jt&&l(`Deep nesting detected (${e} levels): "${n.join(".")}"
8
- Consider splitting into separate stores for better readability.`),true)},lt=(t,n)=>{let e=N(n),r=t;for(let o of e){if(r==null){l(`Path "${e.join(".")}" not found - reached null at "${o}"`);return}if(typeof r!="object"){l(`Cannot go deeper at "${o}" - value is not an object`);return}r=r[o];}return r},W=(t,n,e)=>{let r=N(n);if(r.length===1)return {...t,[r[0]]:e};let[o,...s]=r,c=t[o];return {...t,[o]:W(typeof c=="object"&&c!==null?c:{},s,e)}},dt=t=>typeof t!="string"||t.trim()===""?(m(`Store name must be a non-empty string. Got: ${JSON.stringify(t)}`),false):t.includes(" ")?(m(`Store name "${t}" contains spaces.
9
- Use camelCase or kebab-case: "userName" or "user-name"`),false):true,ft=(t,n)=>{let e=n.find(r=>{let o=r.toLowerCase(),s=t.toLowerCase();return o.includes(s)||s.includes(o)||Nt(o,s)<=2});e?l(`Store "${t}" not found. Did you mean "${e}"?`):m(`Store "${t}" not found.
10
- Available stores: [${n.join(", ")}]
11
- Call createStore("${t}", data) first.`);},Nt=(t,n)=>{let e=Array.from({length:n.length+1},(r,o)=>Array.from({length:t.length+1},(s,c)=>o===0?c:c===0?o:0));for(let r=1;r<=n.length;r++)for(let o=1;o<=t.length;o++)e[r][o]=n[r-1]===t[o-1]?e[r-1][o-1]:Math.min(e[r-1][o-1],e[r][o-1],e[r-1][o])+1;return e[n.length][t.length]};var E=t=>{if(typeof t!="object"||t===null)return t;Object.freeze(t);for(let n of Object.keys(t)){let e=t[n];typeof e=="object"&&e!==null&&!Object.isFrozen(e)&&E(e);}return t};var u=Object.create(null),k=Object.create(null),P=Object.create(null),i=Object.create(null),b=Object.create(null),Et=Object.create(null),K=new Set,Y=false,C=0,Ct=`stroid_${Math.random().toString(16).slice(2)}`,Z={},F=Object.create(null),pt=false,gt=t=>typeof t=="string"?t:t.name,I,U=(()=>{let t=new Map;return {getItem:n=>t.has(n)?t.get(n):null,setItem:(n,e)=>{t.set(n,e);},removeItem:n=>{t.delete(n);},type:"memory"}})(),yt=()=>{if(Y)return;Y=true;let t=()=>{Y=false,K.forEach(n=>{let e=k[n];if(!e||e.length===0)return;let r=typeof performance<"u"&&performance.now?performance.now():Date.now(),o=h(u[n]);e.forEach(f=>{try{f(o);}catch(d){l(`Subscriber for "${n}" threw: ${d?.message??d}`);}});let c=(typeof performance<"u"&&performance.now?performance.now():Date.now())-r,a=i[n]?.metrics||{notifyCount:0,totalNotifyMs:0,lastNotifyMs:0};a.notifyCount+=1,a.totalNotifyMs+=c,a.lastNotifyMs=c,i[n]&&(i[n].metrics=a);}),K.clear();};typeof queueMicrotask=="function"?queueMicrotask(t):Promise.resolve().then(t);},A=t=>{K.add(t),C===0&&yt();},D=t=>u[t]!==void 0?true:(ft(t,Object.keys(u)),false),It=(t,n,e,r)=>{let o=N(e);if(o.length===0)return {ok:true};if(n==null){let c=`Cannot set "${o.join(".")}" on "${t}" because the store value is ${n===null?"null":"undefined"}.`;return l(c),{ok:false,reason:c}}let s=n;for(let c=0;c<o.length;c++){let a=o[c],f=c===o.length-1;if(s==null||typeof s!="object"){let p=`Path "${o.join(".")}" is invalid for "${t}" - "${o.slice(0,c).join(".")||"root"}" is not an object.`;return l(p),{ok:false,reason:p}}if(Array.isArray(s)){let p=Number(a);if(!Number.isInteger(p)||p<0){let g=`Path "${o.join(".")}" targets non-numeric index "${a}" on an array in "${t}".`;return l(g),{ok:false,reason:g}}if(f){let g=s[p];if(g!==void 0){let w=_(g),T=_(r);if(w!==T){let y=`Type mismatch setting "${o.join(".")}" on "${t}": expected ${w}, received ${T}.`;return l(y),{ok:false,reason:y}}}return {ok:true}}s=s[p];continue}if(!Object.prototype.hasOwnProperty.call(s,a)){let p=`Path "${o.join(".")}" does not exist on store "${t}" (missing "${a}").`;return l(p),{ok:false,reason:p}}if(f){let p=s[a];if(p!==void 0){let g=_(p),w=_(r);if(g!==w){let T=`Type mismatch setting "${o.join(".")}" on "${t}": expected ${g}, received ${w}.`;return l(T),{ok:false,reason:T}}}return {ok:true}}s=s[a];}return {ok:true}},Q=t=>{try{return typeof window>"u"?U:t==="session"||t==="sessionStorage"?window.sessionStorage??U:window.localStorage??U}catch{return U}},At=(t,n)=>{if(!t)return null;let e={key:`stroid_${n}`,serialize:JSON.stringify,deserialize:JSON.parse,encrypt:r=>r,decrypt:r=>r};return t===true?{driver:Q("localStorage"),...e}:typeof t=="string"?{driver:Q(t),...e}:{driver:t.driver||t.storage||Q("localStorage"),key:t.key||e.key,serialize:t.serialize||e.serialize,deserialize:t.deserialize||e.deserialize,encrypt:t.encrypt||e.encrypt,decrypt:t.decrypt||e.decrypt}},Mt=t=>{if(!i[t]?.options?.devtools||typeof window>"u")return;let e=window.__REDUX_DEVTOOLS_EXTENSION__||window.__REDUX_DEVTOOLS_EXTENSION__;if(!e||typeof e.connect!="function"){l(`DevTools requested for "${t}" but Redux DevTools extension not found.`);return}I||(I=e.connect({name:"stroid"}),I.init(u));},tt=(t,n)=>{let e=i[t]?.options?.redactor;if(typeof e=="function")try{return e(h(n))}catch{return n}return n},zt=(t,n)=>{if(typeof t!="object"||typeof n!="object"||t===null||n===null)return null;let e=t,r=n,o=[],s=[],c=[],a=new Set(Object.keys(e)),f=new Set(Object.keys(r));return f.forEach(d=>{a.has(d)?Object.is(e[d],r[d])||c.push(d):o.push(d);}),a.forEach(d=>{f.has(d)||s.push(d);}),{added:o,removed:s,changed:c}},q=(t,n,e,r)=>{let o=i[t]?.options?.historyLimit??50;if(o===0)return;b[t]||(b[t]=[]);let s={ts:Date.now(),action:n,prev:tt(t,e),next:tt(t,r),diff:zt(e,r)};b[t].push(s),b[t].length>o&&b[t].splice(0,b[t].length-o);},L=(t,n,e=false)=>{if(!(!I||!e&&!i[t]?.options?.devtools))try{let r={...u,[t]:tt(t,u[t])};I.send({type:`${t}/${n}`},r);}catch{}},St=(t,n)=>{let e=i[t]?.options?.middleware||[];if(!Array.isArray(e))return n.next;let r=n.next;for(let o of e){if(typeof o!="function")continue;let s=o({action:n.action,name:t,prev:n.prev,next:r,path:n.path});s!==void 0&&(r=s);}return r},et=(t,n)=>{let e=i[t]?.options?.schema;if(!e)return {ok:true};let r=ut(e,n);if(!r.ok){let o=`Schema validation failed for "${t}": ${r.error}`;i[t]?.options?.onError?.(o),l(o);}return r},nt=t=>{let n=i[t]?.options?.persist;n&&(Z[t]&&clearTimeout(Z[t]),Z[t]=setTimeout(()=>{try{let e=n.serialize(u[t]),r=J(e),o=JSON.stringify({v:i[t]?.version??1,checksum:r,data:e}),s=n.encrypt(o);n.driver.setItem?.(n.key,s);}catch(e){l(`Could not persist store "${t}" (${e?.message||e})`);}},0));},Ut=(t,{silent:n}={silent:false})=>{let e=i[t]?.options?.persist;if(e)try{let r=e.driver.getItem?.(e.key)??null;if(!r)return;let o=e.decrypt(r),s=JSON.parse(o),{v:c=1,checksum:a,data:f}=s||{};if(!f)return;if(a!==J(f)){l(`Checksum mismatch loading store "${t}". Falling back to initial state.`),u[t]=h(P[t]);return}let d=e.deserialize(f),p=i[t]?.version??1;if(c!==p){let w=i[t]?.options?.migrations||{};Object.keys(w).map(y=>Number(y)).filter(y=>y>c&&y<=p).sort((y,x)=>y-x).forEach(y=>{try{let x=w[y](d);x!==void 0&&(d=x);}catch(x){l(`Migration to v${y} failed for "${t}": ${x?.message||x}`);}});}if(!et(t,d).ok){l(`Persisted state for "${t}" failed schema; resetting to initial.`),u[t]=h(P[t]);return}u[t]=d,n||R(`Store "${t}" loaded from persistence`);}catch(r){l(`Could not load store "${t}" (${r?.message||r})`);}},M=(t,n,e={})=>{if(!dt(t)||!j(n))return;let r=typeof window>"u",o=typeof process<"u"?process.env?.NODE_ENV:void 0,s=r&&o==="production",c=e.allowSSRGlobalStore??false;if(s&&!c){S()&&m(`createStore("${t}") is blocked on the server in production to prevent cross-request memory leaks.
12
- Call createStoreForRequest(...) inside each request scope or pass { allowSSRGlobalStore: true } to opt in.`);return}u[t]!==void 0&&S()&&l(`Store "${t}" already exists and will be overwritten.
13
- Use setStore("${t}", data) to update instead.`);let{persist:a=false,devtools:f=false,middleware:d=[],onSet:p,onReset:g,onDelete:w,onCreate:T,onError:y,validator:x,schema:wt,migrations:kt={},version:ot=1,redactor:xt,historyLimit:mt=50,sync:Tt,allowSSRGlobalStore:st=c}=e;r&&!st&&!pt&&S()&&(pt=true,l(`createStore("${t}") called in a server environment. Use createStoreForRequest(...) per request to avoid cross-request leaks or pass { allowSSRGlobalStore: true } if you really want a global store on the server.`));let O=At(a,t);if(O?.key){let B=F[O.key];B&&B!==t&&S()?l(`Persist key collision: "${O.key}" already used by store "${B}". Store "${t}" will overwrite the same storage key.`):F[O.key]=t;}let V=$(n),$t={persist:O,devtools:!!f,middleware:d??[],onSet:p,onReset:g,onDelete:w,onCreate:T,onError:y,validator:x,schema:wt,migrations:kt,version:ot,redactor:xt,historyLimit:mt,sync:Tt??false,allowSSRGlobalStore:st};return u[t]=V,k[t]=k[t]||[],P[t]=h(V),i[t]={createdAt:new Date().toISOString(),updatedAt:new Date().toISOString(),updateCount:0,version:ot,metrics:{notifyCount:0,totalNotifyMs:0,lastNotifyMs:0},options:$t},O&&(nt(t),Ut(t,{silent:true})),i[t].options.onCreate?.(V),Mt(t),Ft(t),q(t,"create",null,V),R(`Store "${t}" created -> ${JSON.stringify(V)}`),{name:t}};function v(t,n,e){let r=gt(t);if(!D(r))return;let o,s=u[r];if(typeof n=="function"&&e===void 0)o=G(s,n);else if(typeof n=="object"&&!Array.isArray(n)&&e===void 0){if(!j(n))return;o={...s,...$(n)};}else if(typeof n=="string"||Array.isArray(n)){if(!X(n))return;let f=$(e),d=It(r,s,n,f);if(!d.ok){S()&&i[r]?.options?.onError?.(d.reason??`Invalid path for "${r}".`);return}o=W(s,n,f);}else {m(`setStore("${r}") - invalid arguments.
14
- Usage:
15
- setStore("${r}", "field", value)
16
- setStore("${r}", "nested.field", value)
17
- setStore("${r}", { field: value })`);return}if(!j(o))return;let c=i[r]?.options?.validator;if(c&&c(o)===false){i[r]?.options?.onError?.(`Validator blocked update for "${r}"`);return}let a=St(r,{action:"set",prev:s,next:$(o),path:n});u[r]=S()?E(a):a,i[r].updatedAt=new Date().toISOString(),i[r].updateCount++,i[r].options?.persist&&nt(r),i[r].options.onSet?.(s,a),q(r,"set",s,a),L(r,"set"),A(r),R(`Store "${r}" updated`);}var Wt=t=>{C++;try{t();}finally{C=Math.max(0,C-1),C===0&&K.size>0&&yt();}};function rt(t,n){let e=gt(t);if(!D(e))return null;let r=u[e];return n===void 0?Array.isArray(r)?[...r]:r&&typeof r=="object"?{...r}:r:X(n)?lt(r,n):null}var ht=t=>{if(!D(t))return;k[t]?.forEach(o=>o(null)),i[t].options.onDelete?.(u[t]);let e=i[t].options.persist,r=i[t].options.devtools;delete u[t],delete k[t],delete P[t],delete i[t];try{e?.driver?.removeItem&&e.driver.removeItem(e.key);}catch{}e?.key&&F[e.key]===t&&delete F[e.key],r&&L(t,"delete",true),R(`Store "${t}" deleted`);},bt=t=>{if(!D(t)||!P[t])return;let n=u[t],e=h(P[t]);u[t]=S()?E(e):e,i[t].updatedAt=new Date().toISOString(),i[t].options.onReset?.(n,e),q(t,"reset",n,e),L(t,"reset"),A(t),R(`Store "${t}" reset to initial state/value`);},Yt=(t,n)=>{if(!D(t)||!j(n))return;let e=u[t];if(typeof e!="object"||Array.isArray(e)||e===null){m(`mergeStore("${t}") only works on object stores.
18
- Use setStore("${t}", value) instead.`);return}let r={...e,...$(n)},o=St(t,{action:"merge",prev:e,next:r,path:null});u[t]=S()?E(o):o,i[t].updatedAt=new Date().toISOString(),i[t].updateCount++,i[t].options?.persist&&nt(t),i[t].options.onSet?.(e,o),q(t,"merge",e,o),L(t,"merge"),A(t),R(`Store "${t}" merged with data`);},Zt=()=>{let t=Object.keys(u);t.forEach(ht),l(`All stores cleared (${t.length} stores removed)`);},Kt=t=>u[t]!==void 0,Qt=()=>Object.keys(u),te=t=>D(t)?{...i[t]}:null,vt=(t,n)=>(k[t]||(k[t]=[]),k[t].push(n),()=>{k[t]=k[t].filter(e=>e!==n);}),ee=t=>u[t]??null,Ft=t=>{let n=i[t]?.options?.sync;if(!n)return;if(typeof window>"u"||typeof BroadcastChannel>"u"){l(`Sync enabled for "${t}" but BroadcastChannel not available in this environment.`);return}let e=typeof n=="object"&&n.channel?n.channel:`stroid_sync_${t}`;try{let r=new BroadcastChannel(e);Et[t]=r,r.onmessage=o=>{let s=o.data;if(!s||s.source===Ct||s.name!==t)return;let c=new Date(i[t]?.updatedAt||0).getTime(),a=s.updatedAt,f=typeof n=="object"?n.conflictResolver:null;if(a<=c){if(f){let p=f({local:u[t],incoming:s.data,localUpdated:c,incomingUpdated:a});if(p!==void 0){if(!et(t,p).ok)return;u[t]=p,i[t].updatedAt=new Date(Math.max(c,a)).toISOString(),i[t].updateCount++,A(t);}}return}et(t,s.data).ok&&(u[t]=s.data,i[t].updatedAt=new Date(a).toISOString(),i[t].updateCount++,A(t));};}catch(r){l(`Failed to setup sync for "${t}": ${r?.message||r}`);}};var ne=(t,n)=>{let e,r;return ()=>{let o=u[t];return o===void 0?null:(o===e||(e=o,r=n(o)),r??null)}},qt=(t,n,e=Object.is,r)=>{if(typeof n!="function"||typeof r!="function")return l(`subscribeWithSelector("${t}") requires selector and listener functions.`),()=>{};let o=n(u[t]);return vt(t,c=>{let a=n(c);if(!e(a,o)){let f=o;o=a,r(a,f);}})},re=(t,n=0,e={})=>(M(t,{value:n},e),{inc:(r=1)=>v(t,o=>{o.value+=r;}),dec:(r=1)=>v(t,o=>{o.value-=r;}),set:r=>v(t,"value",r),reset:()=>bt(t),get:()=>rt(t,"value")}),oe=(t,n=[],e={})=>(M(t,{items:n},e),{push:r=>v(t,o=>{o.items.push(r);}),removeAt:r=>v(t,o=>{o.items.splice(r,1);}),clear:()=>v(t,{items:[]}),replace:r=>v(t,{items:r}),all:()=>rt(t,"items")}),se=(t,n={})=>(M(t,{entities:{},ids:[]},n),{upsert:e=>v(t,r=>{let o=e.id??e._id??(typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():`${Date.now()}_${Math.random().toString(16).slice(2)}`);r.ids.includes(o)||r.ids.push(o),r.entities[o]=e;}),remove:e=>v(t,r=>{r.ids=r.ids.filter(o=>o!==e),delete r.entities[e];}),all:()=>{let e=u[t];return e?e.ids.map(r=>e.entities[r]):[]},get:e=>rt(t,`entities.${e}`),clear:()=>bt(t)}),ie=()=>h(u),ae=(t,n)=>{if(!b[t])return [];let e=b[t];return n&&n>0?e.slice(-n):[...e]},ce=t=>{t?delete b[t]:Object.keys(b).forEach(n=>delete b[n]);},ue=t=>{let n=i[t];return n?.metrics?{...n.metrics}:null},le=t=>{let n={};return typeof t=="function"&&t({create:(r,o,s={})=>(n[r]=h(o),n[r]),set:(r,o)=>{if(n[r])return n[r]=typeof o=="function"?G(n[r],o):o,n[r]},get:r=>h(n[r])}),{snapshot:()=>h(n),hydrate:(r={})=>Lt(n,r)}},Lt=(t,n={})=>{!t||typeof t!="object"||Object.entries(t).forEach(([e,r])=>{Kt(e)?v(e,r):M(e,r,n[e]||n.default||{});});},de=(t,n={})=>{let e=n.name||`zstore_${Date.now()}`,r=(a,f=false)=>{let d=u[e]??{},p=typeof a=="function"?a(d):a,g=f?p:{...d,...p};v(e,g);},o=()=>u[e],s={setState:r,getState:o,subscribe:a=>vt(e,a),subscribeWithSelector:(a,f=Object.is,d)=>qt(e,a,f,d??(()=>{})),destroy:()=>ht(e)},c=t(r,o,s);return M(e,c,n),s};export{Lt as A,de as B,S as a,l as b,m as c,M as d,v as e,Wt as f,rt as g,ht as h,bt as i,Yt as j,Zt as k,Kt as l,Qt as m,te as n,vt as o,ee as p,ne as q,qt as r,re as s,oe as t,se as u,ie as v,ae as w,ce as x,ue as y,le as z};
@@ -1,3 +0,0 @@
1
- import {r,o,p,l,b,a}from'./chunk-6V676XCZ.js';import {useCallback,useSyncExternalStore,useEffect}from'react';var R=(t,n)=>{if(!n)return t;let r=n.split("."),e=t;for(let s of r){if(e==null)return null;e=e[s];}return e??null},S=new Set;function w(t,n,r$1=Object.is){let e=typeof n=="function",s=typeof n=="string"?n:void 0,i=e?n:void 0,o$1=useCallback(u=>e?r(t,h=>i(h),r$1,u):o(t,()=>u()),[t,e,i,r$1]),l$1=useCallback(()=>{let u=p(t);return e?u==null?null:i(u):R(u,s)},[t,e,i,s]),y=useSyncExternalStore(o$1,l$1,l$1);return useEffect(()=>{l(t)||b(`useStore("${t}") - store not found yet.
2
- Component will update automatically when createStore("${t}") is called.`),a()&&!e&&!s&&!S.has(t)&&(S.add(t),b(`useStore("${t}") without a selector/path subscribes to the entire store and may re-render on every change.
3
- Prefer useSelector/useStoreField for better performance.`));},[t,e,s]),y}var j=(t,n)=>w(t,n),k=(t,n,r$1=Object.is)=>{let e=useCallback(()=>{let o=p(t);return o==null?null:n(o)},[t,n]),s=useCallback(o=>r(t,l=>n(l),r$1,()=>o()),[t,n,r$1]);return useSyncExternalStore(s,e,e)},C=(t,n)=>{let r=p(t);return r==null?null:R(r,n)};export{w as a,j as b,k as c,C as d};
@@ -1,5 +0,0 @@
1
- import {c,a,b,l,d,e}from'./chunk-6V676XCZ.js';var k={},g={},y={},v={},x=new Set,I=e=>new Promise(t=>setTimeout(t,e)),V=(e,t)=>{if(!t)return false;let r=v[e];return r?Date.now()-r.timestamp<t:false},s={cacheHits:0,cacheMisses:0,dedupes:0,requests:0,failures:0,avgMs:0,lastMs:0},J=async(e$1,t,r={})=>{if(!e$1||typeof e$1!="string"){c("fetchStore requires a store name as first argument");return}if(!t){c(`fetchStore("${e$1}") requires a URL or Promise as second argument`);return}let{transform:f,onSuccess:q,onError:F,method:P,headers:_,body:D,ttl:L,staleWhileRevalidate:T=false,dedupe:C=true,retry:O=0,retryDelay:j=400,retryBackoff:U=1.7,signal:w,cacheKey:p}=r;!w&&a()&&!x.has(e$1)&&(x.add(e$1),b(`fetchStore("${e$1}") called without an AbortSignal. Provide "signal" to enable cancellation (recommended).`));let n=p?`${e$1}:${p}`:e$1;if(l(e$1)||d(e$1,{data:null,loading:false,error:null,status:"idle"}),V(n,L)){s.cacheHits+=1;let l=v[n].data;if(e(e$1,{data:l,loading:false,error:null,status:"success",cached:true}),!T)return l}else s.cacheMisses+=1;if(C&&g[n])return s.dedupes+=1,g[n];let b$1=(y[n]??0)+1;y[n]=b$1,e(e$1,{loading:true,error:null,status:"loading"}),s.requests+=1;let H=Date.now(),K=!w&&typeof AbortController<"u"?new AbortController:null,W=w||K?.signal,R=(async()=>{let l=0,M=j??400;for(;;)try{let o;if(typeof t=="string"){let B=N({method:P,headers:_,body:D,signal:W,...r}),i=await fetch(t,B);if(!i.ok)throw new Error(`HTTP ${i.status}: ${i.statusText}`);(i.headers.get("content-type")||"").includes("application/json")?o=await i.json():o=await i.text();}else if(typeof t.then=="function")o=await t;else return c(`fetchStore("${e$1}") - second argument must be a URL string or Promise.
2
- Examples:
3
- fetchStore("users", "https://api.example.com/users")
4
- fetchStore("users", axios.get("/users"))`),null;let u=f?f(o):o;if(y[n]!==b$1)return null;v[n]={timestamp:Date.now(),data:u},e(e$1,{data:u,loading:!1,error:null,status:"success"}),q?.(u);let a=Date.now()-H;return s.lastMs=a,s.avgMs=(s.avgMs*(s.requests-1)+a)/s.requests,u}catch(o){if(l+=1,o?.name==="AbortError")return b(`fetchStore("${e$1}") aborted`),e(e$1,{loading:false,error:"aborted",status:"aborted"}),null;if(l<=(O??0)){await I(M),M*=U??1.7;continue}if(y[n]!==b$1)return null;let a=o?.message||"Something went wrong";return e(e$1,{data:null,loading:false,error:a,status:"error"}),F?.(a),s.failures+=1,b(`fetchStore("${e$1}") failed: ${a}`),null}})().finally(()=>{delete g[n];});return g[n]=R,k[e$1]={url:t,options:{...r,cacheKey:p}},R},A=async e=>{let t=k[e];if(!t){a()&&b(`refetchStore("${e}") - no previous fetch found.
5
- Call fetchStore("${e}", url) first.`);return}return J(e,t.url,t.options)},S=new Set,Y=e=>{if(typeof window>"u"||typeof window.addEventListener!="function")return ()=>{};let t=e??"*";if(S.has(t))return ()=>{};let r=()=>{t==="*"?Object.keys(k).forEach(f=>{A(f);}):A(t);};return window.addEventListener("focus",r),window.addEventListener("online",r),S.add(t),()=>{window.removeEventListener("focus",r),window.removeEventListener("online",r),S.delete(t);}},N=e=>{let t={};return e.method&&(t.method=e.method.toUpperCase()),e.headers?t.headers=e.headers:t.headers={"Content-Type":"application/json"},e.body&&(t.body=typeof e.body=="string"?e.body:JSON.stringify(e.body)),e.signal&&(t.signal=e.signal),t},Z=()=>({...s});export{J as a,A as b,Y as c,Z as d};