@oscarpalmer/timer 0.43.0 → 0.45.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/repeat.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import {TYPE_REPEAT} from './constants';
2
- import {getCallback, getValidNumber} from './get';
2
+ import {getCallback, getValidNumber} from './misc';
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
@@ -12,7 +12,7 @@ import {Timer} from './timer';
12
12
  * @returns Timer instance
13
13
  */
14
14
  export function repeat(callback: (index: number) => void, options?: Partial<RepeatOptions>): Timer {
15
- return new Timer(
15
+ return createTimer(
16
16
  TYPE_REPEAT,
17
17
  {
18
18
  callback: getCallback(callback),
@@ -28,5 +28,3 @@ export function repeat(callback: (index: number) => void, options?: Partial<Repe
28
28
  true,
29
29
  );
30
30
  }
31
-
32
- export type {RepeatOptions, Timer};
package/src/timer.ts CHANGED
@@ -1,135 +1,65 @@
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';
4
- import {stop, work} from './work';
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);
3
+ import type {Timer, TimerName, TimerOptions, TimerState} from './models';
4
+ import {work} from './work';
5
+
6
+ export function createTimer(
7
+ name: TimerName,
8
+ pick: Pick<TimerState, 'callback' | 'trace'>,
9
+ options: TimerOptions,
10
+ start: boolean,
11
+ ): Timer {
12
+ const state: TimerState = {
13
+ ...pick,
14
+ name,
15
+ options,
16
+ active: false,
17
+ destroyed: false,
18
+ elapsed: 0,
19
+ frame: undefined,
20
+ index: 0,
21
+ paused: false,
22
+ timer: undefined as never,
23
+ total: 0,
24
+ };
25
+
26
+ const instance = {
27
+ continue: () => work(WORK_CONTINUE, state),
28
+ destroy: noop,
29
+ pause: () => work(WORK_PAUSE, state),
30
+ restart: () => work(WORK_RESTART, state),
31
+ start: () => work(WORK_START, state),
32
+ stop: () => work(WORK_STOP, state),
33
+ };
34
+
35
+ Object.defineProperties(instance, {
36
+ $timer: {
37
+ enumerable: false,
38
+ value: name,
39
+ },
40
+ active: {
41
+ enumerable: true,
42
+ get: () => state.active,
43
+ },
44
+ destroyed: {
45
+ enumerable: true,
46
+ value: false,
47
+ },
48
+ paused: {
49
+ enumerable: true,
50
+ get: () => state.paused,
51
+ },
52
+ trace: {
53
+ enumerable: true,
54
+ get: () => ((globalThis._oscarpalmer_timer_debug ?? false) ? state.trace : undefined),
55
+ },
56
+ });
57
+
58
+ state.timer = Object.freeze(instance) as Timer;
59
+
60
+ if (start) {
61
+ state.timer.start();
70
62
  }
71
63
 
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(
87
- {
88
- instance: this,
89
- type: this.$timer,
90
- },
91
- this.state,
92
- this.options,
93
- );
94
- }
95
-
96
- /**
97
- * Pause the timer _(if it's running)_
98
- */
99
- pause(): Timer {
100
- return this.#work(WORK_PAUSE);
101
- }
102
-
103
- /**
104
- * Restart the timer _(or start it, if it's not running)_
105
- */
106
- restart(): Timer {
107
- return this.#work(WORK_RESTART);
108
- }
109
-
110
- /**
111
- * Start the timer _(if it's not running)_
112
- */
113
- start(): Timer {
114
- return this.#work(WORK_START);
115
- }
116
-
117
- /**
118
- * Stop the timer _(if it's running)_
119
- */
120
- stop(): Timer {
121
- return this.#work(WORK_STOP);
122
- }
123
-
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
- );
134
- }
64
+ return state.timer;
135
65
  }
package/src/wait.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import {TYPE_WAIT} from './constants';
2
- import {getCallback, getValidNumber} from './get';
2
+ import {getCallback, getValidNumber} from './misc';
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
@@ -11,7 +11,7 @@ import {Timer} from './timer';
11
11
  * @param time How long to wait for _(in milliseconds; defaults to screen refresh rate)_
