@oscarpalmer/timer 0.28.0 → 0.30.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.
Files changed (67) hide show
  1. package/dist/constants.cjs +32 -4
  2. package/dist/constants.js +26 -9
  3. package/dist/delay.cjs +24 -0
  4. package/dist/delay.js +20 -0
  5. package/dist/get.cjs +15 -0
  6. package/dist/get.js +10 -0
  7. package/dist/global.cjs +17 -2
  8. package/dist/global.js +15 -1
  9. package/dist/index.cjs +16 -44
  10. package/dist/index.js +6 -50
  11. package/dist/is.cjs +11 -8
  12. package/dist/is.js +8 -12
  13. package/dist/models.cjs +5 -2
  14. package/dist/models.js +2 -3
  15. package/dist/node_modules/@oscarpalmer/atoms/dist/function.cjs +8 -0
  16. package/dist/node_modules/@oscarpalmer/atoms/dist/function.js +4 -0
  17. package/dist/repeat.cjs +30 -0
  18. package/dist/repeat.js +26 -0
  19. package/dist/timer.cjs +59 -71
  20. package/dist/timer.full.js +472 -0
  21. package/dist/timer.js +56 -72
  22. package/dist/wait.cjs +30 -0
  23. package/dist/wait.js +26 -0
  24. package/dist/when.cjs +59 -44
  25. package/dist/when.js +55 -44
  26. package/dist/work.cjs +72 -0
  27. package/dist/work.js +68 -0
  28. package/package.json +49 -9
  29. package/src/constants.ts +51 -6
  30. package/src/delay.ts +25 -0
  31. package/src/get.ts +12 -0
  32. package/src/global.ts +16 -1
  33. package/src/index.ts +5 -48
  34. package/src/is.ts +6 -6
  35. package/src/models.ts +55 -47
  36. package/src/repeat.ts +32 -0
  37. package/src/timer.ts +67 -151
  38. package/src/wait.ts +31 -0
  39. package/src/when.ts +49 -24
  40. package/src/work.ts +129 -0
  41. package/types/constants.d.cts +49 -74
  42. package/types/constants.d.ts +15 -6
  43. package/types/delay.d.cts +8 -0
  44. package/types/delay.d.ts +4 -0
  45. package/types/get.d.cts +7 -0
  46. package/types/get.d.ts +3 -0
  47. package/types/index.d.cts +89 -94
  48. package/types/index.d.ts +5 -6
  49. package/types/is.d.cts +50 -69
  50. package/types/models.d.cts +61 -63
  51. package/types/models.d.ts +43 -40
  52. package/types/repeat.d.cts +98 -0
  53. package/types/repeat.d.ts +7 -0
  54. package/types/timer.d.cts +35 -97
  55. package/types/timer.d.ts +19 -52
  56. package/types/wait.d.cts +83 -0
  57. package/types/wait.d.ts +8 -0
  58. package/types/when.d.cts +65 -69
  59. package/types/when.d.ts +18 -5
  60. package/types/work.d.cts +78 -0
  61. package/types/work.d.ts +3 -0
  62. package/dist/functions.cjs +0 -99
  63. package/dist/functions.js +0 -99
  64. package/dist/timer.iife.js +0 -431
  65. package/src/functions.ts +0 -158
  66. package/types/functions.d.cts +0 -113
  67. package/types/functions.d.ts +0 -4
package/src/get.ts ADDED
@@ -0,0 +1,12 @@
1
+ import {noop} from '@oscarpalmer/atoms/function';
2
+ import type {GenericCallback} from '@oscarpalmer/atoms/models';
3
+
4
+ export function getCallback(value: unknown): GenericCallback {
5
+ return typeof value === 'function' ? (value as GenericCallback) : noop;
6
+ }
7
+
8
+ export function getValidNumber(value: unknown, defaultValue?: number): number {
9
+ return typeof value === 'number' && value > (defaultValue ?? 0)
10
+ ? value
11
+ : defaultValue ?? 0;
12
+ }
package/src/global.ts CHANGED
@@ -1,5 +1,5 @@
1
+ import {activeTimers, hiddenTimers} from './constants';
1
2
  import type {Timer} from './timer';
2
- import {activeTimers} from './constants';
3
3
 
4
4
  declare global {
5
5
  var _oscarpalmer_timer_debug: boolean | undefined;
@@ -13,3 +13,18 @@ if (globalThis._oscarpalmer_timers == null) {
13
13
  },
14
14
  });
15
15
  }
