stunk 2.8.1 → 3.0.0-beta.2

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/README.md CHANGED
@@ -1,22 +1,21 @@
1
1
  # Stunk
2
2
 
3
- Stunk is a lightweight, framework-agnostic state management library built on atomic state principles. It simplifies state management by breaking state into manageable "chunks", ensuring efficient updates and reactivity.
3
+ Stunk is a lightweight, framework-agnostic state management library built on atomic state principles. Break state into independent **chunks** each one reactive, composable, and self-contained.
4
4
 
5
- - **Pronunciation**: _Stunk_ (A playful blend of "state" and "chunk")
6
-
7
- **Stunk** is like dividing your jar into many smaller containers, each holding a single piece of state. These smaller containers are called **chunks**. Each **chunk** can be updated and accessed easily, and any part of your app can subscribe to changes in a chunk so it gets updated automatically.
5
+ - **Pronunciation**: _Stunk_ (a playful blend of "state" and "chunk")
8
6
 
9
7
  ## Features
10
8
 
11
- - 🚀 **Lightweight and Fast**: No dependencies, minimal overhead
12
- - 🔄 **Reactive**: Automatic updates when state changes
13
- - 📦 **Batch Updates**: Group multiple state updates together
14
- - 🎯 **Atomic State Management**: Break down state into manageable chunks
15
- - 🎭 **State Selection**: Select and derive specific parts of the state
16
- - 🔄 **Async Support**: Handle async state with built-in loading and error states
17
- - 🔌 **Middleware Support**: Extend functionality with custom middleware
18
- - ⏱️ **Time Travel**: Undo/redo state changes
19
- - 🔍 **Type-Safe**: Written in TypeScript with full type inference
9
+ - 🚀 **Lightweight** 3.32kB gzipped, zero dependencies
10
+ - ⚛️ **Atomic** break state into independent chunks
11
+ - 🔄 **Reactive** fine-grained updates, only affected components re-render
12
+ - 🧮 **Auto-tracking computed** no dependency arrays, just call `.get()`
13
+ - 🌐 **Async & Query layer** loading, error, caching, deduplication, pagination built in
14
+ - 🔁 **Mutations** reactive POST/PUT/DELETE with automatic cache invalidation
15
+ - 📦 **Batch updates** group multiple updates into one render
16
+ - 🔌 **Middleware** logging, persistence, validation — plug anything in
17
+ - ⏱️ **Time travel** undo/redo state changes
18
+ - 🔍 **TypeScript first** — full type inference, no annotations needed
20
19
 
21
20
  ## Installation
22
21
 
@@ -25,188 +24,232 @@ npm install stunk
25
24
  # or
26
25
  yarn add stunk
27
26
  # or
28
- pnpm install stunk
27
+ pnpm add stunk
29
28
  ```
30
29
 
31
- Read Docs:
30
+ 📖 [Read the docs](https://stunk.dev)
32
31
 
33
- [Stunk](https://stunk.dev/)
32
+ ---
34
33
 
35
- ## Creating a Chunk
34
+ ## Core State
36
35
 
37
- ```typescript
36
+ ```ts
38
37
  import { chunk } from "stunk";
39
38
 
40
- // Create a chunk holding a number
41
- const count = chunk<number>(0);
39
+ const count = chunk(0);
42
40
 
43
- // Create a chunk holding a string
44
- const name = chunk<string>("Stunky, chunky");
41
+ count.get(); // 0
42
+ count.set(10); // set directly
43
+ count.set((prev) => prev + 1); // updater function
44
+ count.peek(); // read without tracking dependencies
45
+ count.reset(); // back to 0
46
+ count.destroy(); // clear all subscribers
45
47
  ```
46
48
 
