@timekeeper-countdown/core 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -14
- package/dist/{countdown-engine-tLXf4OVD.d.cts → countdown-engine-qnWxt-ed.d.cts} +2 -1
- package/dist/{countdown-engine-tLXf4OVD.d.ts → countdown-engine-qnWxt-ed.d.ts} +2 -1
- package/dist/format.cjs +1 -1
- package/dist/format.d.cts +18 -8
- package/dist/format.d.ts +18 -8
- package/dist/format.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/testing-utils.d.cts +1 -1
- package/dist/testing-utils.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,9 +32,11 @@ Supported runtimes:
|
|
|
32
32
|
|
|
33
33
|
```ts
|
|
34
34
|
import { Countdown, TimerState } from '@timekeeper-countdown/core';
|
|
35
|
+
import { formatTime } from '@timekeeper-countdown/core/format';
|
|
35
36
|
|
|
36
37
|
const countdown = Countdown(300, {
|
|
37
|
-
|
|
38
|
+
onSnapshot: snapshot => {
|
|
39
|
+
const { minutes, seconds } = formatTime(snapshot);
|
|
38
40
|
timerElement.textContent = `${minutes}:${seconds}`;
|
|
39
41
|
},
|
|
40
42
|
onStateChange: state => {
|
|
@@ -42,6 +44,9 @@ const countdown = Countdown(300, {
|
|
|
42
44
|
console.log('Finished!');
|
|
43
45
|
}
|
|
44
46
|
},
|
|
47
|
+
onError: error => {
|
|
48
|
+
console.error('Countdown error:', error);
|
|
49
|
+
},
|
|
45
50
|
});
|
|
46
51
|
|
|
47
52
|
countdown.start(); // Begin the countdown
|
|
@@ -116,6 +121,40 @@ interface CountdownSnapshot {
|
|
|
116
121
|
|
|
117
122
|
---
|
|
118
123
|
|
|
124
|
+
## State Transitions
|
|
125
|
+
|
|
126
|
+
The engine enforces a strict state machine. Invalid transitions are silently ignored and return `false`.
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
start()
|
|
130
|
+
IDLE ──────────────► RUNNING
|
|
131
|
+
▲ │ │
|
|
132
|
+
│ │ │
|
|
133
|
+
│ reset() pause() stop() / complete()
|
|
134
|
+
│ │ │
|
|
135
|
+
│ ▼ │
|
|
136
|
+
│ PAUSED │
|
|
137
|
+
│ │ │
|
|
138
|
+
│ reset() │ │
|
|
139
|
+
│◄───────────────────┘ │
|
|
140
|
+
│ │
|
|
141
|
+
│ reset() ▼
|
|
142
|
+
│◄──────────────────── STOPPED
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
| From | Action | To |
|
|
146
|
+
| --------- | ---------- | --------- |
|
|
147
|
+
| `IDLE` | `start()` | `RUNNING` |
|
|
148
|
+
| `RUNNING` | `pause()` | `PAUSED` |
|
|
149
|
+
| `RUNNING` | `reset()` | `IDLE` |
|
|
150
|
+
| `RUNNING` | `stop()` | `STOPPED` |
|
|
151
|
+
| `PAUSED` | `resume()` | `RUNNING` |
|
|
152
|
+
| `PAUSED` | `reset()` | `IDLE` |
|
|
153
|
+
| `PAUSED` | `stop()` | `STOPPED` |
|
|
154
|
+
| `STOPPED` | `reset()` | `IDLE` |
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
119
158
|
## Formatting Helpers
|
|
120
159
|
|
|
121
160
|
Use the helpers exported at `@timekeeper-countdown/core/format` to avoid reimplementing `padStart` logic.
|
|
@@ -145,28 +184,140 @@ import {
|
|
|
145
184
|
createFakeTimeProvider,
|
|
146
185
|
toTimeProvider,
|
|
147
186
|
buildSnapshot,
|
|
187
|
+
buildSnapshotSequence,
|
|
148
188
|
assertSnapshotState,
|
|
189
|
+
assertSnapshotCompleted,
|
|
190
|
+
assertRemainingSeconds,
|
|
191
|
+
TimerState,
|
|
149
192
|
} from '@timekeeper-countdown/core/testing-utils';
|
|
193
|
+
```
|
|
150
194
|
|
|
151
|
-
|
|
152
|
-
const engine = CountdownEngine(5, {
|
|
153
|
-
timeProvider: toTimeProvider(fake),
|
|
154
|
-
tickIntervalMs: 5,
|
|
155
|
-
});
|
|
195
|
+
### `createFakeTimeProvider(options?)`
|
|
156
196
|
|
|
157
|
-
|
|
158
|
-
|
|
197
|
+
Provides a controllable clock for deterministic testing.
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
interface FakeTimeOptions {
|
|
201
|
+
startMs?: number; // default: 0
|
|
202
|
+
tickMs?: number; // default: 1000 — default step for advance()
|
|
203
|
+
highResolution?: boolean; // default: true
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
interface FakeTimeProvider extends TimeProvider {
|
|
207
|
+
advance(ms?: number): number; // advances by ms (or tickMs if omitted), returns current time
|
|
208
|
+
set(ms: number): number; // sets clock to absolute value, returns current time
|
|
209
|
+
reset(): number; // resets to startMs, returns current time
|
|
210
|
+
getTime(): number; // returns current time without advancing
|
|
211
|
+
now(): number; // same as getTime (inherited from TimeProvider)
|
|
212
|
+
isHighResolution: boolean;
|
|
213
|
+
type: 'fake';
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function createFakeTimeProvider(options?: FakeTimeOptions): FakeTimeProvider;
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
- Negative or non-finite values are clamped to `0`.
|
|
220
|
+
- Values above `Number.MAX_SAFE_INTEGER` are clamped to `Number.MAX_SAFE_INTEGER`.
|
|
221
|
+
- `advance()` without a parameter uses `tickMs` as the default step.
|
|
222
|
+
|
|
223
|
+
### `toTimeProvider(fake)`
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
function toTimeProvider(fake: FakeTimeProvider): TimeProvider;
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Converts a `FakeTimeProvider` to a read-only `TimeProvider`, used to pass to `CountdownEngine` or `useCountdown`.
|
|
230
|
+
|
|
231
|
+
### `buildSnapshot(options?)`
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
interface SnapshotOptions {
|
|
235
|
+
initialSeconds?: number; // fallback: totalSeconds, then 0
|
|
236
|
+
totalSeconds?: number; // fallback: initialSeconds, then 0
|
|
237
|
+
state?: TimerState; // fallback: IDLE if totalSeconds > 0, else STOPPED
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function buildSnapshot(options?: SnapshotOptions): CountdownSnapshot;
|
|
241
|
+
```
|
|
159
242
|
|
|
160
|
-
|
|
243
|
+
```ts
|
|
244
|
+
const snapshot = buildSnapshot({ totalSeconds: 90, state: TimerState.RUNNING });
|
|
245
|
+
// snapshot.parts.minutes === 1
|
|
246
|
+
// snapshot.parts.seconds === 30
|
|
247
|
+
// snapshot.isRunning === true
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### `buildSnapshotSequence(options?)`
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
interface SequenceOptions extends SnapshotOptions {
|
|
254
|
+
step?: number; // default: 1 — decrement per snapshot
|
|
255
|
+
count?: number; // default: 1 — number of snapshots
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function buildSnapshotSequence(options?: SequenceOptions): CountdownSnapshot[];
|
|
259
|
+
```
|
|
161
260
|
|
|
162
|
-
|
|
163
|
-
|
|
261
|
+
Generates `count` snapshots, decrementing `totalSeconds` by `step` each iteration. The last snapshot with `remaining === 0` gets `state: STOPPED`; all others get `state: RUNNING`.
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
const sequence = buildSnapshotSequence({ totalSeconds: 4, step: 2, count: 3 });
|
|
265
|
+
// sequence[0].totalSeconds === 4 (RUNNING)
|
|
266
|
+
// sequence[1].totalSeconds === 2 (RUNNING)
|
|
267
|
+
// sequence[2].totalSeconds === 0 (STOPPED)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### `assertSnapshotState(snapshot, expected, message?)`
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
function assertSnapshotState(
|
|
274
|
+
snapshot: CountdownSnapshot,
|
|
275
|
+
expected: TimerState,
|
|
276
|
+
message?: string // default: "Unexpected countdown state"
|
|
277
|
+
): void;
|
|
164
278
|
```
|
|
165
279
|
|
|
166
|
-
|
|
280
|
+
### `assertSnapshotCompleted(snapshot, message?)`
|
|
167
281
|
|
|
168
|
-
|
|
169
|
-
|
|
282
|
+
```ts
|
|
283
|
+
function assertSnapshotCompleted(
|
|
284
|
+
snapshot: CountdownSnapshot,
|
|
285
|
+
message?: string // default: "Countdown should be completed"
|
|
286
|
+
): void;
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Throws if `snapshot.isCompleted === false` OR `snapshot.totalSeconds !== 0`.
|
|
290
|
+
|
|
291
|
+
### `assertRemainingSeconds(snapshot, expected, tolerance?, message?)`
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
function assertRemainingSeconds(
|
|
295
|
+
snapshot: CountdownSnapshot,
|
|
296
|
+
expected: number,
|
|
297
|
+
tolerance?: number, // default: 0
|
|
298
|
+
message?: string // default: "Unexpected remaining seconds"
|
|
299
|
+
): void;
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Throws if `Math.abs(snapshot.totalSeconds - Math.floor(expected)) > tolerance` or if `expected` is not a finite number.
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
assertRemainingSeconds(snapshot, 5); // exact
|
|
306
|
+
assertRemainingSeconds(snapshot, 5, 0.5); // accepts 4.5–5.5
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### `TimerState` re-export
|
|
310
|
+
|
|
311
|
+
`TimerState` is re-exported via `testing-utils`, avoiding a double import:
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
// Instead of two separate imports:
|
|
315
|
+
import { TimerState } from '@timekeeper-countdown/core';
|
|
316
|
+
import { buildSnapshot } from '@timekeeper-countdown/core/testing-utils';
|
|
317
|
+
|
|
318
|
+
// You can import everything from one place:
|
|
319
|
+
import { buildSnapshot, TimerState } from '@timekeeper-countdown/core/testing-utils';
|
|
320
|
+
```
|
|
170
321
|
|
|
171
322
|
---
|
|
172
323
|
|
|
@@ -56,6 +56,7 @@ interface CountdownEngineInstance {
|
|
|
56
56
|
subscribe: (listener: (snapshot: CountdownSnapshot) => void) => CountdownSubscription;
|
|
57
57
|
destroy: () => void;
|
|
58
58
|
}
|
|
59
|
+
declare function buildSnapshot(initialSeconds: number, totalSeconds: number, state: TimerState): CountdownSnapshot;
|
|
59
60
|
declare function CountdownEngine(initialSecondsInput: number, options?: CountdownEngineOptions): CountdownEngineInstance;
|
|
60
61
|
|
|
61
|
-
export { type CountdownSnapshot as C, TimerState as T, CountdownEngine as a, type CountdownEngineInstance as
|
|
62
|
+
export { type CountdownSnapshot as C, TimerState as T, CountdownEngine as a, buildSnapshot as b, type CountdownEngineInstance as c, type CountdownEngineOptions as d, type CountdownParts as e, type TimeProvider as f };
|
|
@@ -56,6 +56,7 @@ interface CountdownEngineInstance {
|
|
|
56
56
|
subscribe: (listener: (snapshot: CountdownSnapshot) => void) => CountdownSubscription;
|
|
57
57
|
destroy: () => void;
|
|
58
58
|
}
|
|
59
|
+
declare function buildSnapshot(initialSeconds: number, totalSeconds: number, state: TimerState): CountdownSnapshot;
|
|
59
60
|
declare function CountdownEngine(initialSecondsInput: number, options?: CountdownEngineOptions): CountdownEngineInstance;
|
|
60
61
|
|
|
61
|
-
export { type CountdownSnapshot as C, TimerState as T, CountdownEngine as a, type CountdownEngineInstance as
|
|
62
|
+
export { type CountdownSnapshot as C, TimerState as T, CountdownEngine as a, buildSnapshot as b, type CountdownEngineInstance as c, type CountdownEngineOptions as d, type CountdownParts as e, type TimeProvider as f };
|
package/dist/format.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';function
|
|
1
|
+
'use strict';function f(t){return typeof t=="number"?t:t&&typeof t.totalSeconds=="number"?t.totalSeconds:0}function i(t){return typeof t!="number"||!Number.isFinite(t)||t<=0?0:t>=Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:Math.floor(t)}function o(t,a=2){return !Number.isFinite(t)||t<0?"0".repeat(a):Math.floor(t).toString().padStart(a,"0")}function s(t){return Math.floor(t/60)}function c(t){return s(t)%60}function d(t){return Math.floor(t/3600)%24}function g(t){return Math.floor(t/86400)%7}function p(t){return Math.floor(t/604800)%52}function S(t){return Math.floor(t/31536e3)}function m(){let t=r=>i(f(r));return {formatTime:r=>{let n=t(r);return {minutes:o(s(n)),seconds:o(n%60)}},formatMinutes:r=>{let n=t(r);return o(c(n))},formatSeconds:r=>{let n=t(r);return o(n%60)},formatHours:r=>{let n=t(r);return o(d(n))},formatDays:r=>{let n=t(r);return o(g(n))},formatWeeks:r=>{let n=t(r);return o(p(n))},formatYears:r=>{let n=t(r);return o(S(n))}}}var e=m();function l(t){return e.formatTime(t)}function E(t){return e.formatMinutes(t)}function T(t){return e.formatSeconds(t)}function x(t){return e.formatHours(t)}function F(t){return e.formatDays(t)}function b(t){return e.formatWeeks(t)}function _(t){return e.formatYears(t)}exports.Formatter=m;exports.defaultFormatter=e;exports.formatDays=F;exports.formatHours=x;exports.formatMinutes=E;exports.formatSeconds=T;exports.formatTime=l;exports.formatWeeks=b;exports.formatYears=_;
|
package/dist/format.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CountdownSnapshot } from './countdown-engine-
|
|
1
|
+
import { C as CountdownSnapshot } from './countdown-engine-qnWxt-ed.cjs';
|
|
2
2
|
|
|
3
3
|
type FormatTarget = number | Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined;
|
|
4
4
|
declare function Formatter(): {
|
|
@@ -25,15 +25,25 @@ declare const defaultFormatter: {
|
|
|
25
25
|
formatWeeks: (target: FormatTarget) => string;
|
|
26
26
|
formatYears: (target: FormatTarget) => string;
|
|
27
27
|
};
|
|
28
|
-
declare
|
|
28
|
+
declare function formatTime(target: number): {
|
|
29
29
|
minutes: string;
|
|
30
30
|
seconds: string;
|
|
31
31
|
};
|
|
32
|
-
declare
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
declare
|
|
37
|
-
declare
|
|
32
|
+
declare function formatTime(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): {
|
|
33
|
+
minutes: string;
|
|
34
|
+
seconds: string;
|
|
35
|
+
};
|
|
36
|
+
declare function formatMinutes(target: number): string;
|
|
37
|
+
declare function formatMinutes(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): string;
|
|
38
|
+
declare function formatSeconds(target: number): string;
|
|
39
|
+
declare function formatSeconds(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): string;
|
|
40
|
+
declare function formatHours(target: number): string;
|
|
41
|
+
declare function formatHours(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): string;
|
|
42
|
+
declare function formatDays(target: number): string;
|
|
43
|
+
declare function formatDays(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): string;
|
|
44
|
+
declare function formatWeeks(target: number): string;
|
|
45
|
+
declare function formatWeeks(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): string;
|
|
46
|
+
declare function formatYears(target: number): string;
|
|
47
|
+
declare function formatYears(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): string;
|
|
38
48
|
|
|
39
49
|
export { type FormatTarget, Formatter, defaultFormatter, formatDays, formatHours, formatMinutes, formatSeconds, formatTime, formatWeeks, formatYears };
|
package/dist/format.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CountdownSnapshot } from './countdown-engine-
|
|
1
|
+
import { C as CountdownSnapshot } from './countdown-engine-qnWxt-ed.js';
|
|
2
2
|
|
|
3
3
|
type FormatTarget = number | Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined;
|
|
4
4
|
declare function Formatter(): {
|
|
@@ -25,15 +25,25 @@ declare const defaultFormatter: {
|
|
|
25
25
|
formatWeeks: (target: FormatTarget) => string;
|
|
26
26
|
formatYears: (target: FormatTarget) => string;
|
|
27
27
|
};
|
|
28
|
-
declare
|
|
28
|
+
declare function formatTime(target: number): {
|
|
29
29
|
minutes: string;
|
|
30
30
|
seconds: string;
|
|
31
31
|
};
|
|
32
|
-
declare
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
declare
|
|
37
|
-
declare
|
|
32
|
+
declare function formatTime(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): {
|
|
33
|
+
minutes: string;
|
|
34
|
+
seconds: string;
|
|
35
|
+
};
|
|
36
|
+
declare function formatMinutes(target: number): string;
|
|
37
|
+
declare function formatMinutes(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): string;
|
|
38
|
+
declare function formatSeconds(target: number): string;
|
|
39
|
+
declare function formatSeconds(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): string;
|
|
40
|
+
declare function formatHours(target: number): string;
|
|
41
|
+
declare function formatHours(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): string;
|
|
42
|
+
declare function formatDays(target: number): string;
|
|
43
|
+
declare function formatDays(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): string;
|
|
44
|
+
declare function formatWeeks(target: number): string;
|
|
45
|
+
declare function formatWeeks(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): string;
|
|
46
|
+
declare function formatYears(target: number): string;
|
|
47
|
+
declare function formatYears(target: Pick<CountdownSnapshot, 'totalSeconds'> | null | undefined): string;
|
|
38
48
|
|
|
39
49
|
export { type FormatTarget, Formatter, defaultFormatter, formatDays, formatHours, formatMinutes, formatSeconds, formatTime, formatWeeks, formatYears };
|
package/dist/format.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function
|
|
1
|
+
function f(t){return typeof t=="number"?t:t&&typeof t.totalSeconds=="number"?t.totalSeconds:0}function i(t){return typeof t!="number"||!Number.isFinite(t)||t<=0?0:t>=Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:Math.floor(t)}function o(t,a=2){return !Number.isFinite(t)||t<0?"0".repeat(a):Math.floor(t).toString().padStart(a,"0")}function s(t){return Math.floor(t/60)}function c(t){return s(t)%60}function d(t){return Math.floor(t/3600)%24}function g(t){return Math.floor(t/86400)%7}function p(t){return Math.floor(t/604800)%52}function S(t){return Math.floor(t/31536e3)}function m(){let t=r=>i(f(r));return {formatTime:r=>{let n=t(r);return {minutes:o(s(n)),seconds:o(n%60)}},formatMinutes:r=>{let n=t(r);return o(c(n))},formatSeconds:r=>{let n=t(r);return o(n%60)},formatHours:r=>{let n=t(r);return o(d(n))},formatDays:r=>{let n=t(r);return o(g(n))},formatWeeks:r=>{let n=t(r);return o(p(n))},formatYears:r=>{let n=t(r);return o(S(n))}}}var e=m();function l(t){return e.formatTime(t)}function E(t){return e.formatMinutes(t)}function T(t){return e.formatSeconds(t)}function x(t){return e.formatHours(t)}function F(t){return e.formatDays(t)}function b(t){return e.formatWeeks(t)}function _(t){return e.formatYears(t)}export{m as Formatter,e as defaultFormatter,F as formatDays,x as formatHours,E as formatMinutes,T as formatSeconds,l as formatTime,b as formatWeeks,_ as formatYears};
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';function Y(){return {now:()=>performance.now(),isHighResolution:true,type:"performance"}}function
|
|
1
|
+
'use strict';function Y(){return {now:()=>performance.now(),isHighResolution:true,type:"performance"}}function W(){return {now:()=>Date.now(),isHighResolution:false,type:"date"}}function w(){let t,e=W();function s(){try{if(typeof performance<"u"&&typeof performance.now=="function"){let n=performance.now();if(typeof n=="number"&&Number.isFinite(n)&&!isNaN(n)&&n>=0&&n<=Number.MAX_SAFE_INTEGER){t=Y();return}}throw new Error("performance.now() not available or invalid")}catch{t=e;}}return s(),{now:()=>{try{let n=t.now();if(typeof n=="number"&&Number.isFinite(n)&&!isNaN(n)&&n>=0&&n<=Number.MAX_SAFE_INTEGER)return n;throw new Error("Invalid time value returned")}catch{t!==e&&(t=e);try{let n=e.now();if(typeof n=="number"&&Number.isFinite(n)&&!isNaN(n)&&n>=0)return n}catch{}return 0}},get isHighResolution(){return t.isHighResolution},get type(){return t.type}}}w();var L=10,V=100;function D(t,e,s={}){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");let n=Math.floor(Math.max(0,t)),m=null,i=null,u=0,c=n,r=n,a=Math.max(L,Math.floor(s.tickIntervalMs??V)),b=w(),d=s.timeProvider??(()=>b.now()),p=()=>setInterval(()=>{try{if(i===null)return;let E=d()-i-u,x=Math.floor(E/1e3),o=Math.max(0,r-x);n=o,o!==c&&(c=o,e.onTick(o)),o===0&&(l(),e.onComplete());}catch(S){l(),e.onError(S instanceof Error?S:new Error(String(S)));}},a),P=()=>{if(m||n<=0)return false;try{if(i!==null&&n<r){let S=(r-n)*1e3;u=d()-i-S;}else i=d(),u=0,c=n;return m=p(),!0}catch(S){return e.onError(S instanceof Error?S:new Error(String(S))),false}},l=()=>{m&&(clearInterval(m),m=null);},g=()=>{l(),n=r,i=null,u=0,c=r;},v=S=>{if(typeof S!="number"||!Number.isFinite(S))return;let E=Math.floor(Math.max(0,Math.min(S,Number.MAX_SAFE_INTEGER)));n=E,i=null,u=0,c=E;};return {start:P,stop:l,reset:g,setSeconds:v,setInitialValue:S=>{if(typeof S!="number"||!Number.isFinite(S))return;let E=Math.floor(Math.max(0,Math.min(S,Number.MAX_SAFE_INTEGER)));r=E,v(E);},getTotalSeconds:()=>n,getInitialValue:()=>r,isRunning:()=>m!==null,destroy:()=>{l(),n=0,i=null,u=0;}}}var f=Object.freeze({IDLE:"IDLE",RUNNING:"RUNNING",PAUSED:"PAUSED",STOPPED:"STOPPED"}),M={[f.IDLE]:{start:f.RUNNING},[f.RUNNING]:{pause:f.PAUSED,reset:f.IDLE,stop:f.STOPPED,complete:f.STOPPED},[f.PAUSED]:{resume:f.RUNNING,reset:f.IDLE,stop:f.STOPPED},[f.STOPPED]:{reset:f.IDLE}};function _(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=f.IDLE;function s(b){let d=M[e]?.[b];if(!d)return false;e=d;try{t?.onStateChange?.(d);}catch{}return true}function n(){return s("start")}function m(){return s("resume")}function i(){return s("pause")}function u(){return s("reset")}function c(){return s("stop")}function r(){return s("complete")}function a(){e===f.IDLE||e===f.STOPPED||s("stop");}return {start:n,resume:m,pause:i,reset:u,stop:c,complete:r,getCurrentState:()=>e,canStart:()=>!!M[e]?.start,canResume:()=>!!M[e]?.resume,canPause:()=>!!M[e]?.pause,isRunning:()=>e===f.RUNNING,destroy:a}}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 J(t){if(!t){let e=w();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 Q(t){let e=Math.max(0,Math.floor(t)),s=Math.floor(e/31536e3),n=Math.floor(e/604800)%52,m=Math.floor(e/86400)%7,i=Math.floor(e/3600)%24,u=Math.floor(e/60)%60,c=e%60,r=Math.floor(e/86400),a=Math.floor(e/3600),b=Math.floor(e/60);return {years:s,weeks:n,days:m,hours:i,minutes:u,seconds:c,totalDays:r,totalHours:a,totalMinutes:b}}function y(t,e,s){let n=Q(e);return {initialSeconds:t,totalSeconds:e,parts:n,state:s,isRunning:s===f.RUNNING,isCompleted:e===0&&s===f.STOPPED}}function R(t,e={}){let s=O(t),n=J(e.timeProvider),m=s,i=y(s,s,f.IDLE),u=new Set,c=o=>{try{e.onSnapshot?.(o);}catch{}for(let N of u)try{N(o);}catch{}},r=o=>{i=y(m,i.totalSeconds,o);try{e.onStateChange?.(o,i);}catch{}c(i);},a=o=>{i=y(m,o,d.getCurrentState()),c(i);},b=o=>{try{e.onError?.(o);}catch{}},d=_({onStateChange:o=>{r(o);}}),p=D(s,{onTick:o=>{a(o);},onComplete:()=>{d.complete(),a(0);},onError:o=>{b(o),d.stop();}},{timeProvider:n,tickIntervalMs:e.tickIntervalMs});return {start:()=>{if(!d.canStart())return false;let o=p.start();return o&&(d.start(),a(p.getTotalSeconds())),o},pause:()=>{if(!d.canPause())return false;p.stop();let o=d.pause();return o&&a(p.getTotalSeconds()),o},resume:()=>{if(!d.canResume())return false;let o=p.start();return o&&(d.resume(),a(p.getTotalSeconds())),o},reset:o=>{if(p.stop(),typeof o=="number"){let N=O(o);m=N,p.setInitialValue(N);}else m=s,p.reset();return d.reset(),a(p.getTotalSeconds()),true},stop:()=>{p.stop();let o=d.stop();return o&&(p.setSeconds(0),a(0)),o},setSeconds:o=>{p.setSeconds(o),a(p.getTotalSeconds());},getSnapshot:()=>i,subscribe:o=>{u.add(o);try{o(i);}catch{}return {unsubscribe:()=>{u.delete(o);}}},destroy:()=>{p.destroy(),d.destroy(),u.clear(),i=y(m,0,f.STOPPED);}}}function Z(t){return typeof t=="number"?t:t&&typeof t.totalSeconds=="number"?t.totalSeconds:0}function $(t){return typeof t!="number"||!Number.isFinite(t)||t<=0?0:t>=Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:Math.floor(t)}function h(t,e=2){return !Number.isFinite(t)||t<0?"0".repeat(e):Math.floor(t).toString().padStart(e,"0")}function k(t){return Math.floor(t/60)}function tt(t){return k(t)%60}function et(t){return Math.floor(t/3600)%24}function nt(t){return Math.floor(t/86400)%7}function ot(t){return Math.floor(t/604800)%52}function rt(t){return Math.floor(t/31536e3)}function I(){let t=r=>$(Z(r));return {formatTime:r=>{let a=t(r);return {minutes:h(k(a)),seconds:h(a%60)}},formatMinutes:r=>{let a=t(r);return h(tt(a))},formatSeconds:r=>{let a=t(r);return h(a%60)},formatHours:r=>{let a=t(r);return h(et(a))},formatDays:r=>{let a=t(r);return h(nt(a))},formatWeeks:r=>{let a=t(r);return h(ot(a))},formatYears:r=>{let a=t(r);return h(rt(a))}}}var T=I();function at(t){return T.formatTime(t)}function st(t){return T.formatMinutes(t)}function it(t){return T.formatSeconds(t)}function ut(t){return T.formatHours(t)}function ct(t){return T.formatDays(t)}function mt(t){return T.formatWeeks(t)}function ft(t){return T.formatYears(t)}function dt(t,e={}){let{onSnapshot:s,onStateChange:n,onError:m}=e;if(s!==void 0&&typeof s!="function")throw new Error("onSnapshot must be a function");if(n!==void 0&&typeof n!="function")throw new Error("onStateChange must be a function");if(m!==void 0&&typeof m!="function")throw new Error("onError must be a function");let i=I(),u=R(t,{onError:m}),c=u.getSnapshot(),r=c.state,a=Number.NaN,b=l=>{if(m)try{m(l);}catch{}},d=l=>{if(s)try{s(l);}catch(g){b(g instanceof Error?g:new Error(String(g)));}},p=l=>{if(n)try{n(l);}catch(g){b(g instanceof Error?g:new Error(String(g)));}},P=u.subscribe(l=>{c=l,l.state!==r&&(r=l.state,p(l.state)),l.totalSeconds!==a&&(a=l.totalSeconds,d(l));});return {start:()=>{u.start();},pause:()=>{u.pause();},resume:()=>{u.resume();},reset:l=>{u.reset(l);},stop:()=>{u.stop();},getSeconds:()=>i.formatSeconds(c.totalSeconds),getMinutes:()=>i.formatMinutes(c.totalSeconds),getHours:()=>i.formatHours(c.totalSeconds),getDays:()=>i.formatDays(c.totalSeconds),getWeeks:()=>i.formatWeeks(c.totalSeconds),getYears:()=>i.formatYears(c.totalSeconds),getCurrentState:()=>c.state,getSnapshot:()=>c,destroy:()=>{try{P.unsubscribe();}finally{u.destroy();}}}}exports.Countdown=dt;exports.CountdownEngine=R;exports.Formatter=I;exports.TimerState=f;exports.buildSnapshot=y;exports.defaultFormatter=T;exports.formatDays=ct;exports.formatHours=ut;exports.formatMinutes=st;exports.formatSeconds=it;exports.formatTime=at;exports.formatWeeks=mt;exports.formatYears=ft;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { a as CountdownEngine,
|
|
1
|
+
import { C as CountdownSnapshot, T as TimerState } from './countdown-engine-qnWxt-ed.cjs';
|
|
2
|
+
export { a as CountdownEngine, c as CountdownEngineInstance, d as CountdownEngineOptions, e as CountdownParts, b as buildSnapshot } from './countdown-engine-qnWxt-ed.cjs';
|
|
3
3
|
export { FormatTarget, Formatter, defaultFormatter, formatDays, formatHours, formatMinutes, formatSeconds, formatTime, formatWeeks, formatYears } from './format.cjs';
|
|
4
4
|
|
|
5
5
|
interface CountdownOptions {
|
|
6
|
-
|
|
6
|
+
onSnapshot?: (snapshot: CountdownSnapshot) => void;
|
|
7
7
|
onStateChange?: (state: TimerState) => void;
|
|
8
|
+
onError?: (error: Error) => void;
|
|
8
9
|
}
|
|
9
10
|
interface CountdownInstance {
|
|
10
11
|
start: () => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { a as CountdownEngine,
|
|
1
|
+
import { C as CountdownSnapshot, T as TimerState } from './countdown-engine-qnWxt-ed.js';
|
|
2
|
+
export { a as CountdownEngine, c as CountdownEngineInstance, d as CountdownEngineOptions, e as CountdownParts, b as buildSnapshot } from './countdown-engine-qnWxt-ed.js';
|
|
3
3
|
export { FormatTarget, Formatter, defaultFormatter, formatDays, formatHours, formatMinutes, formatSeconds, formatTime, formatWeeks, formatYears } from './format.js';
|
|
4
4
|
|
|
5
5
|
interface CountdownOptions {
|
|
6
|
-
|
|
6
|
+
onSnapshot?: (snapshot: CountdownSnapshot) => void;
|
|
7
7
|
onStateChange?: (state: TimerState) => void;
|
|
8
|
+
onError?: (error: Error) => void;
|
|
8
9
|
}
|
|
9
10
|
interface CountdownInstance {
|
|
10
11
|
start: () => void;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function Y(){return {now:()=>performance.now(),isHighResolution:true,type:"performance"}}function
|
|
1
|
+
function Y(){return {now:()=>performance.now(),isHighResolution:true,type:"performance"}}function W(){return {now:()=>Date.now(),isHighResolution:false,type:"date"}}function w(){let t,e=W();function s(){try{if(typeof performance<"u"&&typeof performance.now=="function"){let n=performance.now();if(typeof n=="number"&&Number.isFinite(n)&&!isNaN(n)&&n>=0&&n<=Number.MAX_SAFE_INTEGER){t=Y();return}}throw new Error("performance.now() not available or invalid")}catch{t=e;}}return s(),{now:()=>{try{let n=t.now();if(typeof n=="number"&&Number.isFinite(n)&&!isNaN(n)&&n>=0&&n<=Number.MAX_SAFE_INTEGER)return n;throw new Error("Invalid time value returned")}catch{t!==e&&(t=e);try{let n=e.now();if(typeof n=="number"&&Number.isFinite(n)&&!isNaN(n)&&n>=0)return n}catch{}return 0}},get isHighResolution(){return t.isHighResolution},get type(){return t.type}}}w();var L=10,V=100;function D(t,e,s={}){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");let n=Math.floor(Math.max(0,t)),m=null,i=null,u=0,c=n,r=n,a=Math.max(L,Math.floor(s.tickIntervalMs??V)),b=w(),d=s.timeProvider??(()=>b.now()),p=()=>setInterval(()=>{try{if(i===null)return;let E=d()-i-u,x=Math.floor(E/1e3),o=Math.max(0,r-x);n=o,o!==c&&(c=o,e.onTick(o)),o===0&&(l(),e.onComplete());}catch(S){l(),e.onError(S instanceof Error?S:new Error(String(S)));}},a),P=()=>{if(m||n<=0)return false;try{if(i!==null&&n<r){let S=(r-n)*1e3;u=d()-i-S;}else i=d(),u=0,c=n;return m=p(),!0}catch(S){return e.onError(S instanceof Error?S:new Error(String(S))),false}},l=()=>{m&&(clearInterval(m),m=null);},g=()=>{l(),n=r,i=null,u=0,c=r;},v=S=>{if(typeof S!="number"||!Number.isFinite(S))return;let E=Math.floor(Math.max(0,Math.min(S,Number.MAX_SAFE_INTEGER)));n=E,i=null,u=0,c=E;};return {start:P,stop:l,reset:g,setSeconds:v,setInitialValue:S=>{if(typeof S!="number"||!Number.isFinite(S))return;let E=Math.floor(Math.max(0,Math.min(S,Number.MAX_SAFE_INTEGER)));r=E,v(E);},getTotalSeconds:()=>n,getInitialValue:()=>r,isRunning:()=>m!==null,destroy:()=>{l(),n=0,i=null,u=0;}}}var f=Object.freeze({IDLE:"IDLE",RUNNING:"RUNNING",PAUSED:"PAUSED",STOPPED:"STOPPED"}),M={[f.IDLE]:{start:f.RUNNING},[f.RUNNING]:{pause:f.PAUSED,reset:f.IDLE,stop:f.STOPPED,complete:f.STOPPED},[f.PAUSED]:{resume:f.RUNNING,reset:f.IDLE,stop:f.STOPPED},[f.STOPPED]:{reset:f.IDLE}};function _(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=f.IDLE;function s(b){let d=M[e]?.[b];if(!d)return false;e=d;try{t?.onStateChange?.(d);}catch{}return true}function n(){return s("start")}function m(){return s("resume")}function i(){return s("pause")}function u(){return s("reset")}function c(){return s("stop")}function r(){return s("complete")}function a(){e===f.IDLE||e===f.STOPPED||s("stop");}return {start:n,resume:m,pause:i,reset:u,stop:c,complete:r,getCurrentState:()=>e,canStart:()=>!!M[e]?.start,canResume:()=>!!M[e]?.resume,canPause:()=>!!M[e]?.pause,isRunning:()=>e===f.RUNNING,destroy:a}}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 J(t){if(!t){let e=w();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 Q(t){let e=Math.max(0,Math.floor(t)),s=Math.floor(e/31536e3),n=Math.floor(e/604800)%52,m=Math.floor(e/86400)%7,i=Math.floor(e/3600)%24,u=Math.floor(e/60)%60,c=e%60,r=Math.floor(e/86400),a=Math.floor(e/3600),b=Math.floor(e/60);return {years:s,weeks:n,days:m,hours:i,minutes:u,seconds:c,totalDays:r,totalHours:a,totalMinutes:b}}function y(t,e,s){let n=Q(e);return {initialSeconds:t,totalSeconds:e,parts:n,state:s,isRunning:s===f.RUNNING,isCompleted:e===0&&s===f.STOPPED}}function R(t,e={}){let s=O(t),n=J(e.timeProvider),m=s,i=y(s,s,f.IDLE),u=new Set,c=o=>{try{e.onSnapshot?.(o);}catch{}for(let N of u)try{N(o);}catch{}},r=o=>{i=y(m,i.totalSeconds,o);try{e.onStateChange?.(o,i);}catch{}c(i);},a=o=>{i=y(m,o,d.getCurrentState()),c(i);},b=o=>{try{e.onError?.(o);}catch{}},d=_({onStateChange:o=>{r(o);}}),p=D(s,{onTick:o=>{a(o);},onComplete:()=>{d.complete(),a(0);},onError:o=>{b(o),d.stop();}},{timeProvider:n,tickIntervalMs:e.tickIntervalMs});return {start:()=>{if(!d.canStart())return false;let o=p.start();return o&&(d.start(),a(p.getTotalSeconds())),o},pause:()=>{if(!d.canPause())return false;p.stop();let o=d.pause();return o&&a(p.getTotalSeconds()),o},resume:()=>{if(!d.canResume())return false;let o=p.start();return o&&(d.resume(),a(p.getTotalSeconds())),o},reset:o=>{if(p.stop(),typeof o=="number"){let N=O(o);m=N,p.setInitialValue(N);}else m=s,p.reset();return d.reset(),a(p.getTotalSeconds()),true},stop:()=>{p.stop();let o=d.stop();return o&&(p.setSeconds(0),a(0)),o},setSeconds:o=>{p.setSeconds(o),a(p.getTotalSeconds());},getSnapshot:()=>i,subscribe:o=>{u.add(o);try{o(i);}catch{}return {unsubscribe:()=>{u.delete(o);}}},destroy:()=>{p.destroy(),d.destroy(),u.clear(),i=y(m,0,f.STOPPED);}}}function Z(t){return typeof t=="number"?t:t&&typeof t.totalSeconds=="number"?t.totalSeconds:0}function $(t){return typeof t!="number"||!Number.isFinite(t)||t<=0?0:t>=Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:Math.floor(t)}function h(t,e=2){return !Number.isFinite(t)||t<0?"0".repeat(e):Math.floor(t).toString().padStart(e,"0")}function k(t){return Math.floor(t/60)}function tt(t){return k(t)%60}function et(t){return Math.floor(t/3600)%24}function nt(t){return Math.floor(t/86400)%7}function ot(t){return Math.floor(t/604800)%52}function rt(t){return Math.floor(t/31536e3)}function I(){let t=r=>$(Z(r));return {formatTime:r=>{let a=t(r);return {minutes:h(k(a)),seconds:h(a%60)}},formatMinutes:r=>{let a=t(r);return h(tt(a))},formatSeconds:r=>{let a=t(r);return h(a%60)},formatHours:r=>{let a=t(r);return h(et(a))},formatDays:r=>{let a=t(r);return h(nt(a))},formatWeeks:r=>{let a=t(r);return h(ot(a))},formatYears:r=>{let a=t(r);return h(rt(a))}}}var T=I();function at(t){return T.formatTime(t)}function st(t){return T.formatMinutes(t)}function it(t){return T.formatSeconds(t)}function ut(t){return T.formatHours(t)}function ct(t){return T.formatDays(t)}function mt(t){return T.formatWeeks(t)}function ft(t){return T.formatYears(t)}function dt(t,e={}){let{onSnapshot:s,onStateChange:n,onError:m}=e;if(s!==void 0&&typeof s!="function")throw new Error("onSnapshot must be a function");if(n!==void 0&&typeof n!="function")throw new Error("onStateChange must be a function");if(m!==void 0&&typeof m!="function")throw new Error("onError must be a function");let i=I(),u=R(t,{onError:m}),c=u.getSnapshot(),r=c.state,a=Number.NaN,b=l=>{if(m)try{m(l);}catch{}},d=l=>{if(s)try{s(l);}catch(g){b(g instanceof Error?g:new Error(String(g)));}},p=l=>{if(n)try{n(l);}catch(g){b(g instanceof Error?g:new Error(String(g)));}},P=u.subscribe(l=>{c=l,l.state!==r&&(r=l.state,p(l.state)),l.totalSeconds!==a&&(a=l.totalSeconds,d(l));});return {start:()=>{u.start();},pause:()=>{u.pause();},resume:()=>{u.resume();},reset:l=>{u.reset(l);},stop:()=>{u.stop();},getSeconds:()=>i.formatSeconds(c.totalSeconds),getMinutes:()=>i.formatMinutes(c.totalSeconds),getHours:()=>i.formatHours(c.totalSeconds),getDays:()=>i.formatDays(c.totalSeconds),getWeeks:()=>i.formatWeeks(c.totalSeconds),getYears:()=>i.formatYears(c.totalSeconds),getCurrentState:()=>c.state,getSnapshot:()=>c,destroy:()=>{try{P.unsubscribe();}finally{u.destroy();}}}}export{dt as Countdown,R as CountdownEngine,I as Formatter,f as TimerState,y as buildSnapshot,T as defaultFormatter,ct as formatDays,ut as formatHours,st as formatMinutes,it as formatSeconds,at as formatTime,mt as formatWeeks,ft as formatYears};
|
package/dist/testing-utils.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as TimeProvider, T as TimerState, C as CountdownSnapshot } from './countdown-engine-qnWxt-ed.cjs';
|
|
2
2
|
|
|
3
3
|
interface FakeTimeProvider extends TimeProvider {
|
|
4
4
|
advance: (ms?: number) => number;
|
package/dist/testing-utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as TimeProvider, T as TimerState, C as CountdownSnapshot } from './countdown-engine-qnWxt-ed.js';
|
|
2
2
|
|
|
3
3
|
interface FakeTimeProvider extends TimeProvider {
|
|
4
4
|
advance: (ms?: number) => number;
|
package/package.json
CHANGED