@oscarpalmer/timer 0.42.0 → 0.44.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/src/models.ts CHANGED
@@ -1,5 +1,3 @@
1
- import type {Timer} from './timer';
2
-
3
1
  /**
4
2
  * Options for a repeating timer
5
3
  */
@@ -26,6 +24,60 @@ export type RepeatOptions = {
26
24
  timeout: number;
27
25
  };
28
26
 
27
+ export type Timer = {
28
+ /**
29
+ * Is the timer active?
30
+ */
31
+ get active(): boolean;
32
+
33
+ /**
34
+ * Is the timer destroyed?
35
+ */
36
+ get destroyed(): boolean;
37
+
38
+ /**
39
+ * Is the timer paused?
40
+ */
41
+ get paused(): boolean;
42
+
43
+ /**
44
+ * Get the timer's origin _(if debugging is enabled)_
45
+ */
46
+ get trace(): string | undefined;
47
+
48
+ /**
49
+ * Continue running the timer _(if it's paused)_
50
+ */
51
+ continue(): Timer;
52
+
53
+ /**
54
+ * Destroy the timer
55
+ */
56
+ destroy(): void;
57
+
58
+ /**
59
+ * Pause the timer _(if it's running)_
60
+ */
61
+ pause(): Timer;
62
+
63
+ /**
64
+ * Restart the timer _(or start it, if it's not running)_
65
+ */
66
+ restart(): Timer;
67
+
68
+ /**
69
+ * Start the timer _(if it's not running)_
70
+ */
71
+ start(): Timer;
72
+
73
+ /**
74
+ * Stop the timer _(if it's running)_
75
+ */
76
+ stop(): Timer;
77
+ };
78
+
79
+ export type TimerName = 'repeat' | 'wait' | 'when';
80
+
29
81
  export type TimerOptions = {
30
82
  onAfter: ((finished: boolean) => void) | undefined;
31
83
  onError: (() => void) | undefined;
@@ -54,7 +106,55 @@ export class TimerTrace extends Error {
54
106
  }
55
107
  }
56
108
 
57
- export type TimerType = 'repeat' | 'wait' | 'when';
109
+ export type When = {
110
+ /**
111
+ * Is the timer active?
112
+ */
113
+ get active(): boolean;
114
+
115
+ /**
116
+ * Is the timer destroyed?
117
+ */
118
+ get destroyed(): boolean;
119
+
120
+ /**
121
+ * Is the timer paused?
122
+ */
123
+ get paused(): boolean;
124
+
125
+ /**
126
+ * Get the timer's origin _(if debugging is enabled)_
127
+ */
128
+ get trace(): string | undefined;
129
+
130
+ /**
131
+ * Continues the timer _(if it was paused)_
132
+ */
133
+ continue(): When;
134
+
135
+ /**
136
+ * Destroys the timer _(and stops it,if it was running)_
137
+ */
138
+ destroy(): void;
139
+
140
+ /**
141
+ * Pauses the timer _(if it was running)_
142
+ */
143
+ pause(): When;
144
+
145
+ /**
146
+ * Start the timer
147
+ *
148
+ * @param resolve Optional resolve callback
149
+ * @returns Promise that resolves when the condition is met
150
+ */
151
+ start(resolve?: (() => void) | null): Promise<void>;
152
+
153
+ /**
154
+ * Stops the timer _(if it was running)_
155
+ */
156
+ stop(): When;
157
+ };
58
158
 
59
159
  /**
60
160
  * Options for a conditional timer
@@ -78,6 +178,7 @@ export type WhenState = {
78
178
  promise: Promise<void>;
79
179
  rejecter?: () => void;
80
180
  resolver?: () => void;
181
+ result: boolean;
81
182
  started: boolean;
82
183
  timer: Timer;
83
184
  };
@@ -91,7 +192,7 @@ export type WorkHandler = (
91
192
 
92
193
  export type WorkHandlerTimer = {
93
194
  instance: Timer;
94
- type: TimerType;
195
+ name: TimerName;
95
196
  };
96
197
 
97
198
  export type WorkHandlerType = 'continue' | 'pause' | 'restart' | 'start' | 'stop';
package/src/repeat.ts CHANGED
@@ -1,17 +1,18 @@
1
1
  import {TYPE_REPEAT} from './constants';
2
2
  import {getCallback, getValidNumber} from './get';
3
3
  import './global';
4
- import {type RepeatOptions, TimerTrace} from './models';
5
- import {Timer} from './timer';
4
+ import {type RepeatOptions, type Timer, TimerTrace} from './models';
5
+ import {createTimer} from './timer';
6
6
 
7
7
  /**
8
8
  * Create a repeating timer
9
+ *
9
10
  * @param callback Callback to run on each interval
10
11
  * @param options Timer options
11
12
  * @returns Timer instance
12
13
  */
