@yakocloud/state-vocab 4.0.3 → 4.1.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
@@ -366,14 +366,28 @@ export const storage = setupStorage({
366
366
  })
367
367
  ```
368
368
 
369
- **2. Create server and client storage handles:**
369
+ **2. Create a client context, then create server and client storage handles:**
370
+
371
+ Each storage tree needs its own React context so that multiple independent providers can coexist on the same page without their state bleeding into each other.
372
+
373
+ ```ts
374
+ // storage.context.client.ts ("use client")
375
+ "use client"
376
+
377
+ import { createContext } from 'react'
378
+
379
+ export const StorageClientContext = createContext({})
380
+ ```
370
381
 
371
382
  ```ts
372
383
  // storage.server.ts
373
384
  import { storage } from '@/storage'
374
385
  import { serverify } from '@yakocloud/state-vocab/server'
386
+ import { StorageClientContext } from '@/storage.context.client'
375
387
 
376
- export const serverStorage = serverify(storage)
388
+ export const serverStorage = serverify(storage, {
389
+ clientContext: StorageClientContext,
390
+ })
377
391
  ```
378
392
 
379
393
  ```ts
@@ -382,8 +396,11 @@ export const serverStorage = serverify(storage)
382
396
 
383
397
  import { storage } from '@/storage'
384
398
  import { clientify } from '@yakocloud/state-vocab/client'
399
+ import { StorageClientContext } from '@/storage.context.client'
385
400
 
386
- export const clientStorage = clientify(storage)
401
+ export const clientStorage = clientify(storage, {
402
+ clientContext: StorageClientContext,
403
+ })
387
404
  ```
388
405
 
389
406
  **3. Seed initial state in the server component and read it in Server and Client children:**
@@ -437,6 +454,40 @@ export default function UserInfoClient() {
437
454
  }
438
455
  ```
439
456
 
457
+ #### Multiple independent storage trees
458
+
459
+ Because each `serverify`/`clientify` pair is bound to its own context, you can have multiple independent providers active at the same time — for example, a layout-level store and a page-level store — without them interfering with each other:
460
+
461
+ ```ts
462
+ // layout.context.client.ts ("use client")
463
+ export const LayoutClientContext = createContext({})
464
+
465
+ // page.context.client.ts ("use client")
466
+ export const PageClientContext = createContext({})
467
+
468
+ // layout.storage.server.ts
469
+ export const layoutServerStorage = serverify(layoutStorage, {
470
+ clientContext: LayoutClientContext
471
+ })
472
+
473
+ // layout.storage.client.ts ("use client")
474
+ export const layoutClientStorage = clientify(layoutStorage, {
475
+ clientContext: LayoutClientContext
476
+ })
477
+
478
+ // page.storage.server.ts
479
+ export const pageServerStorage = serverify(pageStorage, {
480
+ clientContext: PageClientContext
481
+ })
482
+
483
+ // page.storage.client.ts ("use client")
484
+ export const pageClientStorage = clientify(pageStorage, {
485
+ clientContext: PageClientContext
486
+ })
487
+ ```
488
+
489
+ Each `StateVocabProvider` wraps its own subtree; a component that calls `pageClientStorage.user.name.useState()` reads only from the nearest `pageServerStorage.StateVocabProvider`, not from the layout provider.
490
+
440
491
  #### `serverify(storage)`
441
492
 
442
493
  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.
@@ -694,15 +745,21 @@ import { StateVocabClientProvider } from '@yakocloud/state-vocab/client'
694
745
  </StateVocabClientProvider>
695
746
  ```
696
747
 
697
- ### `serverify<T>(storage: T)`
748
+ ### `serverify<T>(storage: T, options)`
698
749
 
699
750
  Converts a storage tree to its server-side counterpart. Available from `@yakocloud/state-vocab/server`.
700
751
 
752
+ | Option | Type | Description |
753
+ |---|---|---|
754
+ | `clientContext` | `Context<object>` | **Required.** A React context created with `createContext({})` in a `"use client"` file. Must match the `clientContext` passed to `clientify` for the same storage tree. |
755
+
701
756
  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`.
702
757
 