12
12
  */
13
13
  export function wait(callback: () => void, time?: number): Timer {
14
- return new Timer(
14
+ return createTimer(
15
15
  TYPE_WAIT,
16
16
  {
17
17
  callback: getCallback(callback),
@@ -27,5 +27,3 @@ export function wait(callback: () => void, time?: number): Timer {
27
27
  true,
28
28
  );
29
29
  }
30
-
31
- export type {Timer};
package/src/when.ts CHANGED
@@ -1,166 +1,52 @@
1
1
  import {noop} from '@oscarpalmer/atoms/function';
2
- import {MESSAGE_DESTROYED, MESSAGE_STARTED, TYPE_WHEN} from './constants';
3
- import {getValidNumber, getValidTimeout} from './get';
2
+ import {MESSAGE_STARTED, TYPE_WHEN, WORK_CONTINUE, WORK_PAUSE, WORK_STOP} from './constants';
3
+ import {getValidNumber, getValidTimeout} from './misc';
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
- }
39
-
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;
45
- }
46
-
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
- );
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 onAfter(state: WhenState): void {
15
+ if (state.result) {
16
+ state.resolver?.();
17
+ } else {
18
+ state.rejecter?.();
98
19
  }
20
+ }
99
21
 
100
- /**
101
- * Continues the timer _(if it was paused)_
102
- */
103
- continue(): When {
104
- this.state.timer?.continue();
22
+ function onCallback(condition: () => boolean, state: WhenState): void {
23
+ try {
24
+ if (condition()) {
25
+ state.result = true;
105
26
 
106
- return this;
27
+ state.timer.stop();
28
+ }
29
+ } catch {
30
+ state.timer.stop();
107
31
  }
32
+ }
108
33
 
109
- /**
110
- * Destroys the timer _(and stops it,if it was running)_
111
- */
112
- destroy(): void {
113
- const {state} = this;
114
-
115
- state.timer?.destroy();
116
-
117
- state.promise = undefined as never;
118
- state.resolver = noop;
119
- state.rejecter = noop;
120
- state.timer = undefined as never;
121
- }
34
+ function onWhen(type: WorkHandlerType, instance: When, state: WhenState): When {
35
+ state.timer?.[type]?.();
122
36
 
123
- /**
124
- * Pauses the timer _(if it was running)_
125
- */
126
- pause(): When {
127
- this.state.timer?.pause();
37
+ return instance;
38
+ }
128
39
 
129
- return this;
40
+ function startWhen(state: WhenState, resolve?: (() => void) | null): Promise<void> {
41
+ if (state.started) {
42
+ throw new Error(MESSAGE_STARTED);
130
43
  }
131
44
 
132
- /**
133
- * Start the timer
134
- *
135
- * @param resolve Optional resolve callback
136
- * @returns Promise that resolves when the condition is met
137
- */
138
- start(resolve?: (() => 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;
45
+ state.started = true;
150
46
 
151
- state.timer.start();
152
-
153
- return state.promise.then(resolve);
154
- }
47
+ state.timer.start();
155
48
 
156
- /**
157
- * Stops the timer _(if it was running)_
158
- */
159
- stop(): When {
160
- this.state.timer?.stop();
161
-
162
- return this;
163
- }
49
+ return state.promise.then(resolve);
164
50
  }
165
51
 
166
52
  /**
@@ -170,7 +56,66 @@ class When {
170
56
  * @returns Timer instance
171
57
  */
172
58
  export function when(condition: () => boolean, options?: Partial<WhenOptions>): When {
173
- return new When(condition, options);
174
- }
59
+ const state: WhenState = {
60
+ promise: undefined as never,
61
+ result: false,
62
+ started: false,
63
+ timer: undefined as never,
64
+ };
175
65
 
176
- export type {When};
66
+ let instance: When;
67
+
68
+ state.promise = new Promise<void>((resolve, reject) => {
69
+ state.resolver = resolve;
70
+ state.rejecter = reject;
71
+ });
72
+
73
+ state.timer = createTimer(
74
+ TYPE_WHEN,
75
+ {
76
+ callback: () => onCallback(condition, state),
77
+ trace: new TimerTrace().stack,
78
+ },
79
+ {
80
+ onAfter: () => onAfter(state),
81
+ onError: () => state.rejecter?.(),
82
+ count: getValidNumber(options?.count),
83
+ interval: getValidNumber(options?.interval),
84
+ timeout: getValidTimeout(options?.timeout),
85
+ },
86
+ false,
87
+ );
88
+
89
+ instance = {
90
+ continue: () => onWhen(WORK_CONTINUE, instance, state),
91
+ destroy: noop,
92
+ pause: () => onWhen(WORK_PAUSE, instance, state),
93
+ start: (resolve: never) => startWhen(state, resolve),
94
+ stop: () => onWhen(WORK_STOP, instance, state),
95
+ } as When;
96
+
97
+ Object.defineProperties(instance, {
98
+ $timer: {
99
+ enumerable: false,
100
+ value: TYPE_WHEN,
101
+ },
102
+ active: {
103
+ enumerable: true,
104
+ get: () => state.timer.active,
105
+ },
106
+ destroyed: {
107
+ enumerable: true,
108
+ value: false,
109
+ },
110
+ paused: {
111
+ enumerable: true,
112
+ get: () => state.timer.paused,
113
+ },
114
+ trace: {
115
+ enumerable: true,
116
+ get: () => ((globalThis._oscarpalmer_timer_debug ?? false) ? state.timer?.trace : undefined),
117
+ },
118
+ });
119
+
120
+ return Object.freeze(instance) as When;
121
+ }
package/src/work.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import {
2
- TIMERS_ACTIVE,
3
2
  BUFFER_INTERVAL,
4
3
  TYPE_WAIT,
5
4
  WORK_CONTINUE,
@@ -8,35 +7,26 @@ import {
8
7
  WORK_START,
9
8
  WORK_STOP,
10
9
  } from './constants';
11
- import type {TimerOptions, TimerState, WorkHandlerTimer, WorkHandlerType} from './models';
12
- import type {Timer} from './timer';
13
-
14
- function finish(
15
- timer: WorkHandlerTimer,
16
- state: TimerState,
17
- options: TimerOptions,
18
- success: boolean,
19
- ): void {
20
- cancelAnimationFrame(state.frame as never);
10
+ import {updateStates} from './misc';
11
+ import type {Timer, TimerState, WorkHandlerType} from './models';
12
+
13
+ function finish(state: TimerState, success: boolean): void {
14
+ updateStates(state);
21
15
 
22
- TIMERS_ACTIVE.delete(timer.instance);
16
+ cancelAnimationFrame(state.frame as never);
23
17
 
24
18
  state.active = false;
25
19
  state.elapsed = 0;
26
20
  state.frame = undefined;
27
21
 
28
- if (timer.type === TYPE_WAIT) {
22
+ if (state.name === TYPE_WAIT) {
29
23
  state.callback();
30
24
  } else {
31
- options.onAfter?.(success);
25
+ state.options.onAfter?.(success);
32
26
  }
33
27
  }
34
28
 
35
29
  function ignore(type: WorkHandlerType, state: TimerState): boolean {
36
- if (state.destroyed) {
37
- return type !== WORK_STOP;
38
- }
39
-
40
30
  if (state.paused) {
41
31
  return type === WORK_PAUSE || type === WORK_START;
42
32
  }
@@ -44,19 +34,7 @@ function ignore(type: WorkHandlerType, state: TimerState): boolean {
44
34
  return state.active && type === WORK_START;
45
35
  }
46
36
 
47
- function pause(timer: WorkHandlerTimer, state: TimerState): Timer {
48
- cancelAnimationFrame(state.frame as never);
49
-
50
- state.frame = undefined;
51
-
52
- return timer.instance;
53
- }
54
-
55
- function run(
56
- timer: WorkHandlerTimer,
57
- state: TimerState,
58
- options: TimerOptions,
59
- ): (now: DOMHighResTimeStamp) => void {
37
+ function run(state: TimerState): (now: DOMHighResTimeStamp) => void {
60
38
  let last: DOMHighResTimeStamp | undefined;
61
39
  let start: DOMHighResTimeStamp | undefined;
62
40
 
@@ -75,16 +53,16 @@ function run(
75
53
 
76
54
  last = now;
77
55
 
78
- if (options.timeout > 0 && state.total >= options.timeout) {
79
- options.onError?.();
56
+ if (state.options.timeout > 0 && state.total >= state.options.timeout) {
57
+ state.options.onError?.();
80
58
 
81
- finish(timer, state, options, false);
59
+ finish(state, false);
82
60
 
83
61
  return;
84
62
  }
85
63
 
86
- if (options.interval === 0 || state.elapsed >= options.interval - BUFFER_INTERVAL) {
87
- if (options.count > -1) {
64
+ if (state.options.interval === 0 || state.elapsed >= state.options.interval - BUFFER_INTERVAL) {
65
+ if (state.options.count > -1) {
88
66
  (state.callback as (index: number) => void)(state.index);
89
67
  }
90
68
 
@@ -93,8 +71,11 @@ function run(
93
71
  state.elapsed = 0;
94
72
  state.index += 1;
95
73
 
96
- if (options.count === -1 || (options.count > 0 && state.index >= options.count)) {
97
- finish(timer, state, options, true);
74
+ if (
75
+ state.options.count === -1 ||
76
+ (state.options.count > 0 && state.index >= state.options.count)
77
+ ) {
78
+ finish(state, true);
98
79
 
99
80
  return;
100
81
  }
@@ -112,34 +93,29 @@ function setState(type: WorkHandlerType, state: TimerState): void {
112
93
  state.total = pausable ? state.total : 0;
113
94
  }
114
95
 
115
- export function stop(timer: WorkHandlerTimer, state: TimerState, options: TimerOptions): Timer {
116
- cancelAnimationFrame(state.frame as never);
96
+ export function stop(state: TimerState): Timer {
97
+ updateStates(state);
117
98
 
118
- TIMERS_ACTIVE.delete(timer.instance);
99
+ cancelAnimationFrame(state.frame as never);
119
100
 
120
- options.onAfter?.(false);
101
+ state.options.onAfter?.(false);
121
102
 
122
- state.active = !stop;
103
+ state.active = false;
123
104
  state.frame = undefined;
124
105
  state.paused = false;
125
106
 
126
- return timer.instance;
107
+ return state.timer;
127
108
  }
128
109
 
129
- export function work(
130
- type: WorkHandlerType,
131
- timer: WorkHandlerTimer,
132
- state: TimerState,
133
- options: TimerOptions,
134
- ): Timer {
110
+ export function work(type: WorkHandlerType, state: TimerState, hide?: boolean): Timer {
135
111
  if (ignore(type, state)) {
136
- return timer.instance;
112
+ return state.timer;
137
113
  }
138
114
 
139
115
  setState(type, state);
140
116
 
141
117
  if (type === WORK_STOP) {
142
- return stop(timer, state, options);
118
+ return stop(state);
143
119
  }
144
120
 
145
121
  if (type === WORK_PAUSE || type === WORK_RESTART) {
@@ -151,15 +127,15 @@ export function work(
151
127
  state.active = true;
152
128
  state.paused = type === WORK_PAUSE;
153
129
 
130
+ updateStates(state, state.paused ? ((hide ?? false) ? 'hidden' : undefined) : 'active');
131
+
154
132
  if (state.paused) {
155
- return pause(timer, state);
133
+ return state.timer;
156
134
  }
157
135
 
158
- TIMERS_ACTIVE.add(timer.instance);
159
-
160
- const runner = run(timer, state, options);
136
+ const runner = run(state);
161
137
 
162
138
  state.frame = requestAnimationFrame(runner);
163
139
 
164
- return timer.instance;
140
+ return state.timer;
165
141
  }
package/dist/delay.d.mts DELETED
@@ -1,3 +0,0 @@
1
- import { delay } from "@oscarpalmer/atoms/promise/delay";
2
- import { PromiseOptions } from "@oscarpalmer/atoms/promise/models";
3
- export { type PromiseOptions, delay };
package/dist/delay.mjs DELETED
@@ -1,2 +0,0 @@
1
- import { delay } from "@oscarpalmer/atoms/promise/delay";
2
- export { delay };