16
+
17
+ 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
+ }
27
+
28
+ hiddenTimers.clear();
29
+ }
30
+ });
package/src/index.ts CHANGED
@@ -1,51 +1,8 @@
1
- import {noop} from '@oscarpalmer/atoms/function';
2
- import {activeTimers, hiddenTimers} from './constants';
3
1
  import './global';
4
- import {type Timer, wait} from './timer';
5
-
6
- /**
7
- * Creates a delayed promise that resolves after a certain amount of time _(or rejects when timed out)_
8
- */
9
- export function delay(time: number, timeout?: number): Promise<void> {
10
- return new Promise((resolve, reject) => {
11
- let delayed: Timer | null = wait(
12
- () => {
13
- delayed?.destroy();
14
-
15
- delayed = null;
16
-
17
- (resolve ?? noop)();
18
- },
19
- {
20
- timeout,
21
- errorCallback: () => {
22
- delayed?.destroy();
23
-
24
- delayed = null;
25
-
26
- (reject ?? noop)();
27
- },
28
- interval: time,
29
- },
30
- );
31
- });
32
- }
33
-
34
- document.addEventListener('visibilitychange', () => {
35
- if (document.hidden) {
36
- for (const timer of activeTimers) {
37
- hiddenTimers.add(timer);
38
- timer.pause();
39
- }
40
- } else {
41
- for (const timer of hiddenTimers) {
42
- timer.continue();
43
- }
44
-
45
- hiddenTimers.clear();
46
- }
47
- });
48
2
 
3
+ export * from './delay';
49
4
  export {isRepeated, isTimer, isWaited, isWhen} from './is';
50
- export {repeat, wait, type Timer} from './timer';
51
- export {when, type When} from './when';
5
+ export * from './repeat';
6
+ export type {Timer} from './timer';
7
+ export * from './wait';
8
+ export * from './when';
package/src/is.ts CHANGED
@@ -2,34 +2,34 @@ import type {PlainObject} from '@oscarpalmer/atoms/models';
2
2
  import type {Timer} from './timer';
3
3
  import type {When} from './when';
4
4
 