703
758
  ```ts
704
759
  import { serverify } from '@yakocloud/state-vocab/server'
705
- const serverStorage = serverify(storage)
760
+ import { MyClientContext } from '@/storage.context.client'
761
+
762
+ const serverStorage = serverify(storage, { clientContext: MyClientContext })
706
763
 
707
764
  const { StateVocabProvider } = serverStorage // server-aware provider
708
765
 
@@ -712,15 +769,21 @@ serverStorage.person.address.seed({ city: 'NY' }) // → { person: { address:
712
769
  serverStorage.seed({ user: { name: 'Alice' } }) // → { user: { name: 'Alice' } } (identity)
713
770
  ```
714
771
 
715
- ### `clientify<T>(storage: T)`
772
+ ### `clientify<T>(storage: T, options?)`
716
773
 
717
774
  Converts a storage tree to its client-side counterpart. Available from `@yakocloud/state-vocab/client`.
718
775
 
776
+ | Option | Type | Description |
777
+ |---|---|---|
778
+ | `clientContext` | `Context<object> \| undefined` | The React context created with `createContext({})` for this storage tree. Must match the `clientContext` passed to `serverify`. Required when using RSC; omit for SPA-only setups. |
779
+
719
780
  Each leaf gains `.useState()` and `.useInitialState()`. The tree structure mirrors the original.
720
781
 
