@oscarpalmer/timer 0.32.0 → 0.34.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/dist/work.cjs CHANGED
@@ -1,87 +1,82 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
-
5
- const constants = require('./constants.cjs');
6
-
7
- function endOrRestart(type, timer, state, options) {
8
- cancelAnimationFrame(state.frame);
9
- if (type === "stop") {
10
- options.onAfter?.(false);
11
- }
12
- state.active = false;
13
- state.frame = void 0;
14
- state.paused = type === "pause";
15
- return type === "restart" ? work("start", timer, state, options) : timer.instance;
16
- }
1
+ const require_constants = require("./constants.cjs");
17
2
  function finish(timer, state, options, success) {
18
- constants.activeTimers.delete(timer.instance);
19
- state.active = false;
20
- state.elapsed = 0;
21
- state.frame = void 0;
22
- if (timer.type === "wait") {
23
- state.callback();
24
- } else {
25
- options.onAfter?.(success);
26
- }
3
+ cancelAnimationFrame(state.frame);
4
+ require_constants.activeTimers.delete(timer.instance);
5
+ state.active = false;
6
+ state.elapsed = 0;
7
+ state.frame = void 0;
8
+ if (timer.type === require_constants.TYPE_WAIT) state.callback();
9
+ else options.onAfter?.(success);
27
10
  }
28
11
  function ignore(type, state) {
29
- return state.destroyed && type !== "stop" || (state.active ? constants.beginTypes.has(type) : constants.endTypes.has(type));
12
+ if (state.destroyed) return type !== require_constants.WORK_STOP;
13
+ if (state.paused) return type === require_constants.WORK_PAUSE || type === require_constants.WORK_START;
14
+ return state.active && type === require_constants.WORK_START;
15
+ }
16
+ function pause(timer, state) {
17
+ cancelAnimationFrame(state.frame);
18
+ state.frame = void 0;
19
+ return timer.instance;
30
20
  }
31
21
  function run(timer, state, options) {
32
- let start;
33
- return function step(now) {
34
- if (!state.active) {
35
- return;
36
- }
37
- start ??= now;
38
- const difference = now - start;
39
- state.elapsed += difference;
40
- state.total += difference;
41
- if (options.timeout > 0 && state.total >= options.timeout) {
42
- options.onError?.();
43
- finish(timer, state, options, false);
44
- return;
45
- }
46
- if (options.interval === constants.milliseconds || state.elapsed >= options.interval - constants.intervalBuffer) {
47
- if (options.count > -1) {
48
- state.callback(state.index);
49
- }
50
- start = now;
51
- state.elapsed = 0;
52
- state.index += 1;
53
- if (options.count === -1 || options.count > 0 && state.index >= options.count) {
54
- finish(timer, state, options, true);
55
- return;
56
- }
57
- }
58
- state.frame = requestAnimationFrame(step);
59
- };
22
+ let last;
23
+ let start;
24
+ return function step(now) {
25
+ if (!state.active) return;
26
+ last ??= now;
27
+ start ??= now;
28
+ const difference = now - last;
29
+ state.elapsed += difference;
30
+ state.total += difference;
31
+ last = now;
32
+ if (options.timeout > 0 && state.total >= options.timeout) {
33
+ options.onError?.();
34
+ finish(timer, state, options, false);
35
+ return;
36
+ }
37
+ if (options.interval === require_constants.milliseconds || state.elapsed >= options.interval - require_constants.intervalBuffer) {
38
+ if (options.count > -1) state.callback(state.index);
39
+ start = now;
40
+ state.elapsed = 0;
41
+ state.index += 1;
42
+ if (options.count === -1 || options.count > 0 && state.index >= options.count) {
43
+ finish(timer, state, options, true);
44
+ return;
45
+ }
46
+ }
47
+ state.frame = requestAnimationFrame(step);
48
+ };
60
49
  }
