@timekeeper-countdown/core 0.1.3 → 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.ts → countdown-engine-qnWxt-ed.d.cts} +2 -1
- package/dist/countdown-engine-qnWxt-ed.d.ts +62 -0
- package/dist/format.cjs +1 -0
- package/dist/format.d.cts +49 -0
- package/dist/format.d.ts +18 -8
- package/dist/format.js +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +28 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/testing-utils.cjs +1 -0
- package/dist/testing-utils.d.cts +33 -0
- package/dist/testing-utils.d.ts +1 -1
- package/package.json +7 -4
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 };
|
|
@@ -0,0 +1,62 @@
|
|
|
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 buildSnapshot(initialSeconds: number, totalSeconds: number, state: TimerState): CountdownSnapshot;
|
|
60
|
+
declare function CountdownEngine(initialSecondsInput: number, options?: CountdownEngineOptions): CountdownEngineInstance;
|
|
61
|
+
|
|
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
ADDED
|
@@ -0,0 +1 @@
|
|
|
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=_;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { C as CountdownSnapshot } from './countdown-engine-qnWxt-ed.cjs';
|
|
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 function formatTime(target: number): {
|
|
29
|
+
minutes: string;
|
|
30
|
+
seconds: string;
|
|
31
|
+
};
|
|
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;
|
|
48
|
+
|
|
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
ADDED
|
@@ -0,0 +1 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
export { FormatTarget, Formatter, defaultFormatter, formatDays, formatHours, formatMinutes, formatSeconds, formatTime, formatWeeks, formatYears } from './format.cjs';
|
|
4
|
+
|
|
5
|
+
interface CountdownOptions {
|
|
6
|
+
onSnapshot?: (snapshot: CountdownSnapshot) => void;
|
|
7
|
+
onStateChange?: (state: TimerState) => void;
|
|
8
|
+
onError?: (error: Error) => void;
|
|
9
|
+
}
|
|
10
|
+
interface CountdownInstance {
|
|
11
|
+
start: () => void;
|
|
12
|
+
pause: () => void;
|
|
13
|
+
resume: () => void;
|
|
14
|
+
reset: (newInitialSeconds?: number) => void;
|
|
15
|
+
stop: () => void;
|
|
16
|
+
getSeconds: () => string;
|
|
17
|
+
getMinutes: () => string;
|
|
18
|
+
getHours: () => string;
|
|
19
|
+
getDays: () => string;
|
|
20
|
+
getWeeks: () => string;
|
|
21
|
+
getYears: () => string;
|
|
22
|
+
getCurrentState: () => TimerState;
|
|
23
|
+
getSnapshot: () => CountdownSnapshot;
|
|
24
|
+
destroy: () => void;
|
|
25
|
+
}
|
|
26
|
+
declare function Countdown(initialSeconds: number, options?: CountdownOptions): CountdownInstance;
|
|
27
|
+
|
|
28
|
+
export { Countdown, type CountdownInstance, type CountdownOptions, CountdownSnapshot, TimerState };
|
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};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'use strict';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}`))}exports.TimerState=n;exports.assertRemainingSeconds=y;exports.assertSnapshotCompleted=I;exports.assertSnapshotState=x;exports.buildSnapshot=b;exports.buildSnapshotSequence=w;exports.createFakeTimeProvider=h;exports.toTimeProvider=T;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { f as TimeProvider, T as TimerState, C as CountdownSnapshot } from './countdown-engine-qnWxt-ed.cjs';
|
|
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 };
|
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timekeeper-countdown/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Lightweight countdown engine that powers the Timekeeper packages. Pure TypeScript with no runtime dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"countdown",
|
|
@@ -29,15 +29,18 @@
|
|
|
29
29
|
"exports": {
|
|
30
30
|
".": {
|
|
31
31
|
"types": "./dist/index.d.ts",
|
|
32
|
-
"import": "./dist/index.js"
|
|
32
|
+
"import": "./dist/index.js",
|
|
33
|
+
"require": "./dist/index.cjs"
|
|
33
34
|
},
|
|
34
35
|
"./format": {
|
|
35
36
|
"types": "./dist/format.d.ts",
|
|
36
|
-
"import": "./dist/format.js"
|
|
37
|
+
"import": "./dist/format.js",
|
|
38
|
+
"require": "./dist/format.cjs"
|
|
37
39
|
},
|
|
38
40
|
"./testing-utils": {
|
|
39
41
|
"types": "./dist/testing-utils.d.ts",
|
|
40
|
-
"import": "./dist/testing-utils.js"
|
|
42
|
+
"import": "./dist/testing-utils.js",
|
|
43
|
+
"require": "./dist/testing-utils.cjs"
|
|
41
44
|
}
|
|
42
45
|
},
|
|
43
46
|
"files": [
|