721
782
  ```ts
722
783
  import { clientify } from '@yakocloud/state-vocab/client'
723
- const clientStorage = clientify(storage)
784
+ import { MyClientContext } from '@/storage.context.client'
785
+
786
+ const clientStorage = clientify(storage, { clientContext: MyClientContext })
724
787
 
725
788
  // In a "use client" component:
726
789
  const [name] = clientStorage.user.name.useState()
@@ -1 +1 @@
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;
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("react"),v=require("./constants-CbsduCZ7.js"),a=require("./utils-0CTNJ4ZE.js"),V=require("./provider-client-CAHe8VnM.js");function D(t,n,s=[]){return f.useMemo(()=>a.debounce(t,n),s)}const y=t=>{const{vocabStore:n,storage:s,statePath:o,value:e,deserialize:l,serialize:c}=t,d=s.getItem(o);d===null?a.isValueDefined(e)&&s.setItem(o,c(e)):n.set(o,l(d))},R=typeof window>"u",m=R?f.useEffect:f.useLayoutEffect,A=new V.VocabStore,P="Make sure your component is wrapped in StateVocabProvider (RSC) or disable ssr option in setupStorage for SPA (RCC only)";function I(t){const n=R?void 0:a.valueOrFactory(t.storage),s=a.valueOrFactory(t.defaultValue),o=t.bidirectional,e=this[v.STATE_PATH],l=this[v.STATE_VERBOSE],c=this[v.STATE_VERBOSE_PATH],d=this[v.STATE_SSR];let i=V.useStateVocabClientContext({clientContext:t.clientContext,verbose:l});if(!(i instanceof V.VocabStore)){if(d)throw new Error(P);i=A}const S=t.serialize??JSON.stringify,u=t.deserialize??JSON.parse,E=D(t.onSet??(()=>{}),t.delayedSet,[]),g=f.useRef(void 0),z=f.useRef(!1);if(!z.current){z.current=!0;let r=i.get(e);a.isValueDefined(r)||(r=s,a.isValueDefined(r)&&i.set(e,r)),!d&&n&&y({vocabStore:i,storage:n,statePath:e,value:r,serialize:S,deserialize:u})}const C=f.useSyncExternalStore(i.subscribe.bind(i),i.getClientSnapshot.bind(i),i.getServerSnapshot.bind(i));if(l)if(c){const r=a.get(C,c);r&&a.logStyled(r)}else a.logStyled(C);const h=a.get(C,e,s);g.current=h,m(()=>{!d||!n||y({vocabStore:i,storage:n,statePath:e,value:h,serialize:S,deserialize:u})},[]);const w=f.useEffectEvent(r=>{if(r.key!==e)return;const b=r.newValue,T=(b===null?null:u(b))??s;a.isValueDefined(T)&&(i.set(e,T),E(T,g.current))});f.useEffect(()=>{if(o)return window.addEventListener("storage",w),()=>window.removeEventListener("storage",w)},[o]);const x=f.useCallback(r=>{const b=a.isTransformer(r)?r(g.current):r;i.set(e,b),E(b,g.current),n&&n.setItem(e,S(b))},[i,e,E,n,S]),_=f.useCallback(()=>{const r=s;if(!a.isValueDefined(r)){n?.removeItem(e);return}i.set(e,r),E(r,g.current),n&&n.setItem(e,S(r))},[s,i,e,E,n,S]);return[h,x,_]}function p(t){const n=R?void 0:a.valueOrFactory(t.storage),s=a.valueOrFactory(t.defaultValue),o=this[v.STATE_PATH],e=this[v.STATE_VERBOSE],l=this[v.STATE_SSR];let c=V.useStateVocabClientContext({clientContext:t.clientContext,verbose:e});if(!(c instanceof V.VocabStore)){if(l)throw new Error(P);c=A}const d=t.serialize??JSON.stringify,i=t.deserialize??JSON.parse,S=f.useRef(!1);let u;S.current||(S.current=!0,u=c.get(o),a.isValueDefined(u)||(u=s,a.isValueDefined(u)&&c.set(o,u)),!l&&n&&y({vocabStore:c,storage:n,statePath:o,value:u,serialize:d,deserialize:i})),m(()=>{!l||!n||y({vocabStore:c,storage:n,statePath:o,value:u,serialize:d,deserialize:i})},[])}function k(t){return typeof t=="object"&&t!==null&&"clientSlot"in t}function O(t,n={}){const s={};for(const o in t){const e=t[o];k(e)?(s[o]=e.clientSlot({useState(l){return I.apply(this,[{clientContext:n.clientContext,...l}])},useInitialState(l){p.apply(this,[{clientContext:n.clientContext,...l}])}}),delete s[o].serverSlot,delete s[o].clientSlot):e!==null&&typeof e=="object"?s[o]=O(e,n):s[o]=e}return s}exports.StateVocabClientProvider=V.StateVocabClientProvider;exports.clientify=O;
package/dist/client.es.js CHANGED
@@ -1,37 +1,39 @@
1
1
  "use client";
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),
2
+ import { useMemo as j, useRef as y, useSyncExternalStore as B, useEffectEvent as D, useEffect as P, useCallback as p, useLayoutEffect as H } from "react";
3
+ import { S as A, a as I, b as O, c as F } from "./constants-BB1YAX6c.mjs";
4
+ import { a as d, d as G, v as g, g as T, l as x, i as q } from "./utils-xV3x3fTc.mjs";
5
+ import { V as C, u as _ } from "./provider-client-D0BmMWcY.mjs";
6
+ import { S as ne } from "./provider-client-D0BmMWcY.mjs";
7
+ function K(t, s, i = []) {
8
+ return j(
9
+ () => G(t, s),
10
10
  // eslint-disable-next-line react-hooks/exhaustive-deps
11
- n
11
+ i
12
12
  );
13
13
  }
14
14
  const E = (t) => {
15
15
  const {
16
- vocabStore: e,
17
- storage: n,
16
+ vocabStore: s,
17
+ storage: i,
18
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)";
19
+ value: e,
20
+ deserialize: a,
21
+ serialize: l
22
+ } = t, u = i.getItem(o);
23
+ u === null ? d(e) && i.setItem(o, l(e)) : s.set(o, a(u));
24
+ }, z = typeof window > "u", k = z ? P : H, J = new C(), L = "Make sure your component is wrapped in StateVocabProvider (RSC) or disable ssr option in setupStorage for SPA (RCC only)";
25
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;
26
+ const s = z ? void 0 : g(t.storage), i = g(t.defaultValue), o = t.bidirectional, e = this[A], a = this[I], l = this[F], u = this[O];
27
+ let r = _({
28
+ clientContext: t.clientContext,
29
+ verbose: a
30
+ });
31
+ if (!(r instanceof C)) {
32
+ if (u)
33
+ throw new Error(L);
34
+ r = J;
33
35
  }
34
- const u = t.serialize ?? JSON.stringify, l = t.deserialize ?? JSON.parse, v = K(
36
+ const f = t.serialize ?? JSON.stringify, c = t.deserialize ?? JSON.parse, v = K(
35
37
  t.onSet ?? (() => {
36
38
  }),
37
39
  t.delayedSet,
@@ -39,14 +41,14 @@ function Q(t) {
39
41
  ), b = y(void 0), w = y(!1);
40
42
  if (!w.current) {
41
43
  w.current = !0;
42
- let i = r.get(s);
43
- d(i) || (i = n, d(i) && r.set(s, i)), !c && e && E({
44
+ let n = r.get(e);
45
+ d(n) || (n = i, d(n) && r.set(e, n)), !u && s && E({
44
46
  vocabStore: r,
45
- storage: e,
46
- statePath: s,
47
- value: i,
48
- serialize: u,
49
- deserialize: l
47
+ storage: s,
48
+ statePath: e,
49
+ value: n,
50
+ serialize: f,
51
+ deserialize: c
50
52
  });
51
53
  }
52
54
  const h = B(
@@ -54,75 +56,77 @@ function Q(t) {
54
56
  r.getClientSnapshot.bind(r),
55
57
  r.getServerSnapshot.bind(r)
56
58
  );
57
- if (f)
58
- if (a) {
59
- const i = C(h, a);
60
- i && P(i);
59
+ if (a)
60
+ if (l) {
61
+ const n = T(h, l);
62
+ n && x(n);
61
63
  } else
62
- P(h);
63
- const m = C(h, s, n);
64
- b.current = m, J(() => {
65
- !c || !e || E({
64
+ x(h);
65
+ const m = T(h, e, i);
66
+ b.current = m, k(() => {
67
+ !u || !s || E({
66
68
  vocabStore: r,
67
- storage: e,
68
- statePath: s,
69
+ storage: s,
70
+ statePath: e,
69
71
  value: m,
70
- serialize: u,
71
- deserialize: l
72
+ serialize: f,
73
+ deserialize: c
72
74
  });
73
75
  }, []);
74
- const R = D((i) => {
75
- if (i.key !== s)
76
+ const R = D((n) => {
77
+ if (n.key !== e)
76
78
  return;
77
- const S = i.newValue, V = (S === null ? null : l(S)) ?? n;
78
- d(V) && (r.set(s, V), v(V, b.current));
79
+ const S = n.newValue, V = (S === null ? null : c(S)) ?? i;
80
+ d(V) && (r.set(e, V), v(V, b.current));
79
81
  });
80
- A(() => {
82
+ P(() => {
81
83
  if (o)
82
84
  return window.addEventListener("storage", R), () => window.removeEventListener("storage", R);
83
85
  }, [o]);
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);
86
+ const N = p((n) => {
87
+ const S = q(n) ? n(b.current) : n;
88
+ r.set(e, S), v(S, b.current), s && s.setItem(e, f(S));
89
+ }, [r, e, v, s, f]), M = p(() => {
90
+ const n = i;
91
+ if (!d(n)) {
92
+ s?.removeItem(e);
91
93
  return;
92
94
  }
93
- r.set(s, i), v(i, b.current), e && e.setItem(s, u(i));
94
- }, [n, r, s, v, e, u]);
95
+ r.set(e, n), v(n, b.current), s && s.setItem(e, f(n));
96
+ }, [i, r, e, v, s, f]);
95
97
  return [
96
98
  m,
97
- M,
98
- j
99
+ N,
100
+ M
99
101
  ];
100
102
  }
101
103
  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;
104
+ const s = z ? void 0 : g(t.storage), i = g(t.defaultValue), o = this[A], e = this[I], a = this[O];
105
+ let l = _({
106
+ clientContext: t.clientContext,
107
+ verbose: e
108
+ });
109
+ if (!(l instanceof C)) {
110
+ if (a)
111
+ throw new Error(L);
112
+ l = J;
109
113
  }
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,
114
+ const u = t.serialize ?? JSON.stringify, r = t.deserialize ?? JSON.parse, f = y(!1);
115
+ let c;
116
+ f.current || (f.current = !0, c = l.get(o), d(c) || (c = i, d(c) && l.set(o, c)), !a && s && E({
117
+ vocabStore: l,
118
+ storage: s,
115
119
  statePath: o,
116
- value: l,
117
- serialize: c,
120
+ value: c,
121
+ serialize: u,
118
122
  deserialize: r
119
- })), J(() => {
120
- !f || !e || E({
121
- vocabStore: a,
122
- storage: e,
123
+ })), k(() => {
124
+ !a || !s || E({
125
+ vocabStore: l,
126
+ storage: s,
123
127
  statePath: o,
124
- value: l,
125
- serialize: c,
128
+ value: c,
129
+ serialize: u,
126
130
  deserialize: r
127
131
  });
128
132
  }, []);
@@ -130,22 +134,28 @@ function U(t) {
130
134
  function W(t) {
131
135
  return typeof t == "object" && t !== null && "clientSlot" in t;
132
136
  }
133
- function X(t) {
134
- const e = {};
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);
137
+ function X(t, s = {}) {
138
+ const i = {};
139
+ for (const o in t) {
140
+ const e = t[o];
141
+ W(e) ? (i[o] = e.clientSlot({
142
+ useState(a) {
143
+ return Q.apply(this, [{
144
+ clientContext: s.clientContext,
145
+ ...a
146
+ }]);
140
147
  },
141
- useInitialState(...s) {
142
- U.apply(this, s);
148
+ useInitialState(a) {
149
+ U.apply(this, [{
150
+ clientContext: s.clientContext,
151
+ ...a
152
+ }]);
143
153
  }
144
- }), delete e[n].serverSlot, delete e[n].clientSlot) : o !== null && typeof o == "object" ? e[n] = X(o) : e[n] = o;
154
+ }), delete i[o].serverSlot, delete i[o].clientSlot) : e !== null && typeof e == "object" ? i[o] = X(e, s) : i[o] = e;
145
155
  }
146
- return e;
156
+ return i;
147
157
  }
148
158
  export {
149
- ie as StateVocabClientProvider,
159
+ ne as StateVocabClientProvider,
150
160
  X as clientify
151
161
  };
@@ -0,0 +1 @@
1
+ "use client";"use strict";const h=require("react/jsx-runtime"),c=require("react"),r=require("./utils-0CTNJ4ZE.js"),a=c.createContext({});function S(e){const t=c.useContext(e.clientContext??a);return e.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 r.get(this.#t,t)}set(t,n){const s=r.get(this.#t,t),i=r.isTransformer(n)?n(s):n,o={...this.#t};r.set(o,t,i),this.#t=o,this.#e.forEach(l=>l())}}function b(e){const{clientContext:t,value:n,children:s}=e,[i]=c.useState(()=>new u(n)),o=t??a;return h.jsx(o.Provider,{value:i,children:s})}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 l, useContext as h, useState as S } from "react";
4
+ import { g as i, i as C, s as b } from "./utils-xV3x3fTc.mjs";
5
+ const c = l({});
6
+ function m(e) {
7
+ const t = h(e.clientContext ?? c);
8
+ return e.verbose && console.log(`[Store uid]: ${t.uid}`), t;
9
+ }
10
+ class d {
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 i(this.#t, t);
27
+ }
28
+ set(t, o) {
29
+ const n = i(this.#t, t), s = C(o) ? o(n) : o, r = { ...this.#t };
30
+ b(r, t, s), this.#t = r, this.#e.forEach((a) => a());
31
+ }
32
+ }
33
+ function v(e) {
34
+ const { clientContext: t, value: o, children: n } = e, [s] = S(() => new d(o));
35
+ return /* @__PURE__ */ u((t ?? c).Provider, { value: s, children: n });
36
+ }
37
+ export {
38
+ v as S,
39
+ d as V,
40
+ m as u
41
+ };
@@ -1 +1 @@
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;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react/jsx-runtime"),S=require("./constants-CbsduCZ7.js"),c=require("react"),d=require("./provider-client-CAHe8VnM.js"),y=require("./utils-0CTNJ4ZE.js"),i=e=>{if(c.useState)throw new Error(`${e} only intended for Server Components`)},a=c.cache(()=>new Map),x=({children:e})=>e,C=async e=>{i("StateVocabServerContext.Provider");const{serverContextKey:n,value:t,children:r}=e;return a().set(n,t),r},f=e=>(i("getStateVocab"),a().get(e)),b={Provider:c.useState?x:C},p=e=>{const{serverContextKey:n,value:t,children:r}=e;return s.jsx(b.Provider,{serverContextKey:n,value:t,children:r})};function P(e){const{children:n,serverContextKey:t,clientContext:r,value:o={}}=e;return s.jsx(p,{serverContextKey:t,value:o,children:s.jsx(d.StateVocabClientProvider,{clientContext:r,value:o,children:n})})}const h="Make sure your component is wrapped in StateVocabProvider";function w(e){const n=this[S.STATE_PATH],t=f(e.serverContextKey);if(!t)throw new Error(h);return y.get(t,n)}function g(e){return typeof e=="object"&&e!==null&&"serverSlot"in e}function l(e,n){const t={};for(const r in e){const o=e[r];if(g(o))t[r]=o.serverSlot({getState(){return w.apply(this,[{serverContextKey:n.serverContextKey}])}}),delete t[r].serverSlot,delete t[r].clientSlot;else if(o!==null&&typeof o=="object"){const u=v=>n.wrap({[r]:v});t[r]=l(o,{serverContextKey:n.serverContextKey,wrap:u})}else t[r]=o}return t.seed=r=>n.wrap(r),t}function K(e,n){const t=Symbol();return{...l(e,{serverContextKey:t,wrap:r=>r}),StateVocabProvider({children:r,value:o}){return s.jsx(P,{clientContext:n.clientContext,serverContextKey:t,value:o,children:r})}}}exports.serverify=K;
package/dist/server.es.js CHANGED
@@ -1,70 +1,104 @@
1
- import { jsxs as u, Fragment as v, jsx as c } from "react/jsx-runtime";
2
- import { S as d } from "./constants-BB1YAX6c.mjs";
3
- import f from "react";
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";
7
- const i = (e) => {
8
- if (f.useState)
1
+ import { jsx as c } from "react/jsx-runtime";
2
+ import { S as u } from "./constants-BB1YAX6c.mjs";
3
+ import s, { cache as d } from "react";
4
+ import { S as f } from "./provider-client-D0BmMWcY.mjs";
5
+ import { g as y } from "./utils-xV3x3fTc.mjs";
6
+ const a = (e) => {
7
+ if (s.useState)
9
8
  throw new Error(`${e} only intended for Server Components`);
9
+ }, i = d(() => /* @__PURE__ */ new Map()), p = ({ children: e }) => e, C = async (e) => {
10
+ a("StateVocabServerContext.Provider");
11
+ const { serverContextKey: o, value: t, children: r } = e;
12
+ return i().set(o, t), r;
13
+ }, x = (e) => (a("getStateVocab"), i().get(e)), b = {
14
+ Provider: s.useState ? p : C
15
+ }, m = (e) => {
16
+ const { serverContextKey: o, value: t, children: r } = e;
17
+ return /* @__PURE__ */ c(
18
+ b.Provider,
19
+ {
20
+ serverContextKey: o,
21
+ value: t,
22
+ children: r
23
+ }
24
+ );
10
25
  };
11
- let s = null;
12
- try {
13
- i("StateVocabServerContext"), s = new p.AsyncLocalStorage();
14
- } catch {
15
- s = null;
16
- }
17
- const h = ({ value: e }) => (s?.enterWith({ value: e }), null), y = ({ children: e }) => e, P = async (e) => {
18
- i("StateVocabServerContext.Provider");
19
- const { value: t, children: r } = e;
20
- return /* @__PURE__ */ u(v, { children: [
21
- /* @__PURE__ */ c(h, { value: t }),
22
- r
23
- ] });
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) {
28
- const { children: t, value: r = {} } = e;
29
- return /* @__PURE__ */ c(E, { value: r, children: /* @__PURE__ */ c(m, { value: r, children: t }) });
26
+ function h(e) {
27
+ const {
28
+ children: o,
29
+ serverContextKey: t,
30
+ clientContext: r,
31
+ value: n = {}
32
+ } = e;
33
+ return /* @__PURE__ */ c(
34
+ m,
35
+ {
36
+ serverContextKey: t,
37
+ value: n,
38
+ children: /* @__PURE__ */ c(
39
+ f,
40
+ {
41
+ clientContext: r,
42
+ value: n,
43
+ children: o
44
+ }
45
+ )
46
+ }
47
+ );
30
48
  }
31
- const C = "Make sure your component is wrapped in StateVocabProvider";
32
- function w() {
33
- const e = this[d], t = g();
49
+ const P = "Make sure your component is wrapped in StateVocabProvider";
50
+ function w(e) {
51
+ const o = this[u], t = x(e.serverContextKey);
34
52
  if (!t)
35
- throw new Error(C);
36
- return b(t, e);
53
+ throw new Error(P);
54
+ return y(t, o);
37
55
  }
38
- function R(e) {
56
+ function K(e) {
39
57
  return typeof e == "object" && e !== null && "serverSlot" in e;
40
58
  }
41
- function l(e, t) {
42
- const r = {};
43
- for (const o in e) {
44
- const n = e[o];
45
- if (R(n))
46
- r[o] = n.serverSlot({
47
- // Slot definition
48
- getState(...a) {
49
- return w.apply(this, a);
59
+ function v(e, o) {
60
+ const t = {};
61
+ for (const r in e) {
62
+ const n = e[r];
63
+ if (K(n))
64
+ t[r] = n.serverSlot({
65
+ getState() {
66
+ return w.apply(this, [{
67
+ serverContextKey: o.serverContextKey
68
+ }]);
50
69
  }
51
- }), delete r[o].serverSlot, delete r[o].clientSlot;
70
+ }), delete t[r].serverSlot, delete t[r].clientSlot;
52
71
  else if (n !== null && typeof n == "object") {
53
- const a = (S) => t({ [o]: S });
54
- r[o] = l(n, a);
72
+ const l = (S) => o.wrap({ [r]: S });
73
+ t[r] = v(n, {
74
+ serverContextKey: o.serverContextKey,
75
+ wrap: l
76
+ });
55
77
  } else
56
- r[o] = n;
78
+ t[r] = n;
57
79
  }
58
- return r.seed = (o) => t(o), r;
80
+ return t.seed = (r) => o.wrap(r), t;
59
81
  }
60
- function M(e) {
82
+ function k(e, o) {
83
+ const t = /* @__PURE__ */ Symbol();
61
84
  return {
62
- ...l(e, (t) => t),
63
- StateVocabProvider({ children: t, value: r }) {
64
- return /* @__PURE__ */ c(x, { value: r, children: t });
85
+ ...v(e, {
86
+ serverContextKey: t,
87
+ wrap: (r) => r
88
+ }),
89
+ StateVocabProvider({ children: r, value: n }) {
90
+ return /* @__PURE__ */ c(
91
+ h,
92
+ {
93
+ clientContext: o.clientContext,
94
+ serverContextKey: t,
95
+ value: n,
96
+ children: r
97
+ }
98
+ );
65
99
  }
66
100
  };
67
101
  }
68
102
  export {
69
- M as serverify
103
+ k as serverify
70
104
  };
@@ -1,8 +1,10 @@
1
+ import { type Context } from "react";
1
2
  import VocabStore from "./store";
2
- export declare const StateVocabClientContext: import("react").Context<VocabStore>;
3
+ export declare const DefaultStateVocabClientContext: Context<VocabStore>;
3
4
  /**
4
5
  * @see method from from https://zustand.docs.pmnd.rs/learn/guides/nextjs
5
6
  */
6
7
  export declare function useStateVocabClientContext(options: {
8
+ clientContext: Context<VocabStore> | undefined;
7
9
  verbose: boolean;
8
10
  }): VocabStore;
@@ -1,10 +1,12 @@
1
+ import React from "react";
1
2
  import type { PropsWithChildren, ReactNode } from "react";
2
3
  import type { Vocab } from "./state.types";
3
- export declare const getStateVocab: () => Vocab | undefined;
4
+ export declare const getStateVocab: (serverContextKey: symbol) => Vocab | undefined;
4
5
  export declare const StateVocabServerContext: {
5
6
  Provider: (({ children }: PropsWithChildren<{
6
7
  value?: Vocab;
7
8
  }>) => ReactNode) | ((props: PropsWithChildren<{
9
+ serverContextKey: symbol;
8
10
  value: Vocab;
9
- }>) => Promise<import("react/jsx-runtime").JSX.Element>);
11
+ }>) => Promise<React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | (string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined)>);
10
12
  };
@@ -1,5 +1,7 @@
1
- import { type PropsWithChildren } from "react";
1
+ import { type Context, type PropsWithChildren } from "react";
2
+ import VocabStore from "./store";
2
3
  import type { Vocab } from "./state.types";
3
4
  export declare function StateVocabClientProvider(props: PropsWithChildren<{
5
+ clientContext?: Context<VocabStore>;
4
6
  value?: Vocab;
5
7
  }>): import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,8 @@
1
- import type { PropsWithChildren } from "react";
1
+ import type { Context, PropsWithChildren } from "react";
2
2
  import type { Vocab } from "./state.types";
3
+ import VocabStore from "./store";
3
4
  export declare function StateVocabProvider(props: PropsWithChildren<{
4
5
  value?: Vocab;
6
+ serverContextKey: symbol;
7
+ clientContext: Context<VocabStore>;
5
8
  }>): import("react/jsx-runtime").JSX.Element;
@@ -2,4 +2,5 @@ import type { Vocab } from "./state.types";
2
2
  import type { PropsWithChildren } from "react";
3
3
  export declare const StateVocabServerProvider: React.FC<PropsWithChildren<{
4
4
  value: Vocab;
5
+ serverContextKey: symbol;
5
6
  }>>;
@@ -1,6 +1,10 @@
1
+ import { type Context } from "react";
1
2
  import type { ValueOrTransformer, VocabThis } from "./state.types";
3
+ import VocabStore from "./store";
2
4
  import type { UseInitialStateOptions, UseStateOptions } from "./setup.types";
3
- declare function useState<V>(this: VocabThis, options?: UseStateOptions<V>): readonly [V, (nextValue: ValueOrTransformer<V>) => void, () => void];
5
+ declare function useState<V>(this: VocabThis, options: UseStateOptions<V> & {
6
+ clientContext: Context<VocabStore> | undefined;
7
+ }): readonly [V, (nextValue: ValueOrTransformer<V>) => void, () => void];
4
8
  type Placeholder<V> = {
5
9
  useState(options?: UseStateOptions<V>): ReturnType<typeof useState<V>>;
6
10
  useInitialState(options?: UseInitialStateOptions<V>): void;
@@ -11,5 +15,7 @@ type Slot<V> = {
11
15
  type Clientified<R> = R extends Slot<infer TValue> ? Placeholder<TValue> : R extends object ? {
12
16
  [K in keyof R]: Clientified<R[K]>;
13
17
  } : R;
14
- export declare function clientify<R extends object>(tree: R): Clientified<R>;
18
+ export declare function clientify<R extends object>(tree: R, clientifyOptions?: {
19
+ clientContext?: Context<object>;
20
+ }): Clientified<R>;
15
21
  export {};
@@ -1,4 +1,4 @@
1
- import type { PropsWithChildren, ReactNode } from "react";
1
+ import type { Context, PropsWithChildren, ReactNode } from "react";
2
2
  import type { Vocab } from "./state.types";
3
3
  type Placeholder<V> = {
4
4
  getState(): V;
@@ -22,5 +22,7 @@ type ServerifyResult<R extends object> = {
22
22
  } & {
23
23
  [K in keyof R]: Serverified<R[K]>;
24
24
  };
25
- export declare function serverify<R extends object>(tree: R): ServerifyResult<R>;
25
+ export declare function serverify<R extends object>(tree: R, serverifyOptions: {
26
+ clientContext: Context<object>;
27
+ }): ServerifyResult<R>;
26
28
  export {};
@@ -51,7 +51,5 @@ export declare function defineState<V>(superOptions?: {
51
51
  }): void;
52
52
  };
53
53
  /** Returns the fully qualified state name (dot-separated path). */
54
- toString(this: {
55
- [STATE_PATH]: string;
56
- }): string;
54
+ toString(this: VocabThis): string;
57
55
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yakocloud/state-vocab",
3
- "version": "4.0.3",
3
+ "version": "4.1.0",
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-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,41 +0,0 @@
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, i as d, s as b } from "./utils-xV3x3fTc.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
- };