61
50
  function setState(type, state) {
62
- const pausable = constants.pauseTypes.has(type);
63
- state.elapsed = pausable ? state.elapsed : 0;
64
- state.index = pausable ? state.index : 0;
65
- state.total = pausable ? state.total : 0;
51
+ const pausable = type === require_constants.WORK_CONTINUE || type === require_constants.WORK_PAUSE;
52
+ state.elapsed = pausable ? state.elapsed : 0;
53
+ state.index = pausable ? state.index : 0;
54
+ state.total = pausable ? state.total : 0;
66
55
  }
67
56
  function stop(timer, state, options) {
68
- endOrRestart("stop", timer, state, options);
57
+ cancelAnimationFrame(state.frame);
58
+ require_constants.activeTimers.delete(timer.instance);
59
+ options.onAfter?.(false);
60
+ state.active = !stop;
61
+ state.frame = void 0;
62
+ state.paused = false;
63
+ return timer.instance;
69
64
  }
70
65
  function work(type, timer, state, options) {
71
- if (ignore(type, state)) {
72
- return timer.instance;
73
- }
74
- setState(type, state);
75
- if (constants.endOrRestartTypes.has(type)) {
76
- return endOrRestart(type, timer, state, options);
77
- }
78
- state.active = true;
79
- state.paused = false;
80
- constants.activeTimers.add(timer.instance);
81
- const runner = run(timer, state, options);
82
- state.frame = requestAnimationFrame(runner);
83
- return timer.instance;
66
+ if (ignore(type, state)) return timer.instance;
67
+ setState(type, state);
68
+ if (type === require_constants.WORK_STOP) return stop(timer, state, options);
69
+ if (type === require_constants.WORK_PAUSE || type === require_constants.WORK_RESTART) {
70
+ cancelAnimationFrame(state.frame);
71
+ state.frame = void 0;
72
+ }
73
+ state.active = true;
74
+ state.paused = type === require_constants.WORK_PAUSE;
75
+ if (state.paused) return pause(timer, state);
76
+ require_constants.activeTimers.add(timer.instance);
77
+ const runner = run(timer, state, options);
78
+ state.frame = requestAnimationFrame(runner);
79
+ return timer.instance;
84
80
  }
85
-
86
81
  exports.stop = stop;
87
82
  exports.work = work;
package/dist/work.js CHANGED
@@ -1,82 +1,81 @@
1
- import { endOrRestartTypes, activeTimers, beginTypes, endTypes, pauseTypes, milliseconds, intervalBuffer } from './constants.js';
2
-
3
- function endOrRestart(type, timer, state, options) {
4
- cancelAnimationFrame(state.frame);
5
- if (type === "stop") {
6
- options.onAfter?.(false);
7
- }
8
- state.active = false;
9
- state.frame = void 0;
10
- state.paused = type === "pause";
11
- return type === "restart" ? work("start", timer, state, options) : timer.instance;
12
- }
1
+ import { TYPE_WAIT, WORK_CONTINUE, WORK_PAUSE, WORK_RESTART, WORK_START, WORK_STOP, activeTimers, intervalBuffer, milliseconds } from "./constants.js";
13
2
  function finish(timer, state, options, success) {
14
- activeTimers.delete(timer.instance);
15
- state.active = false;
16
- state.elapsed = 0;
17
- state.frame = void 0;
18
- if (timer.type === "wait") {
19
- state.callback();
20
- } else {
21
- options.onAfter?.(success);
22
- }
3
+ cancelAnimationFrame(state.frame);
4
+ activeTimers.delete(timer.instance);
5
+ state.active = false;
6
+ state.elapsed = 0;
7
+ state.frame = void 0;
8
+ if (timer.type === TYPE_WAIT) state.callback();
9
+ else options.onAfter?.(success);
23
10
  }
24
11
  function ignore(type, state) {
25
- return state.destroyed && type !== "stop" || (state.active ? beginTypes.has(type) : endTypes.has(type));
12
+ if (state.destroyed) return type !== WORK_STOP;
13
+ if (state.paused) return type === WORK_PAUSE || type === WORK_START;
14
+ return state.active && type === WORK_START;
15
+ }
16
+ function pause(timer, state) {
17
+ cancelAnimationFrame(state.frame);
18
+ state.frame = void 0;
19
+ return timer.instance;
26
20
  }
