@yakocloud/state-vocab 3.1.5 → 4.0.0

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,6 +1,6 @@
1
1
  # @yakocloud/state-vocab
2
2
 
3
- A lightweight React state management library that synchronizes component state with any `Storage`-compatible backend (localStorage, sessionStorage, custom).
3
+ A lightweight React state management library that synchronizes component state with any `Storage`-compatible backend (localStorage, sessionStorage, custom). Supports React Server Components and Next.js SSR out of the box.
4
4
 
5
5
  ## Why use state-vocab?
6
6
 
@@ -82,6 +82,8 @@ function DeepChild() {
82
82
  }
83
83
  ```
84
84
 
85
+ **React Server Components support.** Read state in async server components and seed it into client components — no extra API routes or serialization boilerplate.
86
+
85
87
  **Dot-notation access with full TypeScript inference.** The state tree is navigated like a plain object — autocomplete guides you to the right node, and types flow from `defineState<T>` all the way to the hook return value without any manual annotations.
86
88
 
87
89
  ```ts
@@ -103,7 +105,7 @@ defineState({
103
105
  })
104
106
  ```
105
107
 
106
- **Minimal API surface.** Three exports: `defineState`, `setupStorage`, `VocabStateProvider`. No actions, reducers, selectors, or stores to configure.
108
+ **Minimal API surface.** `defineState` and `setupStorage` define the state tree; `clientify` from `@yakocloud/state-vocab/client` wires up React hooks. No actions, reducers, selectors, or stores to configure.
107
109
 
108
110
  ## Installation
109
111
 
@@ -115,17 +117,19 @@ npm install @yakocloud/state-vocab react react-dom
115
117
 
116
118
  ## Quick Start
117
119
 
118
- Wrap your app with `VocabStateProvider` at the root — this initializes an isolated store for the component tree. Then define and use state anywhere inside it.
120
+ Wrap your app with `StateVocabProvider` at the root — this initializes an isolated store for the component tree. Then define and use state anywhere inside it.
121
+
119
122
  Library SSR-requirements:
120
123
  - `Per-request store`: A Next.js server can handle multiple requests simultaneously. This means that the store should be created per request and should not be shared across requests.
121
124
  - `SSR friendly`: Next.js applications are rendered twice, first on the server and again on the client. Having different outputs on both the client and the server will result in "hydration errors." The store will have to be initialized on the server and then re-initialized on the client with the same data in order to avoid that.
122
125
 
123
126
  ```tsx
124
- import { setupStorage, defineState, VocabStateProvider } from '@yakocloud/state-vocab'
127
+ import { setupStorage, defineState } from '@yakocloud/state-vocab'
128
+ import { clientify, StateVocabProvider } from '@yakocloud/state-vocab/client'
125
129
 
126
130
  type Theme = 'Dark' | 'White' | 'System'
127
131
 