5
- function is(pattern: RegExp, value: unknown) {
6
- return pattern.test((value as PlainObject)?.$timer as string);
5
+ function is(names: string[], value: unknown) {
6
+ return names.includes((value as PlainObject)?.$timer as string);
7
7
  }
8
8
 
9
9
  /**
10
10
  * Is the value a repeating timer?
11
11
  */
12
12
  export function isRepeated(value: unknown): value is Timer {
13
- return is(/^repeat$/, value);
13
+ return is(['repeat'], value);
14
14
  }
15
15
 
16
16
  /**
17
17
  * Is the value a timer?
18
18
  */
19
19
  export function isTimer(value: unknown): value is Timer {
20
- return is(/^repeat|wait$/, value);
20
+ return is(['repeat', 'wait'], value);
21
21
  }
22
22
 
23
23
  /**
24
24
  * Is the value a waiting timer?
25
25
  */
26
26
  export function isWaited(value: unknown): value is Timer {
27
- return is(/^wait$/, value);
27
+ return is(['wait'], value);
28
28
  }
29
29
 
30
30
  /**
31
31
  * Is the value a conditional timer?
32
32
  */
33
33
  export function isWhen(value: unknown): value is When {
34
- return is(/^when$/, value) && typeof (value as When).then === 'function';
34
+ return is(['when'], value) && typeof (value as When).then === 'function';
35
35
  }
package/src/models.ts CHANGED
@@ -1,66 +1,41 @@
1
1
  import type {Timer} from './timer';
2
2
 
3
3
  /**
4
- * Callback that runs after the timer has finished (or is stopped)
5
- * - `finished` is `true` if the timer was allowed to finish, and `false` if it was stopped
4
+ * Options for a repeating timer
6
5
  */
7
- export type AfterCallback = (finished: boolean) => void;
8
-
9
- export type AnyCallback = (() => void) | IndexedCallback;
10
-
11
- /**
12
- * Callback that runs for each iteration of the timer
13
- */
14
- export type IndexedCallback = (index: number) => void;
15
-
16
- export type BaseOptions = {
6
+ export type RepeatOptions = {
17
7
  /**
18
- * Interval between each callback
8
+ * Callback to be called when the timer has stopped, either manually or by completing its work
19
9
  */
20
- interval: number;
21
- /**
22
- * Maximum amount of time the timer may run for
23
- */
24
- timeout: number;
25
- };
26
-
27
- export type OptionsWithCount = {
10
+ onAfter: ((finished: boolean) => void) | undefined;
28
11
  /**
29
12
  * How many times the timer should repeat
30
13
  */
31
14
  count: number;
32
- } & BaseOptions;
33
-
34
- export type OptionsWithError = {
35
15
  /**
36
- * Callback to run when an error occurs _(usually a timeout)_
16
+ * The interval between each repeat
37
17
  */
38
- errorCallback?: () => void;
18
+ interval: number;
39
19
  };
40
20
 
41
- export type RepeatOptions = {
42
- /**
43
- * Callback to run after the timer has finished (or is stopped)
44
- * - `finished` is `true` if the timer was allowed to finish, and `false` if it was stopped
45
- */
46
- afterCallback?: AfterCallback;
47
- } & OptionsWithCount &
48
- OptionsWithError;
49
-
50
- export type TimerOptions = {} & RepeatOptions;
21
+ export type TimerOptions = {
22
+ onAfter: ((finished: boolean) => void) | undefined;
23
+ onError: (() => void) | undefined;
24
+ count: number;
25
+ interval: number;
26
+ timeout: number;
27
+ };
51
28
 
52
29
  export type TimerState = {
53
30
  active: boolean;
54
- callback: AnyCallback;
31
+ callback: () => void;
55
32
  destroyed: boolean;
56
- count?: number;
57
- elapsed?: number;
58
- frame?: number;
59
- index?: number;
60
- isRepeated: boolean;
61
- minimum: number;
33
+ elapsed: number;
34
+ frame: number | undefined;
35
+ index: number;
62
36
  paused: boolean;
63
- trace?: string;
37
+ total: number;
38
+ trace: string | undefined;
64
39
  };
65
40
 
66
41
  export class TimerTrace extends Error {
@@ -71,9 +46,25 @@ export class TimerTrace extends Error {
71
46
  }
72
47
  }
73
48
 
74
- export type WaitOptions = {} & BaseOptions & OptionsWithError;
49
+ export type TimerType = 'repeat' | 'wait' | 'when';
75
50
 
76
- export type WhenOptions = {} & OptionsWithCount;
51
+ /**
52
+ * Options for a conditional timer
53
+ */
54
+ export type WhenOptions = {
55
+ /**
56
+ * How many times the timer should check the condition
57
+ */
58
+ count: number;
59
+ /**
60
+ * Then interval between each condtional check
61
+ */
62
+ interval: number;
63
+ /**
64
+ * The timeout for the timer
65
+ */
66
+ timeout: number;
67
+ };
77
68
 
78
69
  export type WhenState = {
79
70
  promise: Promise<void>;
@@ -83,4 +74,21 @@ export type WhenState = {
83
74
  timer: Timer;
84
75
  };
85
76
 
86
- export type WorkType = 'continue' | 'pause' | 'restart' | 'start' | 'stop';
77
+ export type WorkHandler = (
78
+ type: WorkHandlerType,
79
+ timer: WorkHandlerTimer,
80
+ state: TimerState,
81
+ options: TimerOptions,
82
+ ) => Timer;
83
+
84
+ export type WorkHandlerTimer = {
85
+ instance: Timer;
86
+ type: TimerType;
87
+ };
88
+
89
+ export type WorkHandlerType =
90
+ | 'continue'
91
+ | 'pause'
92
+ | 'restart'
93
+ | 'start'
94
+ | 'stop';
package/src/repeat.ts ADDED
@@ -0,0 +1,32 @@
1
+ import {milliseconds} from './constants';
2
+ import {getCallback, getValidNumber} from './get';
3
+ import {type RepeatOptions, TimerTrace} from './models';
4
+ import {Timer} from './timer';
5
+ import {work} from './work';
6
+
7
+ /**
8
+ * Create a repeating timer
9
+ */
10
+ export function repeat(
11
+ callback: (index: number) => void,
12
+ options?: Partial<RepeatOptions>,
13
+ ): Timer {
14
+ return new Timer(
15
+ 'repeat',
16
+ work,
17
+ {
18
+ callback: getCallback(callback),
19
+ trace: new TimerTrace().stack,
20
+ },
21
+ {
22
+ onAfter: getCallback(options?.onAfter),
23
+ onError: undefined,
24
+ count: getValidNumber(options?.count),
25
+ interval: getValidNumber(options?.interval, milliseconds),
26
+ timeout: 0,
27
+ },
28
+ true,
29
+ );
30
+ }
31
+
32
+ export type {RepeatOptions, Timer};
package/src/timer.ts CHANGED
@@ -1,208 +1,124 @@
1
- import {milliseconds} from './constants';
2
- import {getOptions, work} from './functions';
3
- import {
4
- type AnyCallback,
5
- type IndexedCallback,
6
- type RepeatOptions,
7
- type TimerOptions,
8
- type TimerState,
9
- TimerTrace,
10
- type WaitOptions,
1
+ import type {
2
+ TimerOptions,
3
+ TimerState,
4
+ TimerType,
5
+ WorkHandler,
6
+ WorkHandlerType,
11
7
  } from './models';
12
8
 
13
- export abstract class BasicTimer<State> {
14
- protected declare readonly $timer: string;
15
- protected declare readonly state: State;
9
+ export class Timer {
10
+ private declare readonly $timer: TimerType;
16
11
 
17
- constructor(type: 'repeat' | 'wait' | 'when', state: State) {
18
- this.$timer = type;
19
- this.state = state;
20
- }
12
+ protected readonly state: TimerState;
21
13
 
22
14
  /**
23
- * Is the timer running?
15
+ * Is the timer active?
24
16
  */
25
- abstract readonly active: boolean;
17
+ get active(): boolean {
18
+ return this.state.active;
19
+ }
26
20
 
27
21
  /**
28
22
  * Is the timer destroyed?
29
23
  */
30
- abstract readonly destroyed: boolean;
24
+ get destroyed(): boolean {
25
+ return this.state.destroyed;
26
+ }
31
27
 
32
28
  /**
33
29
  * Is the timer paused?
34
30
  */
35
- abstract readonly paused: boolean;
36
-
37
- /**
38
- * Gets the traced location of the timer
39
- */
40
- abstract readonly trace: string | undefined;
41
- }
42
-
43
- /**
44
- * A timer that can be started, stopped, and restarted as neeeded
45
- */
46
- export class Timer extends BasicTimer<TimerState> {
47
- private declare readonly options: TimerOptions;
48
-
49
- get active() {
50
- return this.state.active;
51
- }
52
-
53
- get destroyed() {
54
- return this.state.destroyed;
55
- }
56
-
57
- get paused() {
31
+ get paused(): boolean {
58
32
  return this.state.paused;
59
33
  }
60
34
 
61
- get trace() {
62
- return globalThis._oscarpalmer_timer_debug ? this.state.trace : undefined;
35
+ /**
36
+ * Get the timer's origin _(if debugging is enabled)_
37
+ */
38
+ get trace(): string | undefined {
39
+ return globalThis._oscarpalmer_timer_debug ?? false
40
+ ? this.state.trace
41
+ : undefined;
63
42
  }
64
43
 
65
44
  constructor(
66
- type: 'repeat' | 'wait',
67
- state: TimerState,
68
- options: TimerOptions,
45
+ type: TimerType,
46
+ protected readonly worker: WorkHandler,
47
+ state: Pick<TimerState, 'callback' | 'trace'>,
48
+ protected readonly options: TimerOptions,
49
+ start: boolean,
69
50
  ) {
70
- super(type, state);
51
+ this.$timer = type;
52
+
53
+ this.state = {
54
+ ...state,
55
+ active: false,
56
+ destroyed: false,
57
+ elapsed: 0,
58
+ frame: undefined,
59
+ index: 0,
60
+ paused: false,
61
+ total: 0,
62
+ };
71
63
 
72
- this.options = options;
64
+ if (start) {
65
+ this.start();
66
+ }
73
67
  }
74
68
 
75
69
  /**
76
- * Continues the timer _(if it was paused)_
70
+ * Continue running the timer _(if it's paused)_
77
71
  */
78
72
  continue(): Timer {
79
- return work('continue', this, this.state, this.options);
73
+ return this.#work('continue');
80
74
  }
81
75
 
82
76
  /**
83
- * Destroys the timer _(after stopping it, if it was running)_
77
+ * Destroy the timer
84
78
  */
85
79
  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;
80
+ this.state.destroyed = true;
93
81
 
94
- this.state.callback = undefined as never;
95
- this.state.trace = undefined as never;
96
- }
82
+ this.#work('stop');
97
83
  }
98
84
 
99
85
  /**
100
- * Pauses the timer _(if it was running)_
86
+ * Pause the timer _(if it's running)_
101
87
  */
102
88
  pause(): Timer {
103
- return work('pause', this, this.state, this.options);
89
+ return this.#work('pause');
104
90
  }
105
91
 
106
92
  /**
107
- * Restarts the timer _(if it was running)_
93
+ * Restart the timer _(or start it, if it's not running)_
108
94
  */
109
95
  restart(): Timer {
110
- return work('restart', this, this.state, this.options);
96
+ return this.#work('restart');
111
97
  }
112
98
 
113
99
  /**
114
- * Starts the timer _(if it was stopped)_
100
+ * Start the timer _(if it's not running)_
115
101
  */
116
102
  start(): Timer {
117
- return work('start', this, this.state, this.options);
103
+ return this.#work('start');
118
104
  }
119
105
 
120
106
  /**
121
- * Stops the timer _(if it was running)_
107
+ * Stop the timer _(if it's running)_
122
108
  */
123
109
  stop(): Timer {
124
- return work('stop', this, this.state, this.options);
110
+ return this.#work('stop');
125
111
  }
126
- }
127
112
 
128
- /**
129
- * Creates a timer which:
130
- * - calls a callback after a certain amount of time...
131
- * - ... and repeats it a certain amount of times
132
- * ---
133
- * - `options.count` defaults to `Infinity`
134
- * - `options.interval` defaults to `1000/60` _(1 frame)_
135
- * - `options.timeout` defaults to `Infinity`
136
- */
137
- export function repeat(
138
- callback: IndexedCallback,
139
- options?: Partial<RepeatOptions>,
140
- ): Timer {
141
- return timer('repeat', callback, options ?? {}, true);
142
- }
143
-
144
- export function timer(
145
- type: 'repeat' | 'wait',
146
- callback: AnyCallback,
147
- partial: Partial<TimerOptions>,
148
- start: boolean,
149
- ): Timer {
150
- const isRepeated = type === 'repeat';
151
- const options = getOptions(partial, isRepeated);
152
-
153
- const instance = new Timer(
154
- type,
155
- {
156
- callback,
157
- isRepeated,
158
- active: false,
159
- destroyed: false,
160
- minimum: options.interval - (options.interval % milliseconds) / 2,
161
- paused: false,
162
- trace: new TimerTrace().stack,
163
- },
164
- options,
165
- );
166
-
167
- if (start) {
168
- instance.start();
113
+ #work(type: WorkHandlerType): Timer {
114
+ return this.worker(
115
+ type,
116
+ {
117
+ instance: this,
118
+ type: this.$timer,
119
+ },
120
+ this.state,
121
+ this.options,
122
+ );
169
123
  }
170
-
171
- return instance;
172
- }
173
-
174
- /**
175
- * Creates a timer which calls a callback after a certain amount of time
176
- */
177
- export function wait(callback: () => void): Timer;
178
-
179
- /**
180
- * Creates a timer which calls a callback after a certain amount of time
181
- */
182
- export function wait(callback: () => void, time: number): Timer;
183
-
184
- /**
185
- * Creates a timer which calls a callback after a certain amount of time
186
- * - `options.interval` defaults to `1000/60` _(1 frame)_
187
- * - `options.timeout` defaults to `30_000` _(30 seconds)_
188
- */
189
- export function wait(
190
- callback: () => void,
191
- options: Partial<WaitOptions>,
192
- ): Timer;
193
-
194
- export function wait(
195
- callback: () => void,
196
- options?: number | Partial<WaitOptions>,
197
- ): Timer {
198
- return timer(
199
- 'wait',
200
- callback,
201
- options == null || typeof options === 'number'
202
- ? {
203
- interval: options,
204
- }
205
- : options,
206
- true,
207
- );
208
124
  }
package/src/wait.ts ADDED
@@ -0,0 +1,31 @@
1
+ import {milliseconds} from './constants';
2
+ import {getCallback, getValidNumber} from './get';
3
+ import {TimerTrace} from './models';
4
+ import {Timer} from './timer';
5
+ import {work} from './work';
6
+
7
+ /**
8
+ * Create a waiting timer
9
+ * @param callback Callback to run when the timer has finished
10
+ * @param time How long to wait for _(in milliseconds; defaults to screen refresh rate)_
11
+ */
12
+ export function wait(callback: () => void, time?: number): Timer {
13
+ return new Timer(
14
+ 'wait',
15
+ work,
16
+ {
17
+ callback: getCallback(callback),
18
+ trace: new TimerTrace().stack,
19
+ },
20
+ {
21
+ onAfter: undefined,
22
+ onError: undefined,
23
+ count: -1,
24
+ interval: getValidNumber(time, milliseconds),
25
+ timeout: 0,
26
+ },
27
+ true,
28
+ );
29
+ }
30
+
31
+ export type {Timer};