stroid 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
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.
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # stroid
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`.
4
+
5
+ ## Quick start
6
+
7
+ ```js
8
+ import { createStore, setStore, useStore } from "stroid";
9
+
10
+ createStore("user", { name: "Alex", theme: "dark" }, { devtools: true, persist: true });
11
+ setStore("user", (draft) => { draft.name = "Jordan"; });
12
+
13
+ function Profile() {
14
+ const name = useStore("user", "name");
15
+ return <div>{name}</div>;
16
+ }
17
+ ```
18
+
19
+ Install: `npm install stroid`
20
+
21
+ ## 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
+
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.
39
+
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.
44
+
45
+ ## Versioning / Semver
46
+ Follows semver. Breaking changes bump MAJOR; features MINOR; fixes PATCH. See CHANGELOG.md.
@@ -0,0 +1,30 @@
1
+ interface FetchOptions {
2
+ transform?: (result: unknown) => unknown;
3
+ onSuccess?: (data: unknown) => void;
4
+ onError?: (message: string) => void;
5
+ method?: string;
6
+ headers?: Record<string, string>;
7
+ body?: unknown;
8
+ ttl?: number;
9
+ staleWhileRevalidate?: boolean;
10
+ dedupe?: boolean;
11
+ retry?: number;
12
+ retryDelay?: number;
13
+ retryBackoff?: number;
14
+ signal?: AbortSignal;
15
+ cacheKey?: string;
16
+ }
17
+ declare const fetchStore: (name: string, urlOrPromise: string | Promise<unknown>, options?: FetchOptions) => Promise<unknown>;
18
+ declare const refetchStore: (name: string) => Promise<unknown>;
19
+ declare const enableRevalidateOnFocus: (name?: string) => (() => void);
20
+ declare const getAsyncMetrics: () => {
21
+ cacheHits: number;
22
+ cacheMisses: number;
23
+ dedupes: number;
24
+ requests: number;
25
+ failures: number;
26
+ avgMs: number;
27
+ lastMs: number;
28
+ };
29
+
30
+ export { type FetchOptions, enableRevalidateOnFocus, fetchStore, getAsyncMetrics, refetchStore };
package/dist/async.js ADDED
@@ -0,0 +1 @@
1
+ export{c as enableRevalidateOnFocus,a as fetchStore,d as getAsyncMetrics,b as refetchStore}from'./chunk-TB5U3OFY.js';import'./chunk-6V676XCZ.js';
@@ -0,0 +1,18 @@
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};
@@ -0,0 +1,3 @@
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};
@@ -0,0 +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};
@@ -0,0 +1,5 @@
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};
@@ -0,0 +1,205 @@
1
+ type PathInput = string | readonly string[] | string[];
2
+
3
+ type Primitive = string | number | boolean | bigint | symbol | null | undefined;
4
+ type PrevDepth = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
5
+ type PathInternal<T, Depth extends number> = Depth extends 0 ? never : T extends Primitive ? never : {
6
+ [K in keyof T & (string | number)]: T[K] extends Primitive | Array<unknown> ? `${K}` : `${K}` | `${K}.${PathInternal<T[K], PrevDepth[Depth]>}`;
7
+ }[keyof T & (string | number)];
8
+ type Path<T, Depth extends number = 6> = PathInternal<T, Depth>;
9
+ type PathValue<T, P extends Path<T>> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? Rest extends Path<T[K]> ? PathValue<T[K], Rest> : never : never : P extends keyof T ? T[P] : never;
10
+ type PartialDeep<T> = T extends Primitive ? T : {
11
+ [K in keyof T]?: PartialDeep<T[K]>;
12
+ };
13
+ type StoreValue = unknown;
14
+ interface StoreDefinition<Name extends string = string, State = StoreValue> {
15
+ name: Name;
16
+ state?: State;
17
+ }
18
+ interface PersistConfig {
19
+ driver: {
20
+ getItem?: (k: string) => string | null;
21
+ setItem?: (k: string, v: string) => void;
22
+ removeItem?: (k: string) => void;
23
+ [key: string]: unknown;
24
+ };
25
+ key: string;
26
+ serialize: (v: unknown) => string;
27
+ deserialize: (v: string) => unknown;
28
+ encrypt: (v: string) => string;
29
+ decrypt: (v: string) => string;
30
+ }
31
+ interface MiddlewareCtx {
32
+ action: string;
33
+ name: string;
34
+ prev: StoreValue;
35
+ next: StoreValue;
36
+ path: unknown;
37
+ }
38
+ interface StoreOptions<State = StoreValue> {
39
+ persist?: boolean | string | PersistConfig;
40
+ devtools?: boolean;
41
+ middleware?: Array<(ctx: MiddlewareCtx) => StoreValue | void>;
42
+ onSet?: (prev: State, next: State) => void;
43
+ onReset?: (prev: State, next: State) => void;
44
+ onDelete?: (prev: State) => void;
45
+ onCreate?: (initial: State) => void;
46
+ onError?: (err: string) => void;
47
+ validator?: (next: State) => boolean;
48
+ schema?: unknown;
49
+ migrations?: Record<number, (state: State) => State>;
50
+ version?: number;
51
+ redactor?: (state: State) => State;
52
+ historyLimit?: number;
53
+ allowSSRGlobalStore?: boolean;
54
+ sync?: boolean | {
55
+ channel?: string;
56
+ conflictResolver?: (args: {
57
+ local: StoreValue;
58
+ incoming: StoreValue;
59
+ localUpdated: number;
60
+ incomingUpdated: number;
61
+ }) => StoreValue | void;
62
+ };
63
+ }
64
+ type NormalizedOptions = {
65
+ persist: PersistConfig | null;
66
+ devtools: boolean;
67
+ middleware: Array<(ctx: MiddlewareCtx) => StoreValue | void>;
68
+ onSet?: (prev: StoreValue, next: StoreValue) => void;
69
+ onReset?: (prev: StoreValue, next: StoreValue) => void;
70
+ onDelete?: (prev: StoreValue) => void;
71
+ onCreate?: (initial: StoreValue) => void;
72
+ onError?: (err: string) => void;
73
+ validator?: (next: StoreValue) => boolean;
74
+ schema?: unknown;
75
+ migrations: Record<number, (state: any) => any>;
76
+ version: number;
77
+ redactor?: (state: StoreValue) => StoreValue;
78
+ historyLimit: number;
79
+ allowSSRGlobalStore?: boolean;
80
+ sync?: boolean | {
81
+ channel?: string;
82
+ conflictResolver?: (args: {
83
+ local: StoreValue;
84
+ incoming: StoreValue;
85
+ localUpdated: number;
86
+ incomingUpdated: number;
87
+ }) => StoreValue | void;
88
+ };
89
+ };
90
+ interface MetaEntry {
91
+ createdAt: string;
92
+ updatedAt: string;
93
+ updateCount: number;
94
+ version: number;
95
+ metrics: {
96
+ notifyCount: number;
97
+ totalNotifyMs: number;
98
+ lastNotifyMs: number;
99
+ };
100
+ options: NormalizedOptions;
101
+ }
102
+ interface HistoryEntry {
103
+ ts: number;
104
+ action: string;
105
+ prev: StoreValue;
106
+ next: StoreValue;
107
+ diff: {
108
+ added: string[];
109
+ removed: string[];
110
+ changed: string[];
111
+ } | null;
112
+ }
113
+ type Subscriber = (value: StoreValue | null) => void;
114
+ declare const createStore: <Name extends string, State>(name: Name, initialData: State, option?: StoreOptions<State>) => StoreDefinition<Name, State> | undefined;
115
+ declare function setStore<Name extends string, State, P extends Path<State>>(name: StoreDefinition<Name, State>, path: P, value: PathValue<State, P>): void;
116
+ declare function setStore<Name extends string, State>(name: StoreDefinition<Name, State>, mutator: (draft: State) => void): void;
117
+ declare function setStore<Name extends string, State>(name: StoreDefinition<Name, State>, data: PartialDeep<State>): void;
118
+ declare function setStore(name: string, data: Record<string, unknown>): void;
119
+ declare function setStore(name: string, path: string | string[], value: unknown): void;
120
+ declare function setStore(name: string, mutator: (draft: any) => void): void;
121
+ declare const setStoreBatch: (fn: () => void) => void;
122
+ declare function getStore<Name extends string, State, P extends Path<State>>(name: StoreDefinition<Name, State>, path: P): PathValue<State, P> | null;
123
+ declare function getStore<Name extends string, State>(name: StoreDefinition<Name, State>, path?: undefined): State | null;
124
+ declare function getStore(name: string, path?: PathInput): StoreValue | null;
125
+ declare const deleteStore: (name: string) => void;
126
+ declare const resetStore: (name: string) => void;
127
+ declare const mergeStore: (name: string, data: Record<string, unknown>) => void;
128
+ declare const clearAllStores: () => void;
129
+ declare const hasStore: (name: string) => boolean;
130
+ declare const listStores: () => string[];
131
+ declare const getStoreMeta: (name: string) => MetaEntry | null;
132
+ declare const createSelector: <TState, TResult>(storeName: string, selectorFn: (state: TState) => TResult) => () => NonNullable<TResult> | null;
133
+ declare const subscribeWithSelector: <R>(name: string, selector: (state: any) => R, equality: ((a: R, b: R) => boolean) | undefined, listener: (next: R, prev: R) => void) => (() => void);
134
+ declare const createCounterStore: (name: string, initial?: number, options?: StoreOptions) => {
135
+ inc: (n?: number) => void;
136
+ dec: (n?: number) => void;
137
+ set: (v: number) => void;
138
+ reset: () => void;
139
+ get: () => unknown;
140
+ };
141
+ declare const createListStore: <T>(name: string, initial?: T[], options?: StoreOptions) => {
142
+ push: (item: T) => void;
143
+ removeAt: (index: number) => void;
144
+ clear: () => void;
145
+ replace: (items: T[]) => void;
146
+ all: () => T[];
147
+ };
148
+ declare const createEntityStore: <T extends {
149
+ id?: string;
150
+ _id?: string;
151
+ }>(name: string, options?: StoreOptions) => {
152
+ upsert: (entity: T) => void;
153
+ remove: (id: string) => void;
154
+ all: () => T[];
155
+ get: (id: string) => T | null;
156
+ clear: () => void;
157
+ };
158
+ declare const getInitialState: () => Record<string, StoreValue>;
159
+ declare const getHistory: (name: string, limit?: number) => HistoryEntry[];
160
+ declare const clearHistory: (name?: string) => void;
161
+ declare const getMetrics: (name: string) => MetaEntry["metrics"] | null;
162
+ declare const createStoreForRequest: (initializer?: (api: {
163
+ create: (name: string, data: any, options?: StoreOptions) => any;
164
+ set: (name: string, updater: any) => any;
165
+ get: (name: string) => any;
166
+ }) => void) => {
167
+ snapshot: () => Record<string, any>;
168
+ hydrate: (options?: Record<string, StoreOptions> & {
169
+ default?: StoreOptions;
170
+ }) => void;
171
+ };
172
+ declare const hydrateStores: (snapshot: Record<string, any>, options?: Record<string, StoreOptions> & {
173
+ default?: StoreOptions;
174
+ }) => void;
175
+ declare const createZustandCompatStore: <T>(initializer: (set: (partial: Partial<T>, replace?: boolean) => void, get: () => T, api: any) => T, options?: StoreOptions & {
176
+ name?: string;
177
+ }) => {
178
+ setState: (partial: Partial<T> | ((state: T) => Partial<T>), replace?: boolean) => void;
179
+ getState: () => T;
180
+ subscribe: (listener: Subscriber) => () => void;
181
+ subscribeWithSelector: (selector: (state: T) => any, equality?: (value1: any, value2: any) => boolean, listener?: (next: any, prev: any) => void) => () => void;
182
+ destroy: () => void;
183
+ };
184
+
185
+ declare class StroidChain {
186
+ private _storeName;
187
+ private _path;
188
+ constructor(storeName: string, path?: string[]);
189
+ nested(key: string): StroidChain;
190
+ target(key: string): TargetNode;
191
+ get value(): unknown;
192
+ set(newValue: unknown): void;
193
+ }
194
+ declare class TargetNode {
195
+ private _storeName;
196
+ private _parentPath;
197
+ private _key;
198
+ private _fullPath;
199
+ constructor(storeName: string, parentPath: string[], key: string | null);
200
+ get value(): unknown;
201
+ set(newValue: unknown): void;
202
+ }
203
+ declare const chain: (storeName: string) => StroidChain;
204
+
205
+ export { clearAllStores as a, clearHistory as b, chain as c, createCounterStore as d, createEntityStore as e, createListStore as f, createSelector as g, createStore as h, createStoreForRequest as i, createZustandCompatStore as j, deleteStore as k, getHistory as l, getInitialState as m, getMetrics as n, getStore as o, getStoreMeta as p, hasStore as q, hydrateStores as r, listStores as s, mergeStore as t, resetStore as u, setStore as v, setStoreBatch as w, subscribeWithSelector as x };
package/dist/core.d.ts ADDED
@@ -0,0 +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';
package/dist/core.js ADDED
@@ -0,0 +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';
@@ -0,0 +1,18 @@
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';
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 };
package/dist/index.js ADDED
@@ -0,0 +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};
@@ -0,0 +1,7 @@
1
+ declare function useStore<T = any>(name: string, path?: string): T | null;
2
+ declare function useStore<T = any, R = any>(name: string, selector: (state: T) => R, equalityFn?: (a: R, b: R) => boolean): R | null;
3
+ declare const useStoreField: <T = any>(storeName: string, field: string) => T | null;
4
+ declare const useSelector: <T = any, R = any>(storeName: string, selectorFn: (state: T) => R, equalityFn?: (a: R, b: R) => boolean) => R | null;
5
+ declare const useStoreStatic: <T = any>(name: string, path?: string) => T | null;
6
+
7
+ export { useSelector, useStore, useStoreField, useStoreStatic };
package/dist/react.js ADDED
@@ -0,0 +1 @@
1
+ export{c as useSelector,a as useStore,b as useStoreField,d as useStoreStatic}from'./chunk-CZARAS3I.js';import'./chunk-6V676XCZ.js';
@@ -0,0 +1,16 @@
1
+ declare const createMockStore: (name?: string, initial?: Record<string, unknown>) => {
2
+ set: (update: Record<string, unknown> | ((draft: any) => void)) => void;
3
+ reset: () => void;
4
+ use: () => {
5
+ name: string;
6
+ };
7
+ };
8
+ declare const withMockedTime: <T>(nowMs: number, fn: () => T) => T;
9
+ declare const resetAllStoresForTest: () => void;
10
+ declare const benchmarkStoreSet: (name: string, iterations?: number, makeUpdate?: (i: number) => Record<string, unknown>) => {
11
+ iterations: number;
12
+ totalMs: number;
13
+ avgMs: number;
14
+ };
15
+
16
+ export { benchmarkStoreSet, createMockStore, resetAllStoresForTest, withMockedTime };
@@ -0,0 +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};
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "stroid",
3
+ "version": "0.0.1",
4
+ "description": "Compact state management library with persistence, async caching, sync, and React hooks.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./core": {
13
+ "types": "./dist/core.d.ts",
14
+ "import": "./dist/core.js"
15
+ },
16
+ "./async": {
17
+ "types": "./dist/async.d.ts",
18
+ "import": "./dist/async.js"
19
+ },
20
+ "./react": {
21
+ "types": "./dist/react.d.ts",
22
+ "import": "./dist/react.js"
23
+ },
24
+ "./testing": {
25
+ "types": "./dist/testing.d.ts",
26
+ "import": "./dist/testing.js"
27
+ },
28
+ "./package.json": "./package.json"
29
+ },
30
+ "types": "dist/index.d.ts",
31
+ "files": [
32
+ "dist",
33
+ "README.md",
34
+ "CHANGELOG.md"
35
+ ],
36
+ "sideEffects": false,
37
+ "scripts": {
38
+ "test": "node --import tsx --test \"tests/**/*.test.ts\"",
39
+ "typecheck": "tsc -p tsconfig.types.json",
40
+ "build": "tsup"
41
+ },
42
+ "engines": {
43
+ "node": ">=18"
44
+ },
45
+ "license": "MIT",
46
+ "peerDependencies": {
47
+ "react": ">=18"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^22.9.0",
51
+ "@types/react": "^18.2.46",
52
+ "eslint": "^8.57.0",
53
+ "eslint-config-standard-with-typescript": "^39.1.1",
54
+ "eslint-plugin-import": "^2.29.1",
55
+ "eslint-plugin-n": "^16.6.2",
56
+ "eslint-plugin-promise": "^6.1.1",
57
+ "tsup": "^8.5.1",
58
+ "tsx": "^4.7.0",
59
+ "typescript": "^5.7.2"
60
+ }
61
+ }