react-shared-states 1.0.6 → 1.0.8

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/dist/main.min.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /*!
2
- * react-shared-states v1.0.6
2
+ * react-shared-states v1.0.8
3
3
  * (c) Hichem Taboukouyout
4
4
  * Released under the MIT License.
5
5
  * Github: github.com/HichemTab-tech/react-shared-states
6
6
  */
7
7
 
8
- (function(g,f){typeof exports=="object"&&typeof module<"u"?f(exports,require("react")):typeof define=="function"&&define.amd?define(["exports","react"],f):(g=typeof globalThis<"u"?globalThis:g||self,f(g.ReactSharedStates={},g.React))})(this,function(g,f){"use strict";var C={exports:{}},j={};/**
8
+ (function(h,g){typeof exports=="object"&&typeof module<"u"?g(exports,require("react")):typeof define=="function"&&define.amd?define(["exports","react"],g):(h=typeof globalThis<"u"?globalThis:h||self,g(h.ReactSharedStates={},h.React))})(this,function(h,g){"use strict";var M={exports:{}},V={};/**
9
9
  * @license React
10
10
  * react-jsx-runtime.production.js
11
11
  *
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * This source code is licensed under the MIT license found in the
15
15
  * LICENSE file in the root directory of this source tree.
16
- */var G;function y(){if(G)return j;G=1;var s=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function r(t,l,h){var _=null;if(h!==void 0&&(_=""+h),l.key!==void 0&&(_=""+l.key),"key"in l){h={};for(var x in l)x!=="key"&&(h[x]=l[x])}else h=l;return l=h.ref,{$$typeof:s,type:t,key:_,ref:l!==void 0?l:null,props:h}}return j.Fragment=e,j.jsx=r,j.jsxs=r,j}var L={};/**
16
+ */var J;function se(){if(J)return V;J=1;var a=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function r(t,s,f){var o=null;if(f!==void 0&&(o=""+f),s.key!==void 0&&(o=""+s.key),"key"in s){f={};for(var A in s)A!=="key"&&(f[A]=s[A])}else f=s;return s=f.ref,{$$typeof:a,type:t,key:o,ref:s!==void 0?s:null,props:f}}return V.Fragment=e,V.jsx=r,V.jsxs=r,V}var C={};/**
17
17
  * @license React
18
18
  * react-jsx-runtime.development.js
19
19
  *
@@ -21,9 +21,9 @@
21
21
  *
22
22
  * This source code is licensed under the MIT license found in the
23
23
  * LICENSE file in the root directory of this source tree.
24
- */var J;function $(){return J||(J=1,process.env.NODE_ENV!=="production"&&function(){function s(n){if(n==null)return null;if(typeof n=="function")return n.$$typeof===we?null:n.displayName||n.name||null;if(typeof n=="string")return n;switch(n){case V:return"Fragment";case ve:return"Profiler";case pe:return"StrictMode";case xe:return"Suspense";case Re:return"SuspenseList";case Ae:return"Activity"}if(typeof n=="object")switch(typeof n.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),n.$$typeof){case Se:return"Portal";case me:return(n.displayName||"Context")+".Provider";case Ee:return(n._context.displayName||"Context")+".Consumer";case _e:var a=n.render;return n=n.displayName,n||(n=a.displayName||a.name||"",n=n!==""?"ForwardRef("+n+")":"ForwardRef"),n;case Te:return a=n.displayName||null,a!==null?a:s(n.type)||"Memo";case B:a=n._payload,n=n._init;try{return s(n(a))}catch{}}return null}function e(n){return""+n}function r(n){try{e(n);var a=!1}catch{a=!0}if(a){a=console;var u=a.error,p=typeof Symbol=="function"&&Symbol.toStringTag&&n[Symbol.toStringTag]||n.constructor.name||"Object";return u.call(a,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",p),e(n)}}function t(n){if(n===V)return"<>";if(typeof n=="object"&&n!==null&&n.$$typeof===B)return"<...>";try{var a=s(n);return a?"<"+a+">":"<...>"}catch{return"<...>"}}function l(){var n=F.A;return n===null?null:n.getOwner()}function h(){return Error("react-stack-top-frame")}function _(n){if(X.call(n,"key")){var a=Object.getOwnPropertyDescriptor(n,"key").get;if(a&&a.isReactWarning)return!1}return n.key!==void 0}function x(n,a){function u(){H||(H=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",a))}u.isReactWarning=!0,Object.defineProperty(n,"key",{get:u,configurable:!0})}function d(){var n=s(this.type);return Z[n]||(Z[n]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),n=this.props.ref,n!==void 0?n:null}function w(n,a,u,p,P,T,I,W){return u=T.ref,n={$$typeof:R,type:n,key:a,props:T,_owner:P},(u!==void 0?u:null)!==null?Object.defineProperty(n,"ref",{enumerable:!1,get:d}):Object.defineProperty(n,"ref",{enumerable:!1,value:null}),n._store={},Object.defineProperty(n._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(n,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(n,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:I}),Object.defineProperty(n,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:W}),Object.freeze&&(Object.freeze(n.props),Object.freeze(n)),n}function S(n,a,u,p,P,T,I,W){var v=a.children;if(v!==void 0)if(p)if(Pe(v)){for(p=0;p<v.length;p++)c(v[p]);Object.freeze&&Object.freeze(v)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else c(v);if(X.call(a,"key")){v=s(n);var O=Object.keys(a).filter(function(Oe){return Oe!=="key"});p=0<O.length?"{key: someKey, "+O.join(": ..., ")+": ...}":"{key: someKey}",q[v+p]||(O=0<O.length?"{"+O.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
24
+ */var z;function ae(){return z||(z=1,process.env.NODE_ENV!=="production"&&function(){function a(n){if(n==null)return null;if(typeof n=="function")return n.$$typeof===ye?null:n.displayName||n.name||null;if(typeof n=="string")return n;switch(n){case c:return"Fragment";case Re:return"Profiler";case _:return"StrictMode";case Pe:return"Suspense";case Oe:return"SuspenseList";case Le:return"Activity"}if(typeof n=="object")switch(typeof n.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),n.$$typeof){case d:return"Portal";case Ae:return(n.displayName||"Context")+".Provider";case Te:return(n._context.displayName||"Context")+".Consumer";case we:var i=n.render;return n=n.displayName,n||(n=i.displayName||i.name||"",n=n!==""?"ForwardRef("+n+")":"ForwardRef"),n;case je:return i=n.displayName||null,i!==null?i:a(n.type)||"Memo";case K:i=n._payload,n=n._init;try{return a(n(i))}catch{}}return null}function e(n){return""+n}function r(n){try{e(n);var i=!1}catch{i=!0}if(i){i=console;var b=i.error,p=typeof Symbol=="function"&&Symbol.toStringTag&&n[Symbol.toStringTag]||n.constructor.name||"Object";return b.call(i,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",p),e(n)}}function t(n){if(n===c)return"<>";if(typeof n=="object"&&n!==null&&n.$$typeof===K)return"<...>";try{var i=a(n);return i?"<"+i+">":"<...>"}catch{return"<...>"}}function s(){var n=D.A;return n===null?null:n.getOwner()}function f(){return Error("react-stack-top-frame")}function o(n){if(q.call(n,"key")){var i=Object.getOwnPropertyDescriptor(n,"key").get;if(i&&i.isReactWarning)return!1}return n.key!==void 0}function A(n,i){function b(){$||($=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",i))}b.isReactWarning=!0,Object.defineProperty(n,"key",{get:b,configurable:!0})}function L(){var n=a(this.type);return ee[n]||(ee[n]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),n=this.props.ref,n!==void 0?n:null}function O(n,i,b,p,j,w,W,U){return b=w.ref,n={$$typeof:S,type:n,key:i,props:w,_owner:j},(b!==void 0?b:null)!==null?Object.defineProperty(n,"ref",{enumerable:!1,get:L}):Object.defineProperty(n,"ref",{enumerable:!1,value:null}),n._store={},Object.defineProperty(n._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(n,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(n,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:W}),Object.defineProperty(n,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:U}),Object.freeze&&(Object.freeze(n.props),Object.freeze(n)),n}function P(n,i,b,p,j,w,W,U){var v=i.children;if(v!==void 0)if(p)if(Ve(v)){for(p=0;p<v.length;p++)u(v[p]);Object.freeze&&Object.freeze(v)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else u(v);if(q.call(i,"key")){v=a(n);var y=Object.keys(i).filter(function(Ce){return Ce!=="key"});p=0<y.length?"{key: someKey, "+y.join(": ..., ")+": ...}":"{key: someKey}",ne[v+p]||(y=0<y.length?"{"+y.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
25
25
  let props = %s;
26
26
  <%s {...props} />
27
27
  React keys must be passed directly to JSX without using spread:
28
28
  let props = %s;
29
- <%s key={someKey} {...props} />`,p,v,O,v),q[v+p]=!0)}if(v=null,u!==void 0&&(r(u),v=""+u),_(a)&&(r(a.key),v=""+a.key),"key"in a){u={};for(var U in a)U!=="key"&&(u[U]=a[U])}else u=a;return v&&x(u,typeof n=="function"?n.displayName||n.name||"Unknown":n),w(n,v,T,P,l(),u,I,W)}function c(n){typeof n=="object"&&n!==null&&n.$$typeof===R&&n._store&&(n._store.validated=1)}var o=f,R=Symbol.for("react.transitional.element"),Se=Symbol.for("react.portal"),V=Symbol.for("react.fragment"),pe=Symbol.for("react.strict_mode"),ve=Symbol.for("react.profiler"),Ee=Symbol.for("react.consumer"),me=Symbol.for("react.context"),_e=Symbol.for("react.forward_ref"),xe=Symbol.for("react.suspense"),Re=Symbol.for("react.suspense_list"),Te=Symbol.for("react.memo"),B=Symbol.for("react.lazy"),Ae=Symbol.for("react.activity"),we=Symbol.for("react.client.reference"),F=o.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,X=Object.prototype.hasOwnProperty,Pe=Array.isArray,Y=console.createTask?console.createTask:function(){return null};o={react_stack_bottom_frame:function(n){return n()}};var H,Z={},Q=o.react_stack_bottom_frame.bind(o,h)(),K=Y(t(h)),q={};L.Fragment=V,L.jsx=function(n,a,u,p,P){var T=1e4>F.recentlyCreatedOwnerStacks++;return S(n,a,u,!1,p,P,T?Error("react-stack-top-frame"):Q,T?Y(t(n)):K)},L.jsxs=function(n,a,u,p,P){var T=1e4>F.recentlyCreatedOwnerStacks++;return S(n,a,u,!0,p,P,T?Error("react-stack-top-frame"):Q,T?Y(t(n)):K)}}()),L}var z;function k(){return z||(z=1,process.env.NODE_ENV==="production"?C.exports=y():C.exports=$()),C.exports}var ee=k();const D=f.createContext(void 0),te=({children:s,scopeName:e})=>(e||(e=f.useMemo(()=>Math.random().toString(36).substring(2,15),[])),ee.jsx(D.Provider,{value:{scopeName:e},children:s})),re=()=>f.useContext(D);g.isDevMode=!1;const ne=s=>{g.isDevMode=s},N=(...s)=>{g.isDevMode&&console.log("%c[react-shared-states]","color: #007acc; font-weight: bold",...s)},A=s=>{if(!s)throw new Error("Value is empty");return s};class i{data=new Map;defaultValue(){return{}}addListener(e,r,t){this.data.has(i.prefix(e,r))||this.data.set(i.prefix(e,r),{...this.defaultValue,listeners:[]}),this.data.get(i.prefix(e,r)).listeners.push(t)}removeListener(e,r,t){this.data.has(i.prefix(e,r))&&(this.data.get(i.prefix(e,r)).listeners=this.data.get(i.prefix(e,r)).listeners.filter(l=>l!==t))}callListeners(e,r){this.data.has(i.prefix(e,r))&&this.data.get(i.prefix(e,r)).listeners.forEach(t=>t())}init(e,r,t){this.data.has(i.prefix(e,r))||this.data.set(i.prefix(e,r),{...t,listeners:[]})}clearAll(e=!1){e||this.data.forEach(r=>{r.listeners.forEach(t=>t())}),this.data.clear()}clear(e,r,t=!1){t||this.callListeners(e,r),this.data.delete(i.prefix(e,r))}get(e,r){let t=this.has(e,r);if(t)return this.data.get(t)}setValue(e,r,t){this.data.has(i.prefix(e,r))&&this.data.set(i.prefix(e,r),{...this.data.get(i.prefix(e,r)),...t})}has(e,r){return this.data.has(i.prefix(e,r))?i.prefix(e,r):this.data.has(i.prefix(e,"_global"))?i.prefix(e,"_global"):void 0}static prefix(e,r){return`${r}_${e}`}useEffect(e,r,t=null){f.useEffect(()=>()=>{t?.(),N(`[${i.prefix(e,r)}]`,"unmount effect"),this.data.get(i.prefix(e,r)).listeners?.length===0&&this.clear(e,r)},[])}}const M=s=>{const e=re();return{prefix:s??e?.scopeName??"_global"}};class se extends i{defaultValue(){return{value:void 0}}init(e,r,t){super.init(e,r,{value:t})}setValue(e,r,t){super.setValue(e,r,{value:t})}removeListener(e,r,t){super.removeListener(e,r,t)}}class ae{get(e,r="_global"){e=A(e);const t=r||"_global";return E.get(e,t)?.value}set(e,r,t="_global"){e=A(e);const l=t||"_global";E.setValue(e,l,{value:r})}clearAll(){E.clearAll()}clear(e,r="_global"){const t=r||"_global";E.clear(e,t)}has(e,r="_global"){const t=r||"_global";return!!E.has(e,t)}getAll(){return E.data}}const oe=new ae,E=new se,ie=(s,e,r)=>{s=A(s);const{prefix:t}=M(r);E.init(s,t,e);const l=f.useMemo(()=>d=>(E.init(s,t,e),E.addListener(s,t,d),()=>{E.removeListener(s,t,d)}),[]),h=f.useMemo(()=>()=>E.get(s,t)?.value,[]),_=f.useSyncExternalStore(l,h),x=d=>{const w=typeof d=="function"?d(E.get(s,t)?.value):d;w!==_&&(E.setValue(s,t,w),E.callListeners(s,t))};return E.useEffect(s,t),[_,x]};class le extends i{defaultValue(){return{fnState:{results:void 0,isLoading:!1,error:void 0}}}init(e,r){super.init(e,r,this.defaultValue())}setValue(e,r,t){super.setValue(e,r,t)}}class ce{get(e,r="_global"){e=A(e);const t=r||"_global";return m.get(e,t)?.fnState}set(e,r,t="_global"){e=A(e);const l=t||"_global";m.setValue(e,l,r)}clearAll(){m.clearAll()}clear(e,r="_global"){const t=r||"_global";m.clear(e,t)}has(e,r="_global"){const t=r||"_global";return!!m.has(e,t)}getAll(){return m.data}}const ue=new ce,m=new le,fe=(s,e,r)=>{s=A(s);const{prefix:t}=M(r);m.init(s,t);const l=f.useMemo(()=>d=>(m.init(s,t),m.addListener(s,t,d),()=>{m.removeListener(s,t,d)}),[]),h=f.useMemo(()=>()=>m.get(s,t).fnState,[]),_=f.useSyncExternalStore(l,h),x=async(d,...w)=>{const S=m.get(s,t);if(!d&&(S.fnState.isLoading||S.fnState.results!==void 0))return S.fnState;S.fnState={...S.fnState,isLoading:!0,error:void 0},S.listeners.forEach(c=>c());try{const c=await e(...w);S.fnState={results:c,isLoading:!1,error:void 0}}catch(c){S.fnState={...S.fnState,isLoading:!1,error:c}}S.listeners.forEach(c=>c())};return m.useEffect(s,t),{state:_,trigger:(...d)=>{x(!1,...d)},forceTrigger:(...d)=>{x(!0,...d)},clear:()=>{m.clear(s,t),m.init(s,t)}}};class de extends i{defaultValue(){return{fnState:{data:void 0,isLoading:!1,error:void 0,subscribed:!1}}}init(e,r){super.init(e,r,this.defaultValue())}setValue(e,r,t){super.setValue(e,r,t)}useEffect(e,r){f.useEffect(()=>()=>{N(`[${i.prefix(e,r)}]`,"unmount effect2"),this.get(e,r)?.listeners.length===0&&this.unsubscribe(e,r)},[]),super.useEffect(e,r)}async unsubscribe(e,r){const t=this.get(e,r);t&&(t.unsubscribe&&(t.unsubscribe(),t.unsubscribe=void 0),t.fnState.subscribed=!1)}}class be{get(e,r="_global"){e=A(e);const t=r||"_global";return b.get(e,t)?.fnState}set(e,r,t="_global"){e=A(e);const l=t||"_global";b.setValue(e,l,r)}clearAll(){b.clearAll()}clear(e,r="_global"){const t=r||"_global";b.clear(e,t)}has(e,r="_global"){const t=r||"_global";return!!b.has(e,t)}getAll(){return b.data}}const ge=new be,b=new de,he=(s,e,r)=>{s=A(s);const{prefix:t}=M(r);b.init(s,t);const l=f.useMemo(()=>c=>(b.init(s,t),b.addListener(s,t,c),()=>{b.removeListener(s,t,c)}),[]),h=f.useMemo(()=>()=>b.get(s,t).fnState,[]),_=f.useSyncExternalStore(l,h),x=c=>{const o=b.get(s,t);o.fnState={...o.fnState,data:c},o.listeners.forEach(R=>R())},d=c=>{const o=b.get(s,t);o.fnState={...o.fnState,isLoading:!1,data:void 0,error:c},o.listeners.forEach(R=>R())},w=()=>{const c=b.get(s,t);c.fnState={...c.fnState,isLoading:!1},c.listeners.forEach(o=>o())},S=async c=>{const o=b.get(s,t);if(c&&(await b.unsubscribe(s,t),o.fnState={...o.fnState,isLoading:!1,data:void 0,error:void 0,subscribed:!1}),o.fnState.subscribed)return o.fnState;N("triggered !!"),o.fnState={...o.fnState,isLoading:!0,error:void 0},o.listeners.forEach(R=>R());try{o.unsubscribe=await e(x,d,w),o.fnState.subscribed=!0}catch(R){o.fnState={...o.fnState,isLoading:!1,error:R}}o.listeners.forEach(R=>R())};return b.useEffect(s,t),{state:_,trigger:()=>{S(!1)},forceTrigger:()=>{S(!0)},unsubscribe:()=>{b.unsubscribe(s,t)}}};g.SharedStatesProvider=te,g.setDevMode=ne,g.sharedFunctionsApi=ue,g.sharedStatesApi=oe,g.sharedSubscriptionsApi=ge,g.useSharedFunction=fe,g.useSharedState=ie,g.useSharedSubscription=he,Object.defineProperty(g,Symbol.toStringTag,{value:"Module"})});
29
+ <%s key={someKey} {...props} />`,p,v,y,v),ne[v+p]=!0)}if(v=null,b!==void 0&&(r(b),v=""+b),o(i)&&(r(i.key),v=""+i.key),"key"in i){b={};for(var G in i)G!=="key"&&(b[G]=i[G])}else b=i;return v&&A(b,typeof n=="function"?n.displayName||n.name||"Unknown":n),O(n,v,w,j,s(),b,W,U)}function u(n){typeof n=="object"&&n!==null&&n.$$typeof===S&&n._store&&(n._store.validated=1)}var m=g,S=Symbol.for("react.transitional.element"),d=Symbol.for("react.portal"),c=Symbol.for("react.fragment"),_=Symbol.for("react.strict_mode"),Re=Symbol.for("react.profiler"),Te=Symbol.for("react.consumer"),Ae=Symbol.for("react.context"),we=Symbol.for("react.forward_ref"),Pe=Symbol.for("react.suspense"),Oe=Symbol.for("react.suspense_list"),je=Symbol.for("react.memo"),K=Symbol.for("react.lazy"),Le=Symbol.for("react.activity"),ye=Symbol.for("react.client.reference"),D=m.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,q=Object.prototype.hasOwnProperty,Ve=Array.isArray,I=console.createTask?console.createTask:function(){return null};m={react_stack_bottom_frame:function(n){return n()}};var $,ee={},te=m.react_stack_bottom_frame.bind(m,f)(),re=I(t(f)),ne={};C.Fragment=c,C.jsx=function(n,i,b,p,j){var w=1e4>D.recentlyCreatedOwnerStacks++;return P(n,i,b,!1,p,j,w?Error("react-stack-top-frame"):te,w?I(t(n)):re)},C.jsxs=function(n,i,b,p,j){var w=1e4>D.recentlyCreatedOwnerStacks++;return P(n,i,b,!0,p,j,w?Error("react-stack-top-frame"):te,w?I(t(n)):re)}}()),C}var X;function oe(){return X||(X=1,process.env.NODE_ENV==="production"?M.exports=se():M.exports=ae()),M.exports}var ie=oe();h.isDevMode=!1;const ce=a=>{h.isDevMode=a},N=(...a)=>{h.isDevMode&&console.log("%c[react-shared-states]","color: #007acc; font-weight: bold",...a)},T=a=>{if(!a)throw new Error("Value is empty");return a},F=()=>Math.random().toString(36).substring(2,15),B=g.createContext(void 0),le=({children:a,scopeName:e})=>{if(e&&e.includes("//"))throw new Error("scopeName cannot contain '//'");return e||(e=g.useMemo(()=>F(),[])),ie.jsx(B.Provider,{value:{scopeName:e},children:a})},ue=()=>g.useContext(B);class l{data=new Map;defaultValue(){return{}}addListener(e,r,t){this.data.has(l.prefix(e,r))||this.data.set(l.prefix(e,r),{...this.defaultValue,listeners:[]}),this.data.get(l.prefix(e,r)).listeners.push(t)}removeListener(e,r,t){this.data.has(l.prefix(e,r))&&(this.data.get(l.prefix(e,r)).listeners=this.data.get(l.prefix(e,r)).listeners.filter(s=>s!==t))}callListeners(e,r){this.data.has(l.prefix(e,r))&&this.data.get(l.prefix(e,r)).listeners.forEach(t=>t())}init(e,r,t){this.data.has(l.prefix(e,r))||this.data.set(l.prefix(e,r),{...t,listeners:[]})}clearAll(e=!1){e||this.data.forEach(r=>{r.listeners.forEach(t=>t())}),this.data.clear()}clear(e,r,t=!1){t||this.callListeners(e,r),this.data.delete(l.prefix(e,r))}get(e,r){let t=this.has(e,r);if(t)return this.data.get(t)}setValue(e,r,t){this.data.has(l.prefix(e,r))&&this.data.set(l.prefix(e,r),{...this.data.get(l.prefix(e,r)),...t})}has(e,r){return this.data.has(l.prefix(e,r))?l.prefix(e,r):this.data.has(l.prefix(e,"_global"))?l.prefix(e,"_global"):void 0}static prefix(e,r){if(e.includes("//"))throw new Error("key cannot contain '//'");return`${r}//${e}`}static extractPrefix(e){return e.split("//")}useEffect(e,r,t=null){g.useEffect(()=>()=>{t?.(),N(`[${l.prefix(e,r)}]`,"unmount effect"),this.data.get(l.prefix(e,r)).listeners?.length===0&&this.clear(e,r)},[])}}class Y{constructor(e){this.sharedData=e}get(e,r){e=T(e);const t=r||"_global";return this.sharedData.get(e,t)}set(e,r,t){e=T(e);const s=t||"_global";this.sharedData.setValue(e,s,r)}clearAll(){this.sharedData.clearAll()}clearScope(e){const r=e||"_global";this.sharedData.data.forEach((t,s)=>{const[f]=l.extractPrefix(s);if(f===r){this.sharedData.clear(s,f);return}})}resolve(e){const{key:r,prefix:t}=e;return this.get(r,t)}clear(e,r){let t,s;typeof e=="string"?(t=e,s=r||"_global"):(t=e.key,s=e.prefix),this.sharedData.clear(t,s)}has(e,r="_global"){const t=r||"_global";return!!this.sharedData.has(e,t)}getAll(){const e={};return this.sharedData.data.forEach((r,t)=>{const[s,f]=l.extractPrefix(t);e[s]=e[s]||{},e[s][f]=r}),e}}const k=a=>{const e=ue();return{prefix:a??e?.scopeName??"_global"}};class fe extends l{defaultValue(){return{value:void 0}}init(e,r,t){super.init(e,r,{value:t})}setValue(e,r,t){super.setValue(e,r,{value:t})}removeListener(e,r,t){super.removeListener(e,r,t)}}class H extends Y{get(e,r="_global"){e=T(e);const t=r||"_global";return R.get(e,t)?.value}set(e,r,t="_global"){e=T(e);const s=t||"_global";R.setValue(e,s,{value:r})}}const R=new fe,de=new H(R),he=(a,e)=>{const r=e??e??"_global";return{key:F(),prefix:r,initialValue:a}};function be(a,e,r){let t,s,f=r;if(typeof a!="string"){const{key:u,initialValue:m,prefix:S}=a;t=u,s=m,f=S}else t=T(a),s=e;const{prefix:o}=k(f);R.init(t,o,s);const A=g.useMemo(()=>u=>(R.init(t,o,e),R.addListener(t,o,u),()=>{R.removeListener(t,o,u)}),[]),L=g.useMemo(()=>()=>R.get(t,o)?.value,[]),O=g.useSyncExternalStore(A,L),P=u=>{const m=typeof u=="function"?u(R.get(t,o)?.value):u;m!==O&&(R.setValue(t,o,m),R.callListeners(t,o))};return R.useEffect(t,o),[O,P]}class Se extends l{defaultValue(){return{fnState:{results:void 0,isLoading:!1,error:void 0}}}init(e,r){super.init(e,r,this.defaultValue())}setValue(e,r,t){super.setValue(e,r,t)}}class Z extends Y{get(e,r="_global"){e=T(e);const t=r||"_global";return x.get(e,t)?.fnState}set(e,r,t="_global"){e=T(e);const s=t||"_global";x.setValue(e,s,r)}}const x=new Se,ge=new Z(x),pe=(a,e)=>{const r=e??e??"_global";return{key:F(),prefix:r,fn:a}};function ve(a,e,r){let t,s,f=r;if(typeof a!="string"){const{key:u,fn:m,prefix:S}=a;t=u,s=m,f=S}else t=T(a),s=e;const{prefix:o}=k(f);x.init(t,o);const A=g.useMemo(()=>u=>(x.init(t,o),x.addListener(t,o,u),()=>{x.removeListener(t,o,u)}),[]),L=g.useMemo(()=>()=>x.get(t,o).fnState,[]),O=g.useSyncExternalStore(A,L),P=async(u,...m)=>{const S=x.get(t,o);if(!u&&(S.fnState.isLoading||S.fnState.results!==void 0))return S.fnState;S.fnState={...S.fnState,isLoading:!0,error:void 0},S.listeners.forEach(d=>d());try{const d=await s(...m);S.fnState={results:d,isLoading:!1,error:void 0}}catch(d){S.fnState={...S.fnState,isLoading:!1,error:d}}S.listeners.forEach(d=>d())};return x.useEffect(t,o),{state:O,trigger:(...u)=>{P(!1,...u)},forceTrigger:(...u)=>{P(!0,...u)},clear:()=>{const u=x.get(t,o);u&&(u.fnState=x.defaultValue().fnState,x.callListeners(t,o))}}}class Ee extends l{defaultValue(){return{fnState:{data:void 0,isLoading:!1,error:void 0,subscribed:!1}}}init(e,r){super.init(e,r,this.defaultValue())}setValue(e,r,t){super.setValue(e,r,t)}useEffect(e,r){g.useEffect(()=>()=>{N(`[${l.prefix(e,r)}]`,"unmount effect2"),this.get(e,r)?.listeners.length===0&&this.unsubscribe(e,r)},[]),super.useEffect(e,r)}async unsubscribe(e,r){const t=this.get(e,r);t&&(t.unsubscribe&&(t.unsubscribe(),t.unsubscribe=void 0),t.fnState={...t.fnState,subscribed:!1},this.callListeners(e,r))}}class Q extends Y{get(e,r="_global"){e=T(e);const t=r||"_global";return E.get(e,t)?.fnState}set(e,r,t="_global"){e=T(e);const s=t||"_global";E.setValue(e,s,r)}}const E=new Ee,me=new Q(E),xe=(a,e)=>{const r=e??e??"_global";return{key:F(),prefix:r,subscriber:a}};function _e(a,e,r){let t,s,f=r;if(typeof a!="string"){const{key:d,subscriber:c,prefix:_}=a;t=d,s=c,f=_}else t=T(a),s=e;const{prefix:o}=k(f);E.init(t,o);const A=g.useMemo(()=>d=>(E.init(t,o),E.addListener(t,o,d),()=>{E.removeListener(t,o,d)}),[]),L=g.useMemo(()=>()=>E.get(t,o).fnState,[]),O=g.useSyncExternalStore(A,L),P=d=>{const c=E.get(t,o);c.fnState={...c.fnState,data:d},c.listeners.forEach(_=>_())},u=d=>{const c=E.get(t,o);c.fnState={...c.fnState,isLoading:!1,data:void 0,error:d},c.listeners.forEach(_=>_())},m=()=>{const d=E.get(t,o);d.fnState={...d.fnState,isLoading:!1},d.listeners.forEach(c=>c())},S=async d=>{const c=E.get(t,o);if(d&&(await E.unsubscribe(t,o),c.fnState={...c.fnState,isLoading:!1,data:void 0,error:void 0,subscribed:!1}),c.fnState.subscribed)return c.fnState;N("triggered !!"),c.fnState={...c.fnState,isLoading:!0,error:void 0},c.listeners.forEach(_=>_());try{c.unsubscribe=await s(P,u,m),c.fnState.subscribed=!0}catch(_){c.fnState={...c.fnState,isLoading:!1,error:_}}c.listeners.forEach(_=>_())};return E.useEffect(t,o),{state:O,trigger:()=>{S(!1)},forceTrigger:()=>{S(!0)},unsubscribe:()=>{E.unsubscribe(t,o)}}}h.SharedFunctionsApi=Z,h.SharedStatesApi=H,h.SharedStatesProvider=le,h.SharedSubscriptionsApi=Q,h.createSharedFunction=pe,h.createSharedState=he,h.createSharedSubscription=xe,h.setDevMode=ce,h.sharedFunctionsApi=ge,h.sharedStatesApi=de,h.sharedSubscriptionsApi=me,h.useSharedFunction=ve,h.useSharedState=be,h.useSharedSubscription=_e,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})});
package/dist/types.d.ts CHANGED
@@ -4,4 +4,7 @@ export type Prefix = "_global" | ({} & string);
4
4
  export interface DataMapValue {
5
5
  listeners: AFunction[];
6
6
  }
7
- export type NonEmptyString<T extends string> = '' extends T ? never : T;
7
+ export interface SharedCreated {
8
+ key: string;
9
+ prefix: string;
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-shared-states",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "type": "module",
5
5
  "description": "Global state made as simple as useState, with zero config, built-in async caching, and automatic scoping.",
6
6
  "keywords": [
@@ -26,10 +26,13 @@
26
26
  },
27
27
  "homepage": "https://github.com/HichemTab-tech/react-shared-states#readme",
28
28
  "devDependencies": {
29
+ "@testing-library/dom": "^10.4.1",
30
+ "@testing-library/react": "^16.3.0",
29
31
  "@types/node": "^24.3.0",
30
32
  "@types/react": "^19.1.11",
31
33
  "@types/react-dom": "^19.1.7",
32
34
  "@vitejs/plugin-react": "^5.0.1",
35
+ "jsdom": "^26.1.0",
33
36
  "react": "^19.1.1",
34
37
  "react-dom": "^19.1.1",
35
38
  "typescript": "^5.9.2",
@@ -39,7 +42,9 @@
39
42
  "vitest": "^3.2.4"
40
43
  },
41
44
  "scripts": {
42
- "build": "vite build",
45
+ "tsc": "tsc",
46
+ "bump": "node scripts/bumper.js",
47
+ "build": "tsc && vite build",
43
48
  "test": "vitest",
44
49
  "preview": "vite preview",
45
50
  "dev:demo": "vite --config demo/vite.config.demo.ts",
@@ -0,0 +1,24 @@
1
+ import fs from 'fs';
2
+
3
+ const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
4
+ const version = packageJson.version;
5
+ const [major, minor, patchWord] = version.split('.');
6
+ let newPatch;
7
+ if (patchWord.includes('-')) {
8
+ // Handle pre-release versions by extracting the numeric part
9
+ newPatch = parseInt(patchWord.split('-')[0], 10);
10
+ newPatch += 1;
11
+ // Reconstruct the pre-release version
12
+ newPatch = `${newPatch}-${patchWord.split('-')[1]}`;
13
+ }
14
+ else{
15
+ newPatch = parseInt(patchWord, 10) + 1;
16
+ }
17
+
18
+ // Increment patch version
19
+ packageJson.version = `${major}.${minor}.${newPatch}`;
20
+
21
+ // Write back to package.json with proper formatting
22
+ fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2) + '\n');
23
+
24
+ console.log(`Version bumped from ${version} to ${packageJson.version}`);
@@ -0,0 +1,358 @@
1
+ import {describe, it, expect, beforeEach, vi, afterEach} from 'vitest'
2
+ import React from 'react'
3
+ import {render, screen, fireEvent, act, cleanup} from '@testing-library/react'
4
+ import {
5
+ createSharedFunction,
6
+ createSharedState, createSharedSubscription,
7
+ SharedStatesProvider,
8
+ useSharedFunction,
9
+ useSharedState,
10
+ useSharedSubscription
11
+ } from "../src";
12
+ import type {SubscriberEvents} from "../src/hooks/use-shared-subscription";
13
+
14
+ // Mocking random to have predictable keys for created states/functions/subscriptions
15
+ vi.mock('../src/lib/utils', async (importActual) => {
16
+ const actual = await importActual<typeof import('../src/lib/utils')>();
17
+ let count = 0;
18
+ // noinspection JSUnusedGlobalSymbols
19
+ return {
20
+ ...actual,
21
+ random: () => `test-key-${count++}`,
22
+ };
23
+ });
24
+
25
+ beforeEach(() => {
26
+ cleanup();
27
+ // Reset the mocked random key counter
28
+ vi.clearAllMocks();
29
+ });
30
+ afterEach(() => {
31
+ vi.useRealTimers();
32
+ })
33
+
34
+ describe('useSharedState', () => {
35
+ it('should share state between two components', () => {
36
+ const TestComponent1 = () => {
37
+ const [count] = useSharedState('count', 0);
38
+ return <span data-testid="value1">{count}</span>;
39
+ };
40
+
41
+ const TestComponent2 = () => {
42
+ const [count, setCount] = useSharedState('count', 0);
43
+ return (
44
+ <div>
45
+ <span data-testid="value2">{count}</span>
46
+ <button onClick={() => setCount(c => c + 1)}>inc</button>
47
+ </div>
48
+ );
49
+ };
50
+
51
+ render(
52
+ <>
53
+ <TestComponent1/>
54
+ <TestComponent2/>
55
+ </>
56
+ );
57
+
58
+ expect(screen.getByTestId('value1').textContent).toBe('0');
59
+ expect(screen.getByTestId('value2').textContent).toBe('0');
60
+
61
+ act(() => {
62
+ fireEvent.click(screen.getByText('inc'));
63
+ });
64
+
65
+ expect(screen.getByTestId('value1').textContent).toBe('1');
66
+ expect(screen.getByTestId('value2').textContent).toBe('1');
67
+ });
68
+
69
+ it('should isolate state with SharedStatesProvider', () => {
70
+ const TestComponent = () => {
71
+ const [count, setCount] = useSharedState('count', 0);
72
+ return (
73
+ <div>
74
+ <span>{count}</span>
75
+ <button onClick={() => setCount(c => c + 1)}>inc</button>
76
+ </div>
77
+ );
78
+ };
79
+
80
+ render(
81
+ <div>
82
+ <div data-testid="scope1">
83
+ <SharedStatesProvider scopeName="scope1">
84
+ <TestComponent/>
85
+ </SharedStatesProvider>
86
+ </div>
87
+ <div data-testid="scope2">
88
+ <SharedStatesProvider scopeName="scope2">
89
+ <TestComponent/>
90
+ </SharedStatesProvider>
91
+ </div>
92
+ </div>
93
+ );
94
+
95
+ const scope1Button = screen.getAllByText('inc')[0];
96
+ const scope2Button = screen.getAllByText('inc')[1];
97
+
98
+ act(() => {
99
+ fireEvent.click(scope1Button);
100
+ });
101
+
102
+ expect(screen.getByTestId('scope1').textContent).toContain('1');
103
+ expect(screen.getByTestId('scope2').textContent).toContain('0');
104
+
105
+ act(() => {
106
+ fireEvent.click(scope2Button);
107
+ fireEvent.click(scope2Button);
108
+ });
109
+
110
+ expect(screen.getByTestId('scope1').textContent).toContain('1');
111
+ expect(screen.getByTestId('scope2').textContent).toContain('2');
112
+ });
113
+
114
+ it('should work with createSharedState', () => {
115
+ const sharedCounter = createSharedState(10);
116
+
117
+ const TestComponent1 = () => {
118
+ const [count] = useSharedState(sharedCounter);
119
+ return <span data-testid="value1">{count}</span>;
120
+ };
121
+
122
+ const TestComponent2 = () => {
123
+ const [count, setCount] = useSharedState(sharedCounter);
124
+ return <button onClick={() => setCount(count + 5)}>inc</button>;
125
+ };
126
+
127
+ render(
128
+ <>
129
+ <TestComponent1/>
130
+ <TestComponent2/>
131
+ </>
132
+ );
133
+
134
+ expect(screen.getByTestId('value1').textContent).toBe('10');
135
+
136
+ act(() => {
137
+ fireEvent.click(screen.getByText('inc'));
138
+ });
139
+
140
+ expect(screen.getByTestId('value1').textContent).toBe('15');
141
+ });
142
+ });
143
+
144
+ describe('useSharedFunction', () => {
145
+ const mockApiCall = vi.fn((...args: any[]) => new Promise(resolve => setTimeout(() => resolve(`result: ${args.join(',')}`), 100)));
146
+
147
+ beforeEach(() => {
148
+ mockApiCall.mockClear();
149
+ vi.useFakeTimers();
150
+ });
151
+
152
+ const TestComponent = ({fnKey, sharedFn}: { fnKey: string, sharedFn?: any }) => {
153
+ const {state, trigger, forceTrigger, clear} = sharedFn ? useSharedFunction(sharedFn) : useSharedFunction(fnKey, mockApiCall);
154
+ return (
155
+ <div>
156
+ {state.isLoading && <span>Loading...</span>}
157
+ {state.error as any && <span>{String(state.error)}</span>}
158
+ {state.results && <span data-testid="result">{String(state.results)}</span>}
159
+ <button onClick={() => trigger('arg1')}>trigger</button>
160
+ <button onClick={() => forceTrigger('arg2')}>force</button>
161
+ <button onClick={() => clear()}>clear</button>
162
+ </div>
163
+ );
164
+ };
165
+
166
+ it('should handle async function lifecycle', async () => {
167
+ render(<TestComponent fnKey="test-fn"/>);
168
+
169
+ // Initial state
170
+ expect(screen.queryByText('Loading...')).toBeNull();
171
+ expect(screen.queryByTestId('result')).toBeNull();
172
+
173
+ // Trigger
174
+ act(() => {
175
+ fireEvent.click(screen.getByText('trigger'));
176
+ });
177
+ expect(screen.getByText('Loading...')).toBeDefined();
178
+
179
+ // Resolve
180
+ await act(async () => {
181
+ await vi.advanceTimersByTimeAsync(100);
182
+ });
183
+ expect(screen.queryByText('Loading...')).toBeNull();
184
+ expect(screen.getByTestId('result').textContent).toBe('result: arg1');
185
+ expect(mockApiCall).toHaveBeenCalledTimes(1);
186
+ expect(mockApiCall).toHaveBeenCalledWith('arg1');
187
+ });
188
+
189
+ it('should not trigger if already running or has data', async () => {
190
+ render(<TestComponent fnKey="test-fn"/>);
191
+ act(() => {
192
+ fireEvent.click(screen.getByText('trigger'));
193
+ });
194
+ await act(async () => {
195
+ await vi.advanceTimersByTimeAsync(100);
196
+ });
197
+ expect(mockApiCall).toHaveBeenCalledTimes(1);
198
+
199
+ // Trigger again, should not call mockApiCall
200
+ act(() => {
201
+ fireEvent.click(screen.getByText('trigger'));
202
+ });
203
+ expect(mockApiCall).toHaveBeenCalledTimes(1);
204
+ });
205
+
206
+ it('should force trigger', async () => {
207
+ render(<TestComponent fnKey="test-fn"/>);
208
+ act(() => {
209
+ fireEvent.click(screen.getByText('trigger'));
210
+ });
211
+ await act(async () => {
212
+ await vi.advanceTimersByTimeAsync(100);
213
+ });
214
+ expect(mockApiCall).toHaveBeenCalledTimes(1);
215
+
216
+ // Force trigger
217
+ act(() => {
218
+ fireEvent.click(screen.getByText('force'));
219
+ });
220
+ expect(screen.getByText('Loading...')).toBeDefined();
221
+ await act(async () => {
222
+ await vi.advanceTimersByTimeAsync(100);
223
+ });
224
+ expect(mockApiCall).toHaveBeenCalledTimes(2);
225
+ expect(mockApiCall).toHaveBeenCalledWith('arg2');
226
+ expect(screen.getByTestId('result').textContent).toBe('result: arg2');
227
+ });
228
+
229
+ it('should clear state', async () => {
230
+ render(<TestComponent fnKey="test-fn"/>);
231
+ act(() => {
232
+ fireEvent.click(screen.getByText('trigger'));
233
+ });
234
+ await act(async () => {
235
+ await vi.advanceTimersByTimeAsync(100);
236
+ });
237
+ expect(screen.getByTestId('result')).toBeDefined();
238
+
239
+ act(() => {
240
+ fireEvent.click(screen.getByText('clear'));
241
+ });
242
+ expect(screen.queryByTestId('result')).toBeNull();
243
+ });
244
+
245
+ it('should work with createSharedFunction', async () => {
246
+ const sharedFunction = createSharedFunction(mockApiCall);
247
+ render(<TestComponent fnKey="unused" sharedFn={sharedFunction}/>);
248
+
249
+ act(() => {
250
+ fireEvent.click(screen.getByText('trigger'));
251
+ });
252
+ await act(async () => {
253
+ await vi.advanceTimersByTimeAsync(100);
254
+ });
255
+ expect(mockApiCall).toHaveBeenCalledTimes(1);
256
+ expect(screen.getByTestId('result').textContent).toBe('result: arg1');
257
+ });
258
+ });
259
+
260
+ describe('useSharedSubscription', () => {
261
+ let mockSubscriber: (set: SubscriberEvents.Set<any>, onError: SubscriberEvents.OnError, onCompletion: SubscriberEvents.OnCompletion) => () => void;
262
+ const mockUnsubscribe = vi.fn();
263
+
264
+ beforeEach(() => {
265
+ mockUnsubscribe.mockClear();
266
+ mockSubscriber = vi.fn((set, _onError, onCompletion) => {
267
+ // Simulate async subscription
268
+ const timeout = setTimeout(() => {
269
+ set('initial data');
270
+ onCompletion();
271
+ }, 100);
272
+ return () => {
273
+ clearTimeout(timeout);
274
+ mockUnsubscribe();
275
+ };
276
+ });
277
+ vi.useFakeTimers();
278
+ });
279
+
280
+ const TestComponent = ({subKey, sharedSub}: { subKey: string, sharedSub?: any }) => {
281
+ const {state, trigger, unsubscribe} = sharedSub ? useSharedSubscription(sharedSub) : useSharedSubscription(subKey, mockSubscriber);
282
+ return (
283
+ <div>
284
+ {state.isLoading && <span>Loading...</span>}
285
+ {state.error && <span>{String(state.error)}</span>}
286
+ {state.data && <span data-testid="data">{String(state.data)}</span>}
287
+ <span>Subscribed: {String(state.subscribed)}</span>
288
+ <button onClick={() => trigger()}>subscribe</button>
289
+ <button onClick={() => unsubscribe()}>unsubscribe</button>
290
+ </div>
291
+ );
292
+ };
293
+
294
+ it('should handle subscription lifecycle', async () => {
295
+ render(<TestComponent subKey="test-sub"/>);
296
+
297
+ // Initial state
298
+ expect(screen.getByText('Subscribed: false')).toBeDefined();
299
+
300
+ // Trigger subscription
301
+ act(() => {
302
+ fireEvent.click(screen.getByText('subscribe'));
303
+ });
304
+ expect(screen.getByText('Loading...')).toBeDefined();
305
+
306
+ // Subscription completes
307
+ await act(async () => {
308
+ await vi.advanceTimersByTimeAsync(100);
309
+ });
310
+ expect(screen.queryByText('Loading...')).toBeNull();
311
+ expect(screen.getByTestId('data').textContent).toBe('initial data');
312
+ expect(screen.getByText('Subscribed: true')).toBeDefined();
313
+ expect(mockSubscriber).toHaveBeenCalledTimes(1);
314
+ });
315
+
316
+ it('should unsubscribe', async () => {
317
+ render(<TestComponent subKey="test-sub"/>);
318
+ act(() => {
319
+ fireEvent.click(screen.getByText('subscribe'));
320
+ });
321
+ await act(async () => {
322
+ await vi.advanceTimersByTimeAsync(100);
323
+ });
324
+
325
+ act(() => {
326
+ fireEvent.click(screen.getByText('unsubscribe'));
327
+ });
328
+ expect(mockUnsubscribe).toHaveBeenCalledTimes(1);
329
+ expect(screen.getByText('Subscribed: false')).toBeDefined();
330
+ });
331
+
332
+ it('should automatically unsubscribe on unmount', async () => {
333
+ const {unmount} = render(<TestComponent subKey="test-sub"/>);
334
+ act(() => {
335
+ fireEvent.click(screen.getByText('subscribe'));
336
+ });
337
+ await act(async () => {
338
+ await vi.advanceTimersByTimeAsync(100);
339
+ });
340
+
341
+ unmount();
342
+ expect(mockUnsubscribe).toHaveBeenCalledTimes(1);
343
+ });
344
+
345
+ it('should work with createSharedSubscription', async () => {
346
+ const sharedSubscription = createSharedSubscription(mockSubscriber);
347
+ render(<TestComponent subKey="unused" sharedSub={sharedSubscription}/>);
348
+
349
+ act(() => {
350
+ fireEvent.click(screen.getByText('subscribe'));
351
+ });
352
+ await act(async () => {
353
+ await vi.advanceTimersByTimeAsync(100);
354
+ });
355
+ expect(mockSubscriber).toHaveBeenCalledTimes(1);
356
+ expect(screen.getByTestId('data').textContent).toBe('initial data');
357
+ });
358
+ });
@@ -0,0 +1,8 @@
1
+ import { defineConfig, mergeConfig } from 'vitest/config'
2
+ import viteConfig from './vite.config'
3
+
4
+ export default mergeConfig(viteConfig, defineConfig({
5
+ test: {
6
+ environment: "jsdom",
7
+ },
8
+ }))