muya 2.0.0-beta.1 → 2.0.0-beta.2

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
@@ -32,19 +32,19 @@ This allows you to write derived state as plain functions, making your code more
32
32
  ## 📦 Installation
33
33
 
34
34
  ```bash
35
- npm install muya
35
+ npm install muya@2.0.0-beta.1
36
36
  ```
37
37
 
38
38
  Or using Yarn:
39
39
 
40
40
  ```bash
41
- yarn add muya
41
+ yarn add muya@2.0.0-beta.1
42
42
  ```
43
43
 
44
44
  Or using Bun:
45
45
 
46
46
  ```bash
47
- bun add muya
47
+ bun add muya@2.0.0-beta.1
48
48
  ```
49
49
 
50
50
  ---
package/cjs/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var h=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var X=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var K=(e,t)=>{for(var r in t)h(e,r,{get:t[r],enumerable:!0})},Y=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of X(t))!N.call(e,n)&&n!==r&&h(e,n,{get:()=>t[n],enumerable:!(o=q(t,n))||o.enumerable});return e};var G=e=>Y(h({},"__esModule",{value:!0}),e);var J={};K(J,{EMPTY_SELECTOR:()=>w,create:()=>U,shallow:()=>j,use:()=>H});module.exports=G(J);var w=e=>e;function p(e){return e instanceof Promise}function g(e){return typeof e=="function"}function R(e){return e instanceof Map}function C(e){return e instanceof Set}function P(e){return Array.isArray(e)}function b(e,t){return e===t?!0:!!Object.is(e,t)}function A(e){return typeof e=="function"}function I(e){return e instanceof DOMException&&e.name==="StateAbortError"}function v(e){return e instanceof Error&&e.name!=="StateAbortError"}function l(e){return e===void 0}function M(e,t){t&&t.abort();let r=new AbortController,{signal:o}=r;return{promise:new Promise((i,s)=>{o.addEventListener("abort",()=>{s(new DOMException("Promise was aborted","StateAbortError"))}),e.then(i).catch(s)}),controller:r}}var W=0;function y(){return W++}function E(e,t=(r,o)=>r===o){if(!l(e.current)){if(!l(e.previous)&&t(e.current,e.previous))return!1;e.previous=e.current}return!0}function S(e,t){let r=new Set,o=[];return{clear:()=>{for(let n of o)n();r.clear()},subscribe:n=>(r.add(n),()=>{r.delete(n)}),emit:(...n)=>{for(let i of r)i(...n)},contains:n=>r.has(n),getSnapshot:e,getInitialSnapshot:t,getSize:()=>r.size,subscribeToOtherEmitter(n){let i=n.subscribe(()=>{this.emit()});o.push(i)}}}function D(e){let t=new Set,{onResolveItem:r,onFinish:o}=e,n=performance.now(),i=!1;function s(){let c=performance.now(),f=c-n,{size:m}=t;if(f<.2&&m>0&&m<10){n=c,u();return}i||(i=!0,Promise.resolve().then(()=>{i=!1,n=performance.now(),u()}))}function u(){if(t.size!==0){for(let c of t)r&&r(c),t.delete(c);if(t.size>0){s();return}o()}}function a(c){t.add(c),s()}return a}var $=Symbol("_");function V(e){let t=[];function r(){if(t.length===0)return e;let i=t.at(-1);return i===$?e:i}function o(i,s){t.push(i);let u=s();return p(u)?(async()=>{try{return await u}finally{t.pop()}})():(t.pop(),u)}function n(i){let s=r();return()=>{t.push(s);let u=i();return p(u)?(async()=>{try{return await u}finally{t.pop()}})():(t.pop(),u)}}return{run:o,use:r,wrap:n}}var F=V(void 0);function L(e){let t=[],r={},o={},n=!1,i=S(()=>n?o.current:(n=!0,c()),()=>(n=!0,c()));async function s(){E(o,b)&&(r.controller&&r.controller.abort(),o.current=c(),i.emit())}let u=y(),a={addEmitter(f){let m=f.subscribe(s);t.push(m)},id:u,sub:s},c=function(){let f=F.run(a,e);if(p(f)){let{controller:m,promise:O}=M(f,r.controller);r.controller=m,O?.then(d=>{o.current=d,i.emit()}).catch(d=>{if(!I(d))throw d});let k=O;return o.current=k,k}return o.current=f,f};return c.emitter=i,c.destroy=function(){for(let f of t)f();i.clear()},c.id=u,c.listen=function(f){return i.subscribe(()=>{let m=o.current;if(l(m))throw new Error("The value is undefined");f(m)})},c.abort=function(){r.controller&&r.controller.abort()},c}function U(e,t=b){let r={};function o(){return l(r.current)&&(r.current=g(e)?e():e),r.current}function n(u){let a=o();r.current=A(u)?u(a):u}let i=D({onFinish(){r.current=o(),E(r,t)&&s.emitter.emit()},onResolveItem:n}),s=function(){let u=o(),a=F.use();return a&&!s.emitter.contains(a.sub)&&a.addEmitter(s.emitter),u};return s.listen=function(u){return s.emitter.subscribe(()=>{let a=r.current;if(l(a))throw new Error("The value is undefined");u(a)})},s.emitter=S(()=>s()),s.id=y(),s.set=i,s}var T=require("react");var z=require("react");var x=new WeakMap;function _(e){return{call(){let t=x.get(e);if(t)return t.count++,t.returnType;let o={count:1,returnType:L(e)};return x.set(e,o),o.returnType},destroy(){let t=x.get(e);t&&(t.count--,t.count===0&&(t.returnType.destroy(),x.delete(e)))}}}function H(e,t=w){let r=_(e),o=r.call(),n=o.emitter.getInitialSnapshot??o.emitter.getSnapshot;(0,T.useEffect)(()=>r.destroy,[e]);let i=(0,z.useSyncExternalStore)(o.emitter.subscribe,()=>t(o.emitter.getSnapshot()),()=>t(n()));if((0,T.useDebugValue)(i),p(i))throw i;if(v(i))throw r.destroy(),i;return i}function j(e,t){if(e==t)return!0;if(typeof e!="object"||e==null||typeof t!="object"||t==null)return!1;if(R(e)&&R(t)){if(e.size!==t.size)return!1;for(let[n,i]of e)if(!Object.is(i,t.get(n)))return!1;return!0}if(C(e)&&C(t)){if(e.size!==t.size)return!1;for(let n of e)if(!t.has(n))return!1;return!0}if(P(e)&&P(t)){if(e.length!==t.length)return!1;for(let[n,i]of e.entries())if(!Object.is(i,t[n]))return!1;return!0}let r=Object.keys(e),o=Object.keys(t);if(r.length!==o.length)return!1;for(let n of r)if(!Object.prototype.hasOwnProperty.call(t,n)||!Object.is(e[n],t[n]))return!1;return!0}
1
+ "use strict";var C=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var Y=Object.prototype.hasOwnProperty;var W=(e,t)=>{for(var n in t)C(e,n,{get:t[n],enumerable:!0})},$=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of K(t))!Y.call(e,o)&&o!==n&&C(e,o,{get:()=>t[o],enumerable:!(s=X(t,o))||s.enumerable});return e};var J=e=>$(C({},"__esModule",{value:!0}),e);var ee={};W(ee,{EMPTY_SELECTOR:()=>F,create:()=>z,shallow:()=>q,use:()=>j});module.exports=J(ee);var F=e=>e;function p(e){return e instanceof Promise}function k(e){return typeof e=="function"}function P(e){return e instanceof Map}function g(e){return e instanceof Set}function R(e){return Array.isArray(e)}function b(e,t){return e===t?!0:!!Object.is(e,t)}function O(e){return typeof e=="function"}function v(e){return e instanceof DOMException&&e.name==="StateAbortError"}function A(e){return e instanceof Error&&e.name!=="StateAbortError"}function d(e){return e===void 0}function I(e,t){t&&t.abort();let n=new AbortController,{signal:s}=n;return{promise:new Promise((r,u)=>{s.addEventListener("abort",()=>{u(new DOMException("Promise was aborted","StateAbortError"))}),e.then(r).catch(u)}),controller:n}}var Q=0;function y(){return Q++}function S(e,t=(n,s)=>n===s){if(!d(e.current)){if(!d(e.previous)&&t(e.current,e.previous))return!1;e.previous=e.current}return!0}function h(e,t){let n=new Set,s=[];return{clear:()=>{for(let o of s)o();n.clear()},subscribe:o=>(n.add(o),()=>{n.delete(o)}),emit:(...o)=>{for(let r of n)r(...o)},contains:o=>n.has(o),getSnapshot:e,getInitialSnapshot:t,getSize:()=>n.size,subscribeToOtherEmitter(o){let r=o.subscribe(()=>{this.emit()});s.push(r)}}}var Z=Symbol("_");function D(e){let t=[];function n(){if(t.length===0)return e;let r=t.at(-1);return r===Z?e:r}function s(r,u){t.push(r);let i=u();return p(i)?(async()=>{try{return await i}finally{t.pop()}})():(t.pop(),i)}function o(r){let u=n();return()=>{t.push(u);let i=r();return p(i)?(async()=>{try{return await i}finally{t.pop()}})():(t.pop(),i)}}return{run:s,use:n,wrap:o}}function x(){let e=new Map,t=new Set,n=performance.now(),s=!1;function o(){let u=performance.now(),i=u-n,{size:c}=t;if(i<.2&&c>0&&c<10){n=u,r();return}s||(s=!0,Promise.resolve().then(()=>{s=!1,n=performance.now(),r()}))}function r(){if(t.size===0)return;let u=new Set;for(let i of t){if(e.has(i.id)){u.add(i.id);let{onResolveItem:c}=e.get(i.id);c&&c(i.value)}t.delete(i)}if(t.size>0){o();return}for(let i of u)e.get(i)?.onFinish()}return{add(u,i){return e.set(u,i),()=>{e.delete(u)}},schedule(u,i){t.add({value:i,id:u}),o()}}}var M=x(),E=D(void 0);function V(e){let t={},n=!1,s=[],o={},r=h(()=>n?t.current:(n=!0,f()),()=>(n=!0,f()));async function u(){S(t,b)&&(o.controller&&o.controller.abort(),M.schedule(i,null))}let i=y(),c=M.add(i,{onFinish(){t.current=f(),r.emit()}}),G={addEmitter(a){let l=a.subscribe(u);s.push(l)},id:i,sub:u};function H(a){let l=I(a,o.controller);return o.controller=l.controller,l.promise?.then(m=>{t.current=m,r.emit()}).catch(m=>{v(m)||(t.current=m,r.emit())}),l.promise}let f=function(){let a=E.run(G,e);if(!p(a))return t.current=a,a;let l=E.wrap(()=>H(a))();p(l)&&l.catch(()=>null);let m=l;return t.current=m,m};return f.emitter=r,f.destroy=function(){for(let a of s)a();r.clear(),c()},f.id=i,f.listen=function(a){return r.subscribe(()=>{let l=t.current;if(d(l))throw new Error("The value is undefined");a(l)})},f.abort=function(){o.controller&&o.controller.abort()},f}var L=x();function z(e,t=b){let n={};function s(){return d(n.current)&&(n.current=k(e)?e():e),n.current}function o(i){let c=s();n.current=O(i)?i(c):i}let r=function(){let i=s(),c=E.use();return c&&!r.emitter.contains(c.sub)&&c.addEmitter(r.emitter),i};r.listen=function(i){return r.emitter.subscribe(()=>{let c=n.current;if(d(c))throw new Error("The value is undefined");i(c)})},r.emitter=h(()=>r()),r.id=y();let u=L.add(r.id,{onFinish(){n.current=s(),S(n,t)&&r.emitter.emit()},onResolveItem:o});return r.set=function(i){L.schedule(r.id,i)},r.destroy=function(){n.current=void 0,s(),u(),r.emitter.clear()},r.withName=function(i){return r.stateName=i,r},r}var T=require("react");var N=require("react");var w=new WeakMap,_=0;function B(){_++}function U(e){return _=0,{call(){let t=w.get(e);if(t)return t.count++,t.returnType;B();let s={count:1,returnType:V(e)};return w.set(e,s),s.returnType},destroy(){let t=w.get(e);t&&(t.count--,t.count===0&&(t.returnType.destroy(),w.delete(e)))}}}function j(e,t=F){let n=U(e),s=n.call(),o=s.emitter.getInitialSnapshot??s.emitter.getSnapshot;(0,T.useEffect)(()=>n.destroy,[e,n.destroy]);let r=(0,N.useSyncExternalStore)(s.emitter.subscribe,()=>t(s.emitter.getSnapshot()),()=>t(o()));if((0,T.useDebugValue)(r),p(r))throw r;if(A(r))throw n.destroy(),r;return r}function q(e,t){if(e==t)return!0;if(typeof e!="object"||e==null||typeof t!="object"||t==null)return!1;if(P(e)&&P(t)){if(e.size!==t.size)return!1;for(let[o,r]of e)if(!Object.is(r,t.get(o)))return!1;return!0}if(g(e)&&g(t)){if(e.size!==t.size)return!1;for(let o of e)if(!t.has(o))return!1;return!0}if(R(e)&&R(t)){if(e.length!==t.length)return!1;for(let[o,r]of e.entries())if(!Object.is(r,t[o]))return!1;return!0}let n=Object.keys(e),s=Object.keys(t);if(n.length!==s.length)return!1;for(let o of n)if(!Object.prototype.hasOwnProperty.call(t,o)||!Object.is(e[o],t[o]))return!1;return!0}
package/esm/create.js CHANGED
@@ -1 +1 @@
1
- import{canUpdate as m,generateId as l}from"./utils/common";import{createEmitter as f}from"./utils/create-emitter";import{isEqualBase as T,isFunction as d,isSetValueFunction as p,isUndefined as o}from"./utils/is";import{createScheduler as S}from"./utils/scheduler";import{context as h}from"./subscriber";function y(i,u=T){const r={};function a(){return o(r.current)&&(r.current=d(i)?i():i),r.current}function c(n){const t=a();r.current=p(n)?n(t):n}const s=S({onFinish(){r.current=a(),m(r,u)&&e.emitter.emit()},onResolveItem:c}),e=function(){const n=a(),t=h.use();return t&&!e.emitter.contains(t.sub)&&t.addEmitter(e.emitter),n};return e.listen=function(n){return e.emitter.subscribe(()=>{const t=r.current;if(o(t))throw new Error("The value is undefined");n(t)})},e.emitter=f(()=>e()),e.id=l(),e.set=s,e}export{y as create};
1
+ import{canUpdate as l,generateId as f}from"./utils/common";import{createEmitter as d}from"./utils/create-emitter";import{isEqualBase as T,isFunction as p,isSetValueFunction as S,isUndefined as o}from"./utils/is";import{context as h}from"./subscriber";import{createGlobalScheduler as w}from"./utils/global-scheduler";const u=w();function R(a,c=T){const r={};function i(){return o(r.current)&&(r.current=p(a)?a():a),r.current}function s(t){const n=i();r.current=S(t)?t(n):t}const e=function(){const t=i(),n=h.use();return n&&!e.emitter.contains(n.sub)&&n.addEmitter(e.emitter),t};e.listen=function(t){return e.emitter.subscribe(()=>{const n=r.current;if(o(n))throw new Error("The value is undefined");t(n)})},e.emitter=d(()=>e()),e.id=f();const m=u.add(e.id,{onFinish(){r.current=i(),l(r,c)&&e.emitter.emit()},onResolveItem:s});return e.set=function(t){u.schedule(e.id,t)},e.destroy=function(){r.current=void 0,i(),m(),e.emitter.clear()},e.withName=function(t){return e.stateName=t,e},e}export{R as create,u as createScheduler};
@@ -0,0 +1 @@
1
+ import{isPromise as r}from"../utils/is";const t=window?.__REDUX_DEVTOOLS_EXTENSION__?.connect({name:"CustomState",trace:!0});t&&t.init({message:"Initial state"});function a(n){if(!t)return;const{message:o,type:e,value:s,name:i}=n;r(s)||t.send(i,{value:s,type:e,message:o},e)}function m(n,o){return e=>{a({name:n,type:o,value:e,message:"update"})}}export{m as developmentToolsListener,a as sendToDevelopmentTools};
package/esm/subscriber.js CHANGED
@@ -1 +1 @@
1
- import{cancelablePromise as p,canUpdate as T,generateId as y}from"./utils/common";import{createContext as F}from"./utils/create-context";import{createEmitter as x}from"./utils/create-emitter";import{isAbortError as h,isEqualBase as C,isPromise as R,isUndefined as E}from"./utils/is";const S=F(void 0);function k(d){const u=[],n={},r={};let s=!1;const o=x(()=>s?r.current:(s=!0,t()),()=>(s=!0,t()));async function a(){T(r,C)&&(n.controller&&n.controller.abort(),r.current=t(),o.emit())}const l=y(),f={addEmitter(e){const i=e.subscribe(a);u.push(i)},id:l,sub:a},t=function(){const e=S.run(f,d);if(R(e)){const{controller:i,promise:m}=p(e,n.controller);n.controller=i,m?.then(c=>{r.current=c,o.emit()}).catch(c=>{if(!h(c))throw c});const b=m;return r.current=b,b}return r.current=e,e};return t.emitter=o,t.destroy=function(){for(const e of u)e();o.clear()},t.id=l,t.listen=function(e){return o.subscribe(()=>{const i=r.current;if(E(i))throw new Error("The value is undefined");e(i)})},t.abort=function(){n.controller&&n.controller.abort()},t}export{S as context,k as subscriber};
1
+ import{cancelablePromise as F,canUpdate as v,generateId as h}from"./utils/common";import{createContext as x}from"./utils/create-context";import{createEmitter as S}from"./utils/create-emitter";import"./debug/development-tools";import{createGlobalScheduler as C}from"./utils/global-scheduler";import{isAbortError as E,isEqualBase as R,isPromise as m,isUndefined as w}from"./utils/is";const d=C(),b=x(void 0);function U(p){const r={};let u=!1;const a=[],i={},s=S(()=>u?r.current:(u=!0,n()),()=>(u=!0,n()));async function l(){v(r,R)&&(i.controller&&i.controller.abort(),d.schedule(c,null))}const c=h(),f=d.add(c,{onFinish(){r.current=n(),s.emit()}}),T={addEmitter(e){const t=e.subscribe(l);a.push(t)},id:c,sub:l};function y(e){const t=F(e,i.controller);return i.controller=t.controller,t.promise?.then(o=>{r.current=o,s.emit()}).catch(o=>{E(o)||(r.current=o,s.emit())}),t.promise}const n=function(){const e=b.run(T,p);if(!m(e))return r.current=e,e;const t=b.wrap(()=>y(e))();m(t)&&t.catch(()=>null);const o=t;return r.current=o,o};return n.emitter=s,n.destroy=function(){for(const e of a)e();s.clear(),f()},n.id=c,n.listen=function(e){return s.subscribe(()=>{const t=r.current;if(w(t))throw new Error("The value is undefined");e(t)})},n.abort=function(){i.controller&&i.controller.abort()},n}export{b as context,U as subscriber};
package/esm/use.js CHANGED
@@ -1 +1 @@
1
- import{useDebugValue as u,useEffect as s,useRef as m}from"react";import{EMPTY_SELECTOR as a}from"./types";import{isAnyOtherError as c,isPromise as T}from"./utils/is";import{useSyncExternalStore as d}from"react";import{subMemo as p}from"./utils/sub-memo";const f=10,E=3;function b(t){const e=m({renders:0,startTime:performance.now()});s(()=>{e.current.renders++,!(performance.now()-e.current.startTime<f)&&(e.current.renders<E||(e.current.startTime=performance.now(),e.current.renders=0,console.warn(`Function ${t.name.length>0?t.name:t} seems to be not memoized, wrap the function to the useCallback or use global defined functions.`)))},[t])}function y(t,e=a){const n=p(t),o=n.call(),i=o.emitter.getInitialSnapshot??o.emitter.getSnapshot;s(()=>n.destroy,[t]);const r=d(o.emitter.subscribe,()=>e(o.emitter.getSnapshot()),()=>e(i()));if(u(r),T(r))throw r;if(c(r))throw n.destroy(),r;return r}export{y as use};
1
+ import{useDebugValue as u,useEffect as s,useRef as m}from"react";import{EMPTY_SELECTOR as a}from"./types";import{isAnyOtherError as c,isPromise as T}from"./utils/is";import{useSyncExternalStore as d}from"react";import{subMemo as p}from"./utils/sub-memo";const f=10,E=3;function R(t){const e=m({renders:0,startTime:performance.now()});s(()=>{e.current.renders++,!(performance.now()-e.current.startTime<f)&&(e.current.renders<E||(e.current.startTime=performance.now(),e.current.renders=0,console.warn(`Function ${t.name.length>0?t.name:t} seems to be not memoized, wrap the function to the useCallback or use global defined functions.`)))},[t])}function b(t,e=a){const n=p(t),o=n.call(),i=o.emitter.getInitialSnapshot??o.emitter.getSnapshot;s(()=>n.destroy,[t,n.destroy]);const r=d(o.emitter.subscribe,()=>e(o.emitter.getSnapshot()),()=>e(i()));if(u(r),T(r))throw r;if(c(r))throw n.destroy(),r;return r}export{b as use};
@@ -0,0 +1 @@
1
+ import{RESCHEDULE_COUNT as l,THRESHOLD as f,THRESHOLD_ITEMS as d}from"./scheduler";function m(){const t=new Map,r=new Set;let s=performance.now(),u=!1;function c(){const n=performance.now(),e=n-s,{size:o}=r;if(e<f&&o>0&&o<d){s=n,i();return}u||(u=!0,Promise.resolve().then(()=>{u=!1,s=performance.now(),i()}))}function i(){if(r.size===0)return;const n=new Set;for(const e of r){if(t.has(e.id)){n.add(e.id);const{onResolveItem:o}=t.get(e.id);o&&o(e.value)}r.delete(e)}if(r.size>l){c();return}for(const e of n)t.get(e)?.onFinish()}return{add(n,e){return t.set(n,e),()=>{t.delete(n)}},schedule(n,e){r.add({value:e,id:n}),c()}}}export{m as createGlobalScheduler};
package/esm/utils/is.js CHANGED
@@ -1 +1 @@
1
- import{Abort as e}from"./common";function o(n){return n instanceof Promise}function i(n){return typeof n=="function"}function u(n){return n instanceof Map}function s(n){return n instanceof Set}function a(n){return Array.isArray(n)}function f(n,r){return n===r?!0:!!Object.is(n,r)}function c(n){return typeof n=="function"}function p(n){return n instanceof DOMException&&n.name===e.Error}function k(n){return n instanceof Error&&n.name!==e.Error}function w(n){return n===void 0}export{p as isAbortError,k as isAnyOtherError,a as isArray,f as isEqualBase,i as isFunction,u as isMap,o as isPromise,s as isSet,c as isSetValueFunction,w as isUndefined};
1
+ import{Abort as t}from"./common";function i(n){return n instanceof Promise}function r(n){return typeof n=="function"}function u(n){return n instanceof Map}function s(n){return n instanceof Set}function a(n){return Array.isArray(n)}function f(n,e){return n===e?!0:!!Object.is(n,e)}function c(n){return typeof n=="function"}function p(n){return n instanceof DOMException&&n.name===t.Error}function k(n){return n instanceof Error&&n.name!==t.Error}function w(n){return n===void 0}function x(n){return r(n)&&n.set!==void 0}export{p as isAbortError,k as isAnyOtherError,a as isArray,x as isCreate,f as isEqualBase,r as isFunction,u as isMap,i as isPromise,s as isSet,c as isSetValueFunction,w as isUndefined};
@@ -1 +1 @@
1
- const T=.2,d=10,E=0;function S(f){const n=new Set,{onResolveItem:r,onFinish:a}=f;let o=performance.now(),t=!1;function s(){const e=performance.now(),u=e-o,{size:c}=n;if(u<.2&&c>0&&c<10){o=e,i();return}t||(t=!0,Promise.resolve().then(()=>{t=!1,o=performance.now(),i()}))}function i(){if(n.size!==0){for(const e of n)r&&r(e),n.delete(e);if(n.size>0){s();return}a()}}function l(e){n.add(e),s()}return l}export{S as createScheduler};
1
+ const d=.2,T=10,S=0;function E(f){const n=new Set,{onResolveItem:r,onFinish:a}=f;let o=performance.now(),t=!1;function s(){const e=performance.now(),u=e-o,{size:c}=n;if(u<.2&&c>0&&c<10){o=e,i();return}t||(t=!0,Promise.resolve().then(()=>{t=!1,o=performance.now(),i()}))}function i(){if(n.size!==0){for(const e of n)r&&r(e),n.delete(e);if(n.size>0){s();return}a()}}function l(e){n.add(e),s()}return l}export{S as RESCHEDULE_COUNT,d as THRESHOLD,T as THRESHOLD_ITEMS,E as createScheduler};
@@ -1 +1 @@
1
- import{subscriber as c}from"../subscriber";const r=new WeakMap;function s(t){return{call(){const e=r.get(t);if(e)return e.count++,e.returnType;const n={count:1,returnType:c(t)};return r.set(t,n),n.returnType},destroy(){const e=r.get(t);e&&(e.count--,e.count===0&&(e.returnType.destroy(),r.delete(t)))}}}export{s as subMemo};
1
+ import{subscriber as c}from"../subscriber";const n=new WeakMap;let r=0;function p(){return r}function o(){r++}function b(t){return r=0,{call(){const e=n.get(t);if(e)return e.count++,e.returnType;o();const u={count:1,returnType:c(t)};return n.set(t,u),u.returnType},destroy(){const e=n.get(t);e&&(e.count--,e.count===0&&(e.returnType.destroy(),n.delete(t)))}}}export{p as getDebugCacheCreation,b as subMemo};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muya",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.0-beta.2",
4
4
  "author": "samuel.gjabel@gmail.com",