128
- const storage = setupStorage({
132
+ const storage = clientify(setupStorage({
129
133
  path: {
130
134
  to: {
131
135
  theme: defineState<Theme>({
@@ -134,13 +138,13 @@ const storage = setupStorage({
134
138
  }),
135
139
  },
136
140
  },
137
- })
141
+ }))
138
142
 
139
143
  function App() {
140
144
  return (
141
- <VocabStateProvider>
145
+ <StateVocabProvider>
142
146
  <Settings />
143
- </VocabStateProvider>
147
+ </StateVocabProvider>
144
148
  )
145
149
  }
146
150
 
@@ -159,19 +163,18 @@ function Settings() {
159
163
 
160
164
  ## Setup
161
165
 
162
- ### `VocabStateProvider`
166
+ ### `StateVocabProvider`
163
167
 
164
- All components that call `.useState()` must be descendants of `VocabStateProvider`. It creates an isolated `VocabStore` instance for its subtree — multiple providers can coexist in the same app without sharing state.
168
+ All components that call `.useState()` must be descendants of `StateVocabProvider`. It creates an isolated `VocabStore` instance for its subtree — multiple providers can coexist in the same app without sharing state.
165
169
 
166
170
  ```tsx
167
- import { VocabStateProvider } from '@yakocloud/state-vocab'
171
+ import { StateVocabProvider } from '@yakocloud/state-vocab/client'
168
172
 
169
- // Wrap your app root
170
173
  createRoot(document.getElementById('root')!).render(
171
174
  <React.StrictMode>
172
- <VocabStateProvider>
175
+ <StateVocabProvider>
173
176
  <App />
174
- </VocabStateProvider>
177
+ </StateVocabProvider>
175
178
  </React.StrictMode>
176
179
  )
177
180
  ```
@@ -180,13 +183,13 @@ You can mount multiple independent providers — each gets its own store:
180
183
 
181
184
  ```tsx
182
185
  // Two isolated state trees — state does not bleed between them
183
- <VocabStateProvider>
186
+ <StateVocabProvider>
184
187
  <WidgetA />
185
- </VocabStateProvider>
188
+ </StateVocabProvider>
186
189
 
187
- <VocabStateProvider>
190
+ <StateVocabProvider>
188
191
  <WidgetB />
189
- </VocabStateProvider>
192
+ </StateVocabProvider>
190
193
  ```
191
194
 
192
195
  ## Core Concepts
@@ -273,7 +276,7 @@ const storage = setupStorage({
273
276
 
274
277
  TypeScript will only accept paths that exist in your tree — `"user"`, `"user.profile"`, `"cart.items"`, etc. Invalid paths are caught at compile time.
275
278
 
276
- ### SSR / Next.js
279
+ ### SSR / Next.js (client components)
277
280
 
278
281
  When using localStorage or sessionStorage in a Next.js app, the server renders with `defaultValue` while the client reads the persisted value — causing a hydration mismatch. Pass `ssr: true` to fix this:
279
282
 
@@ -292,7 +295,150 @@ With `ssr: true`:
292
295
 
293
296
  This guarantees the server and client produce identical markup, and the value from storage is applied without a visible flash.
294
297
 
295
- Wrap your page or app root with `VocabStateProvider` as usual — it works correctly in both SSR and client environments.
298
+ ### React Server Components (RSC)
299
+
300
+ For Next.js apps using the App Router, state-vocab provides dedicated server and client entry points that let you read state in async server components and pass it down to client components via `StateVocabProvider`.
301
+
302
+ **Package entry points:**
303
+
304
+ | Import path | Use in |
305
+ |---|---|
306
+ | `@yakocloud/state-vocab` | Shared files — `defineState`, `setupStorage` |
307
+ | `@yakocloud/state-vocab/server` | Server Components — `serverify`, `StateVocabProvider` |
308
+ | `@yakocloud/state-vocab/client` | Client Components — `clientify` |
309
+
310
+ **1. Define the shared storage schema** (used on both server and client):
311
+
312
+ ```ts
313
+ // storage.ts
314
+ import { setupStorage, defineState } from '@yakocloud/state-vocab'
315
+
316
+ export const storage = setupStorage({
317
+ user: {
318
+ name: defineState<string>(),
319
+ role: defineState<string>(),
320
+ },
321
+ person: {
322
+ address: {
323
+ city: defineState<string>(),
324
+ },
325
+ },
326
+ }, { ssr: true })
327
+ ```
328
+
329
+ **2. Create server and client storage handles:**
330
+
331
+ ```ts
332
+ // storage.server.ts
333
+ import { storage } from '@/storage'
334
+ import { serverify } from '@yakocloud/state-vocab/server'
335
+
336
+ export const serverStorage = serverify(storage)
337
+ ```
338
+
339
+ ```ts
340
+ // storage.client.ts ("use client")
341
+ "use client"
342
+
343
+ import { storage } from '@/storage'
344
+ import { clientify } from '@yakocloud/state-vocab/client'
345
+
346
+ export const clientStorage = clientify(storage)
347
+ ```
348
+
349
+ **3. Seed initial state in the server component and read it in Server and Client children:**
350
+
351
+ ```tsx
352
+ // app/page.tsx (Server Component)
353
+ import { serverStorage } from '@/storage.server'
354
+ import { StateVocabProvider } from '@yakocloud/state-vocab/server'
355
+
356
+ export default async function Page() {
357
+ // Fetch data from DB / API
358
+ const user = await db.getUser()
359
+
360
+ return (
361
+ <StateVocabProvider
362
+ value={serverStorage({
363
+ user: { name: user.name, role: user.role },
364
+ person: { address: { city: user.city } },
365
+ })}
366
+ >
367
+ <UserInfo />
368
+ </StateVocabProvider>
369
+ )
370
+ }
371
+ ```
372
+
373
+ ```tsx
374
+ // app/user-info.server.tsx (Server Component)
375
+ import { serverStorage } from '@/storage.server'
376
+
377
+ export default async function UserInfo() {
378
+ const name = serverStorage.user.name.getState()
379
+ const role = serverStorage.user.role.getState()
380
+
381
+ return <p>{name} — {role}</p>
382
+ }
383
+ ```
384
+
385
+ ```tsx
386
+ // app/user-info.tsx ("use client")
387
+ "use client"
388
+
389
+ import { clientStorage } from '@/storage.client'
390
+
391
+ export default function UserInfoClient() {
392
+ const [name] = clientStorage.user.name.useState()
393
+ const [role] = clientStorage.user.role.useState()
394
+
395
+ return <p>{name} — {role}</p>
396
+ }
397
+ ```
398
+
399
+ #### `serverify(storage)`
400
+
401
+ Converts a storage tree into its server-side counterpart. Each leaf gains a `.getState()` method that reads the value seeded into the nearest `StateVocabProvider`. All namespace nodes become callable functions that build the `value` shape expected by `StateVocabProvider`.
402
+
403
+ **Namespace callable syntax:**
404
+
405
+ ```ts
406
+ // Full tree at once — root call is identity (returns input as-is)
407
+ serverStorage({ user: { name: 'Alice', role: 'Admin' } })
408
+ // → { user: { name: 'Alice', role: 'Admin' } }
409
+
410
+ // Single namespace — wraps input under its key
411
+ serverStorage.user({ name: 'Alice', role: 'Admin' })
412
+ // → { user: { name: 'Alice', role: 'Admin' } }
413
+
414
+ // Nested namespace — wraps up to the root
415
+ serverStorage.person.address({ city: 'NY' })
416
+ // → { person: { address: { city: 'NY' } } }
417
+ ```
418
+
419
+ All three forms return a value ready to pass as `StateVocabProvider`'s `value` prop. They can be combined by spreading:
420
+
421
+ ```tsx
422
+ <StateVocabProvider
423
+ value={{
424
+ ...serverStorage.user({ name: 'Alice', role: 'Admin' }),
425
+ ...serverStorage.person.address({ city: 'NY' }),
426
+ }}
427
+ >
428
+ ```
429
+
430
+ **`node.getState()`** reads the value for that leaf from the surrounding `StateVocabProvider`. Throws if called outside one.
431
+
432
+ #### `clientify(storage)`
433
+
434
+ Converts a storage tree into its client-side counterpart. Each leaf gains `.useState()` and `.useInitialState()` hooks. The tree structure is identical to the original — same dot-notation access.
435
+
436
+ ```ts
437
+ const clientStorage = clientify(storage)
438
+
439
+ // In a client component:
440
+ const [name] = clientStorage.user.name.useState()
441
+ ```
296
442
 
297
443
  ## `useState` Hook
298
444
 
@@ -374,11 +520,12 @@ const storage = setupStorage({
374
520
  ```tsx
375
521
  import React from 'react'
376
522
  import { createRoot } from 'react-dom/client'
377
- import { setupStorage, defineState, VocabStateProvider } from '@yakocloud/state-vocab'
523
+ import { setupStorage, defineState } from '@yakocloud/state-vocab'
524
+ import { clientify, StateVocabProvider } from '@yakocloud/state-vocab/client'
378
525
 
379
526
  type Theme = 'Dark' | 'White' | 'System'
380
527
 
381
- const storage = setupStorage({
528
+ const storage = clientify(setupStorage({
382
529
  preference: {
383
530
  theme: defineState<Theme>({ storage: localStorage, defaultValue: 'Dark' }),
384
531
  nightMode: defineState({ storage: sessionStorage, defaultValue: false }),
@@ -400,7 +547,7 @@ const storage = setupStorage({
400
547
  storage: localStorage,
401
548
  }),
402
549
  },
403
- })
550
+ }))
404
551
 
405
552
  // Root — initializes shared state for the whole subtree
406
553
  function Page() {
@@ -460,9 +607,9 @@ function Dashboard() {
460
607
 
461
608
  createRoot(document.getElementById('root')!).render(
462
609
  <React.StrictMode>
463
- <VocabStateProvider>
610
+ <StateVocabProvider>
464
611
  <Page />
465
- </VocabStateProvider>
612
+ </StateVocabProvider>
466
613
  </React.StrictMode>
467
614
  )
468
615
  ```
@@ -489,14 +636,52 @@ createRoot(document.getElementById('root')!).render(
489
636
 
490
637
  Returns a proxied copy of `tree` with paths injected into all leaf nodes.
491
638
 
492
- ### `VocabStateProvider`
639
+ ### `StateVocabProvider`
493
640
 
494
641
  A React context provider that initializes a `VocabStore` for its subtree. Required — all components calling `.useState()` must be descendants of this provider.
495
642
 
496
643
  ```tsx
497
- <VocabStateProvider>
644
+ <StateVocabProvider>
498
645
  <App />
499
- </VocabStateProvider>
646
+ </StateVocabProvider>
647
+ ```
648
+
649
+ Accepts an optional `value` prop (imported from `@yakocloud/state-vocab/server` in RSC contexts) to pre-seed the store with server-fetched data:
650
+
651
+ ```tsx
652
+ <StateVocabProvider value={serverStorage({ user: { name: 'Alice' } })}>
653
+ <App />
654
+ </StateVocabProvider>
655
+ ```
656
+
657
+ ### `serverify<T>(storage: T)`
658
+
659
+ Converts a storage tree to its server-side counterpart. Available from `@yakocloud/state-vocab/server`.
660
+
661
+ Each leaf gains `.getState()` — reads the value from the nearest `StateVocabProvider`. Each namespace node becomes callable, returning the input wrapped under its full ancestor path (ready for `StateVocabProvider`'s `value` prop).
662
+
663
+ ```ts
664
+ import { serverify } from '@yakocloud/state-vocab/server'
665
+ const serverStorage = serverify(storage)
666
+
667
+ serverStorage.user.name.getState() // reads "user.name" from context
668
+ serverStorage.user({ name: 'Alice' }) // → { user: { name: 'Alice' } }
669
+ serverStorage.person.address({ city: 'NY' }) // → { person: { address: { city: 'NY' } } }
670
+ serverStorage({ user: { name: 'Alice' } }) // → { user: { name: 'Alice' } } (identity)
671
+ ```
672
+
673
+ ### `clientify<T>(storage: T)`
674
+
675
+ Converts a storage tree to its client-side counterpart. Available from `@yakocloud/state-vocab/client`.
676
+
677
+ Each leaf gains `.useState()` and `.useInitialState()`. The tree structure mirrors the original.
678
+
679
+ ```ts
680
+ import { clientify } from '@yakocloud/state-vocab/client'
681
+ const clientStorage = clientify(storage)
682
+
683
+ // In a "use client" component:
684
+ const [name] = clientStorage.user.name.useState()
500
685
  ```
501
686
 
502
687
  ### `node.useState(options?)`
@@ -527,4 +712,4 @@ function Page() {
527
712
 
528
713
  return <Dashboard />
529
714
  }
530
- ```
715
+ ```
@@ -0,0 +1 @@
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("react"),t=require("./utils-33NqsZoR.js"),z=require("./provider.client--OPImdtY.js");function D(s,e,o=[]){return c.useMemo(()=>t.debounce(s,e),o)}const O=typeof window>"u",w=O?c.useEffect:c.useLayoutEffect;function _(s){s??={};const e=O?void 0:t.valueOrFactory(s.storage),o=t.valueOrFactory(s.defaultValue),l=s.bidirectional,n=this[t.STATE_PATH],y=this[t.STATE_VERBOSE],V=this[t.STATE_VERBOSE_PATH],T=this[t.STATE_SSR],r=z.useStateVocabClientContext({verbose:y});if(!(r instanceof z.VocabStore))throw new Error("Make sure your component is wrapped in StateVocabProvider");const d=s.serialize??JSON.stringify,E=s.deserialize??JSON.parse,a=(i,f,m)=>{const v=f.getItem(i);v===null?t.isValueDefined(m)&&f.setItem(i,d(m)):r.set(i,E(v))},u=D(s.onSet??(()=>{}),s.delayedSet,[]),S=c.useRef(void 0),g=c.useRef(!1);if(!g.current){g.current=!0;let i=r.get(n);t.isValueDefined(i)||(i=o,t.isValueDefined(i)&&r.set(n,i)),!T&&e&&a(n,e,i)}const b=c.useSyncExternalStore(r.subscribe.bind(r),r.getClientSnapshot.bind(r),r.getServerSnapshot.bind(r));if(y)if(V){const i=t.get(b,V);i&&t.logStyled(i)}else t.logStyled(b);const h=t.get(b,n,o);S.current=h,w(()=>{!T||!e||a(n,e,h)},[]);const R=c.useEffectEvent(i=>{if(i.key!==n)return;const f=i.newValue,v=(f===null?null:E(f))??o;t.isValueDefined(v)&&(r.set(n,v),u(v,S.current))});c.useEffect(()=>{if(l)return window.addEventListener("storage",R),()=>window.removeEventListener("storage",R)},[l]);const I=c.useCallback(i=>{const f=t.isTransformer(i)?i(S.current):i;r.set(n,f),u(f,S.current),e&&e.setItem(n,d(f))},[r,n,u,e,d]),C=c.useCallback(()=>{const i=o;if(!t.isValueDefined(i)){e?.removeItem(n);return}r.set(n,i),u(i,S.current),e&&e.setItem(n,d(i))},[o,r,n,u,e,d]);return[h,I,C]}function p(s){s??={};const e=O?void 0:t.valueOrFactory(s.storage),o=t.valueOrFactory(s.defaultValue),l=this[t.STATE_PATH],n=this[t.STATE_VERBOSE],y=this[t.STATE_SSR],V=z.useStateVocabClientContext({verbose:n}),T=s.serialize??JSON.stringify,r=s.deserialize??JSON.parse,d=(u,S,g)=>{const b=S.getItem(u);b===null?t.isValueDefined(g)&&S.setItem(u,T(g)):V.set(u,r(b))},E=c.useRef(!1);let a;E.current||(E.current=!0,a=V.get(l),t.isValueDefined(a)||(a=o,t.isValueDefined(a)&&V.set(l,a)),!y&&e&&d(l,e,a)),w(()=>{!y||!e||d(l,e,a)},[])}function P(s){return typeof s=="object"&&s!==null&&"clientSlot"in s}function A(s){const e={};for(const o in s){const l=s[o];P(l)?(e[o]=l.clientSlot({useState(...n){return _.apply(this,n)},useInitialState(...n){p.apply(this,n)}}),delete e[o].serverSlot,delete e[o].clientSlot):l!==null&&typeof l=="object"?e[o]=A(l):e[o]=l}return e}exports.StateVocabProvider=z.StateVocabClientProvider;exports.clientify=A;
@@ -0,0 +1,107 @@
1
+ "use client";
2
+ import { useMemo as j, useRef as T, useSyncExternalStore as x, useEffectEvent as B, useEffect as A, useCallback as I, useLayoutEffect as D } from "react";
3
+ import { d as H, v as E, S as C, a as _, b as k, i as d, c as M, g as O, l as R, e as F } from "./utils-t8tYdd6B.mjs";
4
+ import { u as J, V as q } from "./provider.client-CtAC9tPr.mjs";
5
+ import { S as te } from "./provider.client-CtAC9tPr.mjs";
6
+ function G(s, e, n = []) {
7
+ return j(
8
+ () => H(s, e),
9
+ // eslint-disable-next-line react-hooks/exhaustive-deps
10
+ n
11
+ );
12
+ }
13
+ const p = typeof window > "u", L = p ? A : D;
14
+ function K(s) {
15
+ s ??= {};
16
+ const e = p ? void 0 : E(s.storage), n = E(s.defaultValue), o = s.bidirectional, r = this[C], m = this[_], S = this[M], g = this[k], i = J({ verbose: m });
17
+ if (!(i instanceof q))
18
+ throw new Error("Make sure your component is wrapped in StateVocabProvider");
19
+ const u = s.serialize ?? JSON.stringify, y = s.deserialize ?? JSON.parse, a = (t, l, h) => {
20
+ const v = l.getItem(t);
21
+ v === null ? d(h) && l.setItem(t, u(h)) : i.set(t, y(v));
22
+ }, c = G(
23
+ s.onSet ?? (() => {
24
+ }),
25
+ s.delayedSet,
26
+ []
27
+ ), f = T(void 0), V = T(!1);
28
+ if (!V.current) {
29
+ V.current = !0;
30
+ let t = i.get(r);
31
+ d(t) || (t = n, d(t) && i.set(r, t)), !g && e && a(r, e, t);
32
+ }
33
+ const b = x(
34
+ i.subscribe.bind(i),
35
+ i.getClientSnapshot.bind(i),
36
+ i.getServerSnapshot.bind(i)
37
+ );
38
+ if (m)
39
+ if (S) {
40
+ const t = O(b, S);
41
+ t && R(t);
42
+ } else
43
+ R(b);
44
+ const z = O(b, r, n);
45
+ f.current = z, L(() => {
46
+ !g || !e || a(r, e, z);
47
+ }, []);
48
+ const w = B((t) => {
49
+ if (t.key !== r)
50
+ return;
51
+ const l = t.newValue, v = (l === null ? null : y(l)) ?? n;
52
+ d(v) && (i.set(r, v), c(v, f.current));
53
+ });
54
+ A(() => {
55
+ if (o)
56
+ return window.addEventListener("storage", w), () => window.removeEventListener("storage", w);
57
+ }, [o]);
58
+ const N = I((t) => {
59
+ const l = F(t) ? t(f.current) : t;
60
+ i.set(r, l), c(l, f.current), e && e.setItem(r, u(l));
61
+ }, [i, r, c, e, u]), P = I(() => {
62
+ const t = n;
63
+ if (!d(t)) {
64
+ e?.removeItem(r);
65
+ return;
66
+ }
67
+ i.set(r, t), c(t, f.current), e && e.setItem(r, u(t));
68
+ }, [n, i, r, c, e, u]);
69
+ return [
70
+ z,
71
+ N,
72
+ P
73
+ ];
74
+ }
75
+ function Q(s) {
76
+ s ??= {};
77
+ const e = p ? void 0 : E(s.storage), n = E(s.defaultValue), o = this[C], r = this[_], m = this[k], S = J({ verbose: r }), g = s.serialize ?? JSON.stringify, i = s.deserialize ?? JSON.parse, u = (c, f, V) => {
78
+ const b = f.getItem(c);
79
+ b === null ? d(V) && f.setItem(c, g(V)) : S.set(c, i(b));
80
+ }, y = T(!1);
81
+ let a;
82
+ y.current || (y.current = !0, a = S.get(o), d(a) || (a = n, d(a) && S.set(o, a)), !m && e && u(o, e, a)), L(() => {
83
+ !m || !e || u(o, e, a);
84
+ }, []);
85
+ }
86
+ function U(s) {
87
+ return typeof s == "object" && s !== null && "clientSlot" in s;
88
+ }
89
+ function W(s) {
90
+ const e = {};
91
+ for (const n in s) {
92
+ const o = s[n];
93
+ U(o) ? (e[n] = o.clientSlot({
94
+ useState(...r) {
95
+ return K.apply(this, r);
96
+ },
97
+ useInitialState(...r) {
98
+ Q.apply(this, r);
99
+ }
100
+ }), delete e[n].serverSlot, delete e[n].clientSlot) : o !== null && typeof o == "object" ? e[n] = W(o) : e[n] = o;
101
+ }
102
+ return e;
103
+ }
104
+ export {
105
+ te as StateVocabProvider,
106
+ W as clientify
107
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("./utils-33NqsZoR.js");function V(t={}){return{[a.STATE_DEFINITION]:!0,[a.STATE_PATH]:"",[a.STATE_VERBOSE]:!1,[a.STATE_VERBOSE_PATH]:"",[a.STATE_SSR]:!1,serverSlot(r){return Object.assign(this,{getState(...e){return r.getState.apply(this,e)}})},clientSlot(r){return Object.assign(this,{useState(e){return e??={},r.useState.apply(this,[{defaultValue:a.valueOrFactory(e.defaultValue)??a.valueOrFactory(t.defaultValue),bidirectional:e.bidirectional??t.bidirectional,storage:e.storage??t.storage,serialize:t.serialize??JSON.stringify,deserialize:t.deserialize??JSON.parse,delayedSet:e.delayedSet,onSet(...u){e.onSet&&e.onSet(...u)}}])},useInitialState(e){r.useInitialState.apply(this,[{defaultValue:e.defaultValue,storage:t.storage,serialize:t.serialize,deserialize:t.deserialize}])}})},toString(){return this[a.STATE_PATH]}}}function h(t,r){const{path:e="",verbose:u,verbosePath:y,ssr:A,cache:S}=r;let s=S.proxy.get(t);s||(s=new Map,S.proxy.set(t,s));const d=s.get(e);if(d)return d;const o=new Proxy(t,{get(b,T){const l=b[T],c=e?`${e}.${String(T)}`:String(T);if(l&&typeof l=="object"&&a.STATE_DEFINITION in l){const i=l;let n=S.leaf.get(i);n||(n=new Map,S.leaf.set(i,n));const g=n.get(c);if(g)return g;const P=Reflect.ownKeys(i).filter(f=>typeof i[f]=="function"),_=Object.fromEntries(P.map(f=>[f,(...v)=>i[f].call({...i,[a.STATE_PATH]:c,[a.STATE_VERBOSE]:u,[a.STATE_VERBOSE_PATH]:y,[a.STATE_SSR]:A},...v)])),E={...i,..._};return n.set(c,E),E}return l&&typeof l=="object"?h(l,{...r,path:c}):l}});return s.set(e,o),o}function z(t,r){return h(t,{...r,ssr:r?.ssr??!0,verbosePath:r?.verbosePath??"",cache:{proxy:new WeakMap,leaf:new WeakMap}})}exports.defineState=V;exports.setupStorage=z;
@@ -0,0 +1,119 @@
1
+ import { S as d, v as b, b as T, c as E, a as P, f as v } from "./utils-t8tYdd6B.mjs";
2
+ function m(t = {}) {
3
+ return {
4
+ [v]: !0,
5
+ // marks this object as a leaf in the router tree
6
+ [d]: "",
7
+ // placeholder; injected at runtime by injectPaths()
8
+ [P]: !1,
9
+ // placeholder
10
+ [E]: "",
11
+ // placeholder
12
+ [T]: !1,
13
+ // placeholder
14
+ serverSlot(a) {
15
+ return Object.assign(this, {
16
+ // Impl|RSC
17
+ getState(...e) {
18
+ return a.getState.apply(this, e);
19
+ }
20
+ });
21
+ },
22
+ clientSlot(a) {
23
+ return Object.assign(this, {
24
+ // Impl|RCC
25
+ useState(e) {
26
+ return e ??= {}, a.useState.apply(this, [{
27
+ defaultValue: b(e.defaultValue) ?? b(t.defaultValue),
28
+ bidirectional: e.bidirectional ?? t.bidirectional,
29
+ storage: e.storage ?? t.storage,
30
+ serialize: t.serialize ?? JSON.stringify,
31
+ deserialize: t.deserialize ?? JSON.parse,
32
+ delayedSet: e.delayedSet,
33
+ onSet(...S) {
34
+ e.onSet && e.onSet(...S);
35
+ }
36
+ }]);
37
+ },
38
+ // Impl|RCC
39
+ useInitialState(e) {
40
+ a.useInitialState.apply(this, [{
41
+ defaultValue: e.defaultValue,
42
+ storage: t.storage,
43
+ serialize: t.serialize,
44
+ deserialize: t.deserialize
45
+ }]);
46
+ }
47
+ });
48
+ },
49
+ /** Returns the fully qualified state name (dot-separated path). */
50
+ toString() {
51
+ return this[d];
52
+ }
53
+ };
54
+ }
55
+ function z(t, a) {
56
+ const {
57
+ path: e = "",
58
+ verbose: S,
59
+ verbosePath: x,
60
+ ssr: A,
61
+ cache: n
62
+ } = a;
63
+ let s = n.proxy.get(t);
64
+ s || (s = /* @__PURE__ */ new Map(), n.proxy.set(t, s));
65
+ const h = s.get(e);
66
+ if (h)
67
+ return h;
68
+ const o = new Proxy(t, {
69
+ get(V, u) {
70
+ const r = V[u], c = e ? `${e}.${String(u)}` : String(u);
71
+ if (r && typeof r == "object" && v in r) {
72
+ const l = r;
73
+ let i = n.leaf.get(l);
74
+ i || (i = /* @__PURE__ */ new Map(), n.leaf.set(l, i));
75
+ const g = i.get(c);
76
+ if (g)
77
+ return g;
78
+ const j = Reflect.ownKeys(l).filter(
79
+ (f) => typeof l[f] == "function"
80
+ ), w = Object.fromEntries(
81
+ j.map((f) => [
82
+ f,
83
+ (..._) => l[f].call(
84
+ {
85
+ ...l,
86
+ [d]: c,
87
+ [P]: S,
88
+ [E]: x,
89
+ [T]: A
90
+ },
91
+ ..._
92
+ )
93
+ ])
94
+ ), y = { ...l, ...w };
95
+ return i.set(c, y), y;
96
+ }
97
+ return r && typeof r == "object" ? z(r, {
98
+ ...a,
99
+ path: c
100
+ }) : r;
101
+ }
102
+ });
103
+ return s.set(e, o), o;
104
+ }
105
+ function M(t, a) {
106
+ return z(t, {
107
+ ...a,
108
+ ssr: a?.ssr ?? !0,
109
+ verbosePath: a?.verbosePath ?? "",
110
+ cache: {
111
+ proxy: /* @__PURE__ */ new WeakMap(),
112
+ leaf: /* @__PURE__ */ new WeakMap()
113
+ }
114
+ });
115
+ }
116
+ export {
117
+ m as defineState,
118
+ M as setupStorage
119
+ };
@@ -0,0 +1 @@
1
+ "use client";"use strict";const h=require("react/jsx-runtime"),r=require("react"),s=require("./utils-33NqsZoR.js"),c=r.createContext({});function S(o={}){const t=r.useContext(c);return o.verbose&&console.log(`[Store uid]: ${t.uid}`),t}class u{#t;#e;constructor(t){this.uid=Math.random().toString(36).slice(2),this.#t=t??{},this.#e=new Set}subscribe(t){return this.#e.add(t),()=>this.#e.delete(t)}getClientSnapshot(){return this.#t}getServerSnapshot(){return this.#t}get(t){return s.get(this.#t,t)}set(t,e){const n=s.get(this.#t,t),a=s.isTransformer(e)?e(n):e,i={...this.#t};s.set(i,t,a),this.#t=i,this.#e.forEach(l=>l())}}function b(o){const{children:t,value:e}=o,[n]=r.useState(()=>new u(e));return h.jsx(c.Provider,{value:n,children:t})}exports.StateVocabClientProvider=b;exports.VocabStore=u;exports.useStateVocabClientContext=S;
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ import { jsx as u } from "react/jsx-runtime";
3
+ import { createContext as h, useContext as l, useState as S } from "react";
4
+ import { g as n, e as d, s as b } from "./utils-t8tYdd6B.mjs";
5
+ const i = h({});
6
+ function V(s = {}) {
7
+ const t = l(i);
8
+ return s.verbose && console.log(`[Store uid]: ${t.uid}`), t;
9
+ }
10
+ class f {
11
+ #t;
12
+ #e;
13
+ constructor(t) {
14
+ this.uid = Math.random().toString(36).slice(2), this.#t = t ?? {}, this.#e = /* @__PURE__ */ new Set();
15
+ }
16
+ subscribe(t) {
17
+ return this.#e.add(t), () => this.#e.delete(t);
18
+ }
19
+ getClientSnapshot() {
20
+ return this.#t;
21
+ }
22
+ getServerSnapshot() {
23
+ return this.#t;
24
+ }
25
+ get(t) {
26
+ return n(this.#t, t);
27
+ }
28
+ set(t, e) {
29
+ const o = n(this.#t, t), c = d(e) ? e(o) : e, r = { ...this.#t };
30
+ b(r, t, c), this.#t = r, this.#e.forEach((a) => a());
31
+ }
32
+ }
33
+ function g(s) {
34
+ const { children: t, value: e } = s, [o] = S(() => new f(e));
35
+ return /* @__PURE__ */ u(i.Provider, { value: o, children: t });
36
+ }
37
+ export {
38
+ g as S,
39
+ f as V,
40
+ V as u
41
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("react/jsx-runtime"),v=require("react"),b=require("node:async_hooks"),y=require("./provider.client--OPImdtY.js"),u=require("./utils-33NqsZoR.js");function g(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const r in e)if(r!=="default"){const n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,n.get?n:{enumerable:!0,get:()=>e[r]})}}return t.default=e,Object.freeze(t)}const P=g(b),l=e=>{if(v.useState)throw new Error(`${e} only intended for Server Components`)};let i=null;try{l("StateVocabServerContext"),i=new P.AsyncLocalStorage}catch{i=null}const h=({value:e})=>(i?.enterWith({value:e}),null),j=({children:e})=>e,x=async e=>{l("StateVocabServerContext.Provider");const{value:t,children:r}=e;return c.jsxs(c.Fragment,{children:[c.jsx(h,{value:t}),r]})},V=()=>(l("getStateVocab"),i?.getStore()?.value),m={Provider:i?x:j},p=({value:e,children:t})=>c.jsx(m.Provider,{value:e,children:t});function w(e){const{children:t,value:r={}}=e;return c.jsx(p,{value:r,children:c.jsx(y.StateVocabClientProvider,{value:r,children:t})})}function O(){const e=this[u.STATE_PATH],t=V();if(!t)throw new Error("Make sure your component is wrapped in StateVocabProvider");return u.get(t,e)}function f(e,t){return new Proxy(e,{get(r,n,o){return n in t?Reflect.get(t,n,o):Reflect.get(r,n,o)},has(r,n){return n in t||n in r}})}function k(e){return typeof e=="object"&&e!==null&&"serverSlot"in e}function S(e,t){const r={};for(const n in e){const o=e[n];if(k(o))r[n]=o.serverSlot({getState(...s){return O.apply(this,s)}}),delete r[n].serverSlot,delete r[n].clientSlot;else if(o!==null&&typeof o=="object"){const s=a=>t({[n]:a}),d=S(o,s);r[n]=f(a=>s(a),d)}else r[n]=o}return f(n=>t(n),r)}function C(e){return S(e,t=>t)}exports.StateVocabProvider=w;exports.serverify=C;