27
21
  function run(timer, state, options) {
28
- let start;
29
- return function step(now) {
30
- if (!state.active) {
31
- return;
32
- }
33
- start ??= now;
34
- const difference = now - start;
35
- state.elapsed += difference;
36
- state.total += difference;
37
- if (options.timeout > 0 && state.total >= options.timeout) {
38
- options.onError?.();
39
- finish(timer, state, options, false);
40
- return;
41
- }
42
- if (options.interval === milliseconds || state.elapsed >= options.interval - intervalBuffer) {
43
- if (options.count > -1) {
44
- state.callback(state.index);
45
- }
46
- start = now;
47
- state.elapsed = 0;
48
- state.index += 1;
49
- if (options.count === -1 || options.count > 0 && state.index >= options.count) {
50
- finish(timer, state, options, true);
51
- return;
52
- }
53
- }
54
- state.frame = requestAnimationFrame(step);
55
- };
22
+ let last;
23
+ let start;
24
+ return function step(now) {
25
+ if (!state.active) return;
26
+ last ??= now;
27
+ start ??= now;
28
+ const difference = now - last;
29
+ state.elapsed += difference;
30
+ state.total += difference;
31
+ last = now;
32
+ if (options.timeout > 0 && state.total >= options.timeout) {
33
+ options.onError?.();
34
+ finish(timer, state, options, false);
35
+ return;
36
+ }
37
+ if (options.interval === milliseconds || state.elapsed >= options.interval - intervalBuffer) {
38
+ if (options.count > -1) state.callback(state.index);
39
+ start = now;
40
+ state.elapsed = 0;
41
+ state.index += 1;
42
+ if (options.count === -1 || options.count > 0 && state.index >= options.count) {
43
+ finish(timer, state, options, true);
44
+ return;
45
+ }
46
+ }
47
+ state.frame = requestAnimationFrame(step);
48
+ };
56
49
  }
57
50
  function setState(type, state) {
58
- const pausable = pauseTypes.has(type);
59
- state.elapsed = pausable ? state.elapsed : 0;
60
- state.index = pausable ? state.index : 0;
61
- state.total = pausable ? state.total : 0;
51
+ const pausable = type === WORK_CONTINUE || type === WORK_PAUSE;
52
+ state.elapsed = pausable ? state.elapsed : 0;
53
+ state.index = pausable ? state.index : 0;
54
+ state.total = pausable ? state.total : 0;
62
55
  }
63
56
  function stop(timer, state, options) {
64
- endOrRestart("stop", timer, state, options);
57
+ cancelAnimationFrame(state.frame);
58
+ activeTimers.delete(timer.instance);
59
+ options.onAfter?.(false);
60
+ state.active = !stop;
61
+ state.frame = void 0;
62
+ state.paused = false;
63
+ return timer.instance;
65
64
  }
66
65
  function work(type, timer, state, options) {
67
- if (ignore(type, state)) {
68
- return timer.instance;
69
- }
70
- setState(type, state);
71
- if (endOrRestartTypes.has(type)) {
72
- return endOrRestart(type, timer, state, options);
73
- }
74
- state.active = true;
75
- state.paused = false;
76
- activeTimers.add(timer.instance);
77
- const runner = run(timer, state, options);
78
- state.frame = requestAnimationFrame(runner);
79
- return timer.instance;
66
+ if (ignore(type, state)) return timer.instance;
67
+ setState(type, state);
68
+ if (type === WORK_STOP) return stop(timer, state, options);
69
+ if (type === WORK_PAUSE || type === WORK_RESTART) {
70
+ cancelAnimationFrame(state.frame);
71
+ state.frame = void 0;
72
+ }
73
+ state.active = true;
74
+ state.paused = type === WORK_PAUSE;
75
+ if (state.paused) return pause(timer, state);
76
+ activeTimers.add(timer.instance);
77
+ const runner = run(timer, state, options);
78
+ state.frame = requestAnimationFrame(runner);
79
+ return timer.instance;
80
80
  }
