@yakocloud/state-vocab 4.1.0 → 4.2.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
@@ -412,6 +412,10 @@ import { serverStorage } from '@/storage.server'
412
412
  const { StateVocabProvider } = serverStorage
413
413
 
414
414
  export default async function Page() {
415
+ // Call start() before rendering the provider — registers the store for this
416
+ // request so child components can await getState() concurrently
417
+ serverStorage.start()
418
+
415
419
  // Fetch data from DB / API
416
420
  const user = await db.getUser()
417
421
 
@@ -433,8 +437,8 @@ export default async function Page() {
433
437
  import { serverStorage } from '@/storage.server'
434
438
 
435
439
  export default async function UserInfo() {
436
- const name = serverStorage.user.name.getState()
437
- const role = serverStorage.user.role.getState()
440
+ const name = await serverStorage.user.name.getState()
441
+ const role = await serverStorage.user.role.getState()
438
442
 
439
443
  return <p>{name} — {role}</p>
440
444
  }
@@ -490,7 +494,7 @@ Each `StateVocabProvider` wraps its own subtree; a component that calls `pageCli
490
494
 
491
495
  #### `serverify(storage)`
492
496
 
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.
497
+ Converts a storage tree into its server-side counterpart. Each leaf gains an async `.getState()` method that resolves once the nearest `StateVocabProvider` renders and provides its value. Each namespace node (including the root) gains a `.seed()` method that returns the input wrapped under its full ancestor path. The result also exposes `StateVocabProvider` and `start()` call `start()` at the top of the component that renders `StateVocabProvider` so concurrent server children can await the value without timing out.
494
498
 
495
499
  **`.seed()` syntax:**
496
500
 
@@ -508,7 +512,7 @@ serverStorage.person.address.seed({ city: 'NY' })
508
512
  // → { person: { address: { city: 'NY' } } }
509
513
  ```
510
514
 
511
- **`node.getState()`** reads the value for that leaf from the surrounding `StateVocabProvider`. Throws if called outside one.
515
+ **`node.getState()`** asynchronously reads the value for that leaf once the surrounding `StateVocabProvider` has rendered. Returns `Promise<V>` — always `await` it. Throws (rejects) if called outside a provider scope or if `serverTimeout` expires.
512
516
 
513
517
  #### `clientify(storage)`
514
518
 
@@ -752,21 +756,32 @@ Converts a storage tree to its server-side counterpart. Available from `@yakoclo
752
756
  | Option | Type | Description |
753
757
  |---|---|---|
754
758
  | `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. |
759
+ | `serverTimeout` | `number \| undefined` | Timeout in ms for each `getState()` call. Defaults to `1000`. If the `StateVocabProvider` has not rendered within this window, `getState()` rejects with a descriptive error. |
760
+
761
+ The result exposes:
755
762
 
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`.
763
+ - **`start()`**call once at the top of the component that renders `StateVocabProvider`, before any `await` expressions. It registers a pending promise in the per-request store (`React.cache()` scope), which lets concurrent server components `await getState()` without hanging. Must be called within a React render context.
764
+ - **`StateVocabProvider`** — synchronous server component that accepts a `value` prop and resolves the pending promise registered by `start()`.
765
+ - **`node.getState()`** — async; reads the value for that leaf once the nearest `StateVocabProvider` has rendered. Throws if called outside a provider scope or if the timeout expires.
766
+ - **`node.seed(input)`** — wraps `input` under the node's ancestor path, returning a plain object ready to pass as the `value` prop.
757
767
 
758
768
  ```ts
759
769
  import { serverify } from '@yakocloud/state-vocab/server'
760
770
  import { MyClientContext } from '@/storage.context.client'
761
771
 
762
- const serverStorage = serverify(storage, { clientContext: MyClientContext })
772
+ const serverStorage = serverify(storage, {
773
+ clientContext: MyClientContext,
774
+ serverTimeout: 2000, // optional, default 1000
775
+ })
763
776
 
764
- const { StateVocabProvider } = serverStorage // server-aware provider
777
+ const { StateVocabProvider } = serverStorage
765
778
 
766
- serverStorage.user.name.getState() // reads "user.name" from context
767
- serverStorage.user.seed({ name: 'Alice' }) // { user: { name: 'Alice' } }
768
- serverStorage.person.address.seed({ city: 'NY' }) // { person: { address: { city: 'NY' } } }
769
- serverStorage.seed({ user: { name: 'Alice' } }) // → { user: { name: 'Alice' } } (identity)
779
+ // In a Server Component:
780
+ serverStorage.start() // register before rendering
781
+ await serverStorage.user.name.getState() // reads "user.name" (async)
782
+ serverStorage.user.seed({ name: 'Alice' }) // → { user: { name: 'Alice' } }
783
+ serverStorage.person.address.seed({ city: 'NY' }) // → { person: { address: { city: 'NY' } } }
784
+ serverStorage.seed({ user: { name: 'Alice' } }) // → { user: { name: 'Alice' } } (identity)
770
785
  ```
771
786
 
772
787
  ### `clientify<T>(storage: T, options?)`
@@ -1 +1 @@
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;
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("react"),v=require("./constants-CbsduCZ7.js"),a=require("./utils-CuApuc3e.js"),V=require("./provider-client-DRdFy5dG.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,9 +1,9 @@
1
1
  "use client";
2
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
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";
4
+ import { a as d, d as G, v as g, g as T, l as x, i as q } from "./utils-xNWifFE9.mjs";
5
+ import { V as C, u as _ } from "./provider-client-BhPeK8Kz.mjs";
6
+ import { S as ne } from "./provider-client-BhPeK8Kz.mjs";
7
7
  function K(t, s, i = []) {
8
8
  return j(
9
9
  () => G(t, s),
package/dist/index.cjs.js CHANGED
@@ -1 +1 @@
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;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./constants-CbsduCZ7.js"),h=require("./utils-CuApuc3e.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,5 @@
1
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";
2
+ import { v as b } from "./utils-xNWifFE9.mjs";
3
3
  function M(t = {}) {
4
4
  return {
5
5
  [v]: !0,
@@ -1,13 +1,14 @@
1
1
  "use client";
2
2
  import { jsx as u } from "react/jsx-runtime";
3
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";
4
+ import { g as i, i as C, s as b } from "./utils-xNWifFE9.mjs";
5
5
  const c = l({});
6
6
  function m(e) {
7
7
  const t = h(e.clientContext ?? c);
8
8
  return e.verbose && console.log(`[Store uid]: ${t.uid}`), t;
9
9
  }
10
10
  class d {
11
+ uid;
11
12
  #t;
12
13
  #e;
13
14
  constructor(t) {
@@ -0,0 +1 @@
1
+ "use client";"use strict";const h=require("react/jsx-runtime"),c=require("react"),r=require("./utils-CuApuc3e.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{uid;#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;
@@ -1 +1 @@
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;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("react/jsx-runtime"),S=require("./constants-CbsduCZ7.js"),l=require("react"),m=require("./provider-client-DRdFy5dG.js"),a=require("./utils-CuApuc3e.js"),d=e=>{if(l.useState)throw new Error(`${e} only intended for Server Components`)},c=l.cache(()=>new Map),f=async e=>(d("getStateVocab"),await c().get(e)),u="Make sure your component is wrapped in StateVocabProvider";async function w(e){const t=this[S.STATE_PATH],r=await a.withTimeout(f(e.serverContextKey),e.timeout,`Failed access to "${t}". Reason: Operation timed out after ${e.timeout}ms. ${u}.`);if(!r)throw new Error(u);return a.get(r,t)}function b(e){return typeof e=="object"&&e!==null&&"serverSlot"in e}function v(e,t){const r={};for(const o in e){const n=e[o];if(b(n))r[o]=n.serverSlot({getState(){return w.apply(this,[{serverContextKey:t.serverContextKey,timeout:t.serverTimeout}])}}),delete r[o].serverSlot,delete r[o].clientSlot;else if(n!==null&&typeof n=="object"){const i=s=>t.wrap({[o]:s});r[o]=v(n,{serverContextKey:t.serverContextKey,serverTimeout:t.serverTimeout,wrap:i})}else r[o]=n}return r.seed=o=>t.wrap(o),r}function x(e,t){const r=Object.keys(e).slice(0,3).join("-"),o=Symbol(r),n=Promise.withResolvers();return{...v(e,{serverContextKey:o,serverTimeout:t.serverTimeout??1e3,wrap:i=>i}),start(){const{size:i}=c(),s=Symbol("test");if(c().set(s,n.promise),c().size===i)throw new Error("Start execution only within a React render context (per-request)");c().delete(s),c().set(o,n.promise)},StateVocabProvider({children:i,value:s}){return s??={},n.resolve(s),y.jsx(m.StateVocabClientProvider,{clientContext:t.clientContext,value:s,children:i})}}}exports.serverify=x;
package/dist/server.es.js CHANGED
@@ -1,104 +1,76 @@
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)
1
+ import { jsx as u } from "react/jsx-runtime";
2
+ import { S as m } from "./constants-BB1YAX6c.mjs";
3
+ import S, { cache as v } from "react";
4
+ import { S as y } from "./provider-client-BhPeK8Kz.mjs";
5
+ import { w as f, g as w } from "./utils-xNWifFE9.mjs";
6
+ const p = (e) => {
7
+ if (S.useState)
8
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
- );
25
- };
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
- }
9
+ }, c = v(() => /* @__PURE__ */ new Map()), d = async (e) => (p("getStateVocab"), await c().get(e)), a = "Make sure your component is wrapped in StateVocabProvider";
10
+ async function x(e) {
11
+ const t = this[m], r = await f(
12
+ d(e.serverContextKey),
13
+ e.timeout,
14
+ `Failed access to "${t}". Reason: Operation timed out after ${e.timeout}ms. ${a}.`
47
15
  );
16
+ if (!r)
17
+ throw new Error(a);
18
+ return w(r, t);
48
19
  }
49
- const P = "Make sure your component is wrapped in StateVocabProvider";
50
- function w(e) {
51
- const o = this[u], t = x(e.serverContextKey);
52
- if (!t)
53
- throw new Error(P);
54
- return y(t, o);
55
- }
56
- function K(e) {
20
+ function b(e) {
57
21
  return typeof e == "object" && e !== null && "serverSlot" in e;
58
22
  }
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({
23
+ function l(e, t) {
24
+ const r = {};
25
+ for (const o in e) {
26
+ const n = e[o];
27
+ if (b(n))
28
+ r[o] = n.serverSlot({
65
29
  getState() {
66
- return w.apply(this, [{
67
- serverContextKey: o.serverContextKey
30
+ return x.apply(this, [{
31
+ serverContextKey: t.serverContextKey,
32
+ timeout: t.serverTimeout
68
33
  }]);
69
34
  }
70
- }), delete t[r].serverSlot, delete t[r].clientSlot;
35
+ }), delete r[o].serverSlot, delete r[o].clientSlot;
71
36
  else if (n !== null && typeof n == "object") {
72
- const l = (S) => o.wrap({ [r]: S });
73
- t[r] = v(n, {
74
- serverContextKey: o.serverContextKey,
75
- wrap: l
37
+ const i = (s) => t.wrap({ [o]: s });
38
+ r[o] = l(n, {
39
+ serverContextKey: t.serverContextKey,
40
+ serverTimeout: t.serverTimeout,
41
+ wrap: i
76
42
  });
77
43
  } else
78
- t[r] = n;
44
+ r[o] = n;
79
45
  }
80
- return t.seed = (r) => o.wrap(r), t;
46
+ return r.seed = (o) => t.wrap(o), r;
81
47
  }
82
- function k(e, o) {
83
- const t = /* @__PURE__ */ Symbol();
48
+ function E(e, t) {
49
+ const r = Object.keys(e).slice(0, 3).join("-"), o = Symbol(r), n = Promise.withResolvers();
84
50
  return {
85
- ...v(e, {
86
- serverContextKey: t,
87
- wrap: (r) => r
51
+ ...l(e, {
52
+ serverContextKey: o,
53
+ serverTimeout: t.serverTimeout ?? 1e3,
54
+ wrap: (i) => i
88
55
  }),
89
- StateVocabProvider({ children: r, value: n }) {
90
- return /* @__PURE__ */ c(
91
- h,
56
+ start() {
57
+ const { size: i } = c(), s = /* @__PURE__ */ Symbol("test");
58
+ if (c().set(s, n.promise), c().size === i)
59
+ throw new Error("Start execution only within a React render context (per-request)");
60
+ c().delete(s), c().set(o, n.promise);
61
+ },
62
+ StateVocabProvider({ children: i, value: s }) {
63
+ return s ??= {}, n.resolve(s), /* @__PURE__ */ u(
64
+ y,
92
65
  {
93
- clientContext: o.clientContext,
94
- serverContextKey: t,
95
- value: n,
96
- children: r
66
+ clientContext: t.clientContext,
67
+ value: s,
68
+ children: i
97
69
  }
98
70
  );
99
71
  }
100
72
  };
101
73
  }
102
74
  export {
103
- k as serverify
75
+ E as serverify
104
76
  };
@@ -1,12 +1,3 @@
1
- import React from "react";
2
- import type { PropsWithChildren, ReactNode } from "react";
3
1
  import type { Vocab } from "./state.types";
4
- export declare const getStateVocab: (serverContextKey: symbol) => Vocab | undefined;
5
- export declare const StateVocabServerContext: {
6
- Provider: (({ children }: PropsWithChildren<{
7
- value?: Vocab;
8
- }>) => ReactNode) | ((props: PropsWithChildren<{
9
- serverContextKey: symbol;
10
- value: Vocab;
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)>);
12
- };
2
+ export declare const getRequestStore: () => Map<symbol, Promise<Vocab>>;
3
+ export declare const getStateVocab: (serverContextKey: symbol) => Promise<Vocab | undefined>;
@@ -1,28 +1,36 @@
1
1
  import type { Context, PropsWithChildren, ReactNode } from "react";
2
- import type { Vocab } from "./state.types";
2
+ import type { Vocab, VocabThis } from "./state.types";
3
3
  type Placeholder<V> = {
4
- getState(): V;
5
- };
6
- type Slot<V> = {
7
- serverSlot(input: Placeholder<V>): Placeholder<V>;
4
+ getState(): Promise<V>;
8
5
  };
6
+ type SlotValue<T> = T extends {
7
+ serverSlot(input: {
8
+ getState(this: VocabThis): infer V;
9
+ }): unknown;
10
+ } ? V : never;
9
11
  type ServerifiedValue<R> = {
10
- [K in keyof R]?: R[K] extends Slot<infer V> ? V : R[K] extends object ? ServerifiedValue<R[K]> : R[K];
12
+ [K in keyof R]?: [
13
+ SlotValue<R[K]>
14
+ ] extends [never] ? R[K] extends object ? ServerifiedValue<R[K]> : R[K] : SlotValue<R[K]>;
11
15
  };
12
- type Serverified<R> = R extends Slot<infer TValue> ? Placeholder<TValue> : R extends object ? {
16
+ type Serverified<R> = [
17
+ SlotValue<R>
18
+ ] extends [never] ? R extends object ? {
13
19
  seed(input: ServerifiedValue<R>): Vocab;
14
20
  } & {
15
21
  [K in keyof R]: Serverified<R[K]>;
16
- } : R;
22
+ } : R : Placeholder<SlotValue<R>>;
17
23
  type ServerifyResult<R extends object> = {
24
+ start(): void;
18
25
  seed(input: ServerifiedValue<R>): Vocab;
19
26
  StateVocabProvider(props: PropsWithChildren<{
20
27
  value?: ServerifiedValue<R>;
21
- }>): Promise<ReactNode>;
28
+ }>): ReactNode;
22
29
  } & {
23
30
  [K in keyof R]: Serverified<R[K]>;
24
31
  };
25
32
  export declare function serverify<R extends object>(tree: R, serverifyOptions: {
26
33
  clientContext: Context<object>;
34
+ serverTimeout?: number;
27
35
  }): ServerifyResult<R>;
28
36
  export {};
@@ -7,3 +7,4 @@ export declare function logStyled(obj: unknown): void;
7
7
  export declare const isTransformer: <V>(v: ValueOrTransformer<V>) => v is Transformer<V>;
8
8
  export declare const isValueDefined: <V>(v: V | undefined) => v is V;
9
9
  export declare const valueOrFactory: <V>(input: ValueOrFactory<V>) => V;
10
+ export declare function withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T>;
@@ -0,0 +1,3 @@
1
+ "use strict";function f(e,r,o){if(!r)return e;const n=r.split(".");let t=e;for(const c of n)if(t!==null&&typeof t=="object"&&c in t)t=t[c];else return o;return t===void 0?o:t}function y(e,r,o){const n=r.replace(/\[(\d+)\]/g,".$1").split(".");let t=e;for(let c=0;c<n.length-1;c++){const s=n[c],i=n[c+1];(t[s]===void 0||t[s]===null)&&(t[s]=/^\d+$/.test(i)?[]:{}),t=t[s]}return t[n[n.length-1]]=o,e}function a(e,r=0){let o;return function(...n){o!==void 0&&clearTimeout(o),o=setTimeout(()=>{o=void 0,e.apply(this,n)},r)}}function d(e){const r=JSON.stringify(e,null,2).split(`
2
+ `),o=[],n=[];for(const t of r){const c=t.match(/^(\s*)"([^"]+)"(\s*:\s*)(.+)$/);if(c){const[,s,i,l,u]=c;o.push(`${s}%c"${i}"%c${l}%c${u}`),n.push("color: #9cdcfe; font-weight: bold","color: #cccccc","color: #ce9178")}else o.push(`%c${t}`),n.push("color: #cccccc")}console.log(o.join(`
3
+ `),...n,e)}const m=e=>typeof e=="function",p=e=>typeof e=="function",h=e=>typeof e<"u",g=e=>p(e)?e():e;async function T(e,r,o){let n;try{return await Promise.race([e,new Promise((t,c)=>{n=setTimeout(()=>{c(new Error(o))},r)})])}finally{clearTimeout(n)}}exports.debounce=a;exports.get=f;exports.isTransformer=m;exports.isValueDefined=h;exports.logStyled=d;exports.set=y;exports.valueOrFactory=g;exports.withTimeout=T;
@@ -0,0 +1,73 @@
1
+ function a(t, s, o) {
2
+ if (!s)
3
+ return t;
4
+ const n = s.split(".");
5
+ let e = t;
6
+ for (const c of n)
7
+ if (e !== null && typeof e == "object" && c in e)
8
+ e = e[c];
9
+ else
10
+ return o;
11
+ return e === void 0 ? o : e;
12
+ }
13
+ function y(t, s, o) {
14
+ const n = s.replace(/\[(\d+)\]/g, ".$1").split(".");
15
+ let e = t;
16
+ for (let c = 0; c < n.length - 1; c++) {
17
+ const r = n[c], i = n[c + 1];
18
+ (e[r] === void 0 || e[r] === null) && (e[r] = /^\d+$/.test(i) ? [] : {}), e = e[r];
19
+ }
20
+ return e[n[n.length - 1]] = o, t;
21
+ }
22
+ function d(t, s = 0) {
23
+ let o;
24
+ return function(...n) {
25
+ o !== void 0 && clearTimeout(o), o = setTimeout(() => {
26
+ o = void 0, t.apply(this, n);
27
+ }, s);
28
+ };
29
+ }
30
+ function p(t) {
31
+ const s = JSON.stringify(t, null, 2).split(`
32
+ `), o = [], n = [];
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
+ o.push(`${r}%c"${i}"%c${l}%c${u}`), n.push(
38
+ "color: #9cdcfe; font-weight: bold",
39
+ "color: #cccccc",
40
+ "color: #ce9178"
41
+ );
42
+ } else
43
+ o.push(`%c${e}`), n.push("color: #cccccc");
44
+ }
45
+ console.log(o.join(`
46
+ `), ...n, t);
47
+ }
48
+ const m = (t) => typeof t == "function", f = (t) => typeof t == "function", h = (t) => typeof t < "u", g = (t) => f(t) ? t() : t;
49
+ async function $(t, s, o) {
50
+ let n;
51
+ try {
52
+ return await Promise.race([
53
+ t,
54
+ new Promise((e, c) => {
55
+ n = setTimeout(() => {
56
+ c(new Error(o));
57
+ }, s);
58
+ })
59
+ ]);
60
+ } finally {
61
+ clearTimeout(n);
62
+ }
63
+ }
64
+ export {
65
+ h as a,
66
+ d,
67
+ a as g,
68
+ m as i,
69
+ p as l,
70
+ y as s,
71
+ g as v,
72
+ $ as w
73
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yakocloud/state-vocab",
3
- "version": "4.1.0",
3
+ "version": "4.2.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"),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;
@@ -1,8 +0,0 @@
1
- import type { Context, PropsWithChildren } from "react";
2
- import type { Vocab } from "./state.types";
3
- import VocabStore from "./store";
4
- export declare function StateVocabProvider(props: PropsWithChildren<{
5
- value?: Vocab;
6
- serverContextKey: symbol;
7
- clientContext: Context<VocabStore>;
8
- }>): import("react/jsx-runtime").JSX.Element;
@@ -1,6 +0,0 @@
1
- import type { Vocab } from "./state.types";
2
- import type { PropsWithChildren } from "react";
3
- export declare const StateVocabServerProvider: React.FC<PropsWithChildren<{
4
- value: Vocab;
5
- serverContextKey: symbol;
6
- }>>;
@@ -1,3 +0,0 @@
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=$;
@@ -1,57 +0,0 @@
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
- };