47
- 👉 [See full explanation in docs](https://stunk.dev/chunk.html)
49
+ ---
50
+
51
+ ## Computed — auto dependency tracking
48
52
 
49
- ## Interacting with a Chunk
53
+ No dependency arrays. Any chunk whose `.get()` is called inside the function is tracked automatically:
50
54
 
51
- ```typescript
52
- // Get value
53
- console.log(count.get()); // 0
55
+ ```ts
56
+ import { chunk, computed } from "stunk";
54
57
 
55
- // Set a new value
56
- count.set(10);
58
+ const price = chunk(100);
59
+ const quantity = chunk(3);
57
60
 
58
- // Update based on the previous value
59
- count.set((prev: number) => prev + 1);
61
+ const total = computed(() => price.get() * quantity.get());
60
62
 
61
- // Reset to the initial value
62
- count.reset();
63
+ total.get(); // 300
63
64
 
64
- // Destroy the chunk and all its subscribers.
65
- count.destroy();
65
+ price.set(200);
66
+ total.get(); // 600 — recomputed automatically
66
67
  ```
67
68
 
68
- 👉 [See full explanation in docs](https://stunk.dev/chunk.html)
69
+ Use `.peek()` to read without tracking:
69
70
 
70
- ## React via useChunk
71
+ ```ts
72
+ const taxRate = chunk(0.1);
73
+ const subtotal = computed(() => price.get() * (1 + taxRate.peek()));
74
+ // only recomputes when price changes
75
+ ```
71
76
 
72
- The `useChunk` hook, enables components to reactively read and update state from a Chunk. The counter example below depicts
77
+ ---
73
78
 
74
- ```typescript
75
- import { chunk } from "stunk";
76
- import { useChunk } from "stunk/react";
79
+ ## Async & Query
77
80
 
78
- const count = chunk<number>(0);
81
+ ```ts
82
+ import { asyncChunk } from "stunk/query";
79
83
 
80
- const Counter = () => {
81
- const [value, set, reset] = useChunk(count);
84
+ const userChunk = asyncChunk(
85
+ async ({ id }: { id: number }) => fetchUser(id),
86
+ {
87
+ key: "user", // deduplicates concurrent requests
88
+ keepPreviousData: true, // no UI flicker on param changes
89
+ staleTime: 30_000,
90
+ onError: (err) => toast.error(err.message),
91
+ }
92
+ );
82
93
 
83
- return (
84
- <div>
85
- <p>Count: {value}</p>
86
- <button onClick={() => set((prev: number) => prev + 1)}>Increment</button>
87
- <button onClick={() => reset()}>Reset</button>
88
- </div>
89
- );
90
- };
94
+ userChunk.setParams({ id: 1 });
95
+ // { loading: true, data: null, error: null }
96
+ // { loading: false, data: { id: 1, name: "..." }, error: null }
91
97
  ```
92
98
 
93
- 👉 [See full explanation in docs](https://stunk.dev/useChunk.html)
99
+ ---
94
100
 
95
- ## React via useDerive
101
+ ## Mutations
96
102
 
97
- Hook that lets you create a read-only derived state from a Chunk. It keeps the derived value reactive, automatically updating whenever the source Chunk changes.
103
+ Reactive POST/PUT/DELETE one function, always safe to await or fire and forget:
98
104
 
99
- ```typescript
100
- import { chunk } from "stunk";
101
- import { useDerive } from "stunk/react";
105
+ ```ts
106
+ import { mutation } from "stunk/query";
102
107
 
103
- const count = chunk<number>(0);
108
+ const createPost = mutation(
109
+ async (data: NewPost) => fetchAPI("/posts", { method: "POST", body: data }),
110
+ {
111
+ invalidates: [postsChunk], // auto-reloads on success
112
+ onSuccess: () => toast.success("Created!"),
113
+ onError: (err) => toast.error(err.message),
114
+ }
115
+ );
104
116
 
105
- const DoubledCount = () => {
106
- const double = useDerive(count, (value: number) => value * 2);
117
+ // Fire and forget safe
118
+ createPost.mutate({ title: "Hello" });
107
119
 
108
- return <p>Double: {double}</p>;
109
- };
120
+ // Await for local control — no try/catch needed
121
+ const { data, error } = await createPost.mutate({ title: "Hello" });
122
+ if (!error) router.push("/posts");
110
123
  ```
111
124
 
112
- 👉 [See full explanation in docs](https://stunk.dev/useDerive.html)
125
+ ---
113
126
 
114
- ## React via useComputed
127
+ ## Global Query Config
115
128
 
116
- Hook that derives a computed value from one or more Chunks. It automatically re-evaluates whenever any of its dependencies change, ensuring efficient and reactive updates.
129
+ Set defaults once for all async chunks and mutations:
117
130
 
118
- ```typescript
119
- import { chunk } from "stunk";
120
- import { useComputed } from "stunk/react";
131
+ ```ts
132
+ import { configureQuery } from "stunk/query";
121
133
 
122
- const count = chunk<number>(2);
123
- const multiplier = chunk<number>(3);
134
+ configureQuery({
135
+ query: {
136
+ staleTime: 30_000,
137
+ retryCount: 3,
138
+ onError: (err) => toast.error(err.message),
139
+ },
140
+ mutation: {
141
+ onError: (err) => toast.error(err.message),
142
+ },
143
+ });
144
+ ```
124
145
 
125
- const ComputedExample = () => {
126
- const product = useComputed(
127
- [count, multiplier],
128
- (c: number, m: number) => c * m
129
- );
146
+ ---
147
+
148
+ ## Middleware
149
+
150
+ ```ts
151
+ import { chunk } from "stunk";
152
+ import { logger, nonNegativeValidator } from "stunk/middleware";
130
153
 
131
- return <p>Product: {product}</p>;
132
- };
154
+ const score = chunk(0, {
155
+ middleware: [logger(), nonNegativeValidator]
156
+ });
157
+
158
+ score.set(10); // logs: "Setting value: 10"
159
+ score.set(-1); // throws: "Value must be non-negative!"
133
160
  ```
134
161
 
135
- 👉 [See full explanation in docs](https://stunk.dev/useComputed.html)
162
+ ---
136
163
 
137
- ## React via useAsyncChunk
164
+ ## History (undo/redo)
138
165
 
139
- Hook that manages that manages asynchronous state. It offers built-in reactivity, handling loading, error, and data states, ensuring the UI stays in sync with asynchronous operations.
166
+ ```ts
167
+ import { chunk } from "stunk";
168
+ import { history } from "stunk/middleware";
140
169
 
141
- ```typescript
142
- import { asyncChunk } from "stunk";
143
- import { useAsyncChunk } from "stunk/react";
170
+ const count = chunk(0);
171
+ const tracked = history(count);
144
172
 
145
- interface User {
146
- name: string;
147
- email: string;
148
- }
173
+ tracked.set(1);
174
+ tracked.set(2);
175
+ tracked.undo(); // 1
176
+ tracked.redo(); // 2
177
+ tracked.reset(); // 0 — clears history too
178
+ ```
149
179
 
150
- const fetchUser = asyncChunk<User>(async () => {
151
- const res = await fetch("https://jsonplaceholder.typicode.com/users/1");
152
- return res.json();
153
- });
180
+ ---
154
181
 
155
- const UserProfile = () => {
156
- const { data, loading, error, reload } = useAsyncChunk(fetchUser);
182
+ ## Persist
157
183
 
158
- if (loading) return <p>Loading...</p>;
159
- if (error) return <p>Error: {error.message}</p>;
184
+ ```ts
185
+ import { chunk } from "stunk";
186
+ import { persist } from "stunk/middleware";
160
187
 
161
- return (
162
- <div>
163
- <h2>{data?.name}</h2>
164
- <p>{data?.email}</p>
165
- <button onClick={reload}>Reload</button>
166
- </div>
167
- );
168
- };
188
+ const theme = chunk<"light" | "dark">("light");
189
+ const saved = persist(theme, { key: "theme" });
190
+
191
+ saved.set("dark"); // saved to localStorage
192
+ saved.clearStorage(); // remove from localStorage
169
193
  ```
170
194
 
171
- 👉 [See full explanation in docs](https://stunk.dev/useAysncChunk.html)
195
+ ---
172
196
 
173
- ## React via useChunkValue
197
+ ## React
174
198
 
175
- Hook that subscribes to a Chunk and returns its current value. It is useful for read-only components that don’t need to modify the state.
199
+ ```tsx
200
+ import { chunk, computed } from "stunk";
201
+ import { useChunk, useChunkValue, useAsyncChunk, useMutation } from "stunk/react";
176
202
 
177
- ```typescript
178
- import { chunk } from "stunk";
179
- import { useChunkValue } from "stunk/react";
203
+ const counter = chunk(0);
204
+ const double = computed(() => counter.get() * 2);
180
205
 
181
- const count = chunk<number>(0);
206
+ function Counter() {
207
+ const [count, setCount] = useChunk(counter);
208
+ const doubled = useChunkValue(double);
182
209
 
183
- const CounterDisplay = () => {
184
- const value = useChunkValue(count);
185
-
186
- return <p>Count: {value}</p>;
187
- };
188
- ```
210
+ return (
211
+ <div>
212
+ <p>Count: {count} — Doubled: {doubled}</p>
213
+ <button onClick={() => setCount((n) => n + 1)}>+</button>
214
+ </div>
215
+ );
216
+ }
189
217
 
190
- 👉 [See full explanation in docs](https://stunk.dev/read-only-values.html)
218
+ // Async
219
+ function PostList() {
220
+ const { data, loading, error } = useAsyncChunk(postsChunk);
221
+ if (loading) return <p>Loading...</p>;
222
+ return <ul>{data?.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
223
+ }
191
224
 
192
- Live Examples:
225
+ // Mutation
226
+ function CreatePostForm() {
227
+ const { mutate, loading, error } = useMutation(createPost);
193
228
 
194
- 👉 [Visit](https://stunk-examples.dev/)
229
+ const handleSubmit = async (data: NewPost) => {
230
+ const { error } = await mutate(data);
231
+ if (!error) router.push("/posts");
232
+ };
233
+ }
234
+ ```
195
235
 
196
- Coding Examples:
236
+ ---
197
237
 
198
- 👉 [Visit](https://stunk.dev/examples.html)
238
+ ## Package exports
199
239
 
200
- Further Examples:
240
+ | Import | Contents |
241
+ | ------------------ | --------------------------------------------------------------- |
242
+ | `stunk` | `chunk`, `computed`, `select`, `batch`, `isChunk`, and more |
243
+ | `stunk/react` | `useChunk`, `useChunkValue`, `useAsyncChunk`, `useInfiniteAsyncChunk`, `useMutation` |
244
+ | `stunk/query` | `asyncChunk`, `infiniteAsyncChunk`, `combineAsyncChunks`, `mutation`, `configureQuery` |
245
+ | `stunk/middleware` | `history`, `persist`, `logger`, `nonNegativeValidator` |
201
246
 
202
- 👉 [Visit](https://github.com/I-am-abdulazeez/stunk-examples/)
247
+ ---
203
248
 
204
249
  ## Contributing
205
250
 
206
- Contributions are welcome! Please feel free to submit a Pull Request.
207
-
208
- [Pull Request](https://github.com/I-am-abdulazeez/stunk/pulls)
251
+ Contributions are welcome open a [pull request](https://github.com/I-am-abdulazeez/stunk/pulls) or [issue](https://github.com/I-am-abdulazeez/stunk/issues).
209
252
 
210
253
  ## License
211
254
 
212
- This is licence under MIT
255
+ MIT
@@ -0,0 +1 @@
1
+ function h(e,r){if(e===void 0)throw new Error("Value cannot be undefined.");if(r.length===0)return e;let t=e;for(let o=0;o<r.length;o++){let s=r[o],y=typeof s=="function"?s:s.fn,u=typeof s=="function"?`index ${o}`:s.name||`index ${o}`;try{let f=y(t);if(f===void 0)break;if(f===null)throw new Error(`Middleware "${u}" returned null value.`);t=f;}catch(f){let l=f instanceof Error?f.message:String(f);throw new Error(`Middleware "${u}" threw an error: ${l}`)}}return t}function k(e,r){if(e===r)return true;if(!e||!r||typeof e!=typeof r)return false;if(Array.isArray(e)&&Array.isArray(r)){if(e.length!==r.length)return false;for(let t=0;t<e.length;t++)if(e[t]!==r[t])return false;return true}if(typeof e=="object"&&typeof r=="object"){let t=Object.keys(e),o=Object.keys(r);if(t.length!==o.length)return false;for(let s of t)if(!Object.prototype.hasOwnProperty.call(r,s)||e[s]!==r[s])return false;return true}return false}function a(e,r,t="",o={}){let{checkMissing:s=true,checkTypes:y=true}=o;if(e===null||r===null||e===void 0||r===void 0)return;if(typeof e!=typeof r){console.error(`\u{1F6A8} Stunk: Type mismatch at '${t||"root"}'. Expected ${typeof e}, got ${typeof r}.`);return}if(typeof e!="object"||typeof r!="object")return;if(Array.isArray(e)!==Array.isArray(r)){console.error(`\u{1F6A8} Stunk: Type mismatch at '${t||"root"}'. Expected ${Array.isArray(e)?"array":"object"}, got ${Array.isArray(r)?"array":"object"}.`);return}if(Array.isArray(e)&&Array.isArray(r)){if(e.length>0&&typeof e[0]=="object")for(let n=0;n<r.length;n++)a(e[0],r[n],`${t}[${n}]`,o);return}let u=Object.keys(e),f=Object.keys(r),l=f.filter(n=>!u.includes(n));if(l.length>0&&(console.error(`\u{1F6A8} Stunk: Unknown properties at '${t||"root"}': ${l.join(", ")}`),console.error("Expected keys:",u),console.error("Received keys:",f)),s){let n=u.filter(c=>!f.includes(c));n.length>0&&console.error(`\u{1F6A8} Stunk: Missing properties at '${t||"root"}': ${n.join(", ")}`);}for(let n of u){let c=e[n],i=r[n];c===void 0||i===void 0||i===null||(y&&typeof c!="object"&&typeof c!=typeof i&&console.error(`\u{1F6A8} Stunk: Type mismatch at '${t?t+".":""}${n}'. Expected ${typeof c}, got ${typeof i}.`),a(c,i,t?`${t}.${n}`:n,o));}}export{h as a,k as b,a as c};
@@ -0,0 +1 @@
1
+ import {c,a}from'./chunk-3DOB632D.js';var v=new Set,l=new Map,j=0;function M(n,a$1={}){let s=j++,d=a$1.name||`chunk_${s}`,m=a$1.middleware||[],C=a$1.strict??false;if(n===void 0)throw new Error(`[${d}] Initial value cannot be undefined.`);let f=n!==null&&typeof n=="object"&&!Array.isArray(n)?new Set(Object.keys(n)):null,t=n,i=new Set,y=()=>{i.forEach(e=>e(t));};l.set(s,{notify:y});let h=()=>{if(i.size===0){l.delete(s);return}y();},D=()=>(t),_=()=>t,S=e=>{let r;if(typeof e=="function"?r=e(t):r=e,c(t,r),f&&r!==null&&typeof r=="object"&&!Array.isArray(r)){let u=Object.keys(r).filter(c=>!f.has(c));if(u.length>0){let c=`[${d}] Unexpected keys in set(): ${u.join(", ")}. These keys were not present in the initial shape.`;if(C)throw new Error(`\u{1F6A8} Stunk: ${c}`);console.error(`\u{1F6A8} Stunk: ${c}`);}}let o=a(r,m);o!==t&&(t=o,h());},T=e=>{if(typeof e!="function")throw new Error("Callback must be a function.");return i.add(e),()=>i.delete(e)},k={get:D,peek:_,set:S,subscribe:T,derive:e=>{if(typeof e!="function")throw new Error("Derive function must be a function.");let r=e(t),o=M(r),u=T(()=>{let g=e(t);o.set(g);}),c=o.destroy;return o.destroy=()=>{u(),c();},o},reset:()=>{t=n,h();},destroy:()=>{i.clear(),t=n,v.delete(s),l.delete(s);},[Symbol.for("stunk.meta")]:{name:d,id:s}};return k}export{M as a};
@@ -3,16 +3,21 @@ type Middleware<T> = (value: T) => T | undefined;
3
3
  interface Chunk<T> {
4
4
  /** Get the current value of the chunk. */
5
5
  get: () => T;
6
+ /** Peek at the current value without tracking dependencies. */
7
+ peek: () => T;
6
8
  /** Set a new value for the chunk & Update existing value efficiently. */
7
9
  set: (newValueOrUpdater: T | ((currentValue: T) => T)) => void;
8
10
  /** Subscribe to changes in the chunk. Returns an unsubscribe function. */
9
11
  subscribe: (callback: Subscriber<T>) => () => void;
10
12
  /** Create a derived chunk based on this chunk's value. */
11
- derive: <D>(fn: (value: T) => D) => Chunk<D>;
13
+ derive: <D>(fn: (value: T) => D) => ReadOnlyChunk<D>;
12
14
  /** Reset the chunk to its initial value. */
13
15
  reset: () => void;
14
16
  /** Destroy the chunk and all its subscribers. */
15
17
  destroy: () => void;
16
18
  }
19
+ interface ReadOnlyChunk<T> extends Omit<Chunk<T>, 'set' | 'reset'> {
20
+ derive: <D>(fn: (value: T) => D) => ReadOnlyChunk<D>;
21
+ }
17
22
 
18
23
  export type { Chunk as C, Middleware as M };
@@ -3,16 +3,21 @@ type Middleware<T> = (value: T) => T | undefined;
3
3
  interface Chunk<T> {
4
4
  /** Get the current value of the chunk. */
5
5
  get: () => T;
6
+ /** Peek at the current value without tracking dependencies. */
7
+ peek: () => T;
6
8
  /** Set a new value for the chunk & Update existing value efficiently. */
7
9
  set: (newValueOrUpdater: T | ((currentValue: T) => T)) => void;
8
10
  /** Subscribe to changes in the chunk. Returns an unsubscribe function. */
9
11
  subscribe: (callback: Subscriber<T>) => () => void;
10
12
  /** Create a derived chunk based on this chunk's value. */
11
- derive: <D>(fn: (value: T) => D) => Chunk<D>;
13
+ derive: <D>(fn: (value: T) => D) => ReadOnlyChunk<D>;
12
14
  /** Reset the chunk to its initial value. */
13
15
  reset: () => void;
14
16
  /** Destroy the chunk and all its subscribers. */
15
17
  destroy: () => void;
16
18
  }
19
+ interface ReadOnlyChunk<T> extends Omit<Chunk<T>, 'set' | 'reset'> {
20
+ derive: <D>(fn: (value: T) => D) => ReadOnlyChunk<D>;
21
+ }
17
22
 
18
23
  export type { Chunk as C, Middleware as M };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- 'use strict';var Y=Object.defineProperty;var Z=(e,o)=>{for(var r in o)Y(e,r,{get:o[r],enumerable:true});};function _(e){return e!==null}function ee(e){if(!e||typeof e!="object")return false;let o=e;return ["get","set","subscribe","derive","reset","destroy"].every(t=>typeof o[t]=="function")}function te(e){let o=false,r;return ()=>(o||(r=e(),o=true),r)}function re(e){let o=Object.keys(e).reduce((n,u)=>(n[u]=null,n),{}),r={loading:Object.keys(e).length>0,error:null,errors:{},data:o},t=k(r);return Object.entries(e).forEach(([n,u])=>{u.subscribe(a=>{let s=t.get(),l=false,c=null,f={};Object.entries(e).forEach(([h,x])=>{let p=x.get();p.loading&&(l=true),p.error&&(c||(c=p.error),f[h]=p.error);}),t.set({loading:l,error:c,errors:f,data:{...s.data,[n]:a.data}});});}),t}function N(e,o){if(e===null)throw new Error("Value cannot be null.");let r=e;for(let t=0;t<o.length;t++){let n=o[t],u=typeof n=="function"?n:n.fn,a=typeof n=="function"?`index ${t}`:n.name||`index ${t}`;try{let s=u(r);if(s===void 0)break;if(s===null)throw new Error(`Middleware "${a}" returned null value.`);r=s;}catch(s){let l=s instanceof Error?s.message:String(s);throw new Error(`Middleware "${a}" threw an error: ${l}`)}}return r}function M(e,o){if(e===o)return true;if(!e||!o||typeof e!=typeof o)return false;if(Array.isArray(e)&&Array.isArray(o)){if(e.length!==o.length)return false;for(let r=0;r<e.length;r++)if(e[r]!==o[r])return false;return true}if(typeof e=="object"&&typeof o=="object"){let r=Object.keys(e),t=Object.keys(o);if(r.length!==t.length)return false;for(let n of r)if(!Object.prototype.hasOwnProperty.call(o,n)||e[n]!==o[n])return false;return true}return false}function D(e,o,r=""){if(typeof e=="object"&&e!==null&&typeof o=="object"&&o!==null){if(Array.isArray(e)&&Array.isArray(o)){if(e.length>0&&typeof e[0]=="object")for(let t=0;t<o.length;t++)D(e[0],o[t],`${r}[${t}]`);}else if(!Array.isArray(e)&&!Array.isArray(o)){let t=Object.keys(e),n=Object.keys(o),u=n.filter(a=>!t.includes(a));u.length>0&&(console.error(`\u{1F6A8} Stunk: Unknown properties detected at '${r||"root"}': ${u.join(", ")}. This might cause bugs.`),console.error("Expected keys:",t),console.error("Received keys:",n));for(let a of t)D(e[a],o[a],r?`${r}.${a}`:a);}}}var V=false,R=new Set,O=new Map,ne=0;function oe(e){let o=V;V=true;try{e();}finally{if(!o){V=false;let r=Array.from(R);R.clear(),r.forEach(t=>{let n=O.get(t);n&&n.notify();});}}}function k(e,o=[]){if(e===null)throw new Error("Initial value cannot be null.");let r=e,t=new Set,n=ne++,u=()=>{t.forEach(p=>p(r));};O.set(n,{notify:u});let a=()=>{t.size!==0&&(V?R.add(n):u());},s=()=>r,l=p=>{let y;typeof p=="function"?y=p(r):y=p,D(r,y);let b=N(y,o);b!==r&&(r=b,a());},c=p=>{if(typeof p!="function")throw new Error("Callback must be a function.");return t.add(p),p(r),()=>t.delete(p)};return {get:s,set:l,subscribe:c,derive:p=>{if(typeof p!="function")throw new Error("Derive function must be a function.");let y=p(r),b=k(y),d=c(()=>{let w=p(r);b.set(w);}),g=b.destroy;return b.destroy=()=>{d(),g();},b},reset:()=>{r=e,a();},destroy:()=>{t.clear(),r=e,R.delete(n),O.delete(n);}}}function z(e,o={}){let{initialData:r=null,onError:t,retryCount:n=0,retryDelay:u=1e3,refresh:a={},pagination:s,enabled:l=true}=o,{staleTime:c=0,cacheTime:f=5*60*1e3,refetchInterval:h}=a,x=!!s,p=s?.mode||"replace",y=e.length>0,b={loading:l&&!y,error:null,data:r,lastFetched:void 0,pagination:x?{page:s.initialPage||1,pageSize:s.pageSize||10}:void 0},d=k(b),g={},w=null,v=null,L=()=>{let i=d.get();return !i.lastFetched||c===0?true:Date.now()-i.lastFetched>c},G=()=>{d.set({...d.get(),data:r,lastFetched:void 0});},Q=()=>{v&&clearTimeout(v),f>0&&(v=setTimeout(G,f));},m=async(i,T=n,A=false)=>{if(!l||(i!==void 0&&(g={...g,...i}),!A&&!L()&&d.get().data!==null))return;let C=d.get();d.set({...C,loading:true,error:null});try{let S={...g};x&&C.pagination&&(S.page=C.pagination.page,S.pageSize=C.pagination.pageSize);let P=y?await e(S):await e(),E,H,$;if(P&&typeof P=="object"&&"data"in P){let j=P;E=j.data,H=j.total,$=j.hasMore;}else E=P;x&&p==="accumulate"&&C.data&&Array.isArray(C.data)&&Array.isArray(E)&&(E=[...C.data,...E]);let X=Date.now();d.set({loading:!1,error:null,data:E,lastFetched:X,pagination:x?{...C.pagination,total:H,hasMore:$}:void 0}),Q();}catch(S){if(T>0)return await new Promise(E=>setTimeout(E,u)),m(i,T-1,A);let P=d.get();d.set({loading:false,error:S,data:P.data,lastFetched:P.lastFetched,pagination:P.pagination}),t&&t(S);}};h&&h>0&&l&&(w=setInterval(()=>{l&&m(void 0,0,false);},h)),l&&!y&&m();let F=()=>{w&&(clearInterval(w),w=null),v&&(clearTimeout(v),v=null);},I={...d,reload:async i=>{await m(i,n,true);},refresh:async i=>{await m(i,n,false);},mutate:i=>{let T=d.get(),A=i(T.data);d.set({...T,data:A});},reset:()=>{F(),g={},d.set({...b,loading:l&&!y}),l&&!y&&(m(),h&&h>0&&(w=setInterval(()=>{l&&m(void 0,0,false);},h)));},cleanup:F,setParams:i=>{g={...g,...i},l&&m(i,n,true);}};return x?{...I,nextPage:async()=>{let i=d.get();i.pagination&&i.pagination.hasMore!==false&&(d.set({...i,pagination:{...i.pagination,page:i.pagination.page+1}}),await m(g,n,true));},prevPage:async()=>{let i=d.get();!i.pagination||i.pagination.page<=1||(d.set({...i,pagination:{...i.pagination,page:i.pagination.page-1}}),await m(g,n,true));},goToPage:async i=>{let T=d.get();!T.pagination||i<1||(d.set({...T,pagination:{...T.pagination,page:i}}),await m(g,n,true));},resetPagination:async()=>{let i=d.get();if(!i.pagination)return;let T=s?.initialPage||1;d.set({...i,data:p==="accumulate"?r:i.data,pagination:{...i.pagination,page:T}}),await m(g,n,true);}}:I}function ae(e,o={}){let{pageSize:r=10,staleTime:t,cacheTime:n,retryCount:u,retryDelay:a,onError:s}=o;return z(e,{pagination:{pageSize:r,mode:"accumulate",initialPage:1},refresh:{staleTime:t,cacheTime:n},retryCount:u,retryDelay:a,onError:s})}function se(e,o){let r=e.map(c=>c.get()),t=o(...r),n=k(t),u=n.set,a=false,s=()=>{let c=false;for(let f=0;f<e.length;f++){let h=e[f].get();h!==r[f]&&(r[f]=h,c=true);}if(c){let f=o(...r);f!==t&&(typeof f!="object"||typeof t!="object"||!M(f,t))&&(t=f,u(f)),a=false;}},l=e.map(c=>c.subscribe(()=>{a=true,s();}));return {...n,get:()=>(a&&s(),t),recompute:s,isDirty:()=>a,set:()=>{throw new Error("Cannot set values directly on computed. Modify the source chunk instead.")},reset:()=>(e.forEach(c=>{typeof c.reset=="function"&&c.reset();}),a=true,s(),t),destroy:()=>{l.forEach(c=>c()),n.destroy?.();}}}function q(e,o,r={}){let{useShallowEqual:t=false}=r,n=e.get(),u=o(n),a=k(u),s=()=>{let c=e.get(),f=o(c);n=c,(t?!M(f,u):f!==u)&&(u=f,a.set(f));},l=e.subscribe(s);return {get:()=>a.get(),set:()=>{throw new Error("Cannot set values directly on a selector. Modify the source chunk instead.")},subscribe:a.subscribe,derive:c=>q(a,c,r),reset:()=>{throw new Error("Cannot reset a selector chunk. Reset the source chunk instead.")},destroy:()=>{l(),a.destroy();}}}var U={};Z(U,{logger:()=>K,nonNegativeValidator:()=>W,withHistory:()=>B,withPersistence:()=>J});var K=e=>(console.log("Setting value:",e),e);var W=e=>{if(e<0)throw new Error("Value must be non-negative!");return e};function B(e,o={}){let{maxHistory:r=100}=o,t=[e.get()],n=0,u=false,a={...e,set:s=>{if(u){e.set(s);return}let l;if(typeof s=="function"){let c=e.get();l=s(c);}else l=s;if(t.splice(n+1),t.push(l),t.length>r){console.warn("History limit reached. Removing oldest entries.");let c=t.length-r;t.splice(0,c),n=Math.max(0,n-c);}n=t.length-1,e.set(l);},undo:()=>{a.canUndo()&&(u=true,n--,a.set(t[n]),u=false);},redo:()=>{a.canRedo()&&(u=true,n++,a.set(t[n]),u=false);},canUndo:()=>n>0,canRedo:()=>n<t.length-1,getHistory:()=>[...t],clearHistory:()=>{let s=e.get();t.length=0,t.push(s),n=0;},destroy:()=>{t.length=0,e.destroy();}};return a}function J(e,o){let{key:r,storage:t=localStorage,serialize:n=JSON.stringify,deserialize:u=JSON.parse}=o;try{let a=t.getItem(r);if(a){let s=u(a);e.set(s);}}catch(a){console.error("Failed to load persisted state:",a);}return e.subscribe(a=>{try{let s=n(a);t.setItem(r,s);}catch(s){console.log("Failed to persist chunk",s);}}),e}exports.asyncChunk=z;exports.batch=oe;exports.chunk=k;exports.combineAsyncChunks=re;exports.computed=se;exports.infiniteAsyncChunk=ae;exports.isChunk=ee;exports.isValidChunkValue=_;exports.middleware=U;exports.once=te;exports.select=q;
1
+ 'use strict';var be=Object.defineProperty;var re=(e,a)=>{for(var t in a)be(e,t,{get:a[t],enumerable:true});};function ne(e,a){if(e===void 0)throw new Error("Value cannot be undefined.");if(a.length===0)return e;let t=e;for(let c=0;c<a.length;c++){let i=a[c],s=typeof i=="function"?i:i.fn,f=typeof i=="function"?`index ${c}`:i.name||`index ${c}`;try{let n=s(t);if(n===void 0)break;if(n===null)throw new Error(`Middleware "${f}" returned null value.`);t=n;}catch(n){let r=n instanceof Error?n.message:String(n);throw new Error(`Middleware "${f}" threw an error: ${r}`)}}return t}function O(e,a){if(e===a)return true;if(!e||!a||typeof e!=typeof a)return false;if(Array.isArray(e)&&Array.isArray(a)){if(e.length!==a.length)return false;for(let t=0;t<e.length;t++)if(e[t]!==a[t])return false;return true}if(typeof e=="object"&&typeof a=="object"){let t=Object.keys(e),c=Object.keys(a);if(t.length!==c.length)return false;for(let i of t)if(!Object.prototype.hasOwnProperty.call(a,i)||e[i]!==a[i])return false;return true}return false}function ke(e){return e[Symbol.for("stunk.meta")]}var _=null;function U(e){let a=new Set,t=_;_=a;try{return [e(),Array.from(a)]}finally{_=t;}}function Ee(e){_&&_.add(e);}var Q=false,G=new Set,W=new Map,ve=0;function xe(e){let a=Q;Q=true;try{e();}finally{a||(Q=false,G.forEach(t=>{let c=W.get(t);c&&c.notify();}),G.clear());}}function v(e,a={}){let t=ve++,c=`chunk_${t}`,i=a.middleware||[];a.strict??false;if(e===void 0)throw new Error(`[${c}] Initial value cannot be undefined.`);let n=e,r=new Set,p=()=>{r.forEach(h=>h(n));};W.set(t,{notify:p});let u=()=>{if(r.size===0){W.delete(t);return}Q?G.add(t):p();},o=()=>(Ee(D),n),d=()=>n,m=h=>{let x;typeof h=="function"?x=h(n):x=h;let P=ne(x,i);P!==n&&(n=P,u());},T=h=>{if(typeof h!="function")throw new Error("Callback must be a function.");return r.add(h),()=>r.delete(h)},D={get:o,peek:d,set:m,subscribe:T,derive:h=>{if(typeof h!="function")throw new Error("Derive function must be a function.");let x=h(n),P=v(x),y=T(()=>{let $=h(n);P.set($);}),g=P.destroy;return P.destroy=()=>{y(),g();},P},reset:()=>{n=e,u();},destroy:()=>{r.clear(),n=e,G.delete(t),W.delete(t);}};return D}function oe(e){let[a,t]=U(e),c=a,i=t,s=false,f=[],n=0,r=v(c),p=()=>{if(!s)return;s=false;let[o,d]=U(e);O(d,i)||(f.forEach(T=>T()),f=d.map(T=>T.subscribe(()=>{s=true,n>0&&p();})),i=d),(typeof o=="object"&&typeof c=="object"?!O(o,c):o!==c)&&(c=o,r.set(o));};f=i.map(o=>o.subscribe(()=>{s=true,n>0&&p();}));let u;return u={get:()=>(s&&p(),r.get()),peek:()=>c,subscribe:o=>{let d=r.subscribe(o);return n++,()=>{n=Math.max(0,n-1),d();}},derive:o=>(n++,oe(()=>o(u.get()))),recompute:()=>{s=true,p();},isDirty:()=>s,destroy:()=>{f.forEach(o=>o()),r.destroy(),n=0;}},u}function ae(e,a,t={}){let{useShallowEqual:c=false}=t,i=e.get(),s=a(i),f=v(s),n=()=>{let d=e.get(),m=a(d);(c?!O(m,s):m!==s)&&(s=m,f.set(m));},r=e.subscribe(n),{set:p,reset:u,...o}=f;return {...o,derive:d=>ae(f,d,t),destroy:()=>{r(),f.destroy();}}}var ce={};re(ce,{history:()=>ue,logger:()=>ie,nonNegativeValidator:()=>se,persist:()=>le});function ie(){return e=>(console.log("Setting value:",e),e)}var se=e=>{if(e<0)throw new Error("Value must be non-negative!");return e};function ue(e,a={}){let{maxHistory:t=100,skipDuplicates:c=false}=a,i=[e.get()],s=0;return {...e,get:()=>e.get(),peek:()=>e.peek(),set:n=>{let r;if(typeof n=="function"?r=n(e.get()):r=n,c){let p=i[s];if(r===p||c==="shallow"&&typeof r=="object"&&typeof p=="object"&&r!==null&&p!==null&&O(r,p))return}if(i.splice(s+1),i.push(r),i.length>t){let p=i.length-t;i.splice(0,p);}s=i.length-1,e.set(r);},undo:()=>{s<=0||(s--,e.set(i[s]));},redo:()=>{s>=i.length-1||(s++,e.set(i[s]));},canUndo:()=>s>0,canRedo:()=>s<i.length-1,getHistory:()=>[...i],clearHistory:()=>{let n=e.get();i.length=0,i.push(n),s=0;},reset:()=>{e.reset(),i.length=0,i.push(e.get()),s=0;},destroy:()=>{i.length=0,e.destroy();},subscribe:n=>e.subscribe(n)}}function le(e,a){let{key:t,serialize:c=JSON.stringify,deserialize:i=JSON.parse,onError:s}=a,f="storage"in a?a.storage:typeof window<"u"?localStorage:void 0;if(!f)return console.warn(`persist: Storage not available for key "${t}". Persistence disabled.`),{...e,clearStorage:()=>{}};let n=e.get();try{let u=f.getItem(t);if(u!==null){let o=i(u);if(o!==null&&n!==null&&(typeof o!=typeof n||Array.isArray(o)!==Array.isArray(n))){let T=`persist: Type mismatch for "${t}". Expected ${Array.isArray(n)?"array":typeof n}, got ${Array.isArray(o)?"array":typeof o}. Using initial value.`;console.warn(T),s?.(new Error(T),"load");}else e.set(o);}}catch(u){let o=u instanceof Error?u:new Error(String(u));console.error(`persist: Failed to load state for "${t}":`,o),s?.(o,"load");}let r=e.subscribe(u=>{try{f.setItem(t,c(u));}catch(o){let d=o instanceof Error?o:new Error(String(o));console.error(`persist: Failed to save state for "${t}":`,d),s?.(d,"save");}});return {...e,get:()=>e.get(),peek:()=>e.peek(),set:u=>e.set(u),subscribe:u=>e.subscribe(u),derive:u=>e.derive(u),reset:()=>e.reset(),destroy:()=>{r(),e.destroy();},clearStorage:()=>{try{f.removeItem(t);}catch(u){let o=u instanceof Error?u:new Error(String(u));console.error(`persist: Failed to clear storage for "${t}":`,o),s?.(o,"save");}}}}var he={};re(he,{asyncChunk:()=>K,combineAsyncChunks:()=>pe,configureQuery:()=>de,getGlobalQueryConfig:()=>F,infiniteAsyncChunk:()=>ye,mutation:()=>me,resetQueryConfig:()=>fe});var q={};function de(e){q={...e.query!==void 0&&{query:{...q.query,...e.query}},...e.mutation!==void 0&&{mutation:{...q.mutation,...e.mutation}}};}function F(){return q}function fe(){q={};}var N=new Map,Pe=0;function K(e,a={}){let t=F().query??{},{key:c,initialData:i=null,enabled:s=true,onSuccess:f=t.onSuccess,onError:n=t.onError,retryCount:r=t.retryCount??0,retryDelay:p=t.retryDelay??1e3,keepPreviousData:u=false,staleTime:o=t.staleTime??0,cacheTime:d=t.cacheTime??5*60*1e3,refetchInterval:m=t.refetchInterval,refetchOnWindowFocus:T=t.refetchOnWindowFocus??false,pagination:A}=a,E=c??`async_chunk_${Pe++}`,C=()=>typeof s=="function"?s():s,D=!!A,h=A?.mode||"replace",x=e.length>0,P={loading:C()&&!x,error:null,data:i,lastFetched:void 0,isPlaceholderData:false,pagination:D?{page:A.initialPage||1,pageSize:A.pageSize||10,total:void 0,hasMore:void 0}:void 0},y=v(P),g={},$=null,R=null,I=null,H=0,J=()=>{let l=y.get();return !l.lastFetched||o===0?true:Date.now()-l.lastFetched>o},ge=()=>{y.set({...y.get(),data:i,lastFetched:void 0});},Te=()=>{R&&clearTimeout(R),d>0&&(R=setTimeout(ge,d));},B=()=>{$&&(clearInterval($),$=null),R&&(clearTimeout(R),R=null),I&&typeof window<"u"&&(window.removeEventListener("focus",I),I=null);},X=()=>{C()&&(typeof window>"u"||(m&&m>0&&($=setInterval(()=>{k(void 0,0,false);},m)),T&&(I=()=>{J()&&k(void 0,0,false);},window.addEventListener("focus",I))));},k=async(l,b=r,M=false)=>{if(!C()||(l!==void 0&&(g={...g,...l}),!M&&!J()&&y.get().data!==null))return;if(N.has(E))return N.get(E);let L=y.get();y.set({...L,loading:true,error:null,data:L.data,isPlaceholderData:u&&L.data!==null});let Z=(async()=>{try{let j={...g};if(D){let V=y.get().pagination;V&&(j.page=V.page,j.pageSize=V.pageSize);}let w=x?await e(j):await e(),S,ee,te;if(w&&typeof w=="object"&&"data"in w){let V=w;S=V.data,ee=V.total,te=V.hasMore;}else S=w;let z=y.get();D&&h==="accumulate"&&z.data&&Array.isArray(z.data)&&Array.isArray(S)&&(S=[...z.data,...S]),y.set({loading:!1,error:null,data:S,lastFetched:Date.now(),isPlaceholderData:!1,pagination:D?{...z.pagination,total:ee,hasMore:te}:void 0}),Te(),f&&f(S);}catch(j){if(b>0)return await new Promise(S=>setTimeout(S,p)),N.delete(E),k(l,b-1,M);let w=y.get();y.set({loading:false,error:j,data:w.data,lastFetched:w.lastFetched,isPlaceholderData:false,pagination:w.pagination}),n&&n(j);}finally{N.delete(E);}})();return N.set(E,Z),Z};X(),C()&&!x&&k();let Y={...y,subscribe:l=>{H++;let b=y.subscribe(l);return ()=>{b(),H--;}},reload:async l=>{await k(l,r,true);},refresh:async l=>{await k(l,r,false);},mutate:l=>{let b=y.get();y.set({...b,data:l(b.data)});},reset:()=>{B(),g={},y.set({...P,loading:C()&&!x}),X(),C()&&!x&&k();},cleanup:()=>{H<=0&&B();},forceCleanup:()=>{B();},setParams:l=>{let b={...g};for(let M in l)l[M]===null?delete b[M]:b[M]=l[M];g=b,C()&&k(g,r,true);},clearParams:()=>{g={},C()&&k(void 0,r,true);}};return D?{...Y,nextPage:async()=>{let l=y.get();!l.pagination||l.pagination.hasMore===false||(y.set({...l,pagination:{...l.pagination,page:l.pagination.page+1}}),await k(g,r,true));},prevPage:async()=>{let l=y.get();!l.pagination||l.pagination.page<=1||(y.set({...l,pagination:{...l.pagination,page:l.pagination.page-1}}),await k(g,r,true));},goToPage:async l=>{let b=y.get();!b.pagination||l<1||(y.set({...b,pagination:{...b.pagination,page:l}}),await k(g,r,true));},resetPagination:async()=>{let l=y.get();l.pagination&&(y.set({...l,data:h==="accumulate"?i:l.data,pagination:{...l.pagination,page:A?.initialPage||1}}),await k(g,r,true));}}:Y}function ye(e,a={}){let{pageSize:t=10,...c}=a;return K(e,{...c,pagination:{pageSize:t,mode:"accumulate",initialPage:1}})}function pe(e){let a=Object.entries(e),t=a.reduce((r,[p,u])=>(r[p]=u.get().data,r),{}),c=a.reduce((r,[p,u])=>{let o=u.get().error;return o&&(r[p]=o),r},{}),i=a.some(([,r])=>r.get().loading),s=a.find(([,r])=>r.get().error)?.[1].get().error??null,n=v({loading:i,error:s,errors:c,data:t});return a.forEach(([r,p])=>{p.subscribe(()=>{let u=false,o=null,d={},m={...n.get().data};a.forEach(([T,A])=>{let E=A.get();E.loading&&(u=true),E.error&&(o||(o=E.error),d[T]=E.error),m[T]=E.data;}),n.set({loading:u,error:o,errors:d,data:m});});}),n}function me(e,a={}){let t=F().mutation??{},{invalidates:c=[],onSuccess:i=t.onSuccess,onError:s=t.onError,onSettled:f}=a,n={loading:false,data:null,error:null,isSuccess:false},r=v(n);return {mutate:async(...u)=>{let o=u[0];r.set({loading:true,data:null,error:null,isSuccess:false});try{let d=await e(o);return r.set({loading:!1,data:d,error:null,isSuccess:!0}),c.length>0&&await Promise.all(c.map(m=>m.reload())),i&&i(d,o),f&&f(d,null,o),{data:d,error:null}}catch(d){let m=d;return r.set({loading:false,data:null,error:m,isSuccess:false}),s&&s(m,o),f&&f(null,m,o),{data:null,error:m}}},get:()=>r.get(),subscribe:u=>r.subscribe(u),reset:()=>r.set(n)}}exports.batch=xe;exports.chunk=v;exports.computed=oe;exports.getChunkMeta=ke;exports.middleware=ce;exports.query=he;exports.select=ae;