81
-
82
81
  export { stop, work };
package/package.json CHANGED
@@ -4,22 +4,23 @@
4
4
  "url": "https://oscarpalmer.se"
5
5
  },
6
6
  "dependencies": {
7
- "@oscarpalmer/atoms": "^0.99"
7
+ "@oscarpalmer/atoms": "^0.106"
8
8
  },
9
9
  "description": "A better solution for timeout- and interval-based timers.",
10
10
  "devDependencies": {
11
- "@biomejs/biome": "^1.9",
11
+ "@biomejs/biome": "^2.2",
12
12
  "@rollup/plugin-node-resolve": "^16",
13
13
  "@rollup/plugin-typescript": "^12.1",
14
- "@types/node": "^22.15",
15
- "@vitest/coverage-istanbul": "^3.1",
14
+ "@types/node": "^24.3",
15
+ "@vitest/coverage-istanbul": "^3.2",
16
16
  "dts-bundle-generator": "^9.5",
17
17
  "glob": "^11",
18
18
  "jsdom": "^26.1",
19
+ "rollup": "^4.47",
19
20
  "tslib": "^2.8",
20
- "typescript": "^5.8",
21
- "vite": "^6.3",
22
- "vitest": "^3.1"
21
+ "typescript": "^5.9",
22
+ "vite": "npm:rolldown-vite@latest",
23
+ "vitest": "^3.2"
23
24
  },
24
25
  "exports": {
25
26
  ".": {
@@ -94,5 +95,5 @@
94
95
  },
95
96
  "type": "module",
96
97
  "types": "types/index.d.cts",
97
- "version": "0.32.0"
98
+ "version": "0.34.0"
98
99
  }
package/src/constants.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type {WorkHandlerType} from './models';
1
+ import type {TimerType, WorkHandlerType} from './models';
2
2
  import type {Timer} from './timer';
3
3
 
