@oscarpalmer/timer 0.33.0 → 0.35.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/when.js CHANGED
@@ -1,136 +1,94 @@
1
- import { noop } from '@oscarpalmer/atoms/function';
2
- import { milliseconds, destroyedMessage, startedMessage } from './constants.js';
3
- import { getValidNumber } from './get.js';
4
- import './global.js';
5
- import { TimerTrace } from './models.js';
6
- import { Timer } from './timer.js';
7
-
8
- class When {
9
- $timer = "when";
10
- state = {
11
- promise: void 0,
12
- rejecter: void 0,
13
- resolver: void 0,
14
- started: false,
15
- timer: void 0
16
- };
17
- /**
18
- * Is the timer active?
19
- */
20
- get active() {
21
- return this.state.timer?.active ?? false;
22
- }
23
- /**
24
- * Is the timer destroyed?
25
- */
26
- get destroyed() {
27
- return this.state.timer == null;
28
- }
29
- /**
30
- * Is the timer paused?
31
- */
32
- get paused() {
33
- return this.state.timer?.paused ?? false;
34
- }
35
- /**
36
- * Get the timer's origin _(if debugging is enabled)_
37
- */
38
- get trace() {
39
- return globalThis._oscarpalmer_timer_debug ?? false ? this.state.timer?.trace : void 0;
40
- }
41
- constructor(condition, options) {
42
- const { state } = this;
43
- state.promise = new Promise((resolve, reject) => {
44
- state.resolver = resolve;
45
- state.rejecter = reject;
46
- });
47
- let result = false;
48
- this.state.timer = new Timer(
49
- "when",
50
- {
51
- callback() {
52
- try {
53
- if (condition()) {
54
- result = true;
55
- state.timer.stop();
56
- }
57
- } catch {
58
- state.timer.stop();
59
- }
60
- },
61
- trace: new TimerTrace().stack
62
- },
63
- {
64
- onAfter: () => {
65
- if (result) {
66
- state.resolver?.();
67
- } else {
68
- state.rejecter?.();
69
- }
70
- this.destroy();
71
- },
72
- onError: () => {
73
- state.rejecter?.();
74
- this.destroy();
75
- },
76
- count: getValidNumber(options?.count),
77
- interval: getValidNumber(options?.interval, milliseconds),
78
- timeout: getValidNumber(options?.timeout)
79
- },
80
- false
81
- );
82
- }
83
- /**
84
- * Continues the timer _(if it was paused)_
85
- */
86
- continue() {
87
- this.state.timer?.continue();
88
- return this;
89
- }
90
- /**
91
- * Destroys the timer _(and stops it,if it was running)_
92
- */
93
- destroy() {
94
- const { state } = this;
95
- state.timer?.destroy();
96
- state.promise = void 0;
97
- state.resolver = noop;
98
- state.rejecter = noop;
99
- state.timer = void 0;
100
- }
101
- /**
102
- * Pauses the timer _(if it was running)_
103
- */
104
- pause() {
105
- this.state.timer?.pause();
106
- return this;
107
- }
108
- /**
109
- * Stops the timer _(if it was running)_
110
- */
111
- stop() {
112
- this.state.timer?.stop();
113
- return this;
114
- }
115
- /**
116
- * Starts the timer and returns a promise that resolves when the condition is met
117
- */
118
- // biome-ignore lint/suspicious/noThenProperty: returning a promise-like object, so it's ok ;)
119
- then(resolve, reject) {
120
- const { state } = this;
121
- if (state.timer == null) {
122
- throw new Error(destroyedMessage);
123
- }
124
- if (state.started) {
125
- throw new Error(startedMessage);
126
- }
127
- state.started = true;
128
- state.timer.start();
129
- return state.promise.then(resolve ?? noop, reject ?? noop);
130
- }
131
- }
1
+ import { TYPE_WHEN, defaultTimeout, destroyedMessage, milliseconds, startedMessage } from "./constants.js";
2
+ import { getValidNumber } from "./get.js";
3
+ import "./global.js";
4
+ import { TimerTrace } from "./models.js";
5
+ import { Timer } from "./timer.js";
6
+ import { noop } from "@oscarpalmer/atoms/function";
7
+ var When = class {
8
+ $timer = TYPE_WHEN;
9
+ state = {
10
+ promise: void 0,
11
+ rejecter: void 0,
12
+ resolver: void 0,
13
+ started: false,
14
+ timer: void 0
15
+ };
16
+ get active() {
17
+ return this.state.timer?.active ?? false;
18
+ }
19
+ get destroyed() {
20
+ return this.state.timer == null;
21
+ }
22
+ get paused() {
23
+ return this.state.timer?.paused ?? false;
24
+ }
25
+ get trace() {
26
+ return globalThis._oscarpalmer_timer_debug ?? false ? this.state.timer?.trace : void 0;
27
+ }
28
+ constructor(condition, options) {
29
+ const { state } = this;
30
+ state.promise = new Promise((resolve, reject) => {
31
+ state.resolver = resolve;
32
+ state.rejecter = reject;
33
+ });
34
+ let result = false;
35
+ this.state.timer = new Timer(TYPE_WHEN, {
36
+ callback() {
37
+ try {
38
+ if (condition()) {
39
+ result = true;
40
+ state.timer.stop();
41
+ }
42
+ } catch {
43
+ state.timer.stop();
44
+ }
45
+ },
46
+ trace: new TimerTrace().stack
47
+ }, {
48
+ onAfter: () => {
49
+ if (result) state.resolver?.();
50
+ else state.rejecter?.();
51
+ this.destroy();
52
+ },
53
+ onError: () => {
54
+ state.rejecter?.();
55
+ this.destroy();
56
+ },
57
+ count: getValidNumber(options?.count),
58
+ interval: getValidNumber(options?.interval, milliseconds),
59
+ timeout: getValidNumber(options?.timeout, defaultTimeout)
60
+ }, false);
61
+ }
62
+ continue() {
63
+ this.state.timer?.continue();
64
+ return this;
65
+ }
66
+ destroy() {
67
+ const { state } = this;
68
+ state.timer?.destroy();
69
+ state.promise = void 0;
70
+ state.resolver = noop;
71
+ state.rejecter = noop;
72
+ state.timer = void 0;
73
+ }
74
+ pause() {
75
+ this.state.timer?.pause();
76
+ return this;
77
+ }
78
+ stop() {
79
+ this.state.timer?.stop();
80
+ return this;
81
+ }
82
+ then(resolve, reject) {
83
+ const { state } = this;
84
+ if (state.timer == null) throw new Error(destroyedMessage);
85
+ if (state.started) throw new Error(startedMessage);
86
+ state.started = true;
87
+ state.timer.start();
88
+ return state.promise.then(resolve ?? noop, reject ?? noop);
89
+ }
90
+ };
132
91
  function when(condition, options) {
133
- return new When(condition, options);
92
+ return new When(condition, options);
134
93
  }