5
5
  "description": "👀 Another React state management library",
6
6
  "license": "MIT",
@@ -1,5 +1,10 @@
1
+ /**
2
+ * This is not optimal, so for now just ignore. Its just for view and compare if the state is at least similar to others
3
+ * but this tests are not consider as a real benchmark
4
+ */
1
5
  /* eslint-disable unicorn/consistent-function-scoping */
2
6
  /* eslint-disable no-console */
7
+
3
8
  import { act, renderHook } from '@testing-library/react-hooks'
4
9
  import { useStore, create as zustand } from 'zustand'
5
10
  import { useEffect, useState } from 'react'
@@ -33,7 +38,7 @@ describe('benchmarks comparison measure', () => {
33
38
  beforeEach(() => {
34
39
  jest.clearAllMocks()
35
40
  })
36
- const counts = [1000, 10_000]
41
+ const counts = [10_000]
37
42
  for (const count of counts) {
38
43
  describe(`Count ${count}`, () => {
39
44
  it(`should benchmark ${count} muya first run - idk slow`, async () => {
@@ -0,0 +1,89 @@
1
+ import { waitFor } from '@testing-library/react'
2
+ import { create } from '../create'
3
+ import { subscriber } from '../subscriber'
4
+ import { longPromise } from './test-utils'
5
+
6
+ describe('subscriber', () => {
7
+ it('should test subscriber and cleaning the emitters', () => {
8
+ const state1 = create(1)
9
+ const sub = subscriber(state1)
10
+ // at this point, the emitter is not subscribed yet, as it need to be called first.
11
+ expect(state1.emitter.getSize()).toBe(0)
12
+
13
+ // check if the value is correct
14
+ expect(sub()).toBe(1)
15
+
16
+ // now we can check if the value is subscribed
17
+ expect(state1.emitter.getSize()).toBe(1)
18
+ // we destroy the subscriber, meaning that the emitter should be cleaned
19
+
20
+ sub.destroy()
21
+
22
+ expect(state1.emitter.getSize()).toBe(0)
23
+
24
+ // and test re-aligning the subscriber
25
+ expect(sub()).toBe(1)
26
+ expect(state1.emitter.getSize()).toBe(1)
27
+ })
28
+ it('should test how many events are emitter via singleton state', async () => {
29
+ const state1 = create(1)
30
+ const sub = subscriber(state1)
31
+
32
+ let updateCount = 0
33
+
34
+ sub.listen(() => {
35
+ updateCount++
36
+ })
37
+ sub()
38
+ await waitFor(() => {})
39
+ // we do not received initial value as it is not changed
40
+ expect(updateCount).toBe(0)
41
+
42
+ state1.set(2)
43
+ await waitFor(() => {})
44
+ expect(updateCount).toBe(1)
45
+ })
46
+ it('should test how many events are emitter via singleton async state', async () => {
47
+ const state1 = create(longPromise())
48
+ const sub = subscriber(state1)
49
+
50
+ let updateCount = 0
51
+
52
+ sub.listen(() => {
53
+ updateCount++
54
+ })
55
+ sub()
56
+ await waitFor(() => {})
57
+ // we do not received initial value as it is not changed
58
+ expect(updateCount).toBe(0)
59
+
60
+ state1.set(2)
61
+ await waitFor(() => {})
62
+ expect(updateCount).toBe(1)
63
+ })
64
+
65
+ it('should test how many events are emitter via derived state', async () => {
66
+ const state1 = create(longPromise())
67
+
68
+ async function derived() {
69
+ // await longPromise()
70
+ return await state1()
71
+ }
72
+ const sub = subscriber(derived)
73
+
74
+ let updateCount = 0
75
+
76
+ sub.listen(() => {
77
+ updateCount++
78
+ })
79
+ await sub()
80
+ await waitFor(() => {})
81
+ expect(await sub()).toBe(0)
82
+ // // we do not received initial value as it is not changed
83
+ expect(updateCount).toBe(2)
84
+
85
+ // state1.set(2)
86
+ // await waitFor(() => {})
87
+ // expect(updateCount).toBe(1)
88
+ })
89
+ })
@@ -3,7 +3,7 @@ import { renderHook } from '@testing-library/react-hooks'
3
3
  import { create } from '../create'
4
4
  import { use } from '../use'
5
5
  import { Suspense } from 'react'
6
- import { longPromise } from './test-utils'
6
+ import { waitFor } from '@testing-library/react'
7
7
 
8
8
  describe('use-create', () => {
9
9
  const reRendersBefore = jest.fn()
@@ -26,7 +26,7 @@ describe('use-create', () => {
26
26
  return <div>Loading...</div>
27
27
  }
28
28
 
29
- const { result, waitFor } = renderHook(
29
+ const { result } = renderHook(
30
30
  () => {
31
31
  reRendersBefore()
32
32
  const data = use(getDataWithUser)
@@ -36,8 +36,9 @@ describe('use-create', () => {
36
36
  { wrapper: ({ children }) => <Suspense fallback={<Loading />}>{children}</Suspense> },
37
37
  )
38
38
 
39
- await longPromise(1000)
40
- await waitFor(() => {})
39
+ await waitFor(() => {
40
+ expect(result.current).toEqual({ userId: 1, id: 1, title: 'delectus aut autem', completed: false, age: 30 })
41
+ })
41
42
  expect(suspenseFunction).toHaveBeenCalledTimes(1)
42
43
  expect(result.current).toEqual({ userId: 1, id: 1, title: 'delectus aut autem', completed: false, age: 30 })
43
44
  })
@@ -1,10 +1,9 @@
1
- /* eslint-disable @typescript-eslint/no-shadow */
2
- /* eslint-disable no-shadow */
3
1
  import { act, renderHook } from '@testing-library/react-hooks'
4
2
  import { create } from '../create'
5
3
  import { use } from '../use'
6
- import { subscriber } from '../subscriber'
7
4
  import { waitFor } from '@testing-library/react'
5
+ import { useCallback } from 'react'
6
+ import { getDebugCacheCreation } from '../utils/sub-memo'
8
7
 
9
8
  describe('use-create', () => {
10
9
  const reRendersBefore = jest.fn()
@@ -13,23 +12,10 @@ describe('use-create', () => {
13
12
  jest.clearAllMocks()
14
13
  })
15
14
 
16
- it('should test sub hook', async () => {
17
- const state = create(1)
18
-
19
- const sub = subscriber(state)
20
- expect(sub()).toBe(1)
21
-
22
- act(() => {
23
- state.set(2)
24
- })
25
-
26
- await waitFor(() => {})
27
- expect(sub()).toBe(2)
28
- })
29
15
  it('should test use hook', async () => {
30
16
  const state = create(1)
31
17
 
32
- const { result, waitFor } = renderHook(() => {
18
+ const { result } = renderHook(() => {
33
19
  reRendersBefore()
34
20
  return use(state)
35
21
  })
@@ -47,7 +33,7 @@ describe('use-create', () => {
47
33
  expect(reRendersBefore).toHaveBeenCalledTimes(3)
48
34
  })
49
35
 
50
- it('should test derived state', async () => {
36
+ it('should test derived state with multiple states', async () => {
51
37
  const state1 = create(1)
52
38
  const state2 = create(2)
53
39
 
@@ -59,11 +45,13 @@ describe('use-create', () => {
59
45
  return state1() + state2() + derivedBefore(10)
60
46
  }
61
47
 
62
- const { result, waitFor } = renderHook(() => {
48
+ const { result } = renderHook(() => {
63
49
  reRendersBefore()
64
50
  return use(derived)
65
51
  })
66
52
 
53
+ await waitFor(() => {})
54
+ expect(reRendersBefore).toHaveBeenCalledTimes(1)
67
55
  act(() => {
68
56
  state1.set(2)
69
57
  state2.set(3)
@@ -73,4 +61,65 @@ describe('use-create', () => {
73
61
  expect(result.current).toBe(20)
74
62
  expect(reRendersBefore).toHaveBeenCalledTimes(2)
75
63
  })
64
+
65
+ it('should test use hook without memoize fn', async () => {
66
+ const state1 = create(1)
67
+ const state2 = create(2)
68
+
69
+ function derivedBefore(plusValue: number) {
70
+ return state1() + state2() + plusValue
71
+ }
72
+
73
+ function derived(add: number) {
74
+ return state1() + state2() + derivedBefore(add)
75
+ }
76
+
77
+ const { result } = renderHook(() => {
78
+ reRendersBefore()
79
+ return use(() => derived(10))
80
+ })
81
+ expect(getDebugCacheCreation()).toBe(1)
82
+
83
+ await waitFor(() => {})
84
+ expect(reRendersBefore).toHaveBeenCalledTimes(1)
85
+ act(() => {
86
+ state1.set(2)
87
+ state2.set(3)
88
+ })
89
+ expect(getDebugCacheCreation()).toBe(1)
90
+ await waitFor(() => {})
91
+ expect(result.current).toBe(20)
92
+ expect(reRendersBefore).toHaveBeenCalledTimes(2)
93
+ })
94
+
95
+ it('should test use hook with memoize fn', async () => {
96
+ const state1 = create(1)
97
+ const state2 = create(2)
98
+
99
+ function derivedBefore(plusValue: number) {
100
+ return state1() + state2() + plusValue
101
+ }
102
+
103
+ function derived(add: number) {
104
+ return state1() + state2() + derivedBefore(add)
105
+ }
106
+
107
+ const { result } = renderHook(() => {
108
+ reRendersBefore()
109
+ const memoized = useCallback(() => derived(10), [])
110
+ return use(memoized)
111
+ })
112
+ expect(getDebugCacheCreation()).toBe(1)
113
+
114
+ await waitFor(() => {})
115
+ expect(reRendersBefore).toHaveBeenCalledTimes(1)
116
+ act(() => {
117
+ state1.set(2)
118
+ state2.set(3)
119
+ })
120
+ expect(getDebugCacheCreation()).toBe(1)
121
+ await waitFor(() => {})
122
+ expect(result.current).toBe(20)
123
+ expect(reRendersBefore).toHaveBeenCalledTimes(2)
124
+ })
76
125
  })
@@ -2,16 +2,21 @@ import { canUpdate, generateId } from './utils/common'
2
2
  import type { Emitter } from './utils/create-emitter'
3
3
  import { createEmitter } from './utils/create-emitter'
4
4
  import { isEqualBase, isFunction, isSetValueFunction, isUndefined } from './utils/is'
5
- import { createScheduler } from './utils/scheduler'
5
+ // import { createScheduler } from './utils/scheduler'
6
6
  import type { Cache, Callable, DefaultValue, IsEqual, Listener, SetValue } from './types'
7
7
  import { context } from './subscriber'
8
+ import { createGlobalScheduler } from './utils/global-scheduler'
8
9
 
10
+ export const createScheduler = createGlobalScheduler()
9
11
  interface RawState<T> {
10
12
  (): T
11
13
  id: number
12
14
  set: (value: SetValue<T>) => void
13
15
  emitter: Emitter<T>
14
16
  listen: Listener<T>
17
+ destroy: () => void
18
+ withName: (name: string) => RawState<T>
19
+ stateName?: string
15
20
  }
16
21
 
17
22
  export type State<T> = {
@@ -32,20 +37,21 @@ export function create<T>(initialValue: DefaultValue<T>, isEqual: IsEqual<T> = i
32
37
  cache.current = isSetValueFunction(value) ? value(previous) : value
33
38
  }
34
39
 
35
- const schedule = createScheduler<SetValue<T>>({
36
- onFinish() {
37
- cache.current = getValue()
38
- if (!canUpdate(cache, isEqual)) {
39
- return
40
- }
41
- state.emitter.emit()
42
- },
43
- onResolveItem: resolveValue,
44
- })
40
+ // const schedule = createScheduler<SetValue<T>>({
41
+ // onFinish() {
42
+ // cache.current = getValue()
43
+ // if (!canUpdate(cache, isEqual)) {
44
+ // return
45
+ // }
46
+ // state.emitter.emit()
47
+ // },
48
+ // onResolveItem: resolveValue,
49
+ // })
45
50
 
46
51
  const state: RawState<T> = function () {
47
52
  const stateValue = getValue()
48
53
  const ctx = context.use()
54
+ // console.log('CTX', ctx?.id, 'STATE', state.id)
49
55
  if (ctx && !state.emitter.contains(ctx.sub)) {
50
56
  ctx.addEmitter(state.emitter)
51
57
  }
@@ -62,6 +68,31 @@ export function create<T>(initialValue: DefaultValue<T>, isEqual: IsEqual<T> = i
62
68
  }
63
69
  state.emitter = createEmitter<T>(() => state())
64
70
  state.id = generateId()
65
- state.set = schedule
71
+
72
+ const clearScheduler = createScheduler.add(state.id, {
73
+ onFinish() {
74
+ cache.current = getValue()
75
+ if (!canUpdate(cache, isEqual)) {
76
+ return
77
+ }
78
+ state.emitter.emit()
79
+ },
80
+ onResolveItem: resolveValue,
81
+ })
82
+ state.set = function (value) {
83
+ createScheduler.schedule(state.id, value)
84
+ }
85
+
86
+ state.destroy = function () {
87
+ cache.current = undefined
88
+ getValue()
89
+ clearScheduler()
90
+ state.emitter.clear()
91
+ }
92
+ state.withName = function (name: string) {
93
+ state.stateName = name
94
+ return state
95
+ }
96
+
66
97
  return state
67
98
  }
@@ -0,0 +1,37 @@
1
+ import { isPromise } from '../utils/is'
2
+
3
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
4
+ // @ts-expect-error
5
+ const reduxDevelopmentTools = window?.__REDUX_DEVTOOLS_EXTENSION__?.connect({
6
+ name: 'CustomState', // This will name your instance in the DevTools
7
+ trace: true, // Enables trace if needed
8
+ })
9
+
10
+ if (reduxDevelopmentTools) {
11
+ reduxDevelopmentTools.init({ message: 'Initial state' })
12
+ }
13
+
14
+ export type StateType = 'state' | 'derived'
15
+
16
+ interface SendOptions {
17
+ message?: string
18
+ type: StateType
19
+ value: unknown
20
+ name: string
21
+ }
22
+ export function sendToDevelopmentTools(options: SendOptions) {
23
+ if (!reduxDevelopmentTools) {
24
+ return
25
+ }
26
+ const { message, type, value, name } = options
27
+ if (isPromise(value)) {
28
+ return
29
+ }
30
+ reduxDevelopmentTools.send(name, { value, type, message }, type)
31
+ }
32
+
33
+ export function developmentToolsListener(name: string, type: StateType) {
34
+ return (value: unknown) => {
35
+ sendToDevelopmentTools({ name, type, value, message: 'update' })
36
+ }
37
+ }
@@ -4,8 +4,12 @@ import { cancelablePromise, canUpdate, generateId } from './utils/common'
4
4
  import { createContext } from './utils/create-context'
5
5
  import type { Emitter } from './utils/create-emitter'
6
6
  import { createEmitter } from './utils/create-emitter'
7
- import { isAbortError, isEqualBase, isPromise, isUndefined } from './utils/is'
7
+ import type { StateType } from './debug/development-tools'
8
+ import { developmentToolsListener, sendToDevelopmentTools } from './debug/development-tools'
9
+ import { createGlobalScheduler } from './utils/global-scheduler'
10
+ import { isAbortError, isCreate, isEqualBase, isPromise, isUndefined } from './utils/is'
8
11
 
12
+ const subscriberScheduler = createGlobalScheduler()
9
13
  interface SubscribeContext<T = unknown> {
10
14
  addEmitter: (emitter: Emitter<T>) => void
11
15
  id: number
@@ -29,11 +33,11 @@ export const context = createContext<SubscribeContext | undefined>(undefined)
29
33
  export function subscriber<F extends AnyFunction, T extends ReturnType<F> = ReturnType<F>>(
30
34
  anyFunction: () => T,
31
35
  ): Subscribe<F, T> {
36
+ const cache: Cache<T> = {}
37
+ let isInitialized = false
32
38
  const cleaners: Array<() => void> = []
33
39
  const promiseData: CancelablePromise<T> = {}
34
40
 
35
- const cache: Cache<T> = {}
36
- let isInitialized = false
37
41
  const emitter = createEmitter(
38
42
  () => {
39
43
  if (!isInitialized) {
@@ -56,10 +60,17 @@ export function subscriber<F extends AnyFunction, T extends ReturnType<F> = Retu
56
60
  promiseData.controller.abort()
57
61
  }
58
62
 
59
- cache.current = subscribe()
60
- emitter.emit()
63
+ subscriberScheduler.schedule(id, null)
61
64
  }
65
+
62
66
  const id = generateId()
67
+
68
+ const clearScheduler = subscriberScheduler.add(id, {
69
+ onFinish() {
70
+ cache.current = subscribe()
71
+ emitter.emit()
72
+ },
73
+ })
63
74
  const ctx: SubscribeContext = {
64
75
  addEmitter(stateEmitter) {
65
76
  const clean = stateEmitter.subscribe(sub)
@@ -69,30 +80,40 @@ export function subscriber<F extends AnyFunction, T extends ReturnType<F> = Retu
69
80
  sub,
70
81
  }
71
82
 
83
+ function asyncSub(resultValue: Promise<T>): Promise<T> | undefined {
84
+ const cancel = cancelablePromise<T>(resultValue, promiseData.controller)
85
+ promiseData.controller = cancel.controller
86
+ cancel.promise
87
+ ?.then((value) => {
88
+ cache.current = value
89
+ emitter.emit()
90
+ })
91
+ .catch((error) => {
92
+ if (isAbortError(error)) {
93
+ return
94
+ }
95
+ cache.current = error
96
+ emitter.emit()
97
+ })
98
+ return cancel.promise
99
+ }
100
+
72
101
  const subscribe = function (): T {
73
102
  const resultValue = context.run(ctx, anyFunction)
74
103
 
75
- if (isPromise(resultValue)) {
76
- const { controller, promise: promiseWithSelector } = cancelablePromise<T>(resultValue, promiseData.controller)
77
- promiseData.controller = controller
78
- promiseWithSelector
79
- ?.then((value) => {
80
- cache.current = value
81
- emitter.emit()
82
- })
83
- .catch((error) => {
84
- if (isAbortError(error)) {
85
- return
86
- }
87
-
88
- throw error
89
- })
90
- const promiseResult = promiseWithSelector as T
91
- cache.current = promiseResult
92
- return promiseResult
104
+ if (!isPromise(resultValue)) {
105
+ cache.current = resultValue
106
+ return resultValue
93
107
  }
94
- cache.current = resultValue
95
- return resultValue
108
+
109
+ const promise = context.wrap(() => asyncSub(resultValue))()
110
+ if (isPromise(promise)) {
111
+ // we do not do anything with the promise, because it is already handled in asyncSub
112
+ promise.catch(() => null)
113
+ }
114
+ const promiseAsT = promise as T
115
+ cache.current = promiseAsT
116
+ return promiseAsT
96
117
  }
97
118
 
98
119
  subscribe.emitter = emitter
@@ -101,6 +122,7 @@ export function subscriber<F extends AnyFunction, T extends ReturnType<F> = Retu
101
122
  cleaner()
102
123
  }
103
124
  emitter.clear()
125
+ clearScheduler()
104
126
  }
105
127
  subscribe.id = id
106
128
  subscribe.listen = function (listener: (value: T) => void) {
@@ -112,6 +134,28 @@ export function subscriber<F extends AnyFunction, T extends ReturnType<F> = Retu
112
134
  listener(final)
113
135
  })
114
136
  }
137
+
138
+ if (process.env.NODE_ENV === 'development') {
139
+ let name: string | undefined
140
+ let type: StateType = 'derived'
141
+ if (isCreate(anyFunction)) {
142
+ type = 'state'
143
+ name = anyFunction.stateName
144
+ }
145
+
146
+ if (!name) {
147
+ name = anyFunction.name.length > 0 ? anyFunction.name : anyFunction.toString()
148
+ }
149
+
150
+ sendToDevelopmentTools({
151
+ name,
152
+ type,
153
+ value: subscribe(),
154
+ message: 'init',
155
+ })
156
+ const listener = developmentToolsListener(name, type)
157
+ subscribe.listen(listener)
158
+ }
115
159
  subscribe.abort = function () {
116
160
  if (promiseData.controller) {
117
161
  promiseData.controller.abort()
@@ -37,10 +37,8 @@ export function use<F extends AnyFunction, T extends ReturnType<F>, S extends Re
37
37
  const sub = memo.call()
38
38
  const initialSnapshot = sub.emitter.getInitialSnapshot ?? sub.emitter.getSnapshot
39
39
  useEffect(() => {
40
- // memo.call()
41
40
  return memo.destroy
42
- // eslint-disable-next-line react-hooks/exhaustive-deps
43
- }, [anyFunction])
41
+ }, [anyFunction, memo.destroy])
44
42
 
45
43
  const value = useSyncExternalStore<S>(
46
44
  sub.emitter.subscribe,
@@ -20,12 +20,14 @@ export interface Emitter<T, P = undefined> {
20
20
  */
21
21
  export function createEmitter<T, P = undefined>(getSnapshot: () => T, getInitialSnapshot?: () => T): Emitter<T, P> {
22
22
  const listeners = new Set<(...params: P[]) => void>()
23
+ // const listeners = new WeakSet<(...params: P[]) => void>()
23
24
  const otherCleaners: Array<() => void> = []
24
25
  return {
25
26
  clear: () => {
26
27
  for (const cleaner of otherCleaners) {
27
28
  cleaner()
28
29
  }
30
+
29
31
  listeners.clear()
30
32
  },
31
33
  subscribe: (listener) => {
@@ -0,0 +1,75 @@
1
+ import type { SchedulerOptions } from './scheduler'
2
+ import { RESCHEDULE_COUNT, THRESHOLD, THRESHOLD_ITEMS } from './scheduler'
3
+
4
+ interface GlobalSchedulerItem<T> {
5
+ value: T
6
+ id: number
7
+ }
8
+
9
+ export function createGlobalScheduler() {
10
+ const listeners = new Map<number, SchedulerOptions<unknown>>()
11
+ const batches = new Set<GlobalSchedulerItem<unknown>>()
12
+
13
+ let frame = performance.now()
14
+ let scheduled = false
15
+
16
+ function schedule() {
17
+ const startFrame = performance.now()
18
+ const frameSizeDiffIn = startFrame - frame
19
+ const { size } = batches
20
+ if (frameSizeDiffIn < THRESHOLD && size > 0 && size < THRESHOLD_ITEMS) {
21
+ frame = startFrame
22
+ flush()
23
+ return
24
+ }
25
+
26
+ if (!scheduled) {
27
+ scheduled = true
28
+ Promise.resolve().then(() => {
29
+ scheduled = false
30
+ frame = performance.now()
31
+ flush()
32
+ })
33
+ }
34
+ }
35
+
36
+ function flush() {
37
+ if (batches.size === 0) {
38
+ return
39
+ }
40
+
41
+ const effectedListeners = new Set<number>()
42
+ for (const value of batches) {
43
+ if (listeners.has(value.id)) {
44
+ effectedListeners.add(value.id)
45
+ const { onResolveItem } = listeners.get(value.id)!
46
+ if (onResolveItem) {
47
+ onResolveItem(value.value)
48
+ }
49
+ }
50
+ batches.delete(value)
51
+ }
52
+
53
+ if (batches.size > RESCHEDULE_COUNT) {
54
+ schedule()
55
+ return
56
+ }
57
+
58
+ for (const id of effectedListeners) {
59
+ listeners.get(id)?.onFinish()
60
+ }
61
+ }
62
+
63
+ return {
64
+ add<T>(id: number, option: SchedulerOptions<T>) {
65
+ listeners.set(id, option as SchedulerOptions<unknown>)
66
+ return () => {
67
+ listeners.delete(id)
68
+ }
69
+ },
70
+ schedule<T>(id: number, value: T) {
71
+ batches.add({ value, id })
72
+ schedule()
73
+ },
74
+ }
75
+ }
@@ -1,5 +1,6 @@
1
1
  import { Abort } from './common'
2
2
  import type { SetStateCb, SetValue } from '../types'
3
+ import type { State } from '../create'
3
4
 
4
5
  export function isPromise<T>(value: unknown): value is Promise<T> {
5
6
  return value instanceof Promise
@@ -41,3 +42,9 @@ export function isAnyOtherError(value: unknown): value is Error {
41
42
  export function isUndefined(value: unknown): value is undefined {
42
43
  return value === undefined
43
44
  }
45
+
46
+ export function isCreate(value: unknown): value is State<unknown> {
47
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
48
+ // @ts-expect-error
49
+ return isFunction(value) && value.set !== undefined
50
+ }
@@ -1,13 +1,13 @@
1
- const THRESHOLD = 0.2
2
- const THRESHOLD_ITEMS = 10
3
- const RESCHEDULE_COUNT = 0
1
+ export const THRESHOLD = 0.2
2
+ export const THRESHOLD_ITEMS = 10
3
+ export const RESCHEDULE_COUNT = 0
4
4
 
5
- interface Options<T> {
5
+ export interface SchedulerOptions<T> {
6
6
  readonly onResolveItem?: (item: T) => void
7
7
  readonly onFinish: () => void
8
8
  }
9
9
 
10
- export function createScheduler<T>(options: Options<T>) {
10
+ export function createScheduler<T>(options: SchedulerOptions<T>) {
11
11
  const batches = new Set<T>()
12
12
  const { onResolveItem, onFinish } = options
13
13
  let frame = performance.now()
@@ -8,15 +8,27 @@ interface CacheItem<T extends AnyFunction> {
8
8
  }
9
9
 
10
10
  const cache = new WeakMap<AnyFunction, CacheItem<AnyFunction>>()
11
+ let cacheCount = 0
12
+
13
+ export function getDebugCacheCreation() {
14
+ return cacheCount
15
+ }
16
+ function incrementDebugFunctionCreationCount() {
17
+ cacheCount++
18
+ }
11
19
 
12
20
  export function subMemo<F extends AnyFunction>(anyFunction: F) {
21
+ cacheCount = 0
13
22
  return {
14
23
  call(): Subscribe<F> {
15
24
  const item = cache.get(anyFunction)
16
25
  if (item) {
17
26
  item.count++
27
+
18
28
  return item.returnType
19
29
  }
30
+
31
+ incrementDebugFunctionCreationCount()
20
32
  const returnType = subscriber(anyFunction)
21
33
  const newItem = { count: 1, returnType }
22
34
  cache.set(anyFunction, newItem)
package/types/create.d.ts CHANGED
@@ -1,11 +1,18 @@
1
1
  import type { Emitter } from './utils/create-emitter';
2
2
  import type { Callable, DefaultValue, IsEqual, Listener, SetValue } from './types';
3
+ export declare const createScheduler: {
4
+ add<T>(id: number, option: import("./utils/scheduler").SchedulerOptions<T>): () => void;
5
+ schedule<T>(id: number, value: T): void;
6
+ };
3
7
  interface RawState<T> {
4
8
  (): T;
5
9
  id: number;
6
10
  set: (value: SetValue<T>) => void;
7
11
  emitter: Emitter<T>;
8
12
  listen: Listener<T>;
13
+ destroy: () => void;
14
+ withName: (name: string) => RawState<T>;
15
+ stateName?: string;
9
16
  }
10
17
  export type State<T> = {
11
18
  readonly [K in keyof RawState<T>]: RawState<T>[K];
@@ -0,0 +1,10 @@
1
+ export type StateType = 'state' | 'derived';
2
+ interface SendOptions {
3
+ message?: string;
4
+ type: StateType;
5
+ value: unknown;
6
+ name: string;
7
+ }
8
+ export declare function sendToDevelopmentTools(options: SendOptions): void;
9
+ export declare function developmentToolsListener(name: string, type: StateType): (value: unknown) => void;
10
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { SchedulerOptions } from './scheduler';
2
+ export declare function createGlobalScheduler(): {
3
+ add<T>(id: number, option: SchedulerOptions<T>): () => void;
4
+ schedule<T>(id: number, value: T): void;
5
+ };
@@ -1,4 +1,5 @@
1
1
  import type { SetStateCb, SetValue } from '../types';
2
+ import type { State } from '../create';
2
3
  export declare function isPromise<T>(value: unknown): value is Promise<T>;
3
4
  export declare function isFunction<T extends (...args: unknown[]) => unknown>(value: unknown): value is T;
4
5
  export declare function isMap(value: unknown): value is Map<unknown, unknown>;
@@ -9,3 +10,4 @@ export declare function isSetValueFunction<T>(value: SetValue<T>): value is SetS
9
10
  export declare function isAbortError(value: unknown): value is DOMException;
10
11
  export declare function isAnyOtherError(value: unknown): value is Error;
11
12
  export declare function isUndefined(value: unknown): value is undefined;
13
+ export declare function isCreate(value: unknown): value is State<unknown>;
@@ -1,6 +1,8 @@
1
- interface Options<T> {
1
+ export declare const THRESHOLD = 0.2;
2
+ export declare const THRESHOLD_ITEMS = 10;
3
+ export declare const RESCHEDULE_COUNT = 0;
4
+ export interface SchedulerOptions<T> {
2
5
  readonly onResolveItem?: (item: T) => void;
3
6
  readonly onFinish: () => void;
4
7
  }
5
- export declare function createScheduler<T>(options: Options<T>): (value: T) => void;
6
- export {};
8
+ export declare function createScheduler<T>(options: SchedulerOptions<T>): (value: T) => void;
@@ -1,5 +1,6 @@
1
1
  import type { Subscribe } from '../subscriber';
2
2
  import type { AnyFunction } from '../types';
3
+ export declare function getDebugCacheCreation(): number;
3
4
  export declare function subMemo<F extends AnyFunction>(anyFunction: F): {
4
5
  call(): Subscribe<F>;
5
6
  destroy(): void;