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 +48 -8
- package/LICENSE +21 -21
- package/README.md +67 -31
- package/dist/async.js +1 -1
- package/dist/chunk-5F2FD6DX.js +17 -0
- package/dist/chunk-G6JMMJYH.js +5 -0
- package/dist/chunk-JBYLHJKN.js +3 -0
- package/dist/{chunk-SKZAS43O.js → chunk-K6QIWMMW.js} +1 -1
- package/dist/{core-CLIi-SLm.d.ts → core-CKzRwVaY.d.ts} +8 -0
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/index.d.ts +2 -17
- package/dist/index.js +1 -1
- package/dist/react.d.ts +14 -1
- package/dist/react.js +1 -1
- package/dist/testing.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-6V676XCZ.js +0 -18
- package/dist/chunk-CZARAS3I.js +0 -3
- package/dist/chunk-TB5U3OFY.js +0 -5
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
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
- Docs:
|
|
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
|
|
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
|
-
#
|
|
1
|
+
# Stroid
|
|
2
2
|
|
|
3
|
-
Compact state management for JavaScript
|
|
3
|
+
Compact, batteries-included state management for JavaScript and React.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
|
18
|
+
import { createStore, setStore } from "stroid/core"
|
|
19
|
+
import { useStore } from "stroid/react"
|
|
9
20
|
|
|
10
|
-
createStore("user", { name: "
|
|
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 <
|
|
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
|
-
|
|
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
|
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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
package/dist/chunk-6V676XCZ.js
DELETED
|
@@ -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};
|
package/dist/chunk-CZARAS3I.js
DELETED
|
@@ -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};
|
package/dist/chunk-TB5U3OFY.js
DELETED
|
@@ -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};
|