@crup/react-timer-hook 0.0.1-alpha.7 → 0.0.1-alpha.9

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
@@ -1,6 +1,6 @@
1
1
  # @crup/react-timer-hook
2
2
 
3
- > Reliable React timer hooks for countdowns, stopwatches, clocks, polling schedules, and many independent timer lifecycles.
3
+ > React timer hooks for countdowns, stopwatches, clocks, polling schedules, and many independent timer lifecycles.
4
4
 
5
5
  [![npm alpha](https://img.shields.io/npm/v/%40crup%2Freact-timer-hook/alpha?label=npm%20alpha&color=00b894)](https://www.npmjs.com/package/@crup/react-timer-hook?activeTab=versions)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/%40crup%2Freact-timer-hook?color=0f766e)](https://www.npmjs.com/package/@crup/react-timer-hook)
@@ -139,8 +139,8 @@ Current build:
139
139
 
140
140
  | File | Raw | Gzip | Brotli |
141
141
  | --- | ---: | ---: | ---: |
142
- | `dist/index.js` | 11.82 kB | 3.55 kB | 3.20 kB |
143
- | `dist/index.cjs` | 12.94 kB | 3.79 kB | 3.42 kB |
142
+ | `dist/index.js` | 12.45 kB | 3.75 kB | 3.36 kB |
143
+ | `dist/index.cjs` | 13.69 kB | 4.01 kB | 3.60 kB |
144
144
  | `dist/index.d.ts` | 3.95 kB | 992 B | 888 B |
145
145
 
146
146
  CI writes a size summary to the GitHub Actions UI and posts bundle-size reports on pull requests.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var ne=Object.defineProperty;var ie=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var ce=Object.prototype.hasOwnProperty;var de=(e,o)=>{for(var a in o)ne(e,a,{get:o[a],enumerable:!0})},me=(e,o,a,u)=>{if(o&&typeof o=="object"||typeof o=="function")for(let I of le(o))!ce.call(e,I)&&I!==a&&ne(e,I,{get:()=>o[I],enumerable:!(u=ie(o,I))||u.enumerable});return e};var fe=e=>me(ne({},"__esModule",{value:!0}),e);var Te={};de(Te,{durationParts:()=>ue,useTimer:()=>se,useTimerGroup:()=>ae});module.exports=fe(Te);function ue(e){let o=Math.max(0,Math.trunc(Number.isFinite(e)?e:0)),a=Math.floor(o/864e5),u=o%864e5,I=Math.floor(u/36e5),y=u%36e5,T=Math.floor(y/6e4),w=y%6e4,M=Math.floor(w/1e3);return{totalMilliseconds:o,totalSeconds:Math.floor(o/1e3),milliseconds:w%1e3,seconds:M,minutes:T,hours:I,days:a}}var m=require("react");function pe(e){return e?e===!0?{enabled:!0,includeTicks:!1,logger:console.debug}:typeof e=="function"?{enabled:!0,includeTicks:!1,logger:e}:{enabled:e.enabled!==!1,includeTicks:e.includeTicks??!1,label:e.label,logger:e.logger??console.debug}:{enabled:!1,includeTicks:!1}}function v(e,o){let a=pe(e);!a.enabled||!a.logger||o.type==="timer:tick"&&!a.includeTicks||a.logger({...o,label:o.label??a.label})}function R(e,o){return{generation:o,tick:e.tick,now:e.now,elapsedMilliseconds:e.elapsedMilliseconds,status:e.status}}function f(){let e=Date.now(),o=typeof performance<"u"&&typeof performance.now=="function"?performance.now():e;return{wallNow:e,monotonicNow:o}}function L(e,o){if(!Number.isFinite(e)||e<=0)throw new RangeError(`${o} must be a finite number greater than 0`)}function Q(e){return{status:"idle",generation:0,tick:0,startedAt:null,pausedAt:null,endedAt:null,cancelledAt:null,cancelReason:null,baseElapsedMilliseconds:0,activeStartedAtMonotonic:null,now:e.wallNow}}function V(e,o){return e.status!=="running"||e.activeStartedAtMonotonic===null?e.baseElapsedMilliseconds:Math.max(0,e.baseElapsedMilliseconds+o.monotonicNow-e.activeStartedAtMonotonic)}function p(e,o){let a=V(e,o);return{status:e.status,now:o.wallNow,tick:e.tick,startedAt:e.startedAt,pausedAt:e.pausedAt,endedAt:e.endedAt,cancelledAt:e.cancelledAt,cancelReason:e.cancelReason,elapsedMilliseconds:a,isIdle:e.status==="idle",isRunning:e.status==="running",isPaused:e.status==="paused",isEnded:e.status==="ended",isCancelled:e.status==="cancelled"}}function Y(e,o){return e.status!=="idle"?!1:(e.status="running",e.startedAt=o.wallNow,e.pausedAt=null,e.endedAt=null,e.cancelledAt=null,e.cancelReason=null,e.activeStartedAtMonotonic=o.monotonicNow,e.now=o.wallNow,!0)}function X(e,o){return e.status!=="running"?!1:(e.baseElapsedMilliseconds=V(e,o),e.activeStartedAtMonotonic=null,e.status="paused",e.pausedAt=o.wallNow,e.now=o.wallNow,!0)}function Z(e,o){return e.status!=="paused"?!1:(e.status="running",e.pausedAt=null,e.activeStartedAtMonotonic=o.monotonicNow,e.now=o.wallNow,!0)}function B(e,o,a={}){return e.generation+=1,e.tick=0,e.status=a.autoStart?"running":"idle",e.startedAt=a.autoStart?o.wallNow:null,e.pausedAt=null,e.endedAt=null,e.cancelledAt=null,e.cancelReason=null,e.baseElapsedMilliseconds=0,e.activeStartedAtMonotonic=a.autoStart?o.monotonicNow:null,e.now=o.wallNow,!0}function _(e,o){return B(e,o,{autoStart:!0})}function ee(e,o,a){return e.status==="ended"||e.status==="cancelled"?!1:(e.baseElapsedMilliseconds=V(e,o),e.activeStartedAtMonotonic=null,e.status="cancelled",e.cancelledAt=o.wallNow,e.cancelReason=a??null,e.now=o.wallNow,!0)}function te(e,o){return e.status!=="running"?!1:(e.baseElapsedMilliseconds=V(e,o),e.activeStartedAtMonotonic=null,e.status="ended",e.endedAt=o.wallNow,e.now=o.wallNow,!0)}function re(e,o){return e.status!=="running"?!1:(e.tick+=1,e.now=o.wallNow,!0)}function se(e={}){let o=e.updateIntervalMs??1e3;L(o,"updateIntervalMs"),ge(e.schedules);let a=(0,m.useRef)(e);a.current=e;let u=(0,m.useRef)(null);u.current===null&&(u.current=Q(f()));let I=(0,m.useRef)(!1),y=(0,m.useRef)(null),T=(0,m.useRef)(new Map),w=(0,m.useRef)(null),[,M]=(0,m.useReducer)(s=>s+1,0),l=(0,m.useCallback)(()=>{y.current!==null&&(clearTimeout(y.current),y.current=null)},[]),N=(0,m.useCallback)((s=f())=>p(u.current,s),[]),S=(0,m.useCallback)((s,c,g={})=>{v(a.current.debug,{type:s,scope:"timer",...R(c,u.current.generation),...g})},[]),D=(0,m.useRef)(null),z=(0,m.useCallback)(s=>{let c=u.current.generation;if(w.current!==c){w.current=c;try{a.current.onEnd?.(s,D.current)}catch(g){v(a.current.debug,{type:"callback:error",scope:"timer",...R(s,c),error:g})}}},[]),A=(0,m.useCallback)((s,c,g,k,G)=>{if(g.pending&&(s.overlap??"skip")==="skip"){v(a.current.debug,{type:"schedule:skip",scope:"timer",scheduleId:s.id??c,reason:"overlap",...R(k,G)});return}g.lastRunAt=k.now,g.pending=!0,v(a.current.debug,{type:"schedule:start",scope:"timer",scheduleId:s.id??c,...R(k,G)}),Promise.resolve().then(()=>s.callback(k,D.current)).then(()=>{v(a.current.debug,{type:"schedule:end",scope:"timer",scheduleId:s.id??c,...R(k,G)})},t=>{v(a.current.debug,{type:"schedule:error",scope:"timer",scheduleId:s.id??c,error:t,...R(k,G)})}).finally(()=>{u.current?.generation===G&&(g.pending=!1)})},[]),P=(0,m.useCallback)((s,c,g=!1)=>{let k=a.current.schedules??[],G=new Set;k.forEach((t,r)=>{let n=t.id??String(r);G.add(n);let i=T.current.get(n);if(i||(i={lastRunAt:null,pending:!1,leadingGeneration:null},T.current.set(n,i)),g&&t.leading&&i.leadingGeneration!==c){i.leadingGeneration=c,A(t,n,i,s,c);return}if(i.lastRunAt===null){i.lastRunAt=s.now;return}s.now-i.lastRunAt>=t.everyMs&&A(t,n,i,s,c)});for(let t of T.current.keys())G.has(t)||T.current.delete(t)},[A]),E=(0,m.useCallback)((s=f(),c=!1)=>{let g=u.current;if(g.status!=="running")return;let k=p(g,s),G=g.generation;if(a.current.endWhen?.(k)){if(te(g,s)){let t=p(g,s);S("timer:end",t),l(),z(t),M()}return}P(k,G,c)},[z,l,S,P]),K=(0,m.useCallback)(()=>{let s=f();if(!Y(u.current,s))return;let c=p(u.current,s);S("timer:start",c),E(s,!0),M()},[S,E]),W=(0,m.useCallback)(()=>{let s=f();if(!X(u.current,s))return;l();let c=p(u.current,s);S("timer:pause",c),M()},[l,S]),j=(0,m.useCallback)(()=>{let s=f();if(!Z(u.current,s))return;let c=p(u.current,s);S("timer:resume",c),E(s,!0),M()},[S,E]),q=(0,m.useCallback)((s={})=>{let c=f();l(),B(u.current,c,s),T.current.clear(),w.current=null;let g=p(u.current,c);S("timer:reset",g),s.autoStart&&E(c,!0),M()},[l,S,E]),O=(0,m.useCallback)(()=>{let s=f();l(),_(u.current,s),T.current.clear(),w.current=null;let c=p(u.current,s);S("timer:restart",c),E(s,!0),M()},[l,S,E]),U=(0,m.useCallback)(s=>{let c=f();if(!ee(u.current,c,s))return;l();let g=p(u.current,c);S("timer:cancel",g,{reason:s}),M()},[l,S]);D.current=(0,m.useMemo)(()=>({start:K,pause:W,resume:j,reset:q,restart:O,cancel:U}),[U,W,q,O,j,K]),(0,m.useEffect)(()=>(I.current=!0,a.current.autoStart&&u.current.status==="idle"&&D.current.start(),()=>{I.current=!1,l()}),[l]);let x=N(),$=u.current.generation,F=x.status;return(0,m.useEffect)(()=>{if(!I.current||F!=="running"){l();return}return l(),S("scheduler:start",N()),y.current=setTimeout(()=>{if(!I.current||u.current.generation!==$||u.current.status!=="running")return;let s=f();re(u.current,s);let c=p(u.current,s);S("timer:tick",c),E(s),M()},a.current.updateIntervalMs??1e3),()=>{y.current!==null&&S("scheduler:stop",N()),l()}},[l,S,$,N,E,x.tick,F]),{...x,...D.current}}function ge(e){e?.forEach(o=>L(o.everyMs,"schedule.everyMs"))}var d=require("react");function ae(e={}){let o=e.updateIntervalMs??1e3;L(o,"updateIntervalMs"),oe(e.items);let a=(0,d.useRef)(e);a.current=e;let u=(0,d.useRef)(new Map),I=(0,d.useRef)(!1),y=(0,d.useRef)(null),[,T]=(0,d.useReducer)(t=>t+1,0),w=(0,d.useCallback)(()=>{y.current!==null&&(clearTimeout(y.current),y.current=null)},[]),M=(0,d.useCallback)((t,r=f())=>p(t.state,r),[]),l=(0,d.useCallback)((t,r,n,i={})=>{v(a.current.debug,{type:t,scope:"timer-group",timerId:r?.id,...R(n,r?.state.generation??0),...i})},[]),N=(0,d.useCallback)(t=>({start:()=>O(t),pause:()=>U(t),resume:()=>x(t),reset:r=>$(t,r),restart:()=>F(t),cancel:r=>s(t,r)}),[]),S=(0,d.useCallback)((t,r)=>{let n=t.state.generation;if(t.endCalledGeneration!==n){t.endCalledGeneration=n;try{t.definition.onEnd?.(r,N(t.id))}catch(i){v(a.current.debug,{type:"callback:error",scope:"timer-group",timerId:t.id,error:i,...R(r,n)})}}},[N]),D=(0,d.useCallback)((t,r,n,i,h,b)=>{if(i.pending&&(r.overlap??"skip")==="skip"){v(a.current.debug,{type:"schedule:skip",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,reason:"overlap",...R(h,b)});return}i.lastRunAt=h.now,i.pending=!0,v(a.current.debug,{type:"schedule:start",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,...R(h,b)}),Promise.resolve().then(()=>r.callback(h,N(t.id))).then(()=>{v(a.current.debug,{type:"schedule:end",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,...R(h,b)})},J=>{v(a.current.debug,{type:"schedule:error",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,error:J,...R(h,b)})}).finally(()=>{u.current.get(t.id)?.state.generation===b&&(i.pending=!1)})},[N]),z=(0,d.useCallback)((t,r,n=!1)=>{let i=t.definition.schedules??[],h=new Set;i.forEach((b,J)=>{let H=b.id??String(J);h.add(H);let C=t.schedules.get(H);if(C||(C={lastRunAt:null,pending:!1,leadingGeneration:null},t.schedules.set(H,C)),n&&b.leading&&C.leadingGeneration!==t.state.generation){C.leadingGeneration=t.state.generation,D(t,b,H,C,r,t.state.generation);return}if(C.lastRunAt===null){C.lastRunAt=t.state.startedAt??r.now,r.now-C.lastRunAt>=b.everyMs&&D(t,b,H,C,r,t.state.generation);return}r.now-C.lastRunAt>=b.everyMs&&D(t,b,H,C,r,t.state.generation)});for(let b of t.schedules.keys())h.has(b)||t.schedules.delete(b)},[D]),A=(0,d.useCallback)((t,r=f(),n=!1)=>{if(t.state.status!=="running")return;let i=p(t.state,r);if(t.definition.endWhen?.(i)){if(te(t.state,r)){let h=p(t.state,r);l("timer:end",t,h),S(t,h)}return}z(t,i,n)},[S,l,z]),P=(0,d.useCallback)(t=>{let r=u.current.get(t.id);if(r)return r.definition=t,{item:r,added:!1};let n={id:t.id,state:Q(f()),definition:t,schedules:new Map,endCalledGeneration:null};return u.current.set(t.id,n),t.autoStart&&Y(n.state,f()),{item:n,added:!0}},[]),E=(0,d.useCallback)(()=>{let t=a.current.items??[],r=new Set,n=!1;t.forEach(i=>{r.add(i.id);let{item:h,added:b}=P(i);n=n||b,i.autoStart&&h.state.status==="idle"&&(n=Y(h.state,f())||n)});for(let i of u.current.keys())r.has(i)||(u.current.delete(i),n=!0);return n},[P]);(0,d.useEffect)(()=>{E()&&T()},[E,e.items]);let K=(0,d.useCallback)(t=>{if(oe([t]),u.current.has(t.id))throw new Error(`Timer item "${t.id}" already exists`);P(t),T()},[P]),W=(0,d.useCallback)((t,r)=>{let n=u.current.get(t);if(!n)return;let i={...n.definition,...r,id:t};oe([i]),n.definition=i,T()},[]),j=(0,d.useCallback)(t=>{u.current.delete(t),T()},[]),q=(0,d.useCallback)(()=>{u.current.clear(),w(),T()},[w]),O=(0,d.useCallback)(t=>{let r=u.current.get(t);if(!r)return;let n=f();Y(r.state,n)&&(l("timer:start",r,p(r.state,n)),A(r,n,!0),T())},[l,A]),U=(0,d.useCallback)(t=>{let r=u.current.get(t);if(!r)return;let n=f();X(r.state,n)&&(l("timer:pause",r,p(r.state,n)),T())},[l]),x=(0,d.useCallback)(t=>{let r=u.current.get(t);if(!r)return;let n=f();Z(r.state,n)&&(l("timer:resume",r,p(r.state,n)),A(r,n,!0),T())},[l,A]),$=(0,d.useCallback)((t,r={})=>{let n=u.current.get(t);if(!n)return;let i=f();B(n.state,i,r),n.schedules.clear(),n.endCalledGeneration=null,l("timer:reset",n,p(n.state,i)),r.autoStart&&A(n,i,!0),T()},[l,A]),F=(0,d.useCallback)(t=>{let r=u.current.get(t);if(!r)return;let n=f();_(r.state,n),r.schedules.clear(),r.endCalledGeneration=null,l("timer:restart",r,p(r.state,n)),A(r,n,!0),T()},[l,A]),s=(0,d.useCallback)((t,r)=>{let n=u.current.get(t);if(!n)return;let i=f();ee(n.state,i,r)&&(l("timer:cancel",n,p(n.state,i),{reason:r}),T())},[l]),g=Array.from(u.current.keys()).map(t=>`${t}:${u.current.get(t).state.status}:${u.current.get(t).state.generation}:${u.current.get(t).state.tick}`).join("|");(0,d.useEffect)(()=>{I.current=!0;let t=Array.from(u.current.values()).filter(n=>n.state.status==="running");if(t.length===0){w();return}w();let r=t[0];return l("scheduler:start",r,p(r.state,f())),y.current=setTimeout(()=>{if(!I.current)return;let n=f();for(let i of u.current.values()){if(i.state.status!=="running")continue;re(i.state,n);let h=p(i.state,n);l("timer:tick",i,h),A(i,n)}T()},a.current.updateIntervalMs??1e3),()=>{y.current!==null&&l("scheduler:stop",r,p(r.state,f())),w(),I.current=!1}},[g,w,l,A]);let k=(0,d.useCallback)(t=>{let r=u.current.get(t);if(r)return M(r)},[M]),G=f().wallNow;return(0,d.useMemo)(()=>({now:G,size:u.current.size,ids:Array.from(u.current.keys()),get:k,add:K,update:W,remove:j,clear:q,start:O,pause:U,resume:x,reset:$,restart:F,cancel:s,startAll:()=>Array.from(u.current.keys()).forEach(O),pauseAll:()=>Array.from(u.current.keys()).forEach(U),resumeAll:()=>Array.from(u.current.keys()).forEach(x),resetAll:t=>Array.from(u.current.keys()).forEach(r=>$(r,t)),restartAll:()=>Array.from(u.current.keys()).forEach(F),cancelAll:t=>Array.from(u.current.keys()).forEach(r=>s(r,t))}),[K,s,q,k,G,U,j,$,F,x,O,W])}function oe(e){let o=new Set;e?.forEach(a=>{if(o.has(a.id))throw new Error(`Duplicate timer item id "${a.id}"`);o.add(a.id),a.schedules?.forEach(u=>L(u.everyMs,"schedule.everyMs"))})}0&&(module.exports={durationParts,useTimer,useTimerGroup});
1
+ "use strict";var le=Object.defineProperty;var Se=Object.getOwnPropertyDescriptor;var Te=Object.getOwnPropertyNames;var be=Object.prototype.hasOwnProperty;var he=(e,u)=>{for(var a in u)le(e,a,{get:u[a],enumerable:!0})},ye=(e,u,a,o)=>{if(u&&typeof u=="object"||typeof u=="function")for(let w of Te(u))!be.call(e,w)&&w!==a&&le(e,w,{get:()=>u[w],enumerable:!(o=Se(u,w))||o.enumerable});return e};var Ie=e=>ye(le({},"__esModule",{value:!0}),e);var Me={};he(Me,{durationParts:()=>me,useTimer:()=>fe,useTimerGroup:()=>pe});module.exports=Ie(Me);function me(e){let u=Math.max(0,Math.trunc(Number.isFinite(e)?e:0)),a=Math.floor(u/864e5),o=u%864e5,w=Math.floor(o/36e5),M=o%36e5,S=Math.floor(M/6e4),v=M%6e4,D=Math.floor(v/1e3);return{totalMilliseconds:u,totalSeconds:Math.floor(u/1e3),milliseconds:v%1e3,seconds:D,minutes:S,hours:w,days:a}}var m=require("react");function we(e){return e?e===!0?{enabled:!0,includeTicks:!1,logger:console.debug}:typeof e=="function"?{enabled:!0,includeTicks:!1,logger:e}:{enabled:e.enabled!==!1,includeTicks:e.includeTicks??!1,label:e.label,logger:e.logger??console.debug}:{enabled:!1,includeTicks:!1}}function G(e,u){let a=we(e);!a.enabled||!a.logger||u.type==="timer:tick"&&!a.includeTicks||a.logger({...u,label:u.label??a.label})}function C(e,u){return{generation:u,tick:e.tick,now:e.now,elapsedMilliseconds:e.elapsedMilliseconds,status:e.status}}function f(){let e=Date.now(),u=typeof performance<"u"&&typeof performance.now=="function"?performance.now():e;return{wallNow:e,monotonicNow:u}}function W(e,u){if(!Number.isFinite(e)||e<=0)throw new RangeError(`${u} must be a finite number greater than 0`)}function te(e){return{status:"idle",generation:0,tick:0,startedAt:null,pausedAt:null,endedAt:null,cancelledAt:null,cancelReason:null,baseElapsedMilliseconds:0,activeStartedAtMonotonic:null,now:e.wallNow}}function re(e,u){return e.status!=="running"||e.activeStartedAtMonotonic===null?e.baseElapsedMilliseconds:Math.max(0,e.baseElapsedMilliseconds+u.monotonicNow-e.activeStartedAtMonotonic)}function g(e,u){let a=re(e,u);return{status:e.status,now:u.wallNow,tick:e.tick,startedAt:e.startedAt,pausedAt:e.pausedAt,endedAt:e.endedAt,cancelledAt:e.cancelledAt,cancelReason:e.cancelReason,elapsedMilliseconds:a,isIdle:e.status==="idle",isRunning:e.status==="running",isPaused:e.status==="paused",isEnded:e.status==="ended",isCancelled:e.status==="cancelled"}}function j(e,u){return e.status!=="idle"?!1:(e.status="running",e.startedAt=u.wallNow,e.pausedAt=null,e.endedAt=null,e.cancelledAt=null,e.cancelReason=null,e.activeStartedAtMonotonic=u.monotonicNow,e.now=u.wallNow,!0)}function ne(e,u){return e.status!=="running"?!1:(e.baseElapsedMilliseconds=re(e,u),e.activeStartedAtMonotonic=null,e.status="paused",e.pausedAt=u.wallNow,e.now=u.wallNow,!0)}function oe(e,u){return e.status!=="paused"?!1:(e.status="running",e.pausedAt=null,e.activeStartedAtMonotonic=u.monotonicNow,e.now=u.wallNow,!0)}function _(e,u,a={}){return e.generation+=1,e.tick=0,e.status=a.autoStart?"running":"idle",e.startedAt=a.autoStart?u.wallNow:null,e.pausedAt=null,e.endedAt=null,e.cancelledAt=null,e.cancelReason=null,e.baseElapsedMilliseconds=0,e.activeStartedAtMonotonic=a.autoStart?u.monotonicNow:null,e.now=u.wallNow,!0}function ue(e,u){return _(e,u,{autoStart:!0})}function se(e,u,a){return e.status==="ended"||e.status==="cancelled"?!1:(e.baseElapsedMilliseconds=re(e,u),e.activeStartedAtMonotonic=null,e.status="cancelled",e.cancelledAt=u.wallNow,e.cancelReason=a??null,e.now=u.wallNow,!0)}function ae(e,u){return e.status!=="running"?!1:(e.baseElapsedMilliseconds=re(e,u),e.activeStartedAtMonotonic=null,e.status="ended",e.endedAt=u.wallNow,e.now=u.wallNow,!0)}function ie(e,u){return e.status!=="running"?!1:(e.tick+=1,e.now=u.wallNow,!0)}function fe(e={}){let u=e.updateIntervalMs??1e3;W(u,"updateIntervalMs"),Ae(e.schedules);let a=(0,m.useRef)(e);a.current=e;let o=(0,m.useRef)(null);o.current===null&&(o.current=te(f()));let w=(0,m.useRef)(!1),M=(0,m.useRef)(null),S=(0,m.useRef)(new Map),v=(0,m.useRef)(null),[,D]=(0,m.useReducer)(s=>s+1,0),d=(0,m.useCallback)(()=>{M.current!==null&&(clearTimeout(M.current),M.current=null)},[]),O=(0,m.useCallback)((s=f())=>g(o.current,s),[]),T=(0,m.useCallback)((s,c,p={})=>{G(a.current.debug,{type:s,scope:"timer",...C(c,o.current.generation),...p})},[]),x=(0,m.useRef)(null),q=(0,m.useCallback)(s=>{let c=o.current.generation;if(v.current!==c){v.current=c;try{a.current.onEnd?.(s,x.current)}catch(p){G(a.current.debug,{type:"callback:error",scope:"timer",...C(s,c),error:p})}}},[]),k=(0,m.useCallback)((s,c,p,I,R)=>{if(p.pending&&(s.overlap??"skip")==="skip"){G(a.current.debug,{type:"schedule:skip",scope:"timer",scheduleId:s.id??c,reason:"overlap",...C(I,R)});return}p.lastRunAt=I.now,p.pending=!0,G(a.current.debug,{type:"schedule:start",scope:"timer",scheduleId:s.id??c,...C(I,R)}),Promise.resolve().then(()=>s.callback(I,x.current)).then(()=>{G(a.current.debug,{type:"schedule:end",scope:"timer",scheduleId:s.id??c,...C(I,R)})},h=>{G(a.current.debug,{type:"schedule:error",scope:"timer",scheduleId:s.id??c,error:h,...C(I,R)})}).finally(()=>{o.current?.generation===R&&(p.pending=!1)})},[]),B=(0,m.useCallback)((s,c,p=!1)=>{let I=a.current.schedules??[],R=new Set;I.forEach((h,z)=>{let P=h.id??String(z);R.add(P);let E=S.current.get(P);if(E||(E={lastRunAt:null,pending:!1,leadingGeneration:null},S.current.set(P,E)),p&&h.leading&&E.leadingGeneration!==c){E.leadingGeneration=c,k(h,P,E,s,c);return}if(E.lastRunAt===null){E.lastRunAt=s.now;return}s.now-E.lastRunAt>=h.everyMs&&k(h,P,E,s,c)});for(let h of S.current.keys())R.has(h)||S.current.delete(h)},[k]),A=(0,m.useCallback)((s=f(),c=!1)=>{let p=o.current;if(p.status!=="running")return;let I=g(p,s),R=p.generation;if(a.current.endWhen?.(I)){if(ae(p,s)){let h=g(p,s);T("timer:end",h),d(),q(h),D()}return}B(I,R,c)},[q,d,T,B]),J=(0,m.useCallback)((s=f())=>{let c=a.current.updateIntervalMs??1e3,p=c,I=o.current;return I.status!=="running"?c:((a.current.schedules??[]).forEach((h,z)=>{let P=h.id??String(z),ee=S.current.get(P)?.lastRunAt??I.startedAt??s.wallNow;p=Math.min(p,Math.max(1,ee+h.everyMs-s.wallNow))}),p)},[]),Q=(0,m.useCallback)(()=>{let s=f();if(!j(o.current,s))return;let c=g(o.current,s);T("timer:start",c),A(s,!0),D()},[T,A]),V=(0,m.useCallback)(()=>{let s=f();if(!ne(o.current,s))return;d();let c=g(o.current,s);T("timer:pause",c),D()},[d,T]),X=(0,m.useCallback)(()=>{let s=f();if(!oe(o.current,s))return;let c=g(o.current,s);T("timer:resume",c),A(s,!0),D()},[T,A]),Z=(0,m.useCallback)((s={})=>{let c=f();d(),_(o.current,c,s),S.current.clear(),v.current=null;let p=g(o.current,c);T("timer:reset",p),s.autoStart&&A(c,!0),D()},[d,T,A]),$=(0,m.useCallback)(()=>{let s=f();d(),ue(o.current,s),S.current.clear(),v.current=null;let c=g(o.current,s);T("timer:restart",c),A(s,!0),D()},[d,T,A]),F=(0,m.useCallback)(s=>{let c=f();if(!se(o.current,c,s))return;d();let p=g(o.current,c);T("timer:cancel",p,{reason:s}),D()},[d,T]);x.current=(0,m.useMemo)(()=>({start:Q,pause:V,resume:X,reset:Z,restart:$,cancel:F}),[F,V,Z,$,X,Q]),(0,m.useEffect)(()=>(w.current=!0,a.current.autoStart&&o.current.status==="idle"&&x.current.start(),()=>{w.current=!1,d()}),[d]);let U=O(),H=o.current.generation,L=U.status;return(0,m.useEffect)(()=>{if(!w.current||L!=="running"){d();return}return d(),T("scheduler:start",O()),M.current=setTimeout(()=>{if(!w.current||o.current.generation!==H||o.current.status!=="running")return;let s=f();ie(o.current,s);let c=g(o.current,s);T("timer:tick",c),A(s),D()},J()),()=>{M.current!==null&&T("scheduler:stop",O()),d()}},[d,T,H,J,O,A,U.tick,L]),{...U,...x.current}}function Ae(e){e?.forEach(u=>W(u.everyMs,"schedule.everyMs"))}var l=require("react");function pe(e={}){let u=e.updateIntervalMs??1e3;W(u,"updateIntervalMs"),ce(e.items);let a=(0,l.useRef)(e);a.current=e;let o=(0,l.useRef)(new Map),w=(0,l.useRef)(!1),M=(0,l.useRef)(null),[,S]=(0,l.useReducer)(t=>t+1,0),v=(0,l.useCallback)(()=>{M.current!==null&&(clearTimeout(M.current),M.current=null)},[]),D=(0,l.useCallback)((t,r=f())=>g(t.state,r),[]),d=(0,l.useCallback)((t,r,n,i={})=>{G(a.current.debug,{type:t,scope:"timer-group",timerId:r?.id,...C(n,r?.state.generation??0),...i})},[]),O=(0,l.useCallback)(t=>({start:()=>$(t),pause:()=>F(t),resume:()=>U(t),reset:r=>H(t,r),restart:()=>L(t),cancel:r=>s(t,r)}),[]),T=(0,l.useCallback)((t,r)=>{let n=t.state.generation;if(t.endCalledGeneration!==n){t.endCalledGeneration=n;try{t.definition.onEnd?.(r,O(t.id))}catch(i){G(a.current.debug,{type:"callback:error",scope:"timer-group",timerId:t.id,error:i,...C(r,n)})}}},[O]),x=(0,l.useCallback)((t,r,n,i,y,b)=>{if(i.pending&&(r.overlap??"skip")==="skip"){G(a.current.debug,{type:"schedule:skip",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,reason:"overlap",...C(y,b)});return}i.lastRunAt=y.now,i.pending=!0,G(a.current.debug,{type:"schedule:start",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,...C(y,b)}),Promise.resolve().then(()=>r.callback(y,O(t.id))).then(()=>{G(a.current.debug,{type:"schedule:end",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,...C(y,b)})},K=>{G(a.current.debug,{type:"schedule:error",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,error:K,...C(y,b)})}).finally(()=>{o.current.get(t.id)?.state.generation===b&&(i.pending=!1)})},[O]),q=(0,l.useCallback)((t,r,n=!1)=>{let i=t.definition.schedules??[],y=new Set;i.forEach((b,K)=>{let Y=b.id??String(K);y.add(Y);let N=t.schedules.get(Y);if(N||(N={lastRunAt:null,pending:!1,leadingGeneration:null},t.schedules.set(Y,N)),n&&b.leading&&N.leadingGeneration!==t.state.generation){N.leadingGeneration=t.state.generation,x(t,b,Y,N,r,t.state.generation);return}if(N.lastRunAt===null){N.lastRunAt=t.state.startedAt??r.now,r.now-N.lastRunAt>=b.everyMs&&x(t,b,Y,N,r,t.state.generation);return}r.now-N.lastRunAt>=b.everyMs&&x(t,b,Y,N,r,t.state.generation)});for(let b of t.schedules.keys())y.has(b)||t.schedules.delete(b)},[x]),k=(0,l.useCallback)((t,r=f(),n=!1)=>{if(t.state.status!=="running")return;let i=g(t.state,r);if(t.definition.endWhen?.(i)){if(ae(t.state,r)){let y=g(t.state,r);d("timer:end",t,y),T(t,y)}return}q(t,i,n)},[T,d,q]),B=(0,l.useCallback)((t=f())=>{let n=a.current.updateIntervalMs??1e3;for(let i of o.current.values()){if(i.state.status!=="running")continue;(i.definition.schedules??[]).forEach((b,K)=>{let Y=b.id??String(K),ge=i.schedules.get(Y)?.lastRunAt??i.state.startedAt??t.wallNow;n=Math.min(n,Math.max(1,ge+b.everyMs-t.wallNow))})}return n},[]),A=(0,l.useCallback)(t=>{let r=o.current.get(t.id);if(r)return r.definition=t,{item:r,added:!1};let n={id:t.id,state:te(f()),definition:t,schedules:new Map,endCalledGeneration:null};return o.current.set(t.id,n),t.autoStart&&j(n.state,f()),{item:n,added:!0}},[]),J=(0,l.useCallback)(()=>{let t=a.current.items??[],r=new Set,n=!1;t.forEach(i=>{r.add(i.id);let{item:y,added:b}=A(i);n=n||b,i.autoStart&&y.state.status==="idle"&&(n=j(y.state,f())||n)});for(let i of o.current.keys())r.has(i)||(o.current.delete(i),n=!0);return n},[A]);(0,l.useEffect)(()=>{J()&&S()},[J,e.items]);let Q=(0,l.useCallback)(t=>{if(ce([t]),o.current.has(t.id))throw new Error(`Timer item "${t.id}" already exists`);A(t),S()},[A]),V=(0,l.useCallback)((t,r)=>{let n=o.current.get(t);if(!n)return;let i={...n.definition,...r,id:t};ce([i]),n.definition=i,S()},[]),X=(0,l.useCallback)(t=>{o.current.delete(t),S()},[]),Z=(0,l.useCallback)(()=>{o.current.clear(),v(),S()},[v]),$=(0,l.useCallback)(t=>{let r=o.current.get(t);if(!r)return;let n=f();j(r.state,n)&&(d("timer:start",r,g(r.state,n)),k(r,n,!0),S())},[d,k]),F=(0,l.useCallback)(t=>{let r=o.current.get(t);if(!r)return;let n=f();ne(r.state,n)&&(d("timer:pause",r,g(r.state,n)),S())},[d]),U=(0,l.useCallback)(t=>{let r=o.current.get(t);if(!r)return;let n=f();oe(r.state,n)&&(d("timer:resume",r,g(r.state,n)),k(r,n,!0),S())},[d,k]),H=(0,l.useCallback)((t,r={})=>{let n=o.current.get(t);if(!n)return;let i=f();_(n.state,i,r),n.schedules.clear(),n.endCalledGeneration=null,d("timer:reset",n,g(n.state,i)),r.autoStart&&k(n,i,!0),S()},[d,k]),L=(0,l.useCallback)(t=>{let r=o.current.get(t);if(!r)return;let n=f();ue(r.state,n),r.schedules.clear(),r.endCalledGeneration=null,d("timer:restart",r,g(r.state,n)),k(r,n,!0),S()},[d,k]),s=(0,l.useCallback)((t,r)=>{let n=o.current.get(t);if(!n)return;let i=f();se(n.state,i,r)&&(d("timer:cancel",n,g(n.state,i),{reason:r}),S())},[d]),p=Array.from(o.current.keys()).map(t=>`${t}:${o.current.get(t).state.status}:${o.current.get(t).state.generation}:${o.current.get(t).state.tick}`).join("|");(0,l.useEffect)(()=>{w.current=!0;let t=Array.from(o.current.values()).filter(n=>n.state.status==="running");if(t.length===0){v();return}v();let r=t[0];return d("scheduler:start",r,g(r.state,f())),M.current=setTimeout(()=>{if(!w.current)return;let n=f();for(let i of o.current.values()){if(i.state.status!=="running")continue;ie(i.state,n);let y=g(i.state,n);d("timer:tick",i,y),k(i,n)}S()},B()),()=>{M.current!==null&&d("scheduler:stop",r,g(r.state,f())),v(),w.current=!1}},[p,v,d,B,k]);let I=(0,l.useCallback)(t=>{let r=o.current.get(t);if(r)return D(r)},[D]),R=f().wallNow,h=(0,l.useCallback)(()=>Array.from(o.current.keys()).forEach($),[$]),z=(0,l.useCallback)(()=>Array.from(o.current.keys()).forEach(F),[F]),P=(0,l.useCallback)(()=>Array.from(o.current.keys()).forEach(U),[U]),E=(0,l.useCallback)(t=>Array.from(o.current.keys()).forEach(r=>H(r,t)),[H]),ee=(0,l.useCallback)(()=>Array.from(o.current.keys()).forEach(L),[L]),de=(0,l.useCallback)(t=>Array.from(o.current.keys()).forEach(r=>s(r,t)),[s]);return(0,l.useMemo)(()=>({now:R,size:o.current.size,ids:Array.from(o.current.keys()),get:I,add:Q,update:V,remove:X,clear:Z,start:$,pause:F,resume:U,reset:H,restart:L,cancel:s,startAll:h,pauseAll:z,resumeAll:P,resetAll:E,restartAll:ee,cancelAll:de}),[Q,s,de,Z,I,R,F,z,X,H,E,L,ee,U,P,$,h,V])}function ce(e){let u=new Set;e?.forEach(a=>{if(u.has(a.id))throw new Error(`Duplicate timer item id "${a.id}"`);u.add(a.id),a.schedules?.forEach(o=>W(o.everyMs,"schedule.everyMs"))})}0&&(module.exports={durationParts,useTimer,useTimerGroup});
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- function ie(e){let u=Math.max(0,Math.trunc(Number.isFinite(e)?e:0)),i=Math.floor(u/864e5),o=u%864e5,C=Math.floor(o/36e5),h=o%36e5,p=Math.floor(h/6e4),I=h%6e4,v=Math.floor(I/1e3);return{totalMilliseconds:u,totalSeconds:Math.floor(u/1e3),milliseconds:I%1e3,seconds:v,minutes:p,hours:C,days:i}}import{useCallback as E,useEffect as se,useMemo as ce,useReducer as de,useRef as H}from"react";function le(e){return e?e===!0?{enabled:!0,includeTicks:!1,logger:console.debug}:typeof e=="function"?{enabled:!0,includeTicks:!1,logger:e}:{enabled:e.enabled!==!1,includeTicks:e.includeTicks??!1,label:e.label,logger:e.logger??console.debug}:{enabled:!1,includeTicks:!1}}function A(e,u){let i=le(e);!i.enabled||!i.logger||u.type==="timer:tick"&&!i.includeTicks||i.logger({...u,label:u.label??i.label})}function k(e,u){return{generation:u,tick:e.tick,now:e.now,elapsedMilliseconds:e.elapsedMilliseconds,status:e.status}}function d(){let e=Date.now(),u=typeof performance<"u"&&typeof performance.now=="function"?performance.now():e;return{wallNow:e,monotonicNow:u}}function Y(e,u){if(!Number.isFinite(e)||e<=0)throw new RangeError(`${u} must be a finite number greater than 0`)}function V(e){return{status:"idle",generation:0,tick:0,startedAt:null,pausedAt:null,endedAt:null,cancelledAt:null,cancelReason:null,baseElapsedMilliseconds:0,activeStartedAtMonotonic:null,now:e.wallNow}}function X(e,u){return e.status!=="running"||e.activeStartedAtMonotonic===null?e.baseElapsedMilliseconds:Math.max(0,e.baseElapsedMilliseconds+u.monotonicNow-e.activeStartedAtMonotonic)}function m(e,u){let i=X(e,u);return{status:e.status,now:u.wallNow,tick:e.tick,startedAt:e.startedAt,pausedAt:e.pausedAt,endedAt:e.endedAt,cancelledAt:e.cancelledAt,cancelReason:e.cancelReason,elapsedMilliseconds:i,isIdle:e.status==="idle",isRunning:e.status==="running",isPaused:e.status==="paused",isEnded:e.status==="ended",isCancelled:e.status==="cancelled"}}function z(e,u){return e.status!=="idle"?!1:(e.status="running",e.startedAt=u.wallNow,e.pausedAt=null,e.endedAt=null,e.cancelledAt=null,e.cancelReason=null,e.activeStartedAtMonotonic=u.monotonicNow,e.now=u.wallNow,!0)}function Z(e,u){return e.status!=="running"?!1:(e.baseElapsedMilliseconds=X(e,u),e.activeStartedAtMonotonic=null,e.status="paused",e.pausedAt=u.wallNow,e.now=u.wallNow,!0)}function _(e,u){return e.status!=="paused"?!1:(e.status="running",e.pausedAt=null,e.activeStartedAtMonotonic=u.monotonicNow,e.now=u.wallNow,!0)}function J(e,u,i={}){return e.generation+=1,e.tick=0,e.status=i.autoStart?"running":"idle",e.startedAt=i.autoStart?u.wallNow:null,e.pausedAt=null,e.endedAt=null,e.cancelledAt=null,e.cancelReason=null,e.baseElapsedMilliseconds=0,e.activeStartedAtMonotonic=i.autoStart?u.monotonicNow:null,e.now=u.wallNow,!0}function ee(e,u){return J(e,u,{autoStart:!0})}function te(e,u,i){return e.status==="ended"||e.status==="cancelled"?!1:(e.baseElapsedMilliseconds=X(e,u),e.activeStartedAtMonotonic=null,e.status="cancelled",e.cancelledAt=u.wallNow,e.cancelReason=i??null,e.now=u.wallNow,!0)}function re(e,u){return e.status!=="running"?!1:(e.baseElapsedMilliseconds=X(e,u),e.activeStartedAtMonotonic=null,e.status="ended",e.endedAt=u.wallNow,e.now=u.wallNow,!0)}function ne(e,u){return e.status!=="running"?!1:(e.tick+=1,e.now=u.wallNow,!0)}function me(e={}){let u=e.updateIntervalMs??1e3;Y(u,"updateIntervalMs"),fe(e.schedules);let i=H(e);i.current=e;let o=H(null);o.current===null&&(o.current=V(d()));let C=H(!1),h=H(null),p=H(new Map),I=H(null),[,v]=de(s=>s+1,0),l=E(()=>{h.current!==null&&(clearTimeout(h.current),h.current=null)},[]),N=E((s=d())=>m(o.current,s),[]),g=E((s,c,f={})=>{A(i.current.debug,{type:s,scope:"timer",...k(c,o.current.generation),...f})},[]),D=H(null),K=E(s=>{let c=o.current.generation;if(I.current!==c){I.current=c;try{i.current.onEnd?.(s,D.current)}catch(f){A(i.current.debug,{type:"callback:error",scope:"timer",...k(s,c),error:f})}}},[]),y=E((s,c,f,w,M)=>{if(f.pending&&(s.overlap??"skip")==="skip"){A(i.current.debug,{type:"schedule:skip",scope:"timer",scheduleId:s.id??c,reason:"overlap",...k(w,M)});return}f.lastRunAt=w.now,f.pending=!0,A(i.current.debug,{type:"schedule:start",scope:"timer",scheduleId:s.id??c,...k(w,M)}),Promise.resolve().then(()=>s.callback(w,D.current)).then(()=>{A(i.current.debug,{type:"schedule:end",scope:"timer",scheduleId:s.id??c,...k(w,M)})},t=>{A(i.current.debug,{type:"schedule:error",scope:"timer",scheduleId:s.id??c,error:t,...k(w,M)})}).finally(()=>{o.current?.generation===M&&(f.pending=!1)})},[]),P=E((s,c,f=!1)=>{let w=i.current.schedules??[],M=new Set;w.forEach((t,r)=>{let n=t.id??String(r);M.add(n);let a=p.current.get(n);if(a||(a={lastRunAt:null,pending:!1,leadingGeneration:null},p.current.set(n,a)),f&&t.leading&&a.leadingGeneration!==c){a.leadingGeneration=c,y(t,n,a,s,c);return}if(a.lastRunAt===null){a.lastRunAt=s.now;return}s.now-a.lastRunAt>=t.everyMs&&y(t,n,a,s,c)});for(let t of p.current.keys())M.has(t)||p.current.delete(t)},[y]),R=E((s=d(),c=!1)=>{let f=o.current;if(f.status!=="running")return;let w=m(f,s),M=f.generation;if(i.current.endWhen?.(w)){if(re(f,s)){let t=m(f,s);g("timer:end",t),l(),K(t),v()}return}P(w,M,c)},[K,l,g,P]),W=E(()=>{let s=d();if(!z(o.current,s))return;let c=m(o.current,s);g("timer:start",c),R(s,!0),v()},[g,R]),j=E(()=>{let s=d();if(!Z(o.current,s))return;l();let c=m(o.current,s);g("timer:pause",c),v()},[l,g]),q=E(()=>{let s=d();if(!_(o.current,s))return;let c=m(o.current,s);g("timer:resume",c),R(s,!0),v()},[g,R]),B=E((s={})=>{let c=d();l(),J(o.current,c,s),p.current.clear(),I.current=null;let f=m(o.current,c);g("timer:reset",f),s.autoStart&&R(c,!0),v()},[l,g,R]),O=E(()=>{let s=d();l(),ee(o.current,s),p.current.clear(),I.current=null;let c=m(o.current,s);g("timer:restart",c),R(s,!0),v()},[l,g,R]),U=E(s=>{let c=d();if(!te(o.current,c,s))return;l();let f=m(o.current,c);g("timer:cancel",f,{reason:s}),v()},[l,g]);D.current=ce(()=>({start:W,pause:j,resume:q,reset:B,restart:O,cancel:U}),[U,j,B,O,q,W]),se(()=>(C.current=!0,i.current.autoStart&&o.current.status==="idle"&&D.current.start(),()=>{C.current=!1,l()}),[l]);let x=N(),$=o.current.generation,F=x.status;return se(()=>{if(!C.current||F!=="running"){l();return}return l(),g("scheduler:start",N()),h.current=setTimeout(()=>{if(!C.current||o.current.generation!==$||o.current.status!=="running")return;let s=d();ne(o.current,s);let c=m(o.current,s);g("timer:tick",c),R(s),v()},i.current.updateIntervalMs??1e3),()=>{h.current!==null&&g("scheduler:stop",N()),l()}},[l,g,$,N,R,x.tick,F]),{...x,...D.current}}function fe(e){e?.forEach(u=>Y(u.everyMs,"schedule.everyMs"))}import{useCallback as T,useEffect as ae,useMemo as pe,useReducer as ge,useRef as oe}from"react";function Te(e={}){let u=e.updateIntervalMs??1e3;Y(u,"updateIntervalMs"),ue(e.items);let i=oe(e);i.current=e;let o=oe(new Map),C=oe(!1),h=oe(null),[,p]=ge(t=>t+1,0),I=T(()=>{h.current!==null&&(clearTimeout(h.current),h.current=null)},[]),v=T((t,r=d())=>m(t.state,r),[]),l=T((t,r,n,a={})=>{A(i.current.debug,{type:t,scope:"timer-group",timerId:r?.id,...k(n,r?.state.generation??0),...a})},[]),N=T(t=>({start:()=>O(t),pause:()=>U(t),resume:()=>x(t),reset:r=>$(t,r),restart:()=>F(t),cancel:r=>s(t,r)}),[]),g=T((t,r)=>{let n=t.state.generation;if(t.endCalledGeneration!==n){t.endCalledGeneration=n;try{t.definition.onEnd?.(r,N(t.id))}catch(a){A(i.current.debug,{type:"callback:error",scope:"timer-group",timerId:t.id,error:a,...k(r,n)})}}},[N]),D=T((t,r,n,a,b,S)=>{if(a.pending&&(r.overlap??"skip")==="skip"){A(i.current.debug,{type:"schedule:skip",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,reason:"overlap",...k(b,S)});return}a.lastRunAt=b.now,a.pending=!0,A(i.current.debug,{type:"schedule:start",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,...k(b,S)}),Promise.resolve().then(()=>r.callback(b,N(t.id))).then(()=>{A(i.current.debug,{type:"schedule:end",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,...k(b,S)})},Q=>{A(i.current.debug,{type:"schedule:error",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,error:Q,...k(b,S)})}).finally(()=>{o.current.get(t.id)?.state.generation===S&&(a.pending=!1)})},[N]),K=T((t,r,n=!1)=>{let a=t.definition.schedules??[],b=new Set;a.forEach((S,Q)=>{let L=S.id??String(Q);b.add(L);let G=t.schedules.get(L);if(G||(G={lastRunAt:null,pending:!1,leadingGeneration:null},t.schedules.set(L,G)),n&&S.leading&&G.leadingGeneration!==t.state.generation){G.leadingGeneration=t.state.generation,D(t,S,L,G,r,t.state.generation);return}if(G.lastRunAt===null){G.lastRunAt=t.state.startedAt??r.now,r.now-G.lastRunAt>=S.everyMs&&D(t,S,L,G,r,t.state.generation);return}r.now-G.lastRunAt>=S.everyMs&&D(t,S,L,G,r,t.state.generation)});for(let S of t.schedules.keys())b.has(S)||t.schedules.delete(S)},[D]),y=T((t,r=d(),n=!1)=>{if(t.state.status!=="running")return;let a=m(t.state,r);if(t.definition.endWhen?.(a)){if(re(t.state,r)){let b=m(t.state,r);l("timer:end",t,b),g(t,b)}return}K(t,a,n)},[g,l,K]),P=T(t=>{let r=o.current.get(t.id);if(r)return r.definition=t,{item:r,added:!1};let n={id:t.id,state:V(d()),definition:t,schedules:new Map,endCalledGeneration:null};return o.current.set(t.id,n),t.autoStart&&z(n.state,d()),{item:n,added:!0}},[]),R=T(()=>{let t=i.current.items??[],r=new Set,n=!1;t.forEach(a=>{r.add(a.id);let{item:b,added:S}=P(a);n=n||S,a.autoStart&&b.state.status==="idle"&&(n=z(b.state,d())||n)});for(let a of o.current.keys())r.has(a)||(o.current.delete(a),n=!0);return n},[P]);ae(()=>{R()&&p()},[R,e.items]);let W=T(t=>{if(ue([t]),o.current.has(t.id))throw new Error(`Timer item "${t.id}" already exists`);P(t),p()},[P]),j=T((t,r)=>{let n=o.current.get(t);if(!n)return;let a={...n.definition,...r,id:t};ue([a]),n.definition=a,p()},[]),q=T(t=>{o.current.delete(t),p()},[]),B=T(()=>{o.current.clear(),I(),p()},[I]),O=T(t=>{let r=o.current.get(t);if(!r)return;let n=d();z(r.state,n)&&(l("timer:start",r,m(r.state,n)),y(r,n,!0),p())},[l,y]),U=T(t=>{let r=o.current.get(t);if(!r)return;let n=d();Z(r.state,n)&&(l("timer:pause",r,m(r.state,n)),p())},[l]),x=T(t=>{let r=o.current.get(t);if(!r)return;let n=d();_(r.state,n)&&(l("timer:resume",r,m(r.state,n)),y(r,n,!0),p())},[l,y]),$=T((t,r={})=>{let n=o.current.get(t);if(!n)return;let a=d();J(n.state,a,r),n.schedules.clear(),n.endCalledGeneration=null,l("timer:reset",n,m(n.state,a)),r.autoStart&&y(n,a,!0),p()},[l,y]),F=T(t=>{let r=o.current.get(t);if(!r)return;let n=d();ee(r.state,n),r.schedules.clear(),r.endCalledGeneration=null,l("timer:restart",r,m(r.state,n)),y(r,n,!0),p()},[l,y]),s=T((t,r)=>{let n=o.current.get(t);if(!n)return;let a=d();te(n.state,a,r)&&(l("timer:cancel",n,m(n.state,a),{reason:r}),p())},[l]),f=Array.from(o.current.keys()).map(t=>`${t}:${o.current.get(t).state.status}:${o.current.get(t).state.generation}:${o.current.get(t).state.tick}`).join("|");ae(()=>{C.current=!0;let t=Array.from(o.current.values()).filter(n=>n.state.status==="running");if(t.length===0){I();return}I();let r=t[0];return l("scheduler:start",r,m(r.state,d())),h.current=setTimeout(()=>{if(!C.current)return;let n=d();for(let a of o.current.values()){if(a.state.status!=="running")continue;ne(a.state,n);let b=m(a.state,n);l("timer:tick",a,b),y(a,n)}p()},i.current.updateIntervalMs??1e3),()=>{h.current!==null&&l("scheduler:stop",r,m(r.state,d())),I(),C.current=!1}},[f,I,l,y]);let w=T(t=>{let r=o.current.get(t);if(r)return v(r)},[v]),M=d().wallNow;return pe(()=>({now:M,size:o.current.size,ids:Array.from(o.current.keys()),get:w,add:W,update:j,remove:q,clear:B,start:O,pause:U,resume:x,reset:$,restart:F,cancel:s,startAll:()=>Array.from(o.current.keys()).forEach(O),pauseAll:()=>Array.from(o.current.keys()).forEach(U),resumeAll:()=>Array.from(o.current.keys()).forEach(x),resetAll:t=>Array.from(o.current.keys()).forEach(r=>$(r,t)),restartAll:()=>Array.from(o.current.keys()).forEach(F),cancelAll:t=>Array.from(o.current.keys()).forEach(r=>s(r,t))}),[W,s,B,w,M,U,q,$,F,x,O,j])}function ue(e){let u=new Set;e?.forEach(i=>{if(u.has(i.id))throw new Error(`Duplicate timer item id "${i.id}"`);u.add(i.id),i.schedules?.forEach(o=>Y(o.everyMs,"schedule.everyMs"))})}export{ie as durationParts,me as useTimer,Te as useTimerGroup};
1
+ function Se(e){let s=Math.max(0,Math.trunc(Number.isFinite(e)?e:0)),a=Math.floor(s/864e5),o=s%864e5,N=Math.floor(o/36e5),w=o%36e5,g=Math.floor(w/6e4),A=w%6e4,C=Math.floor(A/1e3);return{totalMilliseconds:s,totalSeconds:Math.floor(s/1e3),milliseconds:A%1e3,seconds:C,minutes:g,hours:N,days:a}}import{useCallback as G,useEffect as fe,useMemo as be,useReducer as he,useRef as z}from"react";function Te(e){return e?e===!0?{enabled:!0,includeTicks:!1,logger:console.debug}:typeof e=="function"?{enabled:!0,includeTicks:!1,logger:e}:{enabled:e.enabled!==!1,includeTicks:e.includeTicks??!1,label:e.label,logger:e.logger??console.debug}:{enabled:!1,includeTicks:!1}}function R(e,s){let a=Te(e);!a.enabled||!a.logger||s.type==="timer:tick"&&!a.includeTicks||a.logger({...s,label:s.label??a.label})}function E(e,s){return{generation:s,tick:e.tick,now:e.now,elapsedMilliseconds:e.elapsedMilliseconds,status:e.status}}function d(){let e=Date.now(),s=typeof performance<"u"&&typeof performance.now=="function"?performance.now():e;return{wallNow:e,monotonicNow:s}}function j(e,s){if(!Number.isFinite(e)||e<=0)throw new RangeError(`${s} must be a finite number greater than 0`)}function re(e){return{status:"idle",generation:0,tick:0,startedAt:null,pausedAt:null,endedAt:null,cancelledAt:null,cancelReason:null,baseElapsedMilliseconds:0,activeStartedAtMonotonic:null,now:e.wallNow}}function ne(e,s){return e.status!=="running"||e.activeStartedAtMonotonic===null?e.baseElapsedMilliseconds:Math.max(0,e.baseElapsedMilliseconds+s.monotonicNow-e.activeStartedAtMonotonic)}function p(e,s){let a=ne(e,s);return{status:e.status,now:s.wallNow,tick:e.tick,startedAt:e.startedAt,pausedAt:e.pausedAt,endedAt:e.endedAt,cancelledAt:e.cancelledAt,cancelReason:e.cancelReason,elapsedMilliseconds:a,isIdle:e.status==="idle",isRunning:e.status==="running",isPaused:e.status==="paused",isEnded:e.status==="ended",isCancelled:e.status==="cancelled"}}function q(e,s){return e.status!=="idle"?!1:(e.status="running",e.startedAt=s.wallNow,e.pausedAt=null,e.endedAt=null,e.cancelledAt=null,e.cancelReason=null,e.activeStartedAtMonotonic=s.monotonicNow,e.now=s.wallNow,!0)}function oe(e,s){return e.status!=="running"?!1:(e.baseElapsedMilliseconds=ne(e,s),e.activeStartedAtMonotonic=null,e.status="paused",e.pausedAt=s.wallNow,e.now=s.wallNow,!0)}function ue(e,s){return e.status!=="paused"?!1:(e.status="running",e.pausedAt=null,e.activeStartedAtMonotonic=s.monotonicNow,e.now=s.wallNow,!0)}function ee(e,s,a={}){return e.generation+=1,e.tick=0,e.status=a.autoStart?"running":"idle",e.startedAt=a.autoStart?s.wallNow:null,e.pausedAt=null,e.endedAt=null,e.cancelledAt=null,e.cancelReason=null,e.baseElapsedMilliseconds=0,e.activeStartedAtMonotonic=a.autoStart?s.monotonicNow:null,e.now=s.wallNow,!0}function se(e,s){return ee(e,s,{autoStart:!0})}function ae(e,s,a){return e.status==="ended"||e.status==="cancelled"?!1:(e.baseElapsedMilliseconds=ne(e,s),e.activeStartedAtMonotonic=null,e.status="cancelled",e.cancelledAt=s.wallNow,e.cancelReason=a??null,e.now=s.wallNow,!0)}function ie(e,s){return e.status!=="running"?!1:(e.baseElapsedMilliseconds=ne(e,s),e.activeStartedAtMonotonic=null,e.status="ended",e.endedAt=s.wallNow,e.now=s.wallNow,!0)}function le(e,s){return e.status!=="running"?!1:(e.tick+=1,e.now=s.wallNow,!0)}function ye(e={}){let s=e.updateIntervalMs??1e3;j(s,"updateIntervalMs"),Ie(e.schedules);let a=z(e);a.current=e;let o=z(null);o.current===null&&(o.current=re(d()));let N=z(!1),w=z(null),g=z(new Map),A=z(null),[,C]=he(u=>u+1,0),c=G(()=>{w.current!==null&&(clearTimeout(w.current),w.current=null)},[]),O=G((u=d())=>p(o.current,u),[]),S=G((u,l,f={})=>{R(a.current.debug,{type:u,scope:"timer",...E(l,o.current.generation),...f})},[]),x=z(null),B=G(u=>{let l=o.current.generation;if(A.current!==l){A.current=l;try{a.current.onEnd?.(u,x.current)}catch(f){R(a.current.debug,{type:"callback:error",scope:"timer",...E(u,l),error:f})}}},[]),M=G((u,l,f,y,v)=>{if(f.pending&&(u.overlap??"skip")==="skip"){R(a.current.debug,{type:"schedule:skip",scope:"timer",scheduleId:u.id??l,reason:"overlap",...E(y,v)});return}f.lastRunAt=y.now,f.pending=!0,R(a.current.debug,{type:"schedule:start",scope:"timer",scheduleId:u.id??l,...E(y,v)}),Promise.resolve().then(()=>u.callback(y,x.current)).then(()=>{R(a.current.debug,{type:"schedule:end",scope:"timer",scheduleId:u.id??l,...E(y,v)})},b=>{R(a.current.debug,{type:"schedule:error",scope:"timer",scheduleId:u.id??l,error:b,...E(y,v)})}).finally(()=>{o.current?.generation===v&&(f.pending=!1)})},[]),J=G((u,l,f=!1)=>{let y=a.current.schedules??[],v=new Set;y.forEach((b,K)=>{let P=b.id??String(K);v.add(P);let k=g.current.get(P);if(k||(k={lastRunAt:null,pending:!1,leadingGeneration:null},g.current.set(P,k)),f&&b.leading&&k.leadingGeneration!==l){k.leadingGeneration=l,M(b,P,k,u,l);return}if(k.lastRunAt===null){k.lastRunAt=u.now;return}u.now-k.lastRunAt>=b.everyMs&&M(b,P,k,u,l)});for(let b of g.current.keys())v.has(b)||g.current.delete(b)},[M]),I=G((u=d(),l=!1)=>{let f=o.current;if(f.status!=="running")return;let y=p(f,u),v=f.generation;if(a.current.endWhen?.(y)){if(ie(f,u)){let b=p(f,u);S("timer:end",b),c(),B(b),C()}return}J(y,v,l)},[B,c,S,J]),Q=G((u=d())=>{let l=a.current.updateIntervalMs??1e3,f=l,y=o.current;return y.status!=="running"?l:((a.current.schedules??[]).forEach((b,K)=>{let P=b.id??String(K),te=g.current.get(P)?.lastRunAt??y.startedAt??u.wallNow;f=Math.min(f,Math.max(1,te+b.everyMs-u.wallNow))}),f)},[]),V=G(()=>{let u=d();if(!q(o.current,u))return;let l=p(o.current,u);S("timer:start",l),I(u,!0),C()},[S,I]),X=G(()=>{let u=d();if(!oe(o.current,u))return;c();let l=p(o.current,u);S("timer:pause",l),C()},[c,S]),Z=G(()=>{let u=d();if(!ue(o.current,u))return;let l=p(o.current,u);S("timer:resume",l),I(u,!0),C()},[S,I]),_=G((u={})=>{let l=d();c(),ee(o.current,l,u),g.current.clear(),A.current=null;let f=p(o.current,l);S("timer:reset",f),u.autoStart&&I(l,!0),C()},[c,S,I]),$=G(()=>{let u=d();c(),se(o.current,u),g.current.clear(),A.current=null;let l=p(o.current,u);S("timer:restart",l),I(u,!0),C()},[c,S,I]),F=G(u=>{let l=d();if(!ae(o.current,l,u))return;c();let f=p(o.current,l);S("timer:cancel",f,{reason:u}),C()},[c,S]);x.current=be(()=>({start:V,pause:X,resume:Z,reset:_,restart:$,cancel:F}),[F,X,_,$,Z,V]),fe(()=>(N.current=!0,a.current.autoStart&&o.current.status==="idle"&&x.current.start(),()=>{N.current=!1,c()}),[c]);let U=O(),H=o.current.generation,L=U.status;return fe(()=>{if(!N.current||L!=="running"){c();return}return c(),S("scheduler:start",O()),w.current=setTimeout(()=>{if(!N.current||o.current.generation!==H||o.current.status!=="running")return;let u=d();le(o.current,u);let l=p(o.current,u);S("timer:tick",l),I(u),C()},Q()),()=>{w.current!==null&&S("scheduler:stop",O()),c()}},[c,S,H,Q,O,I,U.tick,L]),{...U,...x.current}}function Ie(e){e?.forEach(s=>j(s.everyMs,"schedule.everyMs"))}import{useCallback as m,useEffect as pe,useMemo as we,useReducer as Ae,useRef as ce}from"react";function Me(e={}){let s=e.updateIntervalMs??1e3;j(s,"updateIntervalMs"),de(e.items);let a=ce(e);a.current=e;let o=ce(new Map),N=ce(!1),w=ce(null),[,g]=Ae(t=>t+1,0),A=m(()=>{w.current!==null&&(clearTimeout(w.current),w.current=null)},[]),C=m((t,r=d())=>p(t.state,r),[]),c=m((t,r,n,i={})=>{R(a.current.debug,{type:t,scope:"timer-group",timerId:r?.id,...E(n,r?.state.generation??0),...i})},[]),O=m(t=>({start:()=>$(t),pause:()=>F(t),resume:()=>U(t),reset:r=>H(t,r),restart:()=>L(t),cancel:r=>u(t,r)}),[]),S=m((t,r)=>{let n=t.state.generation;if(t.endCalledGeneration!==n){t.endCalledGeneration=n;try{t.definition.onEnd?.(r,O(t.id))}catch(i){R(a.current.debug,{type:"callback:error",scope:"timer-group",timerId:t.id,error:i,...E(r,n)})}}},[O]),x=m((t,r,n,i,h,T)=>{if(i.pending&&(r.overlap??"skip")==="skip"){R(a.current.debug,{type:"schedule:skip",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,reason:"overlap",...E(h,T)});return}i.lastRunAt=h.now,i.pending=!0,R(a.current.debug,{type:"schedule:start",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,...E(h,T)}),Promise.resolve().then(()=>r.callback(h,O(t.id))).then(()=>{R(a.current.debug,{type:"schedule:end",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,...E(h,T)})},W=>{R(a.current.debug,{type:"schedule:error",scope:"timer-group",timerId:t.id,scheduleId:r.id??n,error:W,...E(h,T)})}).finally(()=>{o.current.get(t.id)?.state.generation===T&&(i.pending=!1)})},[O]),B=m((t,r,n=!1)=>{let i=t.definition.schedules??[],h=new Set;i.forEach((T,W)=>{let Y=T.id??String(W);h.add(Y);let D=t.schedules.get(Y);if(D||(D={lastRunAt:null,pending:!1,leadingGeneration:null},t.schedules.set(Y,D)),n&&T.leading&&D.leadingGeneration!==t.state.generation){D.leadingGeneration=t.state.generation,x(t,T,Y,D,r,t.state.generation);return}if(D.lastRunAt===null){D.lastRunAt=t.state.startedAt??r.now,r.now-D.lastRunAt>=T.everyMs&&x(t,T,Y,D,r,t.state.generation);return}r.now-D.lastRunAt>=T.everyMs&&x(t,T,Y,D,r,t.state.generation)});for(let T of t.schedules.keys())h.has(T)||t.schedules.delete(T)},[x]),M=m((t,r=d(),n=!1)=>{if(t.state.status!=="running")return;let i=p(t.state,r);if(t.definition.endWhen?.(i)){if(ie(t.state,r)){let h=p(t.state,r);c("timer:end",t,h),S(t,h)}return}B(t,i,n)},[S,c,B]),J=m((t=d())=>{let n=a.current.updateIntervalMs??1e3;for(let i of o.current.values()){if(i.state.status!=="running")continue;(i.definition.schedules??[]).forEach((T,W)=>{let Y=T.id??String(W),ge=i.schedules.get(Y)?.lastRunAt??i.state.startedAt??t.wallNow;n=Math.min(n,Math.max(1,ge+T.everyMs-t.wallNow))})}return n},[]),I=m(t=>{let r=o.current.get(t.id);if(r)return r.definition=t,{item:r,added:!1};let n={id:t.id,state:re(d()),definition:t,schedules:new Map,endCalledGeneration:null};return o.current.set(t.id,n),t.autoStart&&q(n.state,d()),{item:n,added:!0}},[]),Q=m(()=>{let t=a.current.items??[],r=new Set,n=!1;t.forEach(i=>{r.add(i.id);let{item:h,added:T}=I(i);n=n||T,i.autoStart&&h.state.status==="idle"&&(n=q(h.state,d())||n)});for(let i of o.current.keys())r.has(i)||(o.current.delete(i),n=!0);return n},[I]);pe(()=>{Q()&&g()},[Q,e.items]);let V=m(t=>{if(de([t]),o.current.has(t.id))throw new Error(`Timer item "${t.id}" already exists`);I(t),g()},[I]),X=m((t,r)=>{let n=o.current.get(t);if(!n)return;let i={...n.definition,...r,id:t};de([i]),n.definition=i,g()},[]),Z=m(t=>{o.current.delete(t),g()},[]),_=m(()=>{o.current.clear(),A(),g()},[A]),$=m(t=>{let r=o.current.get(t);if(!r)return;let n=d();q(r.state,n)&&(c("timer:start",r,p(r.state,n)),M(r,n,!0),g())},[c,M]),F=m(t=>{let r=o.current.get(t);if(!r)return;let n=d();oe(r.state,n)&&(c("timer:pause",r,p(r.state,n)),g())},[c]),U=m(t=>{let r=o.current.get(t);if(!r)return;let n=d();ue(r.state,n)&&(c("timer:resume",r,p(r.state,n)),M(r,n,!0),g())},[c,M]),H=m((t,r={})=>{let n=o.current.get(t);if(!n)return;let i=d();ee(n.state,i,r),n.schedules.clear(),n.endCalledGeneration=null,c("timer:reset",n,p(n.state,i)),r.autoStart&&M(n,i,!0),g()},[c,M]),L=m(t=>{let r=o.current.get(t);if(!r)return;let n=d();se(r.state,n),r.schedules.clear(),r.endCalledGeneration=null,c("timer:restart",r,p(r.state,n)),M(r,n,!0),g()},[c,M]),u=m((t,r)=>{let n=o.current.get(t);if(!n)return;let i=d();ae(n.state,i,r)&&(c("timer:cancel",n,p(n.state,i),{reason:r}),g())},[c]),f=Array.from(o.current.keys()).map(t=>`${t}:${o.current.get(t).state.status}:${o.current.get(t).state.generation}:${o.current.get(t).state.tick}`).join("|");pe(()=>{N.current=!0;let t=Array.from(o.current.values()).filter(n=>n.state.status==="running");if(t.length===0){A();return}A();let r=t[0];return c("scheduler:start",r,p(r.state,d())),w.current=setTimeout(()=>{if(!N.current)return;let n=d();for(let i of o.current.values()){if(i.state.status!=="running")continue;le(i.state,n);let h=p(i.state,n);c("timer:tick",i,h),M(i,n)}g()},J()),()=>{w.current!==null&&c("scheduler:stop",r,p(r.state,d())),A(),N.current=!1}},[f,A,c,J,M]);let y=m(t=>{let r=o.current.get(t);if(r)return C(r)},[C]),v=d().wallNow,b=m(()=>Array.from(o.current.keys()).forEach($),[$]),K=m(()=>Array.from(o.current.keys()).forEach(F),[F]),P=m(()=>Array.from(o.current.keys()).forEach(U),[U]),k=m(t=>Array.from(o.current.keys()).forEach(r=>H(r,t)),[H]),te=m(()=>Array.from(o.current.keys()).forEach(L),[L]),me=m(t=>Array.from(o.current.keys()).forEach(r=>u(r,t)),[u]);return we(()=>({now:v,size:o.current.size,ids:Array.from(o.current.keys()),get:y,add:V,update:X,remove:Z,clear:_,start:$,pause:F,resume:U,reset:H,restart:L,cancel:u,startAll:b,pauseAll:K,resumeAll:P,resetAll:k,restartAll:te,cancelAll:me}),[V,u,me,_,y,v,F,K,Z,H,k,L,te,U,P,$,b,X])}function de(e){let s=new Set;e?.forEach(a=>{if(s.has(a.id))throw new Error(`Duplicate timer item id "${a.id}"`);s.add(a.id),a.schedules?.forEach(o=>j(o.everyMs,"schedule.everyMs"))})}export{Se as durationParts,ye as useTimer,Me as useTimerGroup};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@crup/react-timer-hook",
3
- "version": "0.0.1-alpha.7",
4
- "description": "Reliable React timer lifecycle hooks for countdowns, stopwatches, schedules, and many independent timers.",
3
+ "version": "0.0.1-alpha.9",
4
+ "description": "React timer lifecycle hooks for countdowns, stopwatches, schedules, and many independent timers.",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",