@the-w1zard/dx 1.0.12 → 1.0.13

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 ADDED
@@ -0,0 +1,309 @@
1
+ export class P2I<T = A> extends Promise<T> implements P2<T> {
2
+ #createdAt = Date.now();
3
+ #status: "pending" | "fulfilled" | "rejected" = "pending";
4
+ #val?: T;
5
+ #err?: A;
6
+ #settledAt?: N;
7
+ #label?: S;
8
+
9
+ constructor(
10
+ executor: (
11
+ resolve: (value: T | PromiseLike<T>) => void,
12
+ reject: (reason?: A) => void,
13
+ ) => void,
14
+ ) {
15
+ super(executor);
16
+
17
+ super.then(
18
+ (v) => { this.#status = "fulfilled"; this.#val = v; this.#settledAt = Date.now(); },
19
+ (e) => { this.#status = "rejected"; this.#err = e as A; this.#settledAt = Date.now(); },
20
+ );
21
+ }
22
+
23
+ static get [Symbol.species](): PromiseConstructor {
24
+ return P2I as unknown as PromiseConstructor;
25
+ }
26
+
27
+ override then<TResult1 = T, TResult2 = never>(
28
+ onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
29
+ onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,
30
+ ): P2I<TResult1 | TResult2> {
31
+ return super.then(onfulfilled, onrejected) as P2I<TResult1 | TResult2>;
32
+ }
33
+
34
+ override catch<TResult = never>(
35
+ onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null,
36
+ ): P2I<T | TResult> {
37
+ return super.catch(onrejected) as P2I<T | TResult>;
38
+ }
39
+
40
+ override finally(onfinally?: (() => void) | null): P2I<T> {
41
+ return super.finally(onfinally) as P2I<T>;
42
+ }
43
+
44
+ // [STATUS]
45
+ // ══════════════════════════════════════════════════════════════════════════════════════════════════════
46
+ status(): "pending" | "fulfilled" | "rejected" { return this.#status; }
47
+ isPending(): B { return this.#status === "pending"; }
48
+ isFulfilled(): B { return this.#status === "fulfilled"; }
49
+ isRejected(): B { return this.#status === "rejected"; }
50
+ value(): T | UD { return this.#status === "fulfilled" ? this.#val : undefined; }
51
+ error(): A | UD { return this.#status === "rejected" ? this.#err : undefined; }
52
+ elapsed(): N | UD { return this.#settledAt ? (this.#settledAt - this.#createdAt) as N : undefined; }
53
+
54
+ static resolve(): P2I<void>;
55
+ static resolve<T>(value: T | PromiseLike<T>): P2I<Awaited<T>>;
56
+ static resolve<T>(value?: T | PromiseLike<T>): P2I<Awaited<T> | void> {
57
+ return new P2I(resolve => resolve(value as Awaited<T>));
58
+ }
59
+
60
+ static reject<T = never>(reason?: A): P2I<T> {
61
+ return new P2I<T>((_, reject) => reject(reason));
62
+ }
63
+
64
+ static all<T extends readonly unknown[] | []>(
65
+ values: T,
66
+ ): P2I<{ -readonly [P in keyof T]: Awaited<T[P]> }> {
67
+ return new P2I((resolve, reject) => {
68
+ Promise.all(values).then(resolve, reject);
69
+ });
70
+ }
71
+
72
+ static allSettled<T extends readonly unknown[] | []>(
73
+ values: T,
74
+ ): P2I<{ -readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>> }> {
75
+ return new P2I((resolve, reject) => {
76
+ Promise.allSettled(values).then(resolve, reject);
77
+ });
78
+ }
79
+
80
+ static race<T extends readonly unknown[] | []>(
81
+ values: T,
82
+ ): P2I<Awaited<T[number]>> {
83
+ return new P2I((resolve, reject) => {
84
+ Promise.race(values).then(resolve, reject);
85
+ });
86
+ }
87
+
88
+ static any<T extends readonly unknown[] | []>(
89
+ values: T,
90
+ ): P2I<Awaited<T[number]>> {
91
+ return new P2I((resolve, reject) => {
92
+ Promise.any(values).then(resolve, reject);
93
+ });
94
+ }
95
+
96
+ static retry<T>(
97
+ fn: () => T | PromiseLike<T>,
98
+ times: N = 3 as N,
99
+ delay: N = 0 as N,
100
+ ): P2I<Awaited<T>> {
101
+ const run = (remaining: number): P2I<Awaited<T>> =>
102
+ P2I.try(fn).catch(error => {
103
+ if (remaining <= 0) throw error;
104
+
105
+ return new P2I<void>(resolve => {
106
+ if (delay > 0) setTimeout(resolve, delay);
107
+ else resolve();
108
+ }).then(() => run(remaining - 1));
109
+ }) as P2I<Awaited<T>>;
110
+
111
+ return run(times);
112
+ }
113
+
114
+ static deferred<T = A>(): {
115
+ promise: P2I<T>;
116
+ resolve: (value: T | PromiseLike<T>) => void;
117
+ reject: (reason?: A) => void;
118
+ } {
119
+ let resolve!: (value: T | PromiseLike<T>) => void;
120
+ let reject!: (reason?: A) => void;
121
+
122
+ const promise = new P2I<T>((res, rej) => {
123
+ resolve = res;
124
+ reject = rej;
125
+ });
126
+
127
+ return { promise, resolve, reject };
128
+ }
129
+
130
+ static try<T>(fn: () => T | PromiseLike<T>): P2I<Awaited<T>> {
131
+ return new P2I((resolve, reject) => {
132
+ try {
133
+ resolve(fn() as Awaited<T>);
134
+ } catch (error) {
135
+ reject(error);
136
+ }
137
+ });
138
+ }
139
+
140
+ // [CONCURRENCY]
141
+ // ══════════════════════════════════════════════════════════════════════════════════════════════════════
142
+ static sleep(ms: N): P2I<void> {
143
+ return new P2I<void>(resolve => setTimeout(resolve, ms));
144
+ }
145
+
146
+ static fromCallback<T>(fn: (cb: (err: A, value?: T) => void) => void): P2I<T> {
147
+ return new P2I<T>((resolve, reject) => {
148
+ fn((err, value) => {
149
+ if (err) reject(err);
150
+ else resolve(value as T);
151
+ });
152
+ });
153
+ }
154
+
155
+ static mapLimit<T, G>(
156
+ items: AR<T>,
157
+ limit: N,
158
+ fn: (item: T, index: N) => G | PromiseLike<G>,
159
+ ): P2I<AR<G>> {
160
+ return new P2I<AR<G>>((resolve, reject) => {
161
+ const results: AR<G> = new Array(items.length);
162
+ let next = 0;
163
+ let active = 0;
164
+ let done = 0;
165
+ let failed = false;
166
+
167
+ const run = (): void => {
168
+ if (failed) return;
169
+ if (done === items.length) { resolve(results); return; }
170
+
171
+ while (active < limit && next < items.length) {
172
+ const i = next++;
173
+ active++;
174
+
175
+ P2I.try(() => fn(items[i], i as N)).then(
176
+ (v) => { results[i] = v as G; active--; done++; run(); },
177
+ (e) => { failed = true; reject(e); },
178
+ );
179
+ }
180
+ };
181
+
182
+ run();
183
+ });
184
+ }
185
+
186
+ static series<T>(fns: AR<() => T | PromiseLike<T>>): P2I<AR<T>> {
187
+ return fns.reduce(
188
+ (acc, fn) => acc.then(results => P2I.resolve(fn()).then(v => [...results, v])),
189
+ P2I.resolve([] as AR<T>),
190
+ ) as P2I<AR<T>>;
191
+ }
192
+
193
+ tap(fn: (value: T) => unknown): P2I<T> {
194
+ return this.then(value =>
195
+ P2I.resolve(fn(value)).then(() => value),
196
+ );
197
+ }
198
+
199
+ tapError(fn: (error: A) => unknown): P2I<T> {
200
+ return this.catch(error =>
201
+ P2I.resolve(fn(error)).then(() => {
202
+ throw error;
203
+ }),
204
+ );
205
+ }
206
+
207
+ recover(fn: (error: A) => T | PromiseLike<T>): P2I<T> {
208
+ return this.catch(fn);
209
+ }
210
+
211
+ fallback(value: T | PromiseLike<T>): P2I<T> {
212
+ return this.catch(() => value);
213
+ }
214
+
215
+ timeout(ms: N, error: A = new Error("Promise timed out") as A): P2I<T> {
216
+ return new P2I<T>((resolve, reject) => {
217
+ const timer = setTimeout(() => reject(error), ms);
218
+
219
+ this.then(
220
+ value => {
221
+ clearTimeout(timer);
222
+ resolve(value);
223
+ },
224
+ reason => {
225
+ clearTimeout(timer);
226
+ reject(reason);
227
+ },
228
+ );
229
+ });
230
+ }
231
+
232
+ delay(ms: N): P2I<T> {
233
+ return this.then(
234
+ value => new P2I<T>(resolve => setTimeout(() => resolve(value), ms)),
235
+ );
236
+ }
237
+
238
+ withDeadline(
239
+ deadline: N,
240
+ error: A = new Error("Deadline exceeded") as A,
241
+ ): P2I<T> {
242
+ return this.timeout(Math.max(0, deadline - Date.now()) as N, error);
243
+ }
244
+
245
+ map<G>(fn: (value: T) => G | PromiseLike<G>): P2I<Awaited<G>> {
246
+ return this.then(fn) as P2I<Awaited<G>>;
247
+ }
248
+
249
+ flatMap<G>(fn: (value: T) => G | PromiseLike<G>): P2I<Awaited<G>> {
250
+ return this.then(fn) as P2I<Awaited<G>>;
251
+ }
252
+
253
+ or<G>(other: G | PromiseLike<G>): P2I<T | Awaited<G>> {
254
+ return this.catch(() => other) as P2I<T | Awaited<G>>;
255
+ }
256
+
257
+ and<G>(other: G | PromiseLike<G>): P2I<[T, Awaited<G>]> {
258
+ return this.then(value =>
259
+ P2I.resolve(other).then(
260
+ result => [value, result] as [T, Awaited<G>],
261
+ ),
262
+ );
263
+ }
264
+
265
+ zip<G>(other: PromiseLike<G>): P2I<[T, Awaited<G>]> {
266
+ return this.and(other);
267
+ }
268
+
269
+ unwrap(): Promise<T> {
270
+ return this;
271
+ }
272
+
273
+ toResult(): P2I<{ ok: true; value: T } | { ok: false; error: A }> {
274
+ return this.then(
275
+ value => ({ ok: true as const, value }),
276
+ error => ({ ok: false as const, error: error as A }),
277
+ );
278
+ }
279
+
280
+ onResolve(fn: (value: T) => unknown): P2I<T> {
281
+ return this.tap(fn);
282
+ }
283
+
284
+ onReject(fn: (error: A) => unknown): P2I<T> {
285
+ return this.tapError(fn);
286
+ }
287
+
288
+ finallyDo(fn: () => unknown): P2I<T> {
289
+ return this.finally(fn);
290
+ }
291
+
292
+ // [DEBUG]
293
+ // ══════════════════════════════════════════════════════════════════════════════════════════════════════
294
+ $label(name: S): this {
295
+ this.#label = name;
296
+ return this;
297
+ }
298
+
299
+ $debug(): this {
300
+ const tag = this.#label ? `:${this.#label}` : "";
301
+ super.then(
302
+ v => console.log(`[P2${tag}] ✓ resolved`, v, `(${this.elapsed()}ms)`),
303
+ e => console.log(`[P2${tag}] ✗ rejected`, e, `(${this.elapsed()}ms)`),
304
+ );
305
+ return this;
306
+ }
307
+ }
308
+
309
+ (globalThis as A).P2 = P2I as P2C;