@oscarpalmer/atoms 0.149.0 → 0.150.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/promise.ts DELETED
@@ -1,538 +0,0 @@
1
- // #region Types
2
-
3
- import type {RequiredKeys} from './models';
4
-
5
- export class CancelablePromise<Value = void> extends Promise<Value> {
6
- #rejector!: (reason: unknown) => void;
7
-
8
- constructor(
9
- executor: (resolve: (value: Value) => void, reject: (reason: unknown) => void) => void,
10
- ) {
11
- let rejector: (reason: unknown) => void;
12
-
13
- super((resolve, reject) => {
14
- rejector = reject;
15
-
16
- executor(resolve, reject);
17
- });
18
-
19
- this.#rejector = rejector!;
20
- }
21
-
22
- /**
23
- * Cancel the promise, rejecting it with an optional reason
24
- * @param reason Optional reason for canceling the promise
25
- */
26
- cancel(reason?: unknown): void {
27
- this.#rejector(reason);
28
- }
29
- }
30
-
31
- type Data<Items extends unknown[]> = {
32
- last: number;
33
- result: Items | PromisesResult<Items>;
34
- };
35
-
36
- type FulfilledPromiseResult<Value> = {
37
- status: typeof TYPE_FULFILLED;
38
- value: Value;
39
- };
40
-
41
- type Handlers<Items extends unknown[]> = {
42
- resolve: (value: Items | PromisesResult<Items>) => void;
43
- reject: (reason: unknown) => void;
44
- };
45
-
46
- type Parameters<Items extends unknown[]> = {
47
- abort: () => void;
48
- complete: boolean;
49
- data: Data<Items>;
50
- handlers: Handlers<Items>;
51
- index: number;
52
- signal?: AbortSignal;
53
- value?: unknown;
54
- };
55
-
56
- type PromiseOptions = {
57
- /**
58
- * AbortSignal for aborting the promise; when aborted, the promise will reject with the reason of the signal
59
- */
60
- signal?: AbortSignal;
61
- /**
62
- * How long to wait for (in milliseconds; defaults to `0`)
63
- */
64
- time?: number;
65
- };
66
-
67
- /**
68
- * Promise handling strategy
69
- */
70
- export type PromiseStrategy = 'complete' | 'first';
71
-
72
- export class PromiseTimeoutError extends Error {
73
- constructor() {
74
- super(MESSAGE_TIMEOUT);
75
-
76
- this.name = ERROR_NAME;
77
- }
78
- }
79
-
80
- type Promises<Items extends unknown[]> = {
81
- [K in keyof Items]: Promise<Items[K]>;
82
- };
83
-
84
- type PromisesOptions = {
85
- signal?: AbortSignal;
86
- strategy?: PromiseStrategy;
87
- };
88
-
89
- type PromisesResult<Items extends unknown[]> = {
90
- [K in keyof Items]: Items[K] extends Promise<infer Value> ? PromisesResultItem<Value> : never;
91
- };
92
-
93
- type PromisesResultItem<Value> = FulfilledPromiseResult<Value> | RejectedPromiseResult;
94
-
95
- type RejectedPromiseResult = {
96
- status: typeof TYPE_REJECTED;
97
- reason: unknown;
98
- };
99
-
100
- // #endregion
101
-
102
- // #region Functions
103
-
104
- /**
105
- * Wrap a promise with safety handlers, with optional abort capabilities and timeout
106
- * @param promise Promise to wrap
107
- * @param options Options for the promise
108
- * @returns Wrapped promise
109
- */
110
- export async function attemptPromise<Value>(
111
- promise: Promise<Value>,
112
- options?: PromiseOptions | AbortSignal | number,
113
- ): Promise<Value>;
114
-
115
- /**
116
- * Wrap a promise-returning callback with safety handlers, with optional abort capabilities and timeout
117
- * @param callback Callback to wrap
118
- * @param options Options for the promise
119
- * @returns Promise-wrapped callback
120
- */
121
- export async function attemptPromise<Value>(
122
- callback: () => Promise<Value>,
123
- options?: PromiseOptions | AbortSignal | number,
124
- ): Promise<Value>;
125
-
126
- /**
127
- * Wrap a callback with a promise and safety handlers, with optional abort capabilities and timeout
128
- * @param callback Callback to wrap
129
- * @param options Options for the promise
130
- * @returns Promise-wrapped callback
131
- */
132
- export async function attemptPromise<Value>(
133
- callback: () => Value,
134
- options?: PromiseOptions | AbortSignal | number,
135
- ): Promise<Value>;
136
-
137
- export async function attemptPromise<Value>(
138
- value: (() => Value) | Promise<Value>,
139
- options?: PromiseOptions | AbortSignal | number,
140
- ): Promise<Value> {
141
- const isFunction = typeof value === 'function';
142
-
143
- if (!isFunction && !(value instanceof Promise)) {
144
- return Promise.reject(new TypeError(MESSAGE_EXPECTATION_ATTEMPT));
145
- }
146
-
147
- const {signal, time} = getPromiseOptions(options);
148
-
149
- if (signal?.aborted ?? false) {
150
- return Promise.reject(signal!.reason);
151
- }
152
-
153
- function abort(): void {
154
- rejector(signal!.reason);
155
- }
156
-
157
- async function handler(
158
- resolve: (value: Value) => void,
159
- reject: (reason: unknown) => void,
160
- ): Promise<void> {
161
- try {
162
- let result = isFunction ? value() : await value;
163
-
164
- if (result instanceof Promise) {
165
- result = await result;
166
- }
167
-
168
- settlePromise(abort, resolve, result, signal);
169
- } catch (error) {
170
- settlePromise(abort, reject, error, signal);
171
- }
172
- }
173
-
174
- let rejector: (reason: unknown) => void;
175
-
176
- signal?.addEventListener(EVENT_NAME, abort, ABORT_OPTIONS);
177
-
178
- const promise = new Promise<Value>((resolve, reject) => {
179
- rejector = reject;
180
-
181
- handler(resolve, reject);
182
- });
183
-
184
- return time > 0 ? getTimed(promise, time, signal) : promise;
185
- }
186
-
187
- /**
188
- * Create a cancelable promise
189
- * @param executor Executor function for the promise
190
- * @returns Cancelable promise
191
- */
192
- export function cancelable<Value>(
193
- executor: (resolve: (value: Value) => void, reject: (reason: unknown) => void) => void,
194
- ): CancelablePromise<Value> {
195
- return new CancelablePromise(executor);
196
- }
197
-
198
- /**
199
- * Create a delayed promise that resolves after a certain amount of time, or rejects if aborted
200
- * @param options Options for the delay
201
- * @returns Delayed promise
202
- */
203
- export function delay(options?: PromiseOptions): Promise<void>;
204
-
205
- /**
206
- * Create a delayed promise that resolves after a certain amount of time
207
- * @param time How long to wait for _(in milliseconds; defaults to `0`)_
208
- * @returns Delayed promise
209
- */
210
- export function delay(time?: number): Promise<void>;
211
-
212
- export function delay(options?: unknown): Promise<void> {
213
- const {signal, time} = getPromiseOptions(options);
214
-
215
- if (signal?.aborted ?? false) {
216
- return Promise.reject(signal!.reason);
217
- }
218
-
219
- function abort(): void {
220
- clearTimeout(timeout);
221
-
222
- rejector(signal!.reason);
223
- }
224
-
225
- signal?.addEventListener('abort', abort, ABORT_OPTIONS);
226
-
227
- let rejector: (reason: unknown) => void;
228
- let timeout: ReturnType<typeof setTimeout>;
229
-
230
- return new Promise((resolve, reject) => {
231
- rejector = reject;
232
-
233
- timeout = setTimeout(() => {
234
- settlePromise(abort, resolve, undefined, signal);
235
- }, time);
236
- });
237
- }
238
-
239
- function getNumberOrDefault(value: unknown): number {
240
- return typeof value === 'number' && value > 0 ? value : 0;
241
- }
242
-
243
- function getPromiseOptions(input: unknown): RequiredKeys<PromiseOptions, 'time'> {
244
- if (typeof input === 'number') {
245
- return {
246
- time: getNumberOrDefault(input),
247
- };
248
- }
249
-
250
- if (input instanceof AbortSignal) {
251
- return {signal: input, time: 0};
252
- }
253
-
254
- const options = typeof input === 'object' && input !== null ? (input as PromiseOptions) : {};
255
-
256
- return {
257
- signal: options.signal instanceof AbortSignal ? options.signal : undefined,
258
- time: getNumberOrDefault(options.time),
259
- };
260
- }
261
-
262
- function getPromisesOptions(input: unknown): RequiredKeys<PromisesOptions, 'strategy'> {
263
- if (typeof input === 'string') {
264
- return {
265
- strategy: getStrategyOrDefault(input),
266
- };
267
- }
268
-
269
- if (input instanceof AbortSignal) {
270
- return {signal: input, strategy: DEFAULT_STRATEGY};
271
- }
272
-
273
- const options = typeof input === 'object' && input !== null ? (input as PromisesOptions) : {};
274
-
275
- return {
276
- signal: options.signal instanceof AbortSignal ? options.signal : undefined,
277
- strategy: getStrategyOrDefault(options.strategy),
278
- };
279
- }
280
-
281
- function getStrategyOrDefault(value: unknown): PromiseStrategy {
282
- return strategies.has(value as PromiseStrategy) ? (value as PromiseStrategy) : DEFAULT_STRATEGY;
283
- }
284
-
285
- async function getTimed<Value>(
286
- promise: Promise<Value>,
287
- time: number,
288
- signal?: AbortSignal,
289
- ): Promise<Value> {
290
- function abort(): void {
291
- clearTimeout(timeout);
292
-
293
- rejector(signal!.reason);
294
- }
295
-
296
- signal?.addEventListener(EVENT_NAME, abort, ABORT_OPTIONS);
297
-
298
- let rejector: (reason: unknown) => void;
299
- let timeout: ReturnType<typeof setTimeout>;
300
-
301
- return Promise.race<Value>([
302
- promise,
303
- new Promise((_, reject) => {
304
- rejector = reject;
305
-
306
- timeout = setTimeout(() => {
307
- settlePromise(abort, reject, new PromiseTimeoutError(), signal);
308
- }, time);
309
- }),
310
- ]).then(value => {
311
- clearTimeout(timeout);
312
-
313
- signal?.removeEventListener(EVENT_NAME, abort);
314
-
315
- return value;
316
- });
317
- }
318
-
319
- function handleResult<Items extends unknown[]>(
320
- status: string,
321
- parameters: Parameters<Items>,
322
- ): void {
323
- const {abort, complete, data, handlers, index, signal, value} = parameters;
324
-
325
- if (signal?.aborted ?? false) {
326
- return;
327
- }
328
-
329
- if (!complete && status === TYPE_REJECTED) {
330
- settlePromise(abort, handlers.reject, value, signal);
331
-
332
- return;
333
- }
334
-
335
- (data.result as unknown[])[index] = !complete
336
- ? value
337
- : status === TYPE_FULFILLED
338
- ? {status, value}
339
- : {status, reason: value};
340
-
341
- if (index === data.last) {
342
- settlePromise(abort, handlers.resolve, data.result, signal);
343
- }
344
- }
345
-
346
- /**
347
- * Is the value a fulfilled promise result?
348
- * @param value Value to check
349
- * @returns `true` if the value is a fulfilled promise result, `false` otherwise
350
- */
351
- export function isFulfilled<Value>(value: unknown): value is FulfilledPromiseResult<Value> {
352
- return isType(value, TYPE_FULFILLED);
353
- }
354
-
355
- /**
356
- * Is the value a rejected promise result?
357
- * @param value Value to check
358
- * @returns `true` if the value is a rejected promise result, `false` otherwise
359
- */
360
- export function isRejected(value: unknown): value is RejectedPromiseResult {
361
- return isType(value, TYPE_REJECTED);
362
- }
363
-
364
- function isType(value: unknown, type: string): boolean {
365
- return (
366
- typeof value === 'object' &&
367
- value !== null &&
368
- (value as PromisesResultItem<unknown>).status === type
369
- );
370
- }
371
-
372
- /**
373
- * Handle a list of promises, returning their results in an ordered array.
374
- *
375
- * Depending on the strategy, the function will either reject on the first error encountered or return an array of rejected and resolved results
376
- * @param items List of promises
377
- * @param options Options for handling the promises
378
- * @returns List of results
379
- */
380
- export async function promises<Items extends unknown[], Options extends PromisesOptions>(
381
- items: Promises<Items>,
382
- options?: Options,
383
- ): Promise<Options['strategy'] extends 'first' ? Items : PromisesResult<Items>>;
384
-
385
- /**
386
- * Handle a list of promises, returning their results in an ordered array.
387
- *
388
- * If any promise in the list is rejected, the whole function will reject
389
- * @param items List of promises
390
- * @param strategy Strategy for handling the promises; rejects on the first error encountered
391
- * @returns List of results
392
- */
393
- export async function promises<Items extends unknown[]>(
394
- items: Promises<Items>,
395
- strategy: 'first',
396
- ): Promise<Items>;
397
-
398
- /**
399
- * Handle a list of promises, returning their results in an ordered array of rejected and resolved results
400
- * @param items List of promises
401
- * @param signal AbortSignal for aborting the operation _(when aborted, the promise will reject with the reason of the signal)_
402
- * @returns List of results
403
- */
404
- export async function promises<Items extends unknown[]>(
405
- items: Promises<Items>,
406
- signal?: AbortSignal,
407
- ): Promise<PromisesResult<Items>>;
408
-
409
- export async function promises<Items extends unknown[]>(
410
- items: Promises<Items>,
411
- options?: unknown,
412
- ): Promise<Items | PromisesResult<Items>> {
413
- const {signal, strategy} = getPromisesOptions(options);
414
-
415
- if (signal?.aborted ?? false) {
416
- return Promise.reject(signal!.reason);
417
- }
418
-
419
- if (!Array.isArray(items)) {
420
- return Promise.reject(new TypeError(MESSAGE_EXPECTATION_PROMISES));
421
- }
422
-
423
- const actual = items.filter(item => item instanceof Promise);
424
- const {length} = actual;
425
-
426
- if (length === 0) {
427
- return actual as unknown as Items | PromisesResult<Items>;
428
- }
429
-
430
- const complete = strategy === DEFAULT_STRATEGY;
431
-
432
- function abort(): void {
433
- handlers.reject(signal!.reason);
434
- }
435
-
436
- signal?.addEventListener('abort', abort, ABORT_OPTIONS);
437
-
438
- const data: Data<Items> = {
439
- last: length - 1,
440
- result: [] as unknown as Items | PromisesResult<Items>,
441
- };
442
-
443
- let handlers: Handlers<Items>;
444
-
445
- return new Promise((resolve, reject) => {
446
- handlers = {reject, resolve};
447
-
448
- for (let index = 0; index < length; index += 1) {
449
- void actual[index]
450
- .then(value =>
451
- handleResult(TYPE_FULFILLED, {abort, complete, data, handlers, index, signal, value}),
452
- )
453
- .catch(reason =>
454
- handleResult(TYPE_REJECTED, {
455
- abort,
456
- complete,
457
- data,
458
- handlers,
459
- index,
460
- signal,
461
- value: reason,
462
- }),
463
- );
464
- }
465
- });
466
- }
467
-
468
- function settlePromise(
469
- aborter: () => void,
470
- settler: (value: any) => void,
471
- value: unknown,
472
- signal?: AbortSignal,
473
- ): void {
474
- signal?.removeEventListener(EVENT_NAME, aborter);
475
-
476
- settler(value);
477
- }
478
-
479
- /**
480
- * Create a promise that should be settled within a certain amount of time
481
- * @param promise Promise to settle
482
- * @param options Timed options
483
- * @returns Timed promise
484
- */
485
- export async function timed<Value>(
486
- promise: Promise<Value>,
487
- options: RequiredKeys<PromiseOptions, 'time'>,
488
- ): Promise<Value>;
489
-
490
- /**
491
- * Create a promise that should be settled within a certain amount of time
492
- * @param promise Promise to settle
493
- * @param time How long to wait for _(in milliseconds; defaults to `0`)_
494
- * @returns Timed promise
495
- */
496
- export async function timed<Value>(promise: Promise<Value>, time: number): Promise<Value>;
497
-
498
- export async function timed<Value>(promise: Promise<Value>, options: unknown): Promise<Value> {
499
- if (!(promise instanceof Promise)) {
500
- return Promise.reject(new TypeError(MESSAGE_EXPECTATION_TIMED));
501
- }
502
-
503
- const {signal, time} = getPromiseOptions(options);
504
-
505
- if (signal?.aborted ?? false) {
506
- return Promise.reject(signal!.reason);
507
- }
508
-
509
- return time > 0 ? getTimed(promise, time, signal) : promise;
510
- }
511
-
512
- // #endregion
513
-
514
- // #region Variables
515
-
516
- const ABORT_OPTIONS = {once: true};
517
-
518
- const DEFAULT_STRATEGY: PromiseStrategy = 'complete';
519
-
520
- const ERROR_NAME = 'PromiseTimeoutError';
521
-
522
- const EVENT_NAME = 'abort';
523
-
524
- const MESSAGE_EXPECTATION_ATTEMPT = 'Attempt expected a function or a promise';
525
-
526
- const MESSAGE_EXPECTATION_PROMISES = 'Promises expected an array of promises';
527
-
528
- const MESSAGE_EXPECTATION_TIMED = 'Timed function expected a Promise';
529
-
530
- const MESSAGE_TIMEOUT = 'Promise timed out';
531
-
532
- const strategies = new Set<PromiseStrategy>(['complete', 'first']);
533
-
534
- const TYPE_FULFILLED = 'fulfilled';
535
-
536
- const TYPE_REJECTED = 'rejected';
537
-
538
- // #endregion
@@ -1,139 +0,0 @@
1
- import type { RequiredKeys } from './models';
2
- export declare class CancelablePromise<Value = void> extends Promise<Value> {
3
- #private;
4
- constructor(executor: (resolve: (value: Value) => void, reject: (reason: unknown) => void) => void);
5
- /**
6
- * Cancel the promise, rejecting it with an optional reason
7
- * @param reason Optional reason for canceling the promise
8
- */
9
- cancel(reason?: unknown): void;
10
- }
11
- type FulfilledPromiseResult<Value> = {
12
- status: typeof TYPE_FULFILLED;
13
- value: Value;
14
- };
15
- type PromiseOptions = {
16
- /**
17
- * AbortSignal for aborting the promise; when aborted, the promise will reject with the reason of the signal
18
- */
19
- signal?: AbortSignal;
20
- /**
21
- * How long to wait for (in milliseconds; defaults to `0`)
22
- */
23
- time?: number;
24
- };
25
- /**
26
- * Promise handling strategy
27
- */
28
- export type PromiseStrategy = 'complete' | 'first';
29
- export declare class PromiseTimeoutError extends Error {
30
- constructor();
31
- }
32
- type Promises<Items extends unknown[]> = {
33
- [K in keyof Items]: Promise<Items[K]>;
34
- };
35
- type PromisesOptions = {
36
- signal?: AbortSignal;
37
- strategy?: PromiseStrategy;
38
- };
39
- type PromisesResult<Items extends unknown[]> = {
40
- [K in keyof Items]: Items[K] extends Promise<infer Value> ? PromisesResultItem<Value> : never;
41
- };
42
- type PromisesResultItem<Value> = FulfilledPromiseResult<Value> | RejectedPromiseResult;
43
- type RejectedPromiseResult = {
44
- status: typeof TYPE_REJECTED;
45
- reason: unknown;
46
- };
47
- /**
48
- * Wrap a promise with safety handlers, with optional abort capabilities and timeout
49
- * @param promise Promise to wrap
50
- * @param options Options for the promise
51
- * @returns Wrapped promise
52
- */
53
- export declare function attemptPromise<Value>(promise: Promise<Value>, options?: PromiseOptions | AbortSignal | number): Promise<Value>;
54
- /**
55
- * Wrap a promise-returning callback with safety handlers, with optional abort capabilities and timeout
56
- * @param callback Callback to wrap
57
- * @param options Options for the promise
58
- * @returns Promise-wrapped callback
59
- */
60
- export declare function attemptPromise<Value>(callback: () => Promise<Value>, options?: PromiseOptions | AbortSignal | number): Promise<Value>;
61
- /**
62
- * Wrap a callback with a promise and safety handlers, with optional abort capabilities and timeout
63
- * @param callback Callback to wrap
64
- * @param options Options for the promise
65
- * @returns Promise-wrapped callback
66
- */
67
- export declare function attemptPromise<Value>(callback: () => Value, options?: PromiseOptions | AbortSignal | number): Promise<Value>;
68
- /**
69
- * Create a cancelable promise
70
- * @param executor Executor function for the promise
71
- * @returns Cancelable promise
72
- */
73
- export declare function cancelable<Value>(executor: (resolve: (value: Value) => void, reject: (reason: unknown) => void) => void): CancelablePromise<Value>;
74
- /**
75
- * Create a delayed promise that resolves after a certain amount of time, or rejects if aborted
76
- * @param options Options for the delay
77
- * @returns Delayed promise
78
- */
79
- export declare function delay(options?: PromiseOptions): Promise<void>;
80
- /**
81
- * Create a delayed promise that resolves after a certain amount of time
82
- * @param time How long to wait for _(in milliseconds; defaults to `0`)_
83
- * @returns Delayed promise
84
- */
85
- export declare function delay(time?: number): Promise<void>;
86
- /**
87
- * Is the value a fulfilled promise result?
88
- * @param value Value to check
89
- * @returns `true` if the value is a fulfilled promise result, `false` otherwise
90
- */
91
- export declare function isFulfilled<Value>(value: unknown): value is FulfilledPromiseResult<Value>;
92
- /**
93
- * Is the value a rejected promise result?
94
- * @param value Value to check
95
- * @returns `true` if the value is a rejected promise result, `false` otherwise
96
- */
97
- export declare function isRejected(value: unknown): value is RejectedPromiseResult;
98
- /**
99
- * Handle a list of promises, returning their results in an ordered array.
100
- *
101
- * Depending on the strategy, the function will either reject on the first error encountered or return an array of rejected and resolved results
102
- * @param items List of promises
103
- * @param options Options for handling the promises
104
- * @returns List of results
105
- */
106
- export declare function promises<Items extends unknown[], Options extends PromisesOptions>(items: Promises<Items>, options?: Options): Promise<Options['strategy'] extends 'first' ? Items : PromisesResult<Items>>;
107
- /**
108
- * Handle a list of promises, returning their results in an ordered array.
109
- *
110
- * If any promise in the list is rejected, the whole function will reject
111
- * @param items List of promises
112
- * @param strategy Strategy for handling the promises; rejects on the first error encountered
113
- * @returns List of results
114
- */
115
- export declare function promises<Items extends unknown[]>(items: Promises<Items>, strategy: 'first'): Promise<Items>;
116
- /**
117
- * Handle a list of promises, returning their results in an ordered array of rejected and resolved results
118
- * @param items List of promises
119
- * @param signal AbortSignal for aborting the operation _(when aborted, the promise will reject with the reason of the signal)_
120
- * @returns List of results
121
- */
122
- export declare function promises<Items extends unknown[]>(items: Promises<Items>, signal?: AbortSignal): Promise<PromisesResult<Items>>;
123
- /**
124
- * Create a promise that should be settled within a certain amount of time
125
- * @param promise Promise to settle
126
- * @param options Timed options
127
- * @returns Timed promise
128
- */
129
- export declare function timed<Value>(promise: Promise<Value>, options: RequiredKeys<PromiseOptions, 'time'>): Promise<Value>;
130
- /**
131
- * Create a promise that should be settled within a certain amount of time
132
- * @param promise Promise to settle
133
- * @param time How long to wait for _(in milliseconds; defaults to `0`)_
134
- * @returns Timed promise
135
- */
136
- export declare function timed<Value>(promise: Promise<Value>, time: number): Promise<Value>;
137
- declare const TYPE_FULFILLED = "fulfilled";
138
- declare const TYPE_REJECTED = "rejected";
139
- export {};