135
-
136
94
  export { when };
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 last;
33
- return function step(now) {
34
- if (!state.active) {
35
- return;
36
- }
37
- last ??= now;
38
- const difference = now - last;
39
- state.elapsed += difference;
40
- state.total += difference;
41
- last = now;
42
- if (options.timeout > 0 && state.total >= options.timeout) {
43
- options.onError?.();
44
- finish(timer, state, options, false);
45
- return;
46
- }
47
- if (options.interval === constants.milliseconds || state.elapsed >= options.interval - constants.intervalBuffer) {
48
- if (options.count > -1) {
49
- state.callback(state.index);
50
- }
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 last;
29
- return function step(now) {
30
- if (!state.active) {
31
- return;
32
- }
33
- last ??= now;
34
- const difference = now - last;
35
- state.elapsed += difference;
36
- state.total += difference;
37
- last = now;
38
- if (options.timeout > 0 && state.total >= options.timeout) {
39
- options.onError?.();
40
- finish(timer, state, options, false);
41
- return;
42
- }
43
- if (options.interval === milliseconds || state.elapsed >= options.interval - intervalBuffer) {
44
- if (options.count > -1) {
45
- state.callback(state.index);
46
- }
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,21 +4,22 @@
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": "^24",
14
+ "@types/node": "^24.3",
15
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.49",
19
20
  "tslib": "^2.8",
20
- "typescript": "^5.8",
21
- "vite": "^6.3",
21
+ "typescript": "^5.9",
22
+ "vite": "npm:rolldown-vite@latest",
22
23
  "vitest": "^3.2"
23
24
  },
24
25
  "exports": {
@@ -94,5 +95,5 @@
94
95
  },
95
96
  "type": "module",
96
97
  "types": "types/index.d.cts",
97
- "version": "0.33.0"
98
+ "version": "0.35.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> {
@@ -32,16 +32,15 @@ function calculate(): Promise<number> {
32
32
  });
33
33
  }
34
34
 
35
+ //
36
+
37
+ export const defaultTimeout = 30_000;
38
+
35
39
  /**
36
40
  * A set of all active timers
37
41
  */
38
42
  export const activeTimers = new Set<Timer>();
39
43
 
40
- /**
41
- * A set of types that allow work to begin
42
- */
43
- export const beginTypes = new Set<WorkHandlerType>(['continue', 'start']);
44
-
45
44
  /**
46
45
  * Buffer value to use when evaluating if a specific time is within a certain range
47
46
  */
@@ -52,32 +51,26 @@ export const intervalBuffer = 5;
52
51
  */
53
52
  export const destroyedMessage = 'Timer has already been destroyed';
54
53
 
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
54
  /**
70
55
  * A set of timers that were paused due to the document being hidden
71
56
  */
72
57
  export const hiddenTimers = new Set<Timer>();
73
58
 
74
- export const pauseTypes = new Set<WorkHandlerType>(['continue', 'pause']);
75
-
76
59
  /**
77
60
  * Message to show when a when-timer is started
78
61
  */
79
62
  export const startedMessage = 'Timer has already been started';
80
63
 
64
+ export const TYPE_REPEAT: TimerType = 'repeat';
65
+ export const TYPE_WAIT: TimerType = 'wait';
66
+ export const TYPE_WHEN: TimerType = 'when';
67
+
68
+ export const WORK_CONTINUE: WorkHandlerType = 'continue';
69
+ export const WORK_PAUSE: WorkHandlerType = 'pause';
70
+ export const WORK_RESTART: WorkHandlerType = 'restart';
71
+ export const WORK_START: WorkHandlerType = 'start';
72
+ export const WORK_STOP: WorkHandlerType = 'stop';
73
+
81
74
  //
82
75
 
83
76
  /**
package/src/get.ts CHANGED
@@ -6,7 +6,9 @@ export function getCallback(value: unknown): GenericCallback {
6
6
  }
7
7
 
8
8
  export function getValidNumber(value: unknown, defaultValue?: number): number {
9
- return typeof value === 'number' && value > (defaultValue ?? 0)
10
- ? value
11
- : defaultValue ?? 0;
9
+ return typeof value === 'number'
10
+ ? value > 0
11
+ ? value
12
+ : 0
13
+ : (defaultValue ?? 0);
12
14
  }