@yakocloud/state-vocab 4.0.1 → 4.0.3

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
@@ -117,7 +117,7 @@ npm install @yakocloud/state-vocab react react-dom
117
117
 
118
118
  ## Quick Start
119
119
 
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.
120
+ Define your storage tree once (`setupStorage`/`defineState`), call `clientify` to attach React hooks. If you use react server components (RSC) wrap your app with `StateVocabProvider` at the root and call `serverify` to attach RSC-methods. Use state anywhere in the tree
121
121
 
122
122
  Library SSR-requirements:
123
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.
@@ -125,11 +125,11 @@ Library SSR-requirements:
125
125
 
126
126
  ```tsx
127
127
  import { setupStorage, defineState } from '@yakocloud/state-vocab'
128
- import { clientify, StateVocabProvider } from '@yakocloud/state-vocab/client'
128
+ import { clientify } from '@yakocloud/state-vocab/client'
129
129
 
130
130
  type Theme = 'Dark' | 'White' | 'System'
131
131
 
132
- const storage = clientify(setupStorage({
132
+ const storage = setupStorage({
133
133
  path: {
134
134
  to: {
135
135
  theme: defineState<Theme>({
@@ -138,18 +138,14 @@ const storage = clientify(setupStorage({
138
138
  }),
139
139
  },
140
140
  },
141
- }))
141
+ }, {
142
+ ssr: false // by default true
143
+ })
142
144
 
143
- function App() {
144
- return (
145
- <StateVocabProvider>
146
- <Settings />
147
- </StateVocabProvider>
148
- )
149
- }
145
+ const clientStorage = clientify(storage)
150
146
 
151
147
  function Settings() {
152
- const [theme, setTheme] = storage.path.to.theme.useState()
148
+ const [theme, setTheme] = clientStorage.path.to.theme.useState()
153
149
 
154
150
  return (
155
151
  <select value={theme} onChange={(e) => setTheme(e.target.value as Theme)}>
@@ -161,37 +157,6 @@ function Settings() {
161
157
  }
162
158
  ```
163
159
 
164
- ## Setup
165
-
166
- ### `StateVocabProvider`
167
-
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.
169
-
170
- ```tsx
171
- import { StateVocabProvider } from '@yakocloud/state-vocab/client'
172
-
173
- createRoot(document.getElementById('root')!).render(
174
- <React.StrictMode>
175
- <StateVocabProvider>
176
- <App />
177
- </StateVocabProvider>
178
- </React.StrictMode>
179
- )
180
- ```
181
-
182
- You can mount multiple independent providers — each gets its own store:
183
-
184
- ```tsx
185
- // Two isolated state trees — state does not bleed between them
186
- <StateVocabProvider>
187
- <WidgetA />
188
- </StateVocabProvider>
189
-
190
- <StateVocabProvider>
191
- <WidgetB />
192
- </StateVocabProvider>
193
- ```
194
-
195
160
  ## Core Concepts
196
161
 
197
162
  ### `defineState(options?)`
@@ -237,7 +202,7 @@ Wraps a nested object of `defineState()` nodes and injects dot-separated paths i
237
202
  |---|---|---|---|
238
203
  | `verbose` | `boolean \| undefined` | Log current state to the browser console on every change | `false` |
239
204
  | `verbosePath` | `string \| undefined` | Narrow verbose logging to a specific subtree (dot-separated path). When set, only that subtree is logged instead of the entire state. TypeScript will autocomplete valid paths based on your tree. | `undefined` |
240
- | `ssr` | `boolean \| undefined` | Defer storage reads until after hydration (Next.js / SSR) | `false` |
205
+ | `ssr` | `boolean \| undefined` | Defer storage reads until after hydration (Next.js / SSR) | `true` |
241
206
 
242
207
  ```ts
243
208
  const storage = setupStorage({
@@ -286,15 +251,90 @@ const storage = setupStorage({
286
251
  preference: {
287
252
  theme: defineState<Theme>({ storage: localStorage, defaultValue: 'Dark' }),
288
253
  },
289
- }, { ssr: true })
254
+ })
290
255
  ```
291
256
 
292
- With `ssr: true`:
257
+ By default `ssr: true`:
293
258
  - **Server & first client render** — always use `defaultValue`, storage is not read
294
259
  - **After hydration** — `useLayoutEffect` fires synchronously before paint, reads storage and updates state
295
260
 
296
261
  This guarantees the server and client produce identical markup, and the value from storage is applied without a visible flash.
297
262
 
263
+ ### Next.js Pages Router (SSR without RSC)
264
+
265
+ If you use Next.js with the **Pages Router** (or any SSR setup without React Server Components), you still need `ssr: true` to prevent hydration mismatches — but you don't have server components to wrap with `StateVocabProvider` from `serverify`.
266
+
267
+ In this case, wrap your app with `StateVocabClientProvider` from `@yakocloud/state-vocab/client`. It creates an isolated `VocabStore` per render, preventing state from leaking between concurrent SSR requests.
268
+
269
+ ```tsx
270
+ // pages/_app.tsx
271
+ import type { AppProps } from 'next/app'
272
+ import { StateVocabClientProvider } from '@yakocloud/state-vocab/client'
273
+
274
+ export default function App({ Component, pageProps }: AppProps) {
275
+ return (
276
+ <StateVocabClientProvider>
277
+ <Component {...pageProps} />
278
+ </StateVocabClientProvider>
279
+ )
280
+ }
281
+ ```
282
+
283
+ Storage is still configured with `ssr: true` (the default):
284
+
285
+ ```ts
286
+ // lib/storage.ts
287
+ import { setupStorage, defineState } from '@yakocloud/state-vocab'
288
+ import { clientify } from '@yakocloud/state-vocab/client'
289
+
290
+ const storage = setupStorage({
291
+ preference: {
292
+ theme: defineState<'Dark' | 'White' | 'System'>({
293
+ storage: localStorage,
294
+ defaultValue: 'Dark',
295
+ }),
296
+ },
297
+ // ssr: true is the default — storage reads are deferred until after hydration
298
+ })
299
+
300
+ export const clientStorage = clientify(storage)
301
+ ```
302
+
303
+ Use `clientStorage` directly in any page or component — no additional wiring needed:
304
+
305
+ ```tsx
306
+ // pages/settings.tsx
307
+ import { clientStorage } from '@/lib/storage'
308
+
309
+ export default function Settings() {
310
+ const [theme, setTheme] = clientStorage.preference.theme.useState()
311
+
312
+ return (
313
+ <select value={theme} onChange={(e) => setTheme(e.target.value as 'Dark' | 'White' | 'System')}>
314
+ <option value="Dark">Dark</option>
315
+ <option value="White">White</option>
316
+ <option value="System">System</option>
317
+ </select>
318
+ )
319
+ }
320
+ ```
321
+
322
+ `StateVocabClientProvider` also accepts an optional `value` prop to pre-seed the store with server-fetched data (e.g., from `getServerSideProps`):
323
+
324
+ ```tsx
325
+ // pages/_app.tsx
326
+ import type { AppProps } from 'next/app'
327
+ import { StateVocabClientProvider } from '@yakocloud/state-vocab/client'
328
+
329
+ export default function App({ Component, pageProps }: AppProps) {
330
+ return (
331
+ <StateVocabClientProvider value={pageProps.initialVocab}>
332
+ <Component {...pageProps} />
333
+ </StateVocabClientProvider>
334
+ )
335
+ }
336
+ ```
337
+
298
338
  ### React Server Components (RSC)