13
14
  export function repeat(callback: (index: number) => void, options?: Partial<RepeatOptions>): Timer {
14
- return new Timer(
15
+ return createTimer(
15
16
  TYPE_REPEAT,
16
17
  {
17
18
  callback: getCallback(callback),
@@ -27,5 +28,3 @@ export function repeat(callback: (index: number) => void, options?: Partial<Repe
27
28
  true,
28
29
  );
29
30
  }
30
-
31
- export type {RepeatOptions, Timer};
package/src/timer.ts CHANGED
@@ -1,135 +1,91 @@
1
1
  import {noop} from '@oscarpalmer/atoms/function';
2
2
  import {WORK_CONTINUE, WORK_PAUSE, WORK_RESTART, WORK_START, WORK_STOP} from './constants';
3
- import type {TimerOptions, TimerState, TimerType, WorkHandlerType} from './models';
3
+ import type {Timer, TimerName, TimerOptions, TimerState, WorkHandlerType} from './models';
4
4
  import {stop, work} from './work';
5
5
 
6
- export class Timer {
7
- declare private readonly $timer: TimerType;
8
-
9
- protected readonly state: TimerState;
10
-
11
- /**
12
- * Is the timer active?
13
- */
14
- get active(): boolean {
15
- return this.state.active;
16
- }
17
-
18
- /**
19
- * Is the timer destroyed?
20
- */
21
- get destroyed(): boolean {
22
- return this.state.destroyed;
23
- }
24
-
25
- /**
26
- * Is the timer paused?
27
- */
28
- get paused(): boolean {
29
- return this.state.paused;
30
- }
31
-
32
- /**
33
- * Get the timer's origin _(if debugging is enabled)_
34
- */
35
- get trace(): string | undefined {
36
- return (globalThis._oscarpalmer_timer_debug ?? false) ? this.state.trace : undefined;
37
- }
38
-
39
- constructor(
40
- type: TimerType,
41
- state: Pick<TimerState, 'callback' | 'trace'>,
42
- protected readonly options: TimerOptions,
43
- start: boolean,
44
- ) {
45
- Object.defineProperty(this, '$timer', {
46
- value: type,
47
- });
48
-
49
- this.state = {
50
- ...state,
51
- active: false,
52
- destroyed: false,
53
- elapsed: 0,
54
- frame: undefined,
55
- index: 0,
56
- paused: false,
57
- total: 0,
58
- };
59
-
60
- if (start) {
61
- this.start();
62
- }
63
- }
64
-
65
- /**
66
- * Continue running the timer _(if it's paused)_
67
- */
68
- continue(): Timer {
69
- return this.#work(WORK_CONTINUE);
70
- }
71
-
72
- /**
73
- * Destroy the timer
74
- */
75
- destroy(): void {
76
- this.state.destroyed = true;
77
-
78
- this.options.onAfter = noop;
79
- this.options.onError = noop;
80
- this.state.callback = noop;
81
-
82
- if (!globalThis._oscarpalmer_timer_debug) {
83
- this.state.trace = undefined;
84
- }
85
-
86
- stop(
6
+ export function createTimer(
7
+ name: TimerName,
8
+ pick: Pick<TimerState, 'callback' | 'trace'>,
9
+ options: TimerOptions,
10
+ start: boolean,
11
+ ): Timer {
12
+ function worker(type: WorkHandlerType): Timer {
13
+ return work(
14
+ type,
87
15
  {
88
- instance: this,
89
- type: this.$timer,
16
+ name,
17
+ instance: instance as Timer,
90
18
  },
91
- this.state,
92
- this.options,
19
+ state,
20
+ options,
93
21
  );
94
22
  }
95
23
 
96
- /**
97
- * Pause the timer _(if it's running)_
98
- */
99
- pause(): Timer {
100
- return this.#work(WORK_PAUSE);
24
+ const state: TimerState = {
25
+ ...pick,
26
+ active: false,
27
+ destroyed: false,
28
+ elapsed: 0,
29
+ frame: undefined,
30
+ index: 0,
31
+ paused: false,
32
+ total: 0,
33
+ };
34
+
35
+ const instance = {
36
+ continue: () => worker(WORK_CONTINUE),
37
+ destroy: () => destroyTimer(name, instance as Timer, state, options),
38
+ pause: () => worker(WORK_PAUSE),
39
+ restart: () => worker(WORK_RESTART),
40
+ start: () => worker(WORK_START),
41
+ stop: () => worker(WORK_STOP),
42
+ };
43
+
44
+ Object.defineProperties(instance, {
45
+ $timer: {
46
+ enumerable: false,
47
+ value: name,
48
+ },
49
+ active: {
50
+ enumerable: true,
51
+ get: () => state.active,
52
+ },
53
+ destroyed: {
54
+ enumerable: true,
55
+ get: () => state.destroyed,
56
+ },
57
+ paused: {
58
+ enumerable: true,
59
+ get: () => state.paused,
60
+ },
61
+ trace: {
62
+ enumerable: true,
63
+ get: () => ((globalThis._oscarpalmer_timer_debug ?? false) ? state.trace : undefined),
64
+ },
65
+ });
66
+
67
+ if (start) {
68
+ instance.start();
101
69
  }
102
70
 
103
- /**
104
- * Restart the timer _(or start it, if it's not running)_
105
- */
106
- restart(): Timer {
107
- return this.#work(WORK_RESTART);
108
- }
71
+ return Object.freeze(instance) as Timer;
72
+ }
109
73
 
110
- /**
111
- * Start the timer _(if it's not running)_
112
- */
113
- start(): Timer {
114
- return this.#work(WORK_START);
115
- }
74
+ function destroyTimer(
75
+ name: TimerName,
76
+ instance: Timer,
77
+ state: TimerState,
78
+ options: TimerOptions,
79
+ ): void {
80
+ state.destroyed = true;
116
81
 
117
- /**
118
- * Stop the timer _(if it's running)_
119
- */
120
- stop(): Timer {
121
- return this.#work(WORK_STOP);
122
- }
82
+ options.onAfter = noop;
83
+ options.onError = noop;
84
+ state.callback = noop;
123
85
 
124
- #work(type: WorkHandlerType): Timer {
125
- return work(
126
- type,
127
- {
128
- instance: this,
129
- type: this.$timer,
130
- },
131
- this.state,
132
- this.options,
133
- );
86
+ if (!globalThis._oscarpalmer_timer_debug) {
87
+ state.trace = undefined;
134
88
  }
89
+
90
+ stop({instance, name}, state, options);
135
91
  }
package/src/wait.ts CHANGED
@@ -1,16 +1,17 @@
1
1
  import {TYPE_WAIT} from './constants';
2
2
  import {getCallback, getValidNumber} from './get';
3
3
  import './global';
4
- import {TimerTrace} from './models';
5
- import {Timer} from './timer';
4
+ import {type Timer, TimerTrace} from './models';
5
+ import {createTimer} from './timer';
6
6
 
7
7
  /**
8
8
  * Create a waiting timer
9
+ *
9
10
  * @param callback Callback to run when the timer has finished
10
11
  * @param time How long to wait for _(in milliseconds; defaults to screen refresh rate)_
11
12
  */
12
13
  export function wait(callback: () => void, time?: number): Timer {
13
- return new Timer(
14
+ return createTimer(
14
15
  TYPE_WAIT,
15
16
  {
16
17
  callback: getCallback(callback),
@@ -26,5 +27,3 @@ export function wait(callback: () => void, time?: number): Timer {
26
27
  true,
27
28
  );
28
29
  }
29
-
30
- export type {Timer};
package/src/when.ts CHANGED
@@ -1,178 +1,73 @@
1
1
  import {noop} from '@oscarpalmer/atoms/function';
2
- import {MESSAGE_DESTROYED, MESSAGE_STARTED, TYPE_WHEN} from './constants';
2
+ import {MESSAGE_DESTROYED, MESSAGE_STARTED, TYPE_WHEN, WORK_CONTINUE} from './constants';
3
3
  import {getValidNumber, getValidTimeout} from './get';
4
4
  import './global';
5
- import {TimerTrace, type WhenOptions, type WhenState} from './models';
6
- import {Timer} from './timer';
7
-
8
- class When {
9
- declare private readonly $timer: string;
10
-
11
- private readonly state: WhenState = {
12
- promise: undefined as never,
13
- rejecter: undefined as never,
14
- resolver: undefined as never,
15
- started: false,
16
- timer: undefined as never,
17
- };
18
-
19
- /**
20
- * Is the timer active?
21
- */
22
- get active() {
23
- return this.state.timer?.active ?? false;
24
- }
25
-
26
- /**
27
- * Is the timer destroyed?
28
- */
29
- get destroyed() {
30
- return this.state.timer == null;
31
- }
32
-
33
- /**
34
- * Is the timer paused?
35
- */
36
- get paused() {
37
- return this.state.timer?.paused ?? false;
38
- }
5
+ import {
6
+ TimerTrace,
7
+ type When,
8
+ type WhenOptions,
9
+ type WhenState,
10
+ type WorkHandlerType,
11
+ } from './models';
12
+ import {createTimer} from './timer';
13
+
14
+ function destroyWhen(state: WhenState): void {
15
+ state.timer?.destroy();
16
+
17
+ state.promise = undefined as never;
18
+ state.resolver = noop;
19
+ state.rejecter = noop;
20
+ state.timer = undefined as never;
21
+ }
39
22
 
40
- /**
41
- * Get the timer's origin _(if debugging is enabled)_
42
- */
43
- get trace(): string | undefined {
44
- return (globalThis._oscarpalmer_timer_debug ?? false) ? this.state.timer?.trace : undefined;
23
+ function onAfter(instance: When, state: WhenState): void {
24
+ if (state.result) {
25
+ state.resolver?.();
26
+ } else {
27
+ state.rejecter?.();
45
28
  }
46
29
 
47
- constructor(condition: () => boolean, options?: Partial<WhenOptions>) {
48
- Object.defineProperty(this, '$timer', {
49
- value: TYPE_WHEN,
50
- });
51
-
52
- const {state} = this;
53
-
54
- state.promise = new Promise<void>((resolve, reject) => {
55
- state.resolver = resolve;
56
- state.rejecter = reject;
57
- });
58
-
59
- let result = false;
60
-
61
- this.state.timer = new Timer(
62
- TYPE_WHEN,
63
- {
64
- callback(): void {
65
- try {
66
- if (condition()) {
67
- result = true;
68
-
69
- state.timer.stop();
70
- }
71
- } catch {
72
- state.timer.stop();
73
- }
74
- },
75
- trace: new TimerTrace().stack,
76
- },
77
- {
78
- onAfter: () => {
79
- if (result) {
80
- state.resolver?.();
81
- } else {
82
- state.rejecter?.();
83
- }
84
-
85
- this.destroy();
86
- },
87
- onError: () => {
88
- state.rejecter?.();
89
-
90
- this.destroy();
91
- },
92
- count: getValidNumber(options?.count),
93
- interval: getValidNumber(options?.interval),
94
- timeout: getValidTimeout(options?.timeout),
95
- },
96
- false,
97
- );
98
- }
30
+ instance.destroy();
31
+ }
99
32
 
100
- /**
101
- * Continues the timer _(if it was paused)_
102
- */
103
- continue(): When {
104
- this.state.timer?.continue();
33
+ function onCallback(condition: () => boolean, state: WhenState): void {
34
+ try {
35
+ if (condition()) {
36
+ state.result = true;
105
37
 
106
- return this;
38
+ state.timer.stop();
39
+ }
40
+ } catch {
41
+ state.timer.stop();
107
42
  }
43
+ }
108
44
 
109
- /**
110
- * Destroys the timer _(and stops it,if it was running)_
111
- */
112
- destroy(): void {
113
- const {state} = this;
45
+ function onError(instance: When, state: WhenState): void {
46
+ state.rejecter?.();
114
47
 
115
- state.timer?.destroy();
48
+ instance.destroy();
49
+ }
116
50
 
117
- state.promise = undefined as never;
118
- state.resolver = noop;
119
- state.rejecter = noop;
120
- state.timer = undefined as never;
121
- }
51
+ function onWhen(type: WorkHandlerType, instance: When, state: WhenState): When {
52
+ state.timer?.[type]?.();
122
53
 
123
- /**
124
- * Pauses the timer _(if it was running)_
125
- */
126
- pause(): When {
127
- this.state.timer?.pause();
54
+ return instance;
55
+ }
128
56
 
129
- return this;
57
+ function startWhen(state: WhenState, resolve?: (() => void) | null): Promise<void> {
58
+ if (state.timer == null) {
59
+ throw new Error(MESSAGE_DESTROYED);
130
60
  }
131
61
 
132
- /**
133
- * Start the timer
134
- * @param resolve Optional resolve callback
135
- * @param reject Optional reject callback
136
- * @returns Promise that resolves when the condition is met
137
- */
138
- start(resolve?: (() => void) | null, reject?: (() => void) | null): Promise<void> {
139
- const {state} = this;
140
-
141
- if (state.timer == null) {
142
- throw new Error(MESSAGE_DESTROYED);
143
- }
144
-
145
- if (state.started) {
146
- throw new Error(MESSAGE_STARTED);
147
- }
148
-
149
- state.started = true;
150
-
151
- state.timer.start();
152
-
153
- return state.promise.then(resolve ?? noop, reject ?? noop);
62
+ if (state.started) {
63
+ throw new Error(MESSAGE_STARTED);
154
64
  }
155
65
 
156
- /**
157
- * Stops the timer _(if it was running)_
158
- */
159
- stop(): When {
160
- this.state.timer?.stop();
66
+ state.started = true;
161
67
 
162
- return this;
163
- }
68
+ state.timer.start();
164
69
 
165
- /**
166
- * Start the timer
167
- * @deprecated Use `start()` instead
168
- * @param resolve Optional resolve callback
169
- * @param reject Optional reject callback
170
- * @returns Promise that resolves when the condition is met
171
- */
172
- // oxlint-disable-next-line no-thenable: Returning a promise-like object, so it's ok ;)
173
- then(resolve?: (() => void) | null, reject?: (() => void) | null): Promise<void> {
174
- return this.start(resolve, reject);
175
- }
70
+ return state.promise.then(resolve);
176
71
  }
177
72
 
178
73
  /**
@@ -182,7 +77,66 @@ class When {
182
77
  * @returns Timer instance
183
78
  */
184
79
  export function when(condition: () => boolean, options?: Partial<WhenOptions>): When {
185
- return new When(condition, options);
186
- }
80
+ const state: WhenState = {
81
+ promise: undefined as never,
82
+ result: false,
83
+ started: false,
84
+ timer: undefined as never,
85
+ };
187
86
 
188
- export type {When};
87
+ let instance: When;
88
+
89
+ state.promise = new Promise<void>((resolve, reject) => {
90
+ state.resolver = resolve;
91
+ state.rejecter = reject;
92
+ });
93
+
94
+ state.timer = createTimer(
95
+ TYPE_WHEN,
96
+ {
97
+ callback: () => onCallback(condition, state),
98
+ trace: new TimerTrace().stack,
99
+ },
100
+ {
101
+ onAfter: () => onAfter(instance, state),
102
+ onError: () => onError(instance, state),
103
+ count: getValidNumber(options?.count),
104
+ interval: getValidNumber(options?.interval),
105
+ timeout: getValidTimeout(options?.timeout),
106
+ },
107
+ false,
108
+ );
109
+
110
+ instance = {
111
+ continue: () => onWhen(WORK_CONTINUE, instance, state),
112
+ destroy: () => destroyWhen(state),
113
+ pause: () => onWhen('pause', instance, state),
114
+ start: (resolve?: (() => void) | null) => startWhen(state, resolve),
115
+ stop: () => onWhen('stop', instance, state),
116
+ } as When;
117
+
118
+ Object.defineProperties(instance, {
119
+ $timer: {
120
+ enumerable: false,
121
+ value: TYPE_WHEN,
122
+ },
123
+ active: {
124
+ enumerable: true,
125
+ get: () => state.timer?.active ?? false,
126
+ },
127
+ destroyed: {
128
+ enumerable: true,
129
+ get: () => state.timer == null,
130
+ },
131
+ paused: {
132
+ enumerable: true,
133
+ get: () => state.timer?.paused ?? false,
134
+ },
135
+ trace: {
136
+ enumerable: true,
137
+ get: () => ((globalThis._oscarpalmer_timer_debug ?? false) ? state.timer?.trace : undefined),
138
+ },
139
+ });
140
+
141
+ return Object.freeze(instance) as When;
142
+ }