4
4
  function calculate(): Promise<number> {
@@ -37,11 +37,6 @@ function calculate(): Promise<number> {
37
37
  */
38
38
  export const activeTimers = new Set<Timer>();
39
39
 
40
- /**
41
- * A set of types that allow work to begin
42
- */
43
- export const beginTypes = new Set<WorkHandlerType>(['continue', 'start']);
44
-
45
40
  /**
46
41
  * Buffer value to use when evaluating if a specific time is within a certain range
47
42
  */
@@ -52,32 +47,26 @@ export const intervalBuffer = 5;
52
47
  */
53
48
  export const destroyedMessage = 'Timer has already been destroyed';
54
49
 
55
- /**
56
- * A set of types that allow work to end
57
- */
58
- export const endTypes = new Set<WorkHandlerType>(['pause', 'stop']);
59
-
60
- /**
61
- * A set of types that allow work to end or restart
62
- */
63
- export const endOrRestartTypes = new Set<WorkHandlerType>([
64
- 'pause',
65
- 'restart',
66
- 'stop',
67
- ]);
68
-
69
50
  /**
70
51
  * A set of timers that were paused due to the document being hidden
71
52
  */
72
53
  export const hiddenTimers = new Set<Timer>();
73
54
 
74
- export const pauseTypes = new Set<WorkHandlerType>(['continue', 'pause']);
75
-
76
55
  /**
77
56
  * Message to show when a when-timer is started
78
57
  */
79
58
  export const startedMessage = 'Timer has already been started';
80
59
 
60
+ export const TYPE_REPEAT: TimerType = 'repeat';
61
+ export const TYPE_WAIT: TimerType = 'wait';
62
+ export const TYPE_WHEN: TimerType = 'when';
63
+
64
+ export const WORK_CONTINUE: WorkHandlerType = 'continue';
65
+ export const WORK_PAUSE: WorkHandlerType = 'pause';
66
+ export const WORK_RESTART: WorkHandlerType = 'restart';
67
+ export const WORK_START: WorkHandlerType = 'start';
68
+ export const WORK_STOP: WorkHandlerType = 'stop';
69
+
81
70
  //
82
71
 
83
72
  /**
package/src/global.ts CHANGED
@@ -1,4 +1,9 @@
1
- import {activeTimers, hiddenTimers} from './constants';
1
+ import {
2
+ activeTimers,
3
+ hiddenTimers,
4
+ WORK_CONTINUE,
5
+ WORK_PAUSE,
6
+ } from './constants';
2
7
  import type {Timer} from './timer';
3
8
 
4
9
  declare global {
@@ -15,16 +20,14 @@ if (globalThis._oscarpalmer_timers == null) {
15
20
  }
16
21
 
17
22
  document.addEventListener('visibilitychange', () => {
18
- if (document.hidden) {
19
- for (const timer of activeTimers) {
20
- hiddenTimers.add(timer);
21
- timer.pause();
22
- }
23
- } else {
24
- for (const timer of hiddenTimers) {
25
- timer.continue();
26
- }
23
+ const from = document.hidden ? activeTimers : hiddenTimers;
24
+ const method = document.hidden ? WORK_PAUSE : WORK_CONTINUE;
25
+ const to = document.hidden ? hiddenTimers : activeTimers;
27
26
 
28
- hiddenTimers.clear();
27
+ for (const timer of from) {
28
+ timer[method]();
29
+ to.add(timer);
29
30
  }
31
+
32
+ from.clear();
30
33
  });
package/src/is.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type {PlainObject} from '@oscarpalmer/atoms/models';
2
+ import {TYPE_REPEAT, TYPE_WAIT, TYPE_WHEN} from './constants';
2
3
  import type {Timer} from './timer';
3
4
  import type {When} from './when';
4
5
 
@@ -10,26 +11,26 @@ function is(names: string[], value: unknown) {
10
11
  * Is the value a repeating timer?
11
12
  */
12
13
  export function isRepeated(value: unknown): value is Timer {
13
- return is(['repeat'], value);
14
+ return is([TYPE_REPEAT], value);
14
15
  }
15
16
 
16
17
  /**
17
18
  * Is the value a timer?
18
19
  */
19
20
  export function isTimer(value: unknown): value is Timer {
20
- return is(['repeat', 'wait'], value);
21
+ return is([TYPE_REPEAT, TYPE_WAIT], value);
21
22
  }
22
23
 
23
24
  /**
24
25
  * Is the value a waiting timer?
25
26
  */
26
27
  export function isWaited(value: unknown): value is Timer {
27
- return is(['wait'], value);
28
+ return is([TYPE_WAIT], value);
28
29
  }
29
30
 
30
31
  /**
31
32
  * Is the value a conditional timer?
32
33
  */
33
34
  export function isWhen(value: unknown): value is When {
34
- return is(['when'], value) && typeof (value as When).then === 'function';
35
+ return is([TYPE_WHEN], value) && typeof (value as When).then === 'function';
35
36
  }
package/src/repeat.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {milliseconds} from './constants';
1
+ import {milliseconds, TYPE_REPEAT} from './constants';
2
2
  import {getCallback, getValidNumber} from './get';
3
3
  import './global';
4
4
  import {type RepeatOptions, TimerTrace} from './models';
@@ -12,7 +12,7 @@ export function repeat(
12
12
  options?: Partial<RepeatOptions>,
13
13
  ): Timer {
14
14
  return new Timer(
15
- 'repeat',
15
+ TYPE_REPEAT,
16
16
  {
17
17
  callback: getCallback(callback),
18
18
  trace: new TimerTrace().stack,
package/src/timer.ts CHANGED
@@ -1,4 +1,11 @@
1
1
  import {noop} from '@oscarpalmer/atoms/function';
2
+ import {
3
+ WORK_CONTINUE,
4
+ WORK_PAUSE,
5
+ WORK_RESTART,
6
+ WORK_START,
7
+ WORK_STOP,
8
+ } from './constants';
2
9
  import type {
3
10
  TimerOptions,
4
11
  TimerState,
@@ -37,7 +44,7 @@ export class Timer {
37
44
  * Get the timer's origin _(if debugging is enabled)_
38
45
  */
39
46
  get trace(): string | undefined {
40
- return globalThis._oscarpalmer_timer_debug ?? false
47
+ return (globalThis._oscarpalmer_timer_debug ?? false)
41
48
  ? this.state.trace
42
49
  : undefined;
43
50
  }
@@ -70,7 +77,7 @@ export class Timer {
70
77
  * Continue running the timer _(if it's paused)_
71
78
  */
72
79
  continue(): Timer {
73
- return this.#work('continue');
80
+ return this.#work(WORK_CONTINUE);
74
81
  }
75
82
 
76
83
  /**
@@ -101,28 +108,28 @@ export class Timer {
101
108
  * Pause the timer _(if it's running)_
102
109
  */
103
110
  pause(): Timer {
104
- return this.#work('pause');
111
+ return this.#work(WORK_PAUSE);
105
112
  }
106
113
 
107
114
  /**
108
115
  * Restart the timer _(or start it, if it's not running)_
109
116
  */
110
117
  restart(): Timer {
111
- return this.#work('restart');
118
+ return this.#work(WORK_RESTART);
112
119
  }
113
120
 
114
121
  /**
115
122
  * Start the timer _(if it's not running)_
116
123
  */
117
124
  start(): Timer {
118
- return this.#work('start');
125
+ return this.#work(WORK_START);
119
126
  }
120
127
 
121
128
  /**
122
129
  * Stop the timer _(if it's running)_
123
130
  */
124
131
  stop(): Timer {
125
- return this.#work('stop');
132
+ return this.#work(WORK_STOP);
126
133
  }
127
134
 
128
135
  #work(type: WorkHandlerType): Timer {
package/src/wait.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {milliseconds} from './constants';
1
+ import {milliseconds, TYPE_WAIT} from './constants';
2
2
  import {getCallback, getValidNumber} from './get';
3
3
  import './global';
4
4
  import {TimerTrace} from './models';
@@ -11,7 +11,7 @@ import {Timer} from './timer';
11
11
  */
12
12
  export function wait(callback: () => void, time?: number): Timer {
13
13
  return new Timer(
14
- 'wait',
14
+ TYPE_WAIT,
15
15
  {
16
16
  callback: getCallback(callback),
17
17
  trace: new TimerTrace().stack,
package/src/when.ts CHANGED
@@ -1,12 +1,17 @@
1
1
  import {noop} from '@oscarpalmer/atoms/function';
2
- import {destroyedMessage, milliseconds, startedMessage} from './constants';
2
+ import {
3
+ destroyedMessage,
4
+ milliseconds,
5
+ startedMessage,
6
+ TYPE_WHEN,
7
+ } from './constants';
3
8
  import {getValidNumber} from './get';
4
9
  import './global';
5
10
  import {TimerTrace, type WhenOptions, type WhenState} from './models';
6
11
  import {Timer} from './timer';
7
12
 
8
13
  class When {
9
- private readonly $timer = 'when';
14
+ private readonly $timer = TYPE_WHEN;
10
15
  private readonly state: WhenState = {
11
16
  promise: undefined as never,
12
17
  rejecter: undefined as never,
@@ -40,7 +45,7 @@ class When {
40
45
  * Get the timer's origin _(if debugging is enabled)_
41
46
  */
42
47
  get trace(): string | undefined {
43
- return globalThis._oscarpalmer_timer_debug ?? false
48
+ return (globalThis._oscarpalmer_timer_debug ?? false)
44
49
  ? this.state.timer?.trace
45
50
  : undefined;
46
51
  }
@@ -56,7 +61,7 @@ class When {
56
61
  let result = false;
57
62
 
58
63
  this.state.timer = new Timer(
59
- 'when',
64
+ TYPE_WHEN,
60
65
  {
61
66
  callback(): void {
62
67
  try {