299
339
 
300
340
  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`.
@@ -304,8 +344,8 @@ For Next.js apps using the App Router, state-vocab provides dedicated server and
304
344
  | Import path | Use in |
305
345
  |---|---|
306
346
  | `@yakocloud/state-vocab` | Shared files — `defineState`, `setupStorage` |
307
- | `@yakocloud/state-vocab/server` | Server Components — `serverify`, `StateVocabProvider` |
308
- | `@yakocloud/state-vocab/client` | Client Components — `clientify` |
347
+ | `@yakocloud/state-vocab/server` | Server Components — `serverify` |
348
+ | `@yakocloud/state-vocab/client` | Client Components — `clientify`, `StateVocabClientProvider` |
309
349
 
310
350
  **1. Define the shared storage schema** (used on both server and client):
311
351
 
@@ -323,7 +363,7 @@ export const storage = setupStorage({
323
363
  city: defineState<string>(),
324
364
  },
325
365
  },
326
- }, { ssr: true })
366
+ })
327
367
  ```
328
368
 
329
369
  **2. Create server and client storage handles:**
@@ -351,7 +391,8 @@ export const clientStorage = clientify(storage)
351
391
  ```tsx
352
392
  // app/page.tsx (Server Component)
353
393
  import { serverStorage } from '@/storage.server'
354
- import { StateVocabProvider } from '@yakocloud/state-vocab/server'
394
+
395
+ const { StateVocabProvider } = serverStorage
355
396
 
356
397
  export default async function Page() {
357
398
  // Fetch data from DB / API
@@ -359,10 +400,10 @@ export default async function Page() {
359
400
 
360
401
  return (
361
402
  <StateVocabProvider
362
- value={serverStorage.set({
403
+ value={{
363
404
  user: { name: user.name, role: user.role },
364
405
  person: { address: { city: user.city } },
365
- })}
406
+ }}
366
407
  >
367
408
  <UserInfo />
368
409
  </StateVocabProvider>
@@ -398,35 +439,24 @@ export default function UserInfoClient() {
398
439
 
399
440
  #### `serverify(storage)`
400
441
 
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`. Each namespace node (including the root) gains a `.set()` method that returns the input wrapped under its full ancestor path, ready for `StateVocabProvider`'s `value` prop.
442
+ Converts a storage tree into its server-side counterpart. Each leaf gains a `.getState()` method that reads the value seeded into the nearest `StateVocabProvider`. Each namespace node (including the root) gains a `.seed()` method that returns the input wrapped under its full ancestor path. This method is optional for using. The result also exposes `StateVocabProvider` — a server-aware provider that accepts a plain object `value` prop to pre-seed the store.
402
443
 
403
- **`.set()` syntax:**
444
+ **`.seed()` syntax:**
404
445
 
405
446
  ```ts
406
- // Full tree at once — root .set() returns input as-is
407
- serverStorage.set({ user: { name: 'Alice', role: 'Admin' } })
447
+ // Full tree at once — root .seed() returns input as-is
448
+ serverStorage.seed({ user: { name: 'Alice', role: 'Admin' } })
408
449
  // → { user: { name: 'Alice', role: 'Admin' } }
409
450
 
410
451
  // Single namespace — wraps input under its key
411
- serverStorage.user.set({ name: 'Alice', role: 'Admin' })
452
+ serverStorage.user.seed({ name: 'Alice', role: 'Admin' })
412
453
  // → { user: { name: 'Alice', role: 'Admin' } }
413
454
 
414
455
  // Nested namespace — wraps up to the root
415
- serverStorage.person.address.set({ city: 'NY' })
456
+ serverStorage.person.address.seed({ city: 'NY' })
416
457
  // → { person: { address: { city: 'NY' } } }
417
458
  ```
418
459
 
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.set({ name: 'Alice', role: 'Admin' }),
425
- ...serverStorage.person.address.set({ city: 'NY' }),
426
- }}
427
- >
428
- ```
429
-
430
460
  **`node.getState()`** reads the value for that leaf from the surrounding `StateVocabProvider`. Throws if called outside one.
431
461
 
432
462
  #### `clientify(storage)`
@@ -521,11 +551,11 @@ const storage = setupStorage({
521
551
  import React from 'react'
522
552
  import { createRoot } from 'react-dom/client'
523
553
  import { setupStorage, defineState } from '@yakocloud/state-vocab'
524
- import { clientify, StateVocabProvider } from '@yakocloud/state-vocab/client'
554
+ import { clientify } from '@yakocloud/state-vocab/client'
525
555
 
526
556
  type Theme = 'Dark' | 'White' | 'System'
527
557
 
528
- const storage = clientify(setupStorage({
558
+ const storage = setupStorage({
529
559
  preference: {
530
560
  theme: defineState<Theme>({ storage: localStorage, defaultValue: 'Dark' }),
531
561
  nightMode: defineState({ storage: sessionStorage, defaultValue: false }),
@@ -547,11 +577,15 @@ const storage = clientify(setupStorage({
547
577
  storage: localStorage,
548
578
  }),
549
579
  },
550
- }))
580
+ }, {
581
+ ssr: false // by default true
582
+ })
583
+
584
+ const clientStorage = clientify(storage)
551
585
 
552
586
  // Root — initializes shared state for the whole subtree
553
587
  function Page() {
554
- storage.demo.pageProps.useState({
588
+ clientStorage.demo.pageProps.useState({
555
589
  defaultValue: { title: 'Hello', count: 42 },
556
590
  })
557
591
 
@@ -560,15 +594,15 @@ function Page() {
560
594
 
561
595
  // Deep child — reads without re-specifying defaults
562
596
  function PageHeader() {
563
- const [pageProps] = storage.demo.pageProps.useState()
597
+ const [pageProps] = clientStorage.demo.pageProps.useState()
564
598
  return <h1>{pageProps.title} ({pageProps.count})</h1>
565
599
  }
566
600
 
567
601
  function Dashboard() {
568
- const [theme, setTheme] = storage.preference.theme.useState()
569
- const [nightMode, setNightMode] = storage.preference.nightMode.useState()
570
- const [counter, setCounter, resetCounter] = storage.stats.counter.useState()
571
- const [note, setNote] = storage.personal.note.useState({
602
+ const [theme, setTheme] = clientStorage.preference.theme.useState()
603
+ const [nightMode, setNightMode] = clientStorage.preference.nightMode.useState()
604
+ const [counter, setCounter, resetCounter] = clientStorage.stats.counter.useState()
605
+ const [note, setNote] = clientStorage.personal.note.useState({
572
606
  delayedSet: 500,
573
607
  onSet: (v) => console.log('Saving note:', v),
574
608
  })
@@ -607,9 +641,7 @@ function Dashboard() {
607
641
 
608
642
  createRoot(document.getElementById('root')!).render(
609
643
  <React.StrictMode>
610
- <StateVocabProvider>
611
- <Page />
612
- </StateVocabProvider>
644
+ <Page />
613
645
  </React.StrictMode>
614
646
  )
615
647
  ```
