@timekeeper-countdown/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/countdown-engine-tLXf4OVD.d.ts +61 -0
- package/dist/format.d.ts +39 -0
- package/dist/format.js +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +1 -0
- package/dist/testing-utils.d.ts +33 -0
- package/dist/testing-utils.js +1 -0
- package/package.json +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time providers with fallbacks for critical APIs
|
|
3
|
+
* Ensures the library works even without modern browser APIs
|
|
4
|
+
*/
|
|
5
|
+
interface TimeProvider {
|
|
6
|
+
now(): number;
|
|
7
|
+
isHighResolution: boolean;
|
|
8
|
+
type: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare const TimerState: Readonly<{
|
|
12
|
+
IDLE: "IDLE";
|
|
13
|
+
RUNNING: "RUNNING";
|
|
14
|
+
PAUSED: "PAUSED";
|
|
15
|
+
STOPPED: "STOPPED";
|
|
16
|
+
}>;
|
|
17
|
+
type TimerState = (typeof TimerState)[keyof typeof TimerState];
|
|
18
|
+
|
|
19
|
+
interface CountdownParts {
|
|
20
|
+
years: number;
|
|
21
|
+
weeks: number;
|
|
22
|
+
days: number;
|
|
23
|
+
hours: number;
|
|
24
|
+
minutes: number;
|
|
25
|
+
seconds: number;
|
|
26
|
+
totalDays: number;
|
|
27
|
+
totalHours: number;
|
|
28
|
+
totalMinutes: number;
|
|
29
|
+
}
|
|
30
|
+
interface CountdownSnapshot {
|
|
31
|
+
initialSeconds: number;
|
|
32
|
+
totalSeconds: number;
|
|
33
|
+
parts: CountdownParts;
|
|
34
|
+
state: TimerState;
|
|
35
|
+
isRunning: boolean;
|
|
36
|
+
isCompleted: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface CountdownSubscription {
|
|
39
|
+
unsubscribe: () => void;
|
|
40
|
+
}
|
|
41
|
+
interface CountdownEngineOptions {
|
|
42
|
+
onSnapshot?: (snapshot: CountdownSnapshot) => void;
|
|
43
|
+
onStateChange?: (state: TimerState, snapshot: CountdownSnapshot) => void;
|
|
44
|
+
onError?: (error: Error) => void;
|
|
45
|
+
timeProvider?: TimeProvider | (() => number);
|
|
46
|
+
tickIntervalMs?: number;
|
|
47
|
+
}
|
|
48
|
+
interface CountdownEngineInstance {
|
|
49
|
+
start: () => boolean;
|
|
50
|
+
pause: () => boolean;
|
|
51
|
+
resume: () => boolean;
|
|
52
|
+
reset: (nextInitialSeconds?: number) => boolean;
|
|
53
|
+
stop: () => boolean;
|
|
54
|
+
setSeconds: (seconds: number) => void;
|
|
55
|
+
getSnapshot: () => CountdownSnapshot;
|
|
56
|
+
subscribe: (listener: (snapshot: CountdownSnapshot) => void) => CountdownSubscription;
|
|
57
|
+
destroy: () => void;
|
|
58
|
+
}
|
|
59
|
+
declare function CountdownEngine(initialSecondsInput: number, options?: CountdownEngineOptions): CountdownEngineInstance;
|
|
60
|
+
|
|
61
|
+
export { type CountdownSnapshot as C, TimerState as T, CountdownEngine as a, type CountdownEngineInstance as b, type CountdownEngineOptions as c, type CountdownParts as d, type TimeProvider as e };
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { C as CountdownSnapshot } from './countdown-engine-tLXf4OVD.js';
|
|
2
|
+
|
|
3
|
+
type FormatTarget = number | Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined;
|
|
4
|
+
declare function Formatter(): {
|
|
5
|
+
formatTime: (target: FormatTarget) => {
|
|
6
|
+
minutes: string;
|
|
7
|
+
seconds: string;
|
|
8
|
+
};
|
|
9
|
+
formatMinutes: (target: FormatTarget) => string;
|
|
10
|
+
formatSeconds: (target: FormatTarget) => string;
|
|
11
|
+
formatHours: (target: FormatTarget) => string;
|
|
12
|
+
formatDays: (target: FormatTarget) => string;
|
|
13
|
+
formatWeeks: (target: FormatTarget) => string;
|
|
14
|
+
formatYears: (target: FormatTarget) => string;
|
|
15
|
+
};
|
|
16
|
+
declare const defaultFormatter: {
|
|
17
|
+
formatTime: (target: FormatTarget) => {
|
|
18
|
+
minutes: string;
|
|
19
|
+
seconds: string;
|
|
20
|
+
};
|
|
21
|
+
formatMinutes: (target: FormatTarget) => string;
|
|
22
|
+
formatSeconds: (target: FormatTarget) => string;
|
|
23
|
+
formatHours: (target: FormatTarget) => string;
|
|
24
|
+
formatDays: (target: FormatTarget) => string;
|
|
25
|
+
formatWeeks: (target: FormatTarget) => string;
|
|
26
|
+
formatYears: (target: FormatTarget) => string;
|
|
27
|
+
};
|
|
28
|
+
declare const formatTime: (target: FormatTarget) => {
|
|
29
|
+
minutes: string;
|
|
30
|
+
seconds: string;
|
|
31
|
+
};
|
|
32
|
+
declare const formatMinutes: (target: FormatTarget) => string;
|
|
33
|
+
declare const formatSeconds: (target: FormatTarget) => string;
|
|
34
|
+
declare const formatHours: (target: FormatTarget) => string;
|
|
35
|
+
declare const formatDays: (target: FormatTarget) => string;
|
|
36
|
+
declare const formatWeeks: (target: FormatTarget) => string;
|
|
37
|
+
declare const formatYears: (target: FormatTarget) => string;
|
|
38
|
+
|
|
39
|
+
export { type FormatTarget, Formatter, defaultFormatter, formatDays, formatHours, formatMinutes, formatSeconds, formatTime, formatWeeks, formatYears };
|
package/dist/format.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function u(t){return typeof t=="number"?t:t&&typeof t.totalSeconds=="number"?t.totalSeconds:0}function c(t){return typeof t!="number"||!Number.isFinite(t)||t<=0?0:t>=Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:Math.floor(t)}function e(t,a=2){try{return !Number.isFinite(t)||t<0?"0".repeat(a):Math.floor(t).toString().padStart(a,"0")}catch{return "0".repeat(a)}}function s(t){return Math.floor(t/60)}function i(t){return Math.floor(t/3600)%24}function S(t){return Math.floor(t/86400)%7}function p(t){return Math.floor(t/604800)%52}function E(t){return Math.floor(t/31536e3)}function f(){let t=r=>c(u(r));return {formatTime:r=>{let o=t(r);return {minutes:e(s(o)),seconds:e(o%60)}},formatMinutes:r=>{let o=t(r);return e(s(o))},formatSeconds:r=>{let o=t(r);return e(o%60)},formatHours:r=>{let o=t(r);return e(i(o))},formatDays:r=>{let o=t(r);return e(S(o))},formatWeeks:r=>{let o=t(r);return e(p(o))},formatYears:r=>{let o=t(r);return e(E(o))}}}var n=f(),F=t=>n.formatTime(t),T=t=>n.formatMinutes(t),d=t=>n.formatSeconds(t),_=t=>n.formatHours(t),b=t=>n.formatDays(t),g=t=>n.formatWeeks(t),M=t=>n.formatYears(t);export{f as Formatter,n as defaultFormatter,b as formatDays,_ as formatHours,T as formatMinutes,d as formatSeconds,F as formatTime,g as formatWeeks,M as formatYears};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { T as TimerState, C as CountdownSnapshot } from './countdown-engine-tLXf4OVD.js';
|
|
2
|
+
export { a as CountdownEngine, b as CountdownEngineInstance, c as CountdownEngineOptions, d as CountdownParts } from './countdown-engine-tLXf4OVD.js';
|
|
3
|
+
export { FormatTarget, Formatter, defaultFormatter, formatDays, formatHours, formatMinutes, formatSeconds, formatTime, formatWeeks, formatYears } from './format.js';
|
|
4
|
+
|
|
5
|
+
interface CountdownOptions {
|
|
6
|
+
onUpdate?: (minutes: string, seconds: string) => void;
|
|
7
|
+
onStateChange?: (state: TimerState) => void;
|
|
8
|
+
}
|
|
9
|
+
interface CountdownInstance {
|
|
10
|
+
start: () => void;
|
|
11
|
+
pause: () => void;
|
|
12
|
+
resume: () => void;
|
|
13
|
+
reset: (newInitialSeconds?: number) => void;
|
|
14
|
+
stop: () => void;
|
|
15
|
+
getSeconds: () => string;
|
|
16
|
+
getMinutes: () => string;
|
|
17
|
+
getHours: () => string;
|
|
18
|
+
getDays: () => string;
|
|
19
|
+
getWeeks: () => string;
|
|
20
|
+
getYears: () => string;
|
|
21
|
+
getCurrentState: () => TimerState;
|
|
22
|
+
getSnapshot: () => CountdownSnapshot;
|
|
23
|
+
destroy: () => void;
|
|
24
|
+
}
|
|
25
|
+
declare function Countdown(initialSeconds: number, options?: CountdownOptions): CountdownInstance;
|
|
26
|
+
|
|
27
|
+
export { Countdown, type CountdownInstance, type CountdownOptions, CountdownSnapshot, TimerState };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function Y(){return {now:()=>performance.now(),isHighResolution:true,type:"performance"}}function G(){return {now:()=>Date.now(),isHighResolution:false,type:"date"}}function y(){let t,e=G();function i(){try{if(typeof performance<"u"&&typeof performance.now=="function"){let o=performance.now();if(typeof o=="number"&&Number.isFinite(o)&&!isNaN(o)&&o>=0&&o<=Number.MAX_SAFE_INTEGER){t=Y();return}}throw new Error("performance.now() not available or invalid")}catch{t=e;}}return i(),{now:()=>{try{let o=t.now();if(typeof o=="number"&&Number.isFinite(o)&&!isNaN(o)&&o>=0&&o<=Number.MAX_SAFE_INTEGER)return o;throw new Error("Invalid time value returned")}catch{t!==e&&(t=e);try{let o=e.now();if(typeof o=="number"&&Number.isFinite(o)&&!isNaN(o)&&o>=0)return o}catch{}return 0}},get isHighResolution(){return t.isHighResolution},get type(){return t.type}}}y();var L=10,V=100;function _(t,e,i={}){if(typeof t!="number"||!Number.isFinite(t))throw new Error("initialSeconds must be a finite number");if(t<0)throw new Error("initialSeconds must be non-negative");if(t>Number.MAX_SAFE_INTEGER)throw new Error("initialSeconds exceeds maximum safe integer");if(!e||typeof e!="object")throw new Error("events must be an object");if(typeof e.onTick!="function")throw new Error("events.onTick must be a function");if(typeof e.onComplete!="function")throw new Error("events.onComplete must be a function");if(typeof e.onError!="function")throw new Error("events.onError must be a function");let o=Math.floor(Math.max(0,t)),c=null,r=null,u=0,S=o,a=o,s=Math.max(L,Math.floor(i.tickIntervalMs??V)),b=y(),d=i.timeProvider??(()=>b.now()),f=()=>setInterval(()=>{try{if(r===null)return;let h=d()-r-u,D=Math.floor(h/1e3),n=Math.max(0,a-D);o=n,n!==S&&(S=n,e.onTick(n)),n===0&&(E(),e.onComplete());}catch(l){E(),e.onError(l instanceof Error?l:new Error(String(l)));}},s),p=()=>{if(c||o<=0)return false;try{if(r!==null&&o<a){let l=(a-o)*1e3;u=d()-r-l;}else r=d(),u=0,S=o;return c=f(),!0}catch(l){return e.onError(l instanceof Error?l:new Error(String(l))),false}},E=()=>{c&&(clearInterval(c),c=null);},C=()=>{E(),o=a,r=null,u=0,S=a;},v=l=>{if(typeof l!="number"||!Number.isFinite(l))return;let h=Math.floor(Math.max(0,Math.min(l,Number.MAX_SAFE_INTEGER)));o=h,r=null,u=0,S=h;};return {start:p,stop:E,reset:C,setSeconds:v,setInitialValue:l=>{if(typeof l!="number"||!Number.isFinite(l))return;let h=Math.floor(Math.max(0,Math.min(l,Number.MAX_SAFE_INTEGER)));a=h,v(h);},getTotalSeconds:()=>o,getInitialValue:()=>a,isRunning:()=>c!==null,destroy:()=>{E(),o=0,r=null,u=0;}}}var m=Object.freeze({IDLE:"IDLE",RUNNING:"RUNNING",PAUSED:"PAUSED",STOPPED:"STOPPED"}),P={[m.IDLE]:{start:m.RUNNING},[m.RUNNING]:{pause:m.PAUSED,reset:m.IDLE,stop:m.STOPPED,complete:m.STOPPED},[m.PAUSED]:{resume:m.RUNNING,reset:m.IDLE,stop:m.STOPPED},[m.STOPPED]:{reset:m.IDLE}};function x(t){if(t!==void 0){if(typeof t!="object"||t===null)throw new Error("events must be an object");if(t.onStateChange!==void 0&&typeof t.onStateChange!="function")throw new Error("events.onStateChange must be a function")}let e=m.IDLE;function i(b){let d=P[e]?.[b];if(!d)return false;e=d;try{t?.onStateChange?.(d);}catch{}return true}function o(){return i("start")}function c(){return i("resume")}function r(){return i("pause")}function u(){return i("reset")}function S(){return i("stop")}function a(){return i("complete")}function s(){e===m.IDLE||e===m.STOPPED||i("stop");}return {start:o,resume:c,pause:r,reset:u,stop:S,complete:a,getCurrentState:()=>e,canStart:()=>!!P[e]?.start,canResume:()=>!!P[e]?.resume,canPause:()=>!!P[e]?.pause,isRunning:()=>e===m.RUNNING,destroy:s}}function O(t){if(typeof t!="number"||!Number.isFinite(t)||!Number.isInteger(t))throw new Error("initialSeconds must be a finite, non-negative integer");if(t<0)throw new Error("initialSeconds must be non-negative");if(t>Number.MAX_SAFE_INTEGER)throw new Error("initialSeconds exceeds maximum safe integer");return t}function q(t){if(!t){let e=y();return ()=>e.now()}if(typeof t=="function")return t;if(typeof t.now=="function"){let e=t;return ()=>e.now()}throw new Error("timeProvider must implement a now(): number method")}function J(t){let e=Math.max(0,Math.floor(t)),i=Math.floor(e/31536e3),o=Math.floor(e/604800)%52,c=Math.floor(e/86400)%7,r=Math.floor(e/3600)%24,u=Math.floor(e/60)%60,S=e%60,a=Math.floor(e/86400),s=Math.floor(e/3600),b=Math.floor(e/60);return {years:i,weeks:o,days:c,hours:r,minutes:u,seconds:S,totalDays:a,totalHours:s,totalMinutes:b}}function I(t,e,i){let o=J(e);return {initialSeconds:t,totalSeconds:e,parts:o,state:i,isRunning:i===m.RUNNING,isCompleted:e===0&&i===m.STOPPED}}function R(t,e={}){let i=O(t),o=q(e.timeProvider),c=i,r=I(i,i,m.IDLE),u=new Set,S=n=>{try{e.onSnapshot?.(n);}catch{}for(let N of u)try{N(n);}catch{}},a=n=>{r=I(c,r.totalSeconds,n);try{e.onStateChange?.(n,r);}catch{}S(r);},s=n=>{r=I(c,n,d.getCurrentState()),S(r);},b=n=>{try{e.onError?.(n);}catch{}},d=x({onStateChange:n=>{a(n);}}),f=_(i,{onTick:n=>{s(n);},onComplete:()=>{d.complete(),s(0);},onError:n=>{b(n),d.stop();}},{timeProvider:o,tickIntervalMs:e.tickIntervalMs});return {start:()=>{if(!d.canStart())return false;let n=f.start();return n&&(d.start(),s(f.getTotalSeconds())),n},pause:()=>{if(!d.canPause())return false;f.stop();let n=d.pause();return n&&s(f.getTotalSeconds()),n},resume:()=>{if(!d.canResume())return false;let n=f.start();return n&&(d.resume(),s(f.getTotalSeconds())),n},reset:n=>{if(f.stop(),typeof n=="number"){let N=O(n);c=N,f.setInitialValue(N);}else c=i,f.reset();return d.reset(),s(f.getTotalSeconds()),true},stop:()=>{f.stop();let n=d.stop();return n&&(f.setSeconds(0),s(0)),n},setSeconds:n=>{f.setSeconds(n),s(f.getTotalSeconds());},getSnapshot:()=>r,subscribe:n=>{u.add(n);try{n(r);}catch{}return {unsubscribe:()=>{u.delete(n);}}},destroy:()=>{f.destroy(),d.destroy(),u.clear(),r=I(c,0,m.STOPPED);}}}function Q(t){return typeof t=="number"?t:t&&typeof t.totalSeconds=="number"?t.totalSeconds:0}function Z(t){return typeof t!="number"||!Number.isFinite(t)||t<=0?0:t>=Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:Math.floor(t)}function T(t,e=2){try{return !Number.isFinite(t)||t<0?"0".repeat(e):Math.floor(t).toString().padStart(e,"0")}catch{return "0".repeat(e)}}function k(t){return Math.floor(t/60)}function $(t){return Math.floor(t/3600)%24}function tt(t){return Math.floor(t/86400)%7}function et(t){return Math.floor(t/604800)%52}function ot(t){return Math.floor(t/31536e3)}function M(){let t=a=>Z(Q(a));return {formatTime:a=>{let s=t(a);return {minutes:T(k(s)),seconds:T(s%60)}},formatMinutes:a=>{let s=t(a);return T(k(s))},formatSeconds:a=>{let s=t(a);return T(s%60)},formatHours:a=>{let s=t(a);return T($(s))},formatDays:a=>{let s=t(a);return T(tt(s))},formatWeeks:a=>{let s=t(a);return T(et(s))},formatYears:a=>{let s=t(a);return T(ot(s))}}}var g=M(),nt=t=>g.formatTime(t),rt=t=>g.formatMinutes(t),at=t=>g.formatSeconds(t),st=t=>g.formatHours(t),it=t=>g.formatDays(t),ut=t=>g.formatWeeks(t),ct=t=>g.formatYears(t);function mt(t,e={}){let{onUpdate:i,onStateChange:o}=e;if(i!==void 0&&typeof i!="function")throw new Error("onUpdate must be a function");if(o!==void 0&&typeof o!="function")throw new Error("onStateChange must be a function");let c=M(),r=R(t),u=r.getSnapshot(),S=u.state,a=Number.NaN,s=p=>{if(i)try{let{minutes:E,seconds:C}=c.formatTime(p.totalSeconds);i(E,C);}catch{}},b=p=>{if(o)try{o(p);}catch{}},d=r.subscribe(p=>{u=p,p.state!==S&&(S=p.state,b(p.state)),p.totalSeconds!==a&&(a=p.totalSeconds,s(p));}),f=p=>{try{p();}catch{}};return {start:()=>{f(()=>{r.start();});},pause:()=>{f(()=>{r.pause();});},resume:()=>{f(()=>{r.resume();});},reset:p=>{f(()=>{r.reset(p);});},stop:()=>{f(()=>{r.stop();});},getSeconds:()=>{try{return c.formatSeconds(u.totalSeconds)}catch{return "00"}},getMinutes:()=>{try{return c.formatMinutes(u.totalSeconds)}catch{return "00"}},getHours:()=>{try{return c.formatHours(u.totalSeconds)}catch{return "00"}},getDays:()=>{try{return c.formatDays(u.totalSeconds)}catch{return "00"}},getWeeks:()=>{try{return c.formatWeeks(u.totalSeconds)}catch{return "00"}},getYears:()=>{try{return c.formatYears(u.totalSeconds)}catch{return "00"}},getCurrentState:()=>{try{return u.state}catch{return m.IDLE}},getSnapshot:()=>u,destroy:()=>{f(()=>{d.unsubscribe(),r.destroy();});}}}export{mt as Countdown,R as CountdownEngine,M as Formatter,m as TimerState,g as defaultFormatter,it as formatDays,st as formatHours,rt as formatMinutes,at as formatSeconds,nt as formatTime,ut as formatWeeks,ct as formatYears};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { e as TimeProvider, T as TimerState, C as CountdownSnapshot } from './countdown-engine-tLXf4OVD.js';
|
|
2
|
+
|
|
3
|
+
interface FakeTimeProvider extends TimeProvider {
|
|
4
|
+
advance: (ms?: number) => number;
|
|
5
|
+
set: (ms: number) => number;
|
|
6
|
+
reset: () => number;
|
|
7
|
+
getTime: () => number;
|
|
8
|
+
}
|
|
9
|
+
interface FakeTimeOptions {
|
|
10
|
+
startMs?: number;
|
|
11
|
+
tickMs?: number;
|
|
12
|
+
highResolution?: boolean;
|
|
13
|
+
}
|
|
14
|
+
declare function createFakeTimeProvider(options?: FakeTimeOptions): FakeTimeProvider;
|
|
15
|
+
declare function toTimeProvider(fake: FakeTimeProvider): TimeProvider;
|
|
16
|
+
|
|
17
|
+
interface SnapshotOptions {
|
|
18
|
+
initialSeconds?: number;
|
|
19
|
+
totalSeconds?: number;
|
|
20
|
+
state?: TimerState;
|
|
21
|
+
}
|
|
22
|
+
declare function buildSnapshot(options?: SnapshotOptions): CountdownSnapshot;
|
|
23
|
+
interface SequenceOptions extends SnapshotOptions {
|
|
24
|
+
step?: number;
|
|
25
|
+
count?: number;
|
|
26
|
+
}
|
|
27
|
+
declare function buildSnapshotSequence(options?: SequenceOptions): CountdownSnapshot[];
|
|
28
|
+
|
|
29
|
+
declare function assertSnapshotState(snapshot: CountdownSnapshot, expected: TimerState, message?: string): void;
|
|
30
|
+
declare function assertSnapshotCompleted(snapshot: CountdownSnapshot, message?: string): void;
|
|
31
|
+
declare function assertRemainingSeconds(snapshot: CountdownSnapshot, expected: number, tolerance?: number, message?: string): void;
|
|
32
|
+
|
|
33
|
+
export { type FakeTimeOptions, type FakeTimeProvider, type SequenceOptions, type SnapshotOptions, TimerState, assertRemainingSeconds, assertSnapshotCompleted, assertSnapshotState, buildSnapshot, buildSnapshotSequence, createFakeTimeProvider, toTimeProvider };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var c=t=>!Number.isFinite(t)||t<0?0:t>Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:Math.floor(t);function h(t={}){let{startMs:e=0,tickMs:r=1e3,highResolution:a=true}=t,o=c(e),i=c(r);return {now:()=>o,advance:(s=i)=>(o=c(o+c(s)),o),set:s=>(o=c(s),o),reset:()=>(o=c(e),o),getTime:()=>o,isHighResolution:a,type:"fake"}}function T(t){return {now:()=>t.now(),isHighResolution:t.isHighResolution,type:t.type}}var n=Object.freeze({IDLE:"IDLE",RUNNING:"RUNNING",PAUSED:"PAUSED",STOPPED:"STOPPED"});({[n.IDLE]:{start:n.RUNNING},[n.RUNNING]:{pause:n.PAUSED,reset:n.IDLE,stop:n.STOPPED,complete:n.STOPPED},[n.PAUSED]:{resume:n.RUNNING,reset:n.IDLE,stop:n.STOPPED},[n.STOPPED]:{reset:n.IDLE}});var m=t=>typeof t!="number"||!Number.isFinite(t)||t<=0?0:t>=Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:Math.floor(t),l=t=>{let e=m(t),r=Math.floor(e/31536e3),a=Math.floor(e/604800)%52,o=Math.floor(e/86400)%7,i=Math.floor(e/3600)%24,S=Math.floor(e/60)%60,d=e%60,p=Math.floor(e/86400),u=Math.floor(e/3600),s=Math.floor(e/60);return {years:r,weeks:a,days:o,hours:i,minutes:S,seconds:d,totalDays:p,totalHours:u,totalMinutes:s}};function b(t={}){let e=m(t.initialSeconds??t.totalSeconds??0),r=m(t.totalSeconds??t.initialSeconds??0),a=t.state??(r>0?n.IDLE:n.STOPPED),o=l(r);return {initialSeconds:e,totalSeconds:r,parts:o,state:a,isRunning:a===n.RUNNING,isCompleted:r===0&&a===n.STOPPED}}function w(t={}){let{totalSeconds:e=0,step:r=1,count:a=1,initialSeconds:o}=t,i=m(e),S=m(r),d=m(a)||1,p=[];for(let u=0;u<d;u+=1){let s=Math.max(i-S*u,0);p.push(b({initialSeconds:o??i,totalSeconds:s,state:s===0?n.STOPPED:n.RUNNING}));}return p}var f=(t,e)=>e?`${t}: ${e}`:t;function x(t,e,r){if(t.state!==e)throw new Error(f(r??"Unexpected countdown state",`expected ${e} but received ${t.state}`))}function I(t,e){if(!t.isCompleted||t.totalSeconds!==0)throw new Error(f(e??"Countdown should be completed"))}function y(t,e,r=0,a){if(typeof e!="number"||!Number.isFinite(e))throw new Error("Expected remaining seconds must be a finite number");if(Math.abs(t.totalSeconds-Math.floor(e))>r)throw new Error(f(a??"Unexpected remaining seconds",`expected ${e}\xB1${r} but received ${t.totalSeconds}`))}export{n as TimerState,y as assertRemainingSeconds,I as assertSnapshotCompleted,x as assertSnapshotState,b as buildSnapshot,w as buildSnapshotSequence,h as createFakeTimeProvider,T as toTimeProvider};
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@timekeeper-countdown/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lightweight countdown engine that powers the Timekeeper packages. Pure TypeScript with no runtime dependencies.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"countdown",
|
|
7
|
+
"timer",
|
|
8
|
+
"javascript",
|
|
9
|
+
"typescript",
|
|
10
|
+
"engine",
|
|
11
|
+
"state-machine",
|
|
12
|
+
"snapshot",
|
|
13
|
+
"scheduler"
|
|
14
|
+
],
|
|
15
|
+
"author": "Eduardo Kohn",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/eagle-head/timekeeper-countdown.git"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://eagle-head.github.io/timekeeper-countdown/",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/eagle-head/timekeeper-countdown/issues"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"module": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./format": {
|
|
35
|
+
"types": "./dist/format.d.ts",
|
|
36
|
+
"import": "./dist/format.js"
|
|
37
|
+
},
|
|
38
|
+
"./testing-utils": {
|
|
39
|
+
"types": "./dist/testing-utils.d.ts",
|
|
40
|
+
"import": "./dist/testing-utils.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist"
|
|
45
|
+
],
|
|
46
|
+
"sideEffects": false,
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup",
|
|
49
|
+
"dev": "tsup --watch",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:ui": "vitest --ui",
|
|
52
|
+
"test:coverage": "vitest run --coverage",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"lint": "eslint .",
|
|
55
|
+
"lint:fix": "eslint . --fix",
|
|
56
|
+
"format": "prettier --write src/**/*.ts",
|
|
57
|
+
"format:check": "prettier --check src/**/*.ts",
|
|
58
|
+
"prepublishOnly": "npm run build && npm run test && npm run typecheck"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=18"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"registry": "https://registry.npmjs.org",
|
|
65
|
+
"access": "public"
|
|
66
|
+
}
|
|
67
|
+
}
|