@oscarpalmer/timer 0.22.1 → 0.24.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
@@ -4,6 +4,13 @@ function noop() {
4
4
 
5
5
  // src/constants.ts
6
6
  var activeTimers = new Set;
7
+ var beginTypes = new Set(["continue", "start"]);
8
+ var endTypes = new Set(["pause", "stop"]);
9
+ var endOrRestartTypes = new Set([
10
+ "pause",
11
+ "restart",
12
+ "stop"
13
+ ]);
7
14
  var hiddenTimers = new Set;
8
15
  var milliseconds = 1000 / 60;
9
16
 
@@ -21,12 +28,12 @@ function getValueOrDefault(value, defaultValue, minimum) {
21
28
  return typeof value === "number" && value > (minimum ?? 0) ? value : defaultValue;
22
29
  }
23
30
  function work(type, timer, state, options) {
24
- if (["continue", "start"].includes(type) && state.active || ["pause", "stop"].includes(type) && !state.active) {
31
+ if (state.destroyed && type !== "stop" || beginTypes.has(type) && state.active || endTypes.has(type) && !state.active) {
25
32
  return timer;
26
33
  }
27
34
  const { count, interval, timeout } = options;
28
35
  const { isRepeated, minimum } = state;
29
- if (["pause", "restart", "stop"].includes(type)) {
36
+ if (endOrRestartTypes.has(type)) {
30
37
  const isStop = type === "stop";
31
38
  activeTimers.delete(timer);
32
39
  cancelAnimationFrame(state.frame);
@@ -95,6 +102,14 @@ function work(type, timer, state, options) {
95
102
  return timer;
96
103
  }
97
104
 
105
+ // src/models.ts
106
+ class TimerTrace extends Error {
107
+ constructor() {
108
+ super();
109
+ this.name = "TimerTrace";
110
+ }
111
+ }
112
+
98
113
  // src/timer.ts
99
114
  function timer(type, callback, partial, start) {
100
115
  const isRepeated = type === "repeat";
@@ -103,6 +118,7 @@ function timer(type, callback, partial, start) {
103
118
  callback,
104
119
  isRepeated,
105
120
  active: false,
121
+ destroyed: false,
106
122
  minimum: options.interval - options.interval % milliseconds / 2,
107
123
  paused: false,
108
124
  trace: new TimerTrace
@@ -123,6 +139,9 @@ class Timer extends BasicTimer {
123
139
  get active() {
124
140
  return this.state.active;
125
141
  }
142
+ get destroyed() {
143
+ return this.state.destroyed;
144
+ }
126
145
  get paused() {
127
146
  return this.state.paused;
128
147
  }
@@ -136,6 +155,16 @@ class Timer extends BasicTimer {
136
155
  continue() {
137
156
  return work("continue", this, this.state, this.options);
138
157
  }
158
+ destroy() {
159
+ if (!this.state.destroyed) {
160
+ this.state.destroyed = true;
161
+ this.stop();
162
+ this.options.afterCallback = undefined;
163
+ this.options.errorCallback = undefined;
164
+ this.state.callback = undefined;
165
+ this.state.trace = undefined;
166
+ }
167
+ }
139
168
  pause() {
140
169
  return work("pause", this, this.state, this.options);
141
170
  }
@@ -150,72 +179,87 @@ class Timer extends BasicTimer {
150
179
  }
151
180
  }
152
181
 
153
- class TimerTrace extends Error {
154
- constructor() {
155
- super();
156
- this.name = "TimerTrace";
157
- }
158
- }
159
-
160
182
  // src/when.ts
161
183
  function when(condition, options) {
162
- const repeated = timer("repeat", () => {
163
- if (condition()) {
164
- repeated.stop();
165
- state.resolver?.();
166
- }
167
- }, {
168
- afterCallback() {
169
- if (!repeated.paused) {
170
- if (condition()) {
171
- state.resolver?.();
172
- } else {
173
- state.rejecter?.();
174
- }
184
+ let result = false;
185
+ const state = {
186
+ started: false,
187
+ timer: timer("repeat", () => {
188
+ if (condition()) {
189
+ result = true;
190
+ state.timer.stop();
175
191
  }
176
- },
177
- errorCallback() {
178
- state.rejecter?.();
179
- },
180
- count: options?.count,
181
- interval: options?.interval,
182
- timeout: options?.timeout
183
- }, false);
184
- const state = {};
185
- state.promise = new Promise((resolve, reject) => {
192
+ }, {
193
+ afterCallback() {
194
+ if (!state.timer.paused) {
195
+ if (result) {
196
+ state.resolver?.();
197
+ } else {
198
+ state.rejecter?.();
199
+ }
200
+ instance.destroy();
201
+ }
202
+ },
203
+ errorCallback() {
204
+ state.rejecter?.();
205
+ instance.destroy();
206
+ },
207
+ count: options?.count,
208
+ interval: options?.interval,
209
+ timeout: options?.timeout
210
+ }, false)
211
+ };
212
+ const promise = new Promise((resolve, reject) => {
186
213
  state.resolver = resolve;
187
214
  state.rejecter = reject;
188
215
  });
189
- state.timer = repeated;
190
- return new When(state);
216
+ state.promise = promise;
217
+ const instance = new When(state);
218
+ return instance;
191
219
  }
220
+ var destroyedMessage = "Timer has already been destroyed";
221
+ var startedMessage = "Timer has already been started";
192
222
 
193
223
  class When extends BasicTimer {
194
224
  get active() {
195
- return this.state.timer.active;
225
+ return this.state.timer?.active ?? false;
226
+ }
227
+ get destroyed() {
228
+ return this.state.timer == null;
196
229
  }
197
230
  get paused() {
198
- return this.state.timer.paused;
231
+ return this.state.timer?.paused ?? false;
232
+ }
233
+ get trace() {
234
+ return this.state.timer?.trace;
199
235
  }
200
236
  constructor(state) {
201
237
  super("when", state);
202
238
  }
203
239
  continue() {
204
- this.state.timer.continue();
240
+ this.state.timer?.continue();
205
241
  return this;
206
242
  }
243
+ destroy() {
244
+ this.state.timer?.destroy();
245
+ this.state.promise = undefined;
246
+ this.state.resolver = noop;
247
+ this.state.rejecter = noop;
248
+ this.state.timer = undefined;
249
+ }
207
250
  pause() {
208
- this.state.timer.pause();
251
+ this.state.timer?.pause();
209
252
  return this;
210
253
  }
211
254
  stop() {
212
- if (this.state.timer.active) {
213
- this.state.timer.stop();
214
- this.state.rejecter?.();
215
- }
255
+ this.state.timer?.stop();
216
256
  return this;
217
257
  }
218
258
  then(resolve, reject) {
259
+ if (this.state.timer == null || this.state?.started) {
260
+ throw new Error(this.state.timer == null ? destroyedMessage : startedMessage);
261
+ }
262
+ this.state.started = true;
219
263
  this.state.timer.start();
220
264
  return this.state.promise.then(resolve ?? noop, reject ?? noop);
221
265
  }
package/dist/when.mjs CHANGED
@@ -2,63 +2,85 @@
2
2
  import {noop} from "@oscarpalmer/atoms/function";
3
3
  import {BasicTimer, timer as timer2} from "./timer";
4
4
  function when(condition, options) {
5
- const repeated = timer2("repeat", () => {
6
- if (condition()) {
7
- repeated.stop();
8
- state.resolver?.();
9
- }
10
- }, {
11
- afterCallback() {
12
- if (!repeated.paused) {
13
- if (condition()) {
14
- state.resolver?.();
15
- } else {
16
- state.rejecter?.();
17
- }
5
+ let result = false;
6
+ const state = {
7
+ started: false,
8
+ timer: timer2("repeat", () => {
9
+ if (condition()) {
10
+ result = true;
11
+ state.timer.stop();
18
12
  }
19
- },
20
- errorCallback() {
21
- state.rejecter?.();
22
- },
23
- count: options?.count,
24
- interval: options?.interval,
25
- timeout: options?.timeout
26
- }, false);
27
- const state = {};
28
- state.promise = new Promise((resolve, reject) => {
13
+ }, {
14
+ afterCallback() {
15
+ if (!state.timer.paused) {
16
+ if (result) {
17
+ state.resolver?.();
18
+ } else {
19
+ state.rejecter?.();
20
+ }
21
+ instance.destroy();
22
+ }
23
+ },
24
+ errorCallback() {
25
+ state.rejecter?.();
26
+ instance.destroy();
27
+ },
28
+ count: options?.count,
29
+ interval: options?.interval,
30
+ timeout: options?.timeout
31
+ }, false)
32
+ };
33
+ const promise = new Promise((resolve, reject) => {
29
34
  state.resolver = resolve;
30
35
  state.rejecter = reject;
31
36
  });
32
- state.timer = repeated;
33
- return new When(state);
37
+ state.promise = promise;
38
+ const instance = new When(state);
39
+ return instance;
34
40
  }
41
+ var destroyedMessage = "Timer has already been destroyed";
42
+ var startedMessage = "Timer has already been started";
35
43
 
36
44
  class When extends BasicTimer {
37
45
  get active() {
38
- return this.state.timer.active;
46
+ return this.state.timer?.active ?? false;
47
+ }
48
+ get destroyed() {
49
+ return this.state.timer == null;
39
50
  }
40
51
  get paused() {
41
- return this.state.timer.paused;
52
+ return this.state.timer?.paused ?? false;
53
+ }
54
+ get trace() {
55
+ return this.state.timer?.trace;
42
56
  }
43
57
  constructor(state) {
44
58
  super("when", state);
45
59
  }
46
60
  continue() {
47
- this.state.timer.continue();
61
+ this.state.timer?.continue();
48
62
  return this;
49
63
  }
64
+ destroy() {
65
+ this.state.timer?.destroy();
66
+ this.state.promise = undefined;
67
+ this.state.resolver = noop;
68
+ this.state.rejecter = noop;
69
+ this.state.timer = undefined;
70
+ }
50
71
  pause() {
51
- this.state.timer.pause();
72
+ this.state.timer?.pause();
52
73
  return this;
53
74
  }
54
75
  stop() {
55
- if (this.state.timer.active) {
56
- this.state.timer.stop();
57
- this.state.rejecter?.();
58
- }
76
+ this.state.timer?.stop();
59
77
  return this;
60
78
  }
61
79
  then(resolve, reject) {
80
+ if (this.state.timer == null || this.state?.started) {
81
+ throw new Error(this.state.timer == null ? destroyedMessage : startedMessage);
82
+ }
83
+ this.state.started = true;
62
84
  this.state.timer.start();
63
85
  return this.state.promise.then(resolve ?? noop, reject ?? noop);
64
86
  }
package/package.json CHANGED
@@ -8,12 +8,12 @@
8
8
  },
9
9
  "description": "A better solution for timeout- and interval-based timers.",
10
10
  "devDependencies": {
11
- "@biomejs/biome": "^1.8.3",
12
- "@happy-dom/global-registrator": "^14.12.3",
13
- "@types/bun": "^1.1.6",
14
- "bun": "^1.1.21",
11
+ "@biomejs/biome": "^1.9.0",
12
+ "@happy-dom/global-registrator": "^15.7.4",
13
+ "@types/bun": "^1.1.9",
14
+ "bun": "^1.1.27",
15
15
  "dts-bundle-generator": "9.5.1",
16
- "typescript": "^5.5.4"
16
+ "typescript": "^5.6.2"
17
17
  },
18
18
  "exports": {
19
19
  ".": {
@@ -50,5 +50,5 @@
50
50
  },
51
51
  "type": "module",
52
52
  "types": "types/index.d.cts",
53
- "version": "0.22.1"
53
+ "version": "0.24.0"
54
54
  }
package/src/constants.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type {WorkType} from './models';
1
2
  import type {Timer} from './timer';
2
3
 
3
4
  /**
@@ -5,6 +6,25 @@ import type {Timer} from './timer';
5
6
  */
6
7
  export const activeTimers = new Set<Timer>();
7
8
 
9
+ /**
10
+ * A set of types that allow work to begin
11
+ */
12
+ export const beginTypes = new Set<WorkType>(['continue', 'start']);
13
+
14
+ /**
15
+ * A set of types that allow work to end
16
+ */
17
+ export const endTypes = new Set<WorkType>(['pause', 'stop']);
18
+
19
+ /**
20
+ * A set of types that allow work to end or restart
21
+ */
22
+ export const endOrRestartTypes = new Set<WorkType>([
23
+ 'pause',
24
+ 'restart',
25
+ 'stop',
26
+ ]);
27
+
8
28
  /**
9
29
  * A set of timers that were paused due to the document being hidden
10
30
  */
package/src/functions.ts CHANGED
@@ -1,5 +1,11 @@
1
- import {activeTimers, milliseconds} from './constants';
2
- import type {TimerOptions, TimerState, WorkType} from './models';
1
+ import {
2
+ activeTimers,
3
+ beginTypes,
4
+ endOrRestartTypes,
5
+ endTypes,
6
+ milliseconds,
7
+ } from './constants';
8
+ import type {TimerOptions, TimerState, WhenState, WorkType} from './models';
3
9
  import type {Timer} from './timer';
4
10
 
5
11
  export function getOptions(
@@ -38,8 +44,9 @@ export function work(
38
44
  options: TimerOptions,
39
45
  ): Timer {
40
46
  if (
41
- (['continue', 'start'].includes(type) && state.active) ||
42
- (['pause', 'stop'].includes(type) && !state.active)
47
+ (state.destroyed && type !== 'stop') ||
48
+ (beginTypes.has(type) && state.active) ||
49
+ (endTypes.has(type) && !state.active)
43
50
  ) {
44
51
  return timer;
45
52
  }
@@ -47,7 +54,7 @@ export function work(
47
54
  const {count, interval, timeout} = options;
48
55
  const {isRepeated, minimum} = state;
49
56
 
50
- if (['pause', 'restart', 'stop'].includes(type)) {
57
+ if (endOrRestartTypes.has(type)) {
51
58
  const isStop = type === 'stop';
52
59
 
53
60
  activeTimers.delete(timer);
package/src/index.ts CHANGED
@@ -8,11 +8,22 @@ import {wait} from './timer';
8
8
  */
9
9
  export function delay(time: number, timeout?: number): Promise<void> {
10
10
  return new Promise((resolve, reject) => {
11
- wait(resolve ?? noop, {
12
- timeout,
13
- errorCallback: reject ?? noop,
14
- interval: time,
15
- });
11
+ const delayed = wait(
12
+ () => {
13
+ delayed.destroy();
14
+
15
+ (resolve ?? noop)();
16
+ },
17
+ {
18
+ timeout,
19
+ errorCallback: () => {
20
+ delayed.destroy();
21
+
22
+ (reject ?? noop)();
23
+ },
24
+ interval: time,
25
+ },
26
+ );
16
27
  });
17
28
  }
18
29
 
@@ -34,4 +45,3 @@ document.addEventListener('visibilitychange', () => {
34
45
  export {isRepeated, isTimer, isWaited, isWhen} from './is';
35
46
  export {repeat, wait, type Timer} from './timer';
36
47
  export {when, type When} from './when';
37
-
package/src/models.ts CHANGED
@@ -52,6 +52,7 @@ export type TimerOptions = {} & RepeatOptions;
52
52
  export type TimerState = {
53
53
  active: boolean;
54
54
  callback: AnyCallback;
55
+ destroyed: boolean;
55
56
  count?: number;
56
57
  elapsed?: number;
57
58
  frame?: number;
@@ -59,9 +60,17 @@ export type TimerState = {
59
60
  isRepeated: boolean;
60
61
  minimum: number;
61
62
  paused: boolean;
62
- trace: unknown;
63
+ trace: TimerTrace;
63
64
  };
64
65
 
66
+ export class TimerTrace extends Error {
67
+ constructor() {
68
+ super();
69
+
70
+ this.name = 'TimerTrace';
71
+ }
72
+ }
73
+
65
74
  export type WaitOptions = {} & BaseOptions & OptionsWithError;
66
75
 
67
76
  export type WhenOptions = {} & OptionsWithCount;
@@ -70,6 +79,7 @@ export type WhenState = {
70
79
  promise: Promise<void>;
71
80
  rejecter?: () => void;
72
81
  resolver?: () => void;
82
+ started: boolean;
73
83
  timer: Timer;
74
84
  };
75
85
 
package/src/timer.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import {milliseconds} from './constants';
2
2
  import {getOptions, work} from './functions';
3
- import type {
4
- AnyCallback,
5
- IndexedCallback,
6
- RepeatOptions,
7
- TimerOptions,
8
- TimerState,
9
- WaitOptions,
3
+ import {
4
+ TimerTrace,
5
+ type AnyCallback,
6
+ type IndexedCallback,
7
+ type RepeatOptions,
8
+ type TimerOptions,
9
+ type TimerState,
10
+ type WaitOptions,
10
11
  } from './models';
11
12
 
12
13
  export abstract class BasicTimer<State> {
@@ -23,10 +24,20 @@ export abstract class BasicTimer<State> {
23
24
  */
24
25
  abstract readonly active: boolean;
25
26
 
27
+ /**
28
+ * Is the timer destroyed?
29
+ */
30
+ abstract readonly destroyed: boolean;
31
+
26
32
  /**
27
33
  * Is the timer paused?
28
34
  */
29
35
  abstract readonly paused: boolean;
36
+
37
+ /**
38
+ * Gets the traced location of the timer
39
+ */
40
+ abstract readonly trace: TimerTrace | undefined;
30
41
  }
31
42
 
32
43
  /**
@@ -39,13 +50,14 @@ export class Timer extends BasicTimer<TimerState> {
39
50
  return this.state.active;
40
51
  }
41
52
 
53
+ get destroyed() {
54
+ return this.state.destroyed;
55
+ }
56
+
42
57
  get paused() {
43
58
  return this.state.paused;
44
59
  }
45
60
 
46
- /**
47
- * Gets the traced location of the timer
48
- */
49
61
  get trace() {
50
62
  return globalThis._oscarpalmer_timer_debug ? this.state.trace : undefined;
51
63
  }
@@ -67,6 +79,23 @@ export class Timer extends BasicTimer<TimerState> {
67
79
  return work('continue', this, this.state, this.options);
68
80
  }
69
81
 
82
+ /**
83
+ * Destroys the timer _(after stopping it, if it was running)_
84
+ */
85
+ destroy(): void {
86
+ if (!this.state.destroyed) {
87
+ this.state.destroyed = true;
88
+
89
+ this.stop();
90
+
91
+ this.options.afterCallback = undefined;
92
+ this.options.errorCallback = undefined;
93
+
94
+ this.state.callback = undefined as never;
95
+ this.state.trace = undefined as never;
96
+ }
97
+ }
98
+
70
99
  /**
71
100
  * Pauses the timer _(if it was running)_
72
101
  */
@@ -96,14 +125,6 @@ export class Timer extends BasicTimer<TimerState> {
96
125
  }
97
126
  }
98
127
 
99
- class TimerTrace extends Error {
100
- constructor() {
101
- super();
102
-
103
- this.name = 'TimerTrace';
104
- }
105
- }
106
-
107
128
  /**
108
129
  * Creates a timer which:
109
130
  * - calls a callback after a certain amount of time...
@@ -135,6 +156,7 @@ export function timer(
135
156
  callback,
136
157
  isRepeated,
137
158
  active: false,
159
+ destroyed: false,
138
160
  minimum: options.interval - (options.interval % milliseconds) / 2,
139
161
  paused: false,
140
162
  trace: new TimerTrace(),