@@ -632,42 +664,52 @@ createRoot(document.getElementById('root')!).render(
632
664
  |---|---|---|
633
665
  | `verbose` | `boolean \| undefined` | `false` |
634
666
  | `verbosePath` | `Path<T> \| undefined` | `undefined` |
635
- | `ssr` | `boolean \| undefined` | `false` |
667
+ | `ssr` | `boolean \| undefined` | `true` |
636
668
 
637
669
  Returns a proxied copy of `tree` with paths injected into all leaf nodes.
638
670
 
639
671
  ### `StateVocabProvider`
640
672
 
641
- A React context provider that initializes a `VocabStore` for its subtree. Required all components calling `.useState()` must be descendants of this provider.
673
+ A React context provider that initializes a `VocabStore` for its subtree. Only required in RSC / Next.js App Router contexts (where `ssr: true`). For standard SPAs without SSR, no provider is needed — hooks use a module-level store automatically.
674
+
675
+ In RSC contexts, `StateVocabProvider` is available on the `serverify()` result — it accepts an optional `value` prop to pre-seed the store with server-fetched data:
642
676
 
643
677
  ```tsx
644
- <StateVocabProvider>
678
+ const { StateVocabProvider } = serverStorage
679
+
680
+ <StateVocabProvider value={{ user: { name: 'Alice' } }}>
645
681
  <App />
646
682
  </StateVocabProvider>
647
683
  ```
648
684
 
649
- Accepts an optional `value` prop (imported from `@yakocloud/state-vocab/server` in RSC contexts) to pre-seed the store with server-fetched data:
685
+ ### `StateVocabClientProvider`
686
+
687
+ A client-only provider for SSR setups **without** React Server Components (e.g. Next.js Pages Router). Import from `@yakocloud/state-vocab/client` and place it at your app root to ensure per-request store isolation. Accepts an optional `value` prop to pre-seed the store.
650
688
 
651
689
  ```tsx
652
- <StateVocabProvider value={serverStorage.set({ user: { name: 'Alice' } })}>
690
+ import { StateVocabClientProvider } from '@yakocloud/state-vocab/client'
691
+
692
+ <StateVocabClientProvider value={initialVocab}>
653
693
  <App />
654
- </StateVocabProvider>
694
+ </StateVocabClientProvider>
655
695
  ```
656
696
 
657
697
  ### `serverify<T>(storage: T)`
658
698
 
659
699
  Converts a storage tree to its server-side counterpart. Available from `@yakocloud/state-vocab/server`.
660
700
 
661
- Each leaf gains `.getState()` — reads the value from the nearest `StateVocabProvider`. Each namespace node gains `.set()`, which returns the input wrapped under its full ancestor path (ready for `StateVocabProvider`'s `value` prop).
701
+ Each leaf gains `.getState()` — reads the value from the nearest `StateVocabProvider`. Each namespace node gains `.seed()`, which returns the input wrapped under its full ancestor path. The result also includes `StateVocabProvider` use it instead of importing from `@yakocloud/state-vocab/server`.
662
702
 
663
703
  ```ts
664
704
  import { serverify } from '@yakocloud/state-vocab/server'
665
705
  const serverStorage = serverify(storage)
666
706
 
707
+ const { StateVocabProvider } = serverStorage // server-aware provider
708
+
667
709
  serverStorage.user.name.getState() // reads "user.name" from context
668
- serverStorage.user.set({ name: 'Alice' }) // → { user: { name: 'Alice' } }
669
- serverStorage.person.address.set({ city: 'NY' }) // → { person: { address: { city: 'NY' } } }
670
- serverStorage.set({ user: { name: 'Alice' } }) // → { user: { name: 'Alice' } } (identity)
710
+ serverStorage.user.seed({ name: 'Alice' }) // → { user: { name: 'Alice' } }
711
+ serverStorage.person.address.seed({ city: 'NY' }) // → { person: { address: { city: 'NY' } } }
712
+ serverStorage.seed({ user: { name: 'Alice' } }) // → { user: { name: 'Alice' } } (identity)
671
713
  ```
672
714
 
673
715
  ### `clientify<T>(storage: T)`
@@ -1 +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;
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react"),v=require("./constants-CbsduCZ7.js"),a=require("./utils-0CTNJ4ZE.js"),V=require("./provider-client-B4XQ24JB.js");function D(t,e,n=[]){return u.useMemo(()=>a.debounce(t,e),n)}const g=t=>{const{vocabStore:e,storage:n,statePath:o,value:i,deserialize:S,serialize:l}=t,f=n.getItem(o);f===null?a.isValueDefined(i)&&n.setItem(o,l(i)):e.set(o,S(f))},z=typeof window>"u",O=z?u.useEffect:u.useLayoutEffect,m=new V.VocabStore,A="Make sure your component is wrapped in StateVocabProvider (RSC) or disable ssr option in setupStorage for SPA (RCC only)";function I(t){t??={};const e=z?void 0:a.valueOrFactory(t.storage),n=a.valueOrFactory(t.defaultValue),o=t.bidirectional,i=this[v.STATE_PATH],S=this[v.STATE_VERBOSE],l=this[v.STATE_VERBOSE_PATH],f=this[v.STATE_SSR];let s=V.useStateVocabClientContext({verbose:S});if(!(s instanceof V.VocabStore)){if(f)throw new Error(A);s=m}const d=t.serialize??JSON.stringify,c=t.deserialize??JSON.parse,E=D(t.onSet??(()=>{}),t.delayedSet,[]),y=u.useRef(void 0),C=u.useRef(!1);if(!C.current){C.current=!0;let r=s.get(i);a.isValueDefined(r)||(r=n,a.isValueDefined(r)&&s.set(i,r)),!f&&e&&g({vocabStore:s,storage:e,statePath:i,value:r,serialize:d,deserialize:c})}const h=u.useSyncExternalStore(s.subscribe.bind(s),s.getClientSnapshot.bind(s),s.getServerSnapshot.bind(s));if(S)if(l){const r=a.get(h,l);r&&a.logStyled(r)}else a.logStyled(h);const T=a.get(h,i,n);y.current=T,O(()=>{!f||!e||g({vocabStore:s,storage:e,statePath:i,value:T,serialize:d,deserialize:c})},[]);const w=u.useEffectEvent(r=>{if(r.key!==i)return;const b=r.newValue,R=(b===null?null:c(b))??n;a.isValueDefined(R)&&(s.set(i,R),E(R,y.current))});u.useEffect(()=>{if(o)return window.addEventListener("storage",w),()=>window.removeEventListener("storage",w)},[o]);const p=u.useCallback(r=>{const b=a.isTransformer(r)?r(y.current):r;s.set(i,b),E(b,y.current),e&&e.setItem(i,d(b))},[s,i,E,e,d]),_=u.useCallback(()=>{const r=n;if(!a.isValueDefined(r)){e?.removeItem(i);return}s.set(i,r),E(r,y.current),e&&e.setItem(i,d(r))},[n,s,i,E,e,d]);return[T,p,_]}function k(t){t??={};const e=z?void 0:a.valueOrFactory(t.storage),n=a.valueOrFactory(t.defaultValue),o=this[v.STATE_PATH],i=this[v.STATE_VERBOSE],S=this[v.STATE_SSR];let l=V.useStateVocabClientContext({verbose:i});if(!(l instanceof V.VocabStore)){if(S)throw new Error(A);l=m}const f=t.serialize??JSON.stringify,s=t.deserialize??JSON.parse,d=u.useRef(!1);let c;d.current||(d.current=!0,c=l.get(o),a.isValueDefined(c)||(c=n,a.isValueDefined(c)&&l.set(o,c)),!S&&e&&g({vocabStore:l,storage:e,statePath:o,value:c,serialize:f,deserialize:s})),O(()=>{!S||!e||g({vocabStore:l,storage:e,statePath:o,value:c,serialize:f,deserialize:s})},[])}function q(t){return typeof t=="object"&&t!==null&&"clientSlot"in t}function P(t){const e={};for(const n in t){const o=t[n];q(o)?(e[n]=o.clientSlot({useState(...i){return I.apply(this,i)},useInitialState(...i){k.apply(this,i)}}),delete e[n].serverSlot,delete e[n].clientSlot):o!==null&&typeof o=="object"?e[n]=P(o):e[n]=o}return e}exports.StateVocabClientProvider=V.StateVocabClientProvider;exports.clientify=P;
package/dist/client.es.js CHANGED
@@ -1,107 +1,151 @@
1
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),
2
+ import { useMemo as x, useRef as y, useSyncExternalStore as B, useEffectEvent as D, useEffect as A, useCallback as T, useLayoutEffect as H } from "react";
3
+ import { S as I, a as O, b as _, c as F } from "./constants-BB1YAX6c.mjs";
4
+ import { a as d, d as G, v as g, g as C, l as P, i as q } from "./utils-xV3x3fTc.mjs";
5
+ import { V as z, u as k } from "./provider-client-C2Aw0Lmi.mjs";
6
+ import { S as ie } from "./provider-client-C2Aw0Lmi.mjs";
7
+ function K(t, e, n = []) {
8
+ return x(
9
+ () => G(t, e),
9
10
  // eslint-disable-next-line react-hooks/exhaustive-deps
10
11
  n
11
12
  );
12
13
  }
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 ?? (() => {
14
+ const E = (t) => {
15
+ const {
16
+ vocabStore: e,
17
+ storage: n,
18
+ statePath: o,
19
+ value: s,
20
+ deserialize: f,
21
+ serialize: a
22
+ } = t, c = n.getItem(o);
23
+ c === null ? d(s) && n.setItem(o, a(s)) : e.set(o, f(c));
24
+ }, p = typeof window > "u", J = p ? A : H, L = new z(), N = "Make sure your component is wrapped in StateVocabProvider (RSC) or disable ssr option in setupStorage for SPA (RCC only)";
25
+ function Q(t) {
26
+ t ??= {};
27
+ const e = p ? void 0 : g(t.storage), n = g(t.defaultValue), o = t.bidirectional, s = this[I], f = this[O], a = this[F], c = this[_];
28
+ let r = k({ verbose: f });
29
+ if (!(r instanceof z)) {
30
+ if (c)
31
+ throw new Error(N);
32
+ r = L;
33
+ }
34
+ const u = t.serialize ?? JSON.stringify, l = t.deserialize ?? JSON.parse, v = K(
35
+ t.onSet ?? (() => {
24
36
  }),
25
- s.delayedSet,
37
+ t.delayedSet,
26
38
  []
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);
39
+ ), b = y(void 0), w = y(!1);
40
+ if (!w.current) {
41
+ w.current = !0;
42
+ let i = r.get(s);
43
+ d(i) || (i = n, d(i) && r.set(s, i)), !c && e && E({
44
+ vocabStore: r,
45
+ storage: e,
46
+ statePath: s,
47
+ value: i,
48
+ serialize: u,
49
+ deserialize: l
50
+ });
32
51
  }
33
- const b = x(
34
- i.subscribe.bind(i),
35
- i.getClientSnapshot.bind(i),
36
- i.getServerSnapshot.bind(i)
52
+ const h = B(
53
+ r.subscribe.bind(r),
54
+ r.getClientSnapshot.bind(r),
55
+ r.getServerSnapshot.bind(r)
37
56
  );
38
- if (m)
39
- if (S) {
40
- const t = O(b, S);
41
- t && R(t);
57
+ if (f)
58
+ if (a) {
59
+ const i = C(h, a);
60
+ i && P(i);
42
61
  } else
43
- R(b);
44
- const z = O(b, r, n);
45
- f.current = z, L(() => {
46
- !g || !e || a(r, e, z);
62
+ P(h);
63
+ const m = C(h, s, n);
64
+ b.current = m, J(() => {
65
+ !c || !e || E({
66
+ vocabStore: r,
67
+ storage: e,
68
+ statePath: s,
69
+ value: m,
70
+ serialize: u,
71
+ deserialize: l
72
+ });
47
73
  }, []);
48
- const w = B((t) => {
49
- if (t.key !== r)
74
+ const R = D((i) => {
75
+ if (i.key !== s)
50
76
  return;
51
- const l = t.newValue, v = (l === null ? null : y(l)) ?? n;
52
- d(v) && (i.set(r, v), c(v, f.current));
77
+ const S = i.newValue, V = (S === null ? null : l(S)) ?? n;
78
+ d(V) && (r.set(s, V), v(V, b.current));
53
79
  });
54
80
  A(() => {
55
81
  if (o)
56
- return window.addEventListener("storage", w), () => window.removeEventListener("storage", w);
82
+ return window.addEventListener("storage", R), () => window.removeEventListener("storage", R);
57
83
  }, [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);
84
+ const M = T((i) => {
85
+ const S = q(i) ? i(b.current) : i;
86
+ r.set(s, S), v(S, b.current), e && e.setItem(s, u(S));
87
+ }, [r, s, v, e, u]), j = T(() => {
88
+ const i = n;
89
+ if (!d(i)) {
90
+ e?.removeItem(s);
65
91
  return;
66
92
  }
67
- i.set(r, t), c(t, f.current), e && e.setItem(r, u(t));
68
- }, [n, i, r, c, e, u]);
93
+ r.set(s, i), v(i, b.current), e && e.setItem(s, u(i));
94
+ }, [n, r, s, v, e, u]);
69
95
  return [
70
- z,
71
- N,
72
- P
96
+ m,
97
+ M,
98
+ j
73
99
  ];
74
100
  }
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);
101
+ function U(t) {
102
+ t ??= {};
103
+ const e = p ? void 0 : g(t.storage), n = g(t.defaultValue), o = this[I], s = this[O], f = this[_];
104
+ let a = k({ verbose: s });
105
+ if (!(a instanceof z)) {
106
+ if (f)
107
+ throw new Error(N);
108
+ a = L;
109
+ }
110
+ const c = t.serialize ?? JSON.stringify, r = t.deserialize ?? JSON.parse, u = y(!1);
111
+ let l;
112
+ u.current || (u.current = !0, l = a.get(o), d(l) || (l = n, d(l) && a.set(o, l)), !f && e && E({
113
+ vocabStore: a,
114
+ storage: e,
115
+ statePath: o,
116
+ value: l,
117
+ serialize: c,
118
+ deserialize: r
119
+ })), J(() => {
120
+ !f || !e || E({
121
+ vocabStore: a,
122
+ storage: e,
123
+ statePath: o,
124
+ value: l,
125
+ serialize: c,
126
+ deserialize: r
127
+ });
84
128
  }, []);
85
129
  }
86
- function U(s) {
87
- return typeof s == "object" && s !== null && "clientSlot" in s;
130
+ function W(t) {
131
+ return typeof t == "object" && t !== null && "clientSlot" in t;
88
132
  }
89
- function W(s) {
133
+ function X(t) {
90
134
  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);
135
+ for (const n in t) {
136
+ const o = t[n];
137
+ W(o) ? (e[n] = o.clientSlot({
138
+ useState(...s) {
139
+ return Q.apply(this, s);
96
140
  },
97
- useInitialState(...r) {
98
- Q.apply(this, r);
141
+ useInitialState(...s) {
142
+ U.apply(this, s);
99
143
  }
100
- }), delete e[n].serverSlot, delete e[n].clientSlot) : o !== null && typeof o == "object" ? e[n] = W(o) : e[n] = o;
144
+ }), delete e[n].serverSlot, delete e[n].clientSlot) : o !== null && typeof o == "object" ? e[n] = X(o) : e[n] = o;
101
145
  }
102
146
  return e;
103
147
  }
104
148
  export {
105
- te as StateVocabProvider,
106
- W as clientify
149
+ ie as StateVocabClientProvider,
150
+ X as clientify
107
151
  };
@@ -0,0 +1,8 @@
1
+ const s = /* @__PURE__ */ Symbol("state-def"), t = /* @__PURE__ */ Symbol("state-path"), S = /* @__PURE__ */ Symbol("state-verbose"), a = /* @__PURE__ */ Symbol("state-verbose-path"), o = /* @__PURE__ */ Symbol("state-ssr");
2
+ export {
3
+ t as S,
4
+ S as a,
5
+ o as b,
6
+ a as c,
7
+ s as d
8
+ };
@@ -0,0 +1 @@
1
+ "use strict";const T=Symbol("state-def"),S=Symbol("state-path"),E=Symbol("state-verbose"),t=Symbol("state-verbose-path"),s=Symbol("state-ssr");exports.STATE_DEFINITION=T;exports.STATE_PATH=S;exports.STATE_SSR=s;exports.STATE_VERBOSE=E;exports.STATE_VERBOSE_PATH=t;
package/dist/index.cjs.js CHANGED
@@ -1 +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;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./constants-CbsduCZ7.js"),h=require("./utils-0CTNJ4ZE.js");function z(t={}){return{[r.STATE_DEFINITION]:!0,[r.STATE_PATH]:"",[r.STATE_VERBOSE]:!1,[r.STATE_VERBOSE_PATH]:"",[r.STATE_SSR]:!1,serverSlot(a){return Object.assign(this,{getState(...e){return a.getState.apply(this,e)}})},clientSlot(a){return Object.assign(this,{useState(e){return e??={},a.useState.apply(this,[{defaultValue:h.valueOrFactory(e.defaultValue)??h.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(...f){e.onSet&&e.onSet(...f)}}])},useInitialState(e){a.useInitialState.apply(this,[{defaultValue:e.defaultValue,storage:t.storage,serialize:t.serialize,deserialize:t.deserialize}])}})},toString(){return this[r.STATE_PATH]}}}function y(t,a){const{path:e="",verbose:f,verbosePath:A,ssr:b,cache:c}=a;let s=c.proxy.get(t);s||(s=new Map,c.proxy.set(t,s));const o=s.get(e);if(o)return o;const d=new Proxy(t,{get(P,T){const l=P[T],S=e?`${e}.${String(T)}`:String(T);if(l&&typeof l=="object"&&r.STATE_DEFINITION in l){const i=l;let n=c.leaf.get(i);n||(n=new Map,c.leaf.set(i,n));const g=n.get(S);if(g)return g;const _=Reflect.ownKeys(i).filter(u=>typeof i[u]=="function"),v=Object.fromEntries(_.map(u=>[u,(...V)=>i[u].call({...i,[r.STATE_PATH]:S,[r.STATE_VERBOSE]:f,[r.STATE_VERBOSE_PATH]:A,[r.STATE_SSR]:b},...V)])),E={...i,...v};return n.set(S,E),E}return l&&typeof l=="object"?y(l,{...a,path:S}):l}});return s.set(e,d),d}function I(t,a){return y(t,{...a,ssr:a?.ssr??!0,verbosePath:a?.verbosePath??"",cache:{proxy:new WeakMap,leaf:new WeakMap}})}exports.defineState=z;exports.setupStorage=I;
package/dist/index.es.js CHANGED
@@ -1,5 +1,6 @@
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 = {}) {
1
+ import { S as d, b as T, c as E, a as P, d as v } from "./constants-BB1YAX6c.mjs";
2
+ import { v as b } from "./utils-xV3x3fTc.mjs";
3
+ function M(t = {}) {
3
4
  return {
4
5
  [v]: !0,
5
6
  // marks this object as a leaf in the router tree
@@ -62,10 +63,10 @@ function z(t, a) {
62
63
  } = a;
63
64
  let s = n.proxy.get(t);
64
65
  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, {
66
+ const o = s.get(e);
67
+ if (o)
68
+ return o;
69
+ const h = new Proxy(t, {
69
70
  get(V, u) {
70
71
  const r = V[u], c = e ? `${e}.${String(u)}` : String(u);
71
72
  if (r && typeof r == "object" && v in r) {
@@ -77,10 +78,10 @@ function z(t, a) {
77
78
  return g;
78
79
  const j = Reflect.ownKeys(l).filter(
79
80
  (f) => typeof l[f] == "function"
80
- ), w = Object.fromEntries(
81
+ ), m = Object.fromEntries(
81
82
  j.map((f) => [
82
83
  f,
83
- (..._) => l[f].call(
84
+ (...w) => l[f].call(
84
85
  {
85
86
  ...l,
86
87
  [d]: c,
@@ -88,10 +89,10 @@ function z(t, a) {
88
89
  [E]: x,
89
90
  [T]: A
90
91
  },
91
- ..._
92
+ ...w
92
93
  )
93
94
  ])
94
- ), y = { ...l, ...w };
95
+ ), y = { ...l, ...m };
95
96
  return i.set(c, y), y;
96
97
  }
97
98
  return r && typeof r == "object" ? z(r, {
@@ -100,9 +101,9 @@ function z(t, a) {
100
101
  }) : r;
101
102
  }
102
103
  });
103
- return s.set(e, o), o;
104
+ return s.set(e, h), h;
104
105
  }
105
- function M(t, a) {
106
+ function N(t, a) {
106
107
  return z(t, {
107
108
  ...a,
108
109
  ssr: a?.ssr ?? !0,
@@ -114,6 +115,6 @@ function M(t, a) {
114
115
  });
115
116
  }
116
117
  export {
117
- m as defineState,
118
- M as setupStorage
118
+ M as defineState,
119
+ N as setupStorage
119
120
  };
@@ -0,0 +1 @@
1
+ "use client";"use strict";const h=require("react/jsx-runtime"),r=require("react"),s=require("./utils-0CTNJ4ZE.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;
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import { jsx as u } from "react/jsx-runtime";
3
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";
4
+ import { g as n, i as d, s as b } from "./utils-xV3x3fTc.mjs";
5
5
  const i = h({});
6
- function V(s = {}) {
6
+ function V(s) {
7
7
  const t = l(i);
8
8
  return s.verbose && console.log(`[Store uid]: ${t.uid}`), t;
9
9
  }
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("react/jsx-runtime"),d=require("react"),v=require("node:async_hooks"),f=require("./provider.client--OPImdtY.js"),l=require("./utils-33NqsZoR.js");function b(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const r in e)if(r!=="default"){const o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:()=>e[r]})}}return t.default=e,Object.freeze(t)}const y=b(v),a=e=>{if(d.useState)throw new Error(`${e} only intended for Server Components`)};let s=null;try{a("StateVocabServerContext"),s=new y.AsyncLocalStorage}catch{s=null}const p=({value:e})=>(s?.enterWith({value:e}),null),g=({children:e})=>e,P=async e=>{a("StateVocabServerContext.Provider");const{value:t,children:r}=e;return c.jsxs(c.Fragment,{children:[c.jsx(p,{value:t}),r]})},h=()=>(a("getStateVocab"),s?.getStore()?.value),j={Provider:s?P:g},V=({value:e,children:t})=>c.jsx(j.Provider,{value:e,children:t});function m(e){const{children:t,value:r={}}=e;return c.jsx(V,{value:r,children:c.jsx(f.StateVocabClientProvider,{value:r,children:t})})}function x(){const e=this[l.STATE_PATH],t=h();if(!t)throw new Error("Make sure your component is wrapped in StateVocabProvider");return l.get(t,e)}function O(e){return typeof e=="object"&&e!==null&&"serverSlot"in e}function u(e,t){const r={};for(const o in e){const n=e[o];if(O(n))r[o]=n.serverSlot({getState(...i){return x.apply(this,i)}}),delete r[o].serverSlot,delete r[o].clientSlot;else if(n!==null&&typeof n=="object"){const i=S=>t({[o]:S});r[o]=u(n,i)}else r[o]=n}return r.set=o=>t(o),r}function k(e){return u(e,t=>t)}exports.StateVocabProvider=m;exports.serverify=k;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react/jsx-runtime"),S=require("./constants-CbsduCZ7.js"),d=require("react"),v=require("node:async_hooks"),f=require("./provider-client-B4XQ24JB.js"),b=require("./utils-0CTNJ4ZE.js");function y(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=y(v),a=e=>{if(d.useState)throw new Error(`${e} only intended for Server Components`)};let s=null;try{a("StateVocabServerContext"),s=new p.AsyncLocalStorage}catch{s=null}const g=({value:e})=>(s?.enterWith({value:e}),null),P=({children:e})=>e,j=async e=>{a("StateVocabServerContext.Provider");const{value:t,children:r}=e;return o.jsxs(o.Fragment,{children:[o.jsx(g,{value:t}),r]})},h=()=>(a("getStateVocab"),s?.getStore()?.value),x={Provider:s?j:P},V=({value:e,children:t})=>o.jsx(x.Provider,{value:e,children:t});function m(e){const{children:t,value:r={}}=e;return o.jsx(V,{value:r,children:o.jsx(f.StateVocabClientProvider,{value:r,children:t})})}const O="Make sure your component is wrapped in StateVocabProvider";function C(){const e=this[S.STATE_PATH],t=h();if(!t)throw new Error(O);return b.get(t,e)}function E(e){return typeof e=="object"&&e!==null&&"serverSlot"in e}function l(e,t){const r={};for(const n in e){const c=e[n];if(E(c))r[n]=c.serverSlot({getState(...i){return C.apply(this,i)}}),delete r[n].serverSlot,delete r[n].clientSlot;else if(c!==null&&typeof c=="object"){const i=u=>t({[n]:u});r[n]=l(c,i)}else r[n]=c}return r.seed=n=>t(n),r}function k(e){return{...l(e,t=>t),StateVocabProvider({children:t,value:r}){return o.jsx(m,{value:r,children:t})}}}exports.serverify=k;
package/dist/server.es.js CHANGED
@@ -1,50 +1,52 @@
1
- import { jsxs as u, Fragment as v, jsx as s } from "react/jsx-runtime";
1
+ import { jsxs as u, Fragment as v, jsx as c } from "react/jsx-runtime";
2
+ import { S as d } from "./constants-BB1YAX6c.mjs";
2
3
  import f from "react";
3
- import * as d from "node:async_hooks";
4
- import { S as p } from "./provider.client-CtAC9tPr.mjs";
5
- import { S as h, g as m } from "./utils-t8tYdd6B.mjs";
4
+ import * as p from "node:async_hooks";
5
+ import { S as m } from "./provider-client-C2Aw0Lmi.mjs";
6
+ import { g as b } from "./utils-xV3x3fTc.mjs";
6
7
  const i = (e) => {
7
8
  if (f.useState)
8
9
  throw new Error(`${e} only intended for Server Components`);
9
10
  };
10
- let c = null;
11
+ let s = null;
11
12
  try {
12
- i("StateVocabServerContext"), c = new d.AsyncLocalStorage();
13
+ i("StateVocabServerContext"), s = new p.AsyncLocalStorage();
13
14
  } catch {
14
- c = null;
15
+ s = null;
15
16
  }
16
- const y = ({ value: e }) => (c?.enterWith({ value: e }), null), b = ({ children: e }) => e, P = async (e) => {
17
+ const h = ({ value: e }) => (s?.enterWith({ value: e }), null), y = ({ children: e }) => e, P = async (e) => {
17
18
  i("StateVocabServerContext.Provider");
18
19
  const { value: t, children: r } = e;
19
20
  return /* @__PURE__ */ u(v, { children: [
20
- /* @__PURE__ */ s(y, { value: t }),
21
+ /* @__PURE__ */ c(h, { value: t }),
21
22
  r
22
23
  ] });
23
- }, g = () => (i("getStateVocab"), c?.getStore()?.value), V = {
24
- Provider: c ? P : b
25
- }, x = ({ value: e, children: t }) => /* @__PURE__ */ s(V.Provider, { value: e, children: t });
26
- function T(e) {
24
+ }, g = () => (i("getStateVocab"), s?.getStore()?.value), V = {
25
+ Provider: s ? P : y
26
+ }, E = ({ value: e, children: t }) => /* @__PURE__ */ c(V.Provider, { value: e, children: t });
27
+ function x(e) {
27
28
  const { children: t, value: r = {} } = e;
28
- return /* @__PURE__ */ s(x, { value: r, children: /* @__PURE__ */ s(p, { value: r, children: t }) });
29
+ return /* @__PURE__ */ c(E, { value: r, children: /* @__PURE__ */ c(m, { value: r, children: t }) });
29
30
  }
30
- function C() {
31
- const e = this[h], t = g();
31
+ const C = "Make sure your component is wrapped in StateVocabProvider";
32
+ function w() {
33
+ const e = this[d], t = g();
32
34
  if (!t)
33
- throw new Error("Make sure your component is wrapped in StateVocabProvider");
34
- return m(t, e);
35
+ throw new Error(C);
36
+ return b(t, e);
35
37
  }
36
- function w(e) {
38
+ function R(e) {
37
39
  return typeof e == "object" && e !== null && "serverSlot" in e;
38
40
  }
39
41
  function l(e, t) {
40
42
  const r = {};
41
43
  for (const o in e) {
42
44
  const n = e[o];
43
- if (w(n))
45
+ if (R(n))
44
46
  r[o] = n.serverSlot({
45
47
  // Slot definition
46
48
  getState(...a) {
47
- return C.apply(this, a);
49
+ return w.apply(this, a);
48
50
  }
49
51
  }), delete r[o].serverSlot, delete r[o].clientSlot;
50
52
  else if (n !== null && typeof n == "object") {
@@ -53,12 +55,16 @@ function l(e, t) {
53
55
  } else
54
56
  r[o] = n;
55
57
  }
56
- return r.set = (o) => t(o), r;
58
+ return r.seed = (o) => t(o), r;
57
59
  }
58
- function H(e) {
59
- return l(e, (t) => t);
60
+ function M(e) {
61
+ return {
62
+ ...l(e, (t) => t),
63
+ StateVocabProvider({ children: t, value: r }) {
64
+ return /* @__PURE__ */ c(x, { value: r, children: t });
65
+ }
66
+ };
60
67
  }
61
68
  export {
62
- T as StateVocabProvider,
63
- H as serverify
69
+ M as serverify
64
70
  };
@@ -1,2 +1,2 @@
1
1
  export { clientify } from "./setup.client";
2
- export { StateVocabClientProvider as StateVocabProvider } from "./provider.client";
2
+ export { StateVocabClientProvider } from "./provider.client";
@@ -3,6 +3,6 @@ export declare const StateVocabClientContext: import("react").Context<VocabStore
3
3
  /**
4
4
  * @see method from from https://zustand.docs.pmnd.rs/learn/guides/nextjs
5
5
  */
6
- export declare function useStateVocabClientContext(options?: {
7
- verbose?: boolean;
6
+ export declare function useStateVocabClientContext(options: {
7
+ verbose: boolean;
8
8
  }): VocabStore;
@@ -1,2 +1 @@
1
- export { StateVocabProvider } from "./provider";
2
1
  export { serverify } from "./setup.server";
@@ -0,0 +1,12 @@
1
+ import { type DependencyList } from "react";
2
+ import VocabStore from "./store";
3
+ import type { Deserialize, Serialize } from "./state.types";
4
+ export declare function useDebounce<T extends (...args: never[]) => unknown>(effect: T, wait: number | undefined, deps?: DependencyList): (...args: Parameters<T>) => void;
5
+ export declare const sync: <V>(options: {
6
+ vocabStore: VocabStore;
7
+ statePath: string;
8
+ storage: Storage;
9
+ serialize: Serialize<V>;
10
+ deserialize: Deserialize<V>;
11
+ value: V | undefined;
12
+ }) => void;
@@ -1,3 +1,4 @@
1
+ import type { PropsWithChildren, ReactNode } from "react";
1
2
  import type { Vocab } from "./state.types";
2
3
  type Placeholder<V> = {
3
4
  getState(): V;
@@ -9,12 +10,15 @@ type ServerifiedValue<R> = {
9
10
  [K in keyof R]?: R[K] extends Slot<infer V> ? V : R[K] extends object ? ServerifiedValue<R[K]> : R[K];
10
11
  };
11
12
  type Serverified<R> = R extends Slot<infer TValue> ? Placeholder<TValue> : R extends object ? {
12
- set(input: ServerifiedValue<R>): Vocab;
13
+ seed(input: ServerifiedValue<R>): Vocab;
13
14
  } & {
14
15
  [K in keyof R]: Serverified<R[K]>;
15
16
  } : R;
16
17
  type ServerifyResult<R extends object> = {
17
- set(input: ServerifiedValue<R>): Vocab;
18
+ seed(input: ServerifiedValue<R>): Vocab;
19
+ StateVocabProvider(props: PropsWithChildren<{
20
+ value?: ServerifiedValue<R>;
21
+ }>): Promise<ReactNode>;
18
22
  } & {
19
23
  [K in keyof R]: Serverified<R[K]>;
20
24
  };
@@ -0,0 +1,3 @@
1
+ "use strict";function f(e,s,n){if(!s)return e;const o=s.split(".");let t=e;for(const c of o)if(t!==null&&typeof t=="object"&&c in t)t=t[c];else return n;return t===void 0?n:t}function y(e,s,n){const o=s.replace(/\[(\d+)\]/g,".$1").split(".");let t=e;for(let c=0;c<o.length-1;c++){const r=o[c],i=o[c+1];(t[r]===void 0||t[r]===null)&&(t[r]=/^\d+$/.test(i)?[]:{}),t=t[r]}return t[o[o.length-1]]=n,e}function d(e,s=0){let n;return function(...o){n!==void 0&&clearTimeout(n),n=setTimeout(()=>{n=void 0,e.apply(this,o)},s)}}function a(e){const s=JSON.stringify(e,null,2).split(`
2
+ `),n=[],o=[];for(const t of s){const c=t.match(/^(\s*)"([^"]+)"(\s*:\s*)(.+)$/);if(c){const[,r,i,l,u]=c;n.push(`${r}%c"${i}"%c${l}%c${u}`),o.push("color: #9cdcfe; font-weight: bold","color: #cccccc","color: #ce9178")}else n.push(`%c${t}`),o.push("color: #cccccc")}console.log(n.join(`
3
+ `),...o,e)}const p=e=>typeof e=="function",g=e=>typeof e=="function",h=e=>typeof e<"u",$=e=>g(e)?e():e;exports.debounce=d;exports.get=f;exports.isTransformer=p;exports.isValueDefined=h;exports.logStyled=a;exports.set=y;exports.valueOrFactory=$;
@@ -0,0 +1,57 @@
1
+ function a(t, s, n) {
2
+ if (!s)
3
+ return t;
4
+ const o = s.split(".");
5
+ let e = t;
6
+ for (const c of o)
7
+ if (e !== null && typeof e == "object" && c in e)
8
+ e = e[c];
9
+ else
10
+ return n;
11
+ return e === void 0 ? n : e;
12
+ }
13
+ function y(t, s, n) {
14
+ const o = s.replace(/\[(\d+)\]/g, ".$1").split(".");
15
+ let e = t;
16
+ for (let c = 0; c < o.length - 1; c++) {
17
+ const r = o[c], i = o[c + 1];
18
+ (e[r] === void 0 || e[r] === null) && (e[r] = /^\d+$/.test(i) ? [] : {}), e = e[r];
19
+ }
20
+ return e[o[o.length - 1]] = n, t;
21
+ }
22
+ function d(t, s = 0) {
23
+ let n;
24
+ return function(...o) {
25
+ n !== void 0 && clearTimeout(n), n = setTimeout(() => {
26
+ n = void 0, t.apply(this, o);
27
+ }, s);
28
+ };
29
+ }
30
+ function p(t) {
31
+ const s = JSON.stringify(t, null, 2).split(`
32
+ `), n = [], o = [];
33
+ for (const e of s) {
34
+ const c = e.match(/^(\s*)"([^"]+)"(\s*:\s*)(.+)$/);
35
+ if (c) {
36
+ const [, r, i, l, u] = c;
37
+ n.push(`${r}%c"${i}"%c${l}%c${u}`), o.push(
38
+ "color: #9cdcfe; font-weight: bold",
39
+ "color: #cccccc",
40
+ "color: #ce9178"
41
+ );
42
+ } else
43
+ n.push(`%c${e}`), o.push("color: #cccccc");
44
+ }
45
+ console.log(n.join(`
46
+ `), ...o, t);
47
+ }
48
+ const g = (t) => typeof t == "function", f = (t) => typeof t == "function", h = (t) => typeof t < "u", $ = (t) => f(t) ? t() : t;
49
+ export {
50
+ h as a,
51
+ d,
52
+ a as g,
53
+ g as i,
54
+ p as l,
55
+ y as s,
56
+ $ as v
57
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yakocloud/state-vocab",
3
- "version": "4.0.1",
3
+ "version": "4.0.3",
4
4
  "main": "dist/index.cjs.js",
5
5
  "module": "dist/index.es.js",
6
6
  "types": "dist/types/index.d.ts",
@@ -1 +0,0 @@
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;
@@ -1,2 +0,0 @@
1
- import { type DependencyList } from "react";
2
- export declare function useDebounce<T extends (...args: never[]) => unknown>(effect: T, wait: number | undefined, deps?: DependencyList): (...args: Parameters<T>) => void;
@@ -1,3 +0,0 @@
1
- "use strict";const T=Symbol("state-def"),f=Symbol("state-path"),S=Symbol("state-verbose"),y=Symbol("state-verbose-path"),a=Symbol("state-ssr");function E(t,s,o){if(!s)return t;const n=s.split(".");let e=t;for(const c of n)if(e!==null&&typeof e=="object"&&c in e)e=e[c];else return o;return e===void 0?o:e}function d(t,s,o){const n=s.replace(/\[(\d+)\]/g,".$1").split(".");let e=t;for(let c=0;c<n.length-1;c++){const r=n[c],l=n[c+1];(e[r]===void 0||e[r]===null)&&(e[r]=/^\d+$/.test(l)?[]:{}),e=e[r]}return e[n[n.length-1]]=o,t}function p(t,s=0){let o;return function(...n){o!==void 0&&clearTimeout(o),o=setTimeout(()=>{o=void 0,t.apply(this,n)},s)}}function A(t){const s=JSON.stringify(t,null,2).split(`
2
- `),o=[],n=[];for(const e of s){const c=e.match(/^(\s*)"([^"]+)"(\s*:\s*)(.+)$/);if(c){const[,r,l,i,u]=c;o.push(`${r}%c"${l}"%c${i}%c${u}`),n.push("color: #9cdcfe; font-weight: bold","color: #cccccc","color: #ce9178")}else o.push(`%c${e}`),n.push("color: #cccccc")}console.log(o.join(`
3
- `),...n,t)}const m=t=>typeof t=="function",_=t=>typeof t=="function",h=t=>typeof t<"u",g=t=>_(t)?t():t;exports.STATE_DEFINITION=T;exports.STATE_PATH=f;exports.STATE_SSR=a;exports.STATE_VERBOSE=S;exports.STATE_VERBOSE_PATH=y;exports.debounce=p;exports.get=E;exports.isTransformer=m;exports.isValueDefined=h;exports.logStyled=A;exports.set=d;exports.valueOrFactory=g;
@@ -1,63 +0,0 @@
1
- const u = /* @__PURE__ */ Symbol("state-def"), y = /* @__PURE__ */ Symbol("state-path"), S = /* @__PURE__ */ Symbol("state-verbose"), d = /* @__PURE__ */ Symbol("state-verbose-path"), T = /* @__PURE__ */ Symbol("state-ssr");
2
- function p(t, c, s) {
3
- if (!c)
4
- return t;
5
- const o = c.split(".");
6
- let e = t;
7
- for (const n of o)
8
- if (e !== null && typeof e == "object" && n in e)
9
- e = e[n];
10
- else
11
- return s;
12
- return e === void 0 ? s : e;
13
- }
14
- function h(t, c, s) {
15
- const o = c.replace(/\[(\d+)\]/g, ".$1").split(".");
16
- let e = t;
17
- for (let n = 0; n < o.length - 1; n++) {
18
- const r = o[n], l = o[n + 1];
19
- (e[r] === void 0 || e[r] === null) && (e[r] = /^\d+$/.test(l) ? [] : {}), e = e[r];
20
- }
21
- return e[o[o.length - 1]] = s, t;
22
- }
23
- function m(t, c = 0) {
24
- let s;
25
- return function(...o) {
26
- s !== void 0 && clearTimeout(s), s = setTimeout(() => {
27
- s = void 0, t.apply(this, o);
28
- }, c);
29
- };
30
- }
31
- function E(t) {
32
- const c = JSON.stringify(t, null, 2).split(`
33
- `), s = [], o = [];
34
- for (const e of c) {
35
- const n = e.match(/^(\s*)"([^"]+)"(\s*:\s*)(.+)$/);
36
- if (n) {
37
- const [, r, l, i, a] = n;
38
- s.push(`${r}%c"${l}"%c${i}%c${a}`), o.push(
39
- "color: #9cdcfe; font-weight: bold",
40
- "color: #cccccc",
41
- "color: #ce9178"
42
- );
43
- } else
44
- s.push(`%c${e}`), o.push("color: #cccccc");
45
- }
46
- console.log(s.join(`
47
- `), ...o, t);
48
- }
49
- const g = (t) => typeof t == "function", f = (t) => typeof t == "function", v = (t) => typeof t < "u", b = (t) => f(t) ? t() : t;
50
- export {
51
- y as S,
52
- S as a,
53
- T as b,
54
- d as c,
55
- m as d,
56
- g as e,
57
- u as f,
58
- p as g,
59
- v as i,
60
- E as l,
61
- h as s,
62
- b as v
63
- };