@byloth/core 2.0.0-rc.1 → 2.0.0-rc.10

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 (43) hide show
  1. package/dist/core.js +1379 -894
  2. package/dist/core.js.map +1 -1
  3. package/dist/core.umd.cjs +3 -3
  4. package/dist/core.umd.cjs.map +1 -1
  5. package/package.json +8 -10
  6. package/src/helpers.ts +7 -0
  7. package/src/index.ts +19 -14
  8. package/src/models/aggregators/aggregated-async-iterator.ts +129 -81
  9. package/src/models/aggregators/aggregated-iterator.ts +128 -82
  10. package/src/models/aggregators/index.ts +1 -3
  11. package/src/models/aggregators/reduced-iterator.ts +118 -30
  12. package/src/models/aggregators/types.ts +15 -10
  13. package/src/models/callbacks/callable-object.ts +23 -0
  14. package/src/models/callbacks/index.ts +5 -0
  15. package/src/models/callbacks/publisher.ts +45 -0
  16. package/src/models/callbacks/switchable-callback.ts +108 -0
  17. package/src/models/callbacks/types.ts +1 -0
  18. package/src/models/exceptions/core.ts +3 -3
  19. package/src/models/exceptions/index.ts +13 -13
  20. package/src/models/game-loop.ts +9 -9
  21. package/src/models/index.ts +3 -6
  22. package/src/models/iterators/smart-async-iterator.ts +109 -23
  23. package/src/models/iterators/smart-iterator.ts +105 -12
  24. package/src/models/iterators/types.ts +17 -7
  25. package/src/models/json/json-storage.ts +2 -3
  26. package/src/models/json/types.ts +3 -1
  27. package/src/models/promises/deferred-promise.ts +1 -1
  28. package/src/models/promises/index.ts +3 -1
  29. package/src/models/promises/long-running-task.ts +294 -0
  30. package/src/models/promises/smart-promise.ts +6 -1
  31. package/src/models/promises/thenable.ts +97 -0
  32. package/src/models/promises/timed-promise.ts +1 -1
  33. package/src/models/promises/types.ts +2 -0
  34. package/src/models/timers/clock.ts +29 -7
  35. package/src/models/timers/countdown.ts +56 -20
  36. package/src/models/types.ts +12 -10
  37. package/src/utils/async.ts +9 -4
  38. package/src/utils/date.ts +3 -0
  39. package/src/utils/index.ts +1 -1
  40. package/src/utils/random.ts +4 -3
  41. package/src/models/aggregators/aggregator.ts +0 -46
  42. package/src/models/aggregators/async-aggregator.ts +0 -56
  43. package/src/models/publisher.ts +0 -39
@@ -1,6 +1,7 @@
1
+ import AggregatedIterator from "../aggregators/aggregated-iterator.js";
1
2
  import { ValueException } from "../exceptions/index.js";
2
3
 
3
- import type { GeneratorFunction, Iteratee, TypeGuardIteratee, Reducer, IterLike } from "./types.js";
4
+ import type { GeneratorFunction, Iteratee, TypeGuardIteratee, Reducer, IteratorLike } from "./types.js";
4
5
 
5
6
  export default class SmartIterator<T, R = void, N = undefined> implements Iterator<T, R, N>
6
7
  {
@@ -9,11 +10,11 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
9
10
  public return?: (value?: R) => IteratorResult<T, R>;
10
11
  public throw?: (error?: unknown) => IteratorResult<T, R>;
11
12
 
12
- public constructor(iterable: Iterable<T>);
13
+ public constructor(iterable: Iterable<T, R, N>);
13
14
  public constructor(iterator: Iterator<T, R, N>);
14
15
  public constructor(generatorFn: GeneratorFunction<T, R, N>);
15
- public constructor(argument: IterLike<T, R, N>);
16
- public constructor(argument: IterLike<T, R, N>)
16
+ public constructor(argument: IteratorLike<T, R, N> | GeneratorFunction<T, R, N>);
17
+ public constructor(argument: IteratorLike<T, R, N> | GeneratorFunction<T, R, N>)
17
18
  {
18
19
  if (argument instanceof Function)
19
20
  {
@@ -28,15 +29,14 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
28
29
  this._iterator = argument;
29
30
  }
30
31
 
31
- if (this._iterator.return) { this.return = (value?: R) => this._iterator.return!(value); }
32
- if (this._iterator.throw) { this.throw = (error?: unknown) => this._iterator.throw!(error); }
32
+ if (this._iterator.return) { this.return = (value) => this._iterator.return!(value); }
33
+ if (this._iterator.throw) { this.throw = (error) => this._iterator.throw!(error); }
33
34
  }
34
35
 
35
36
  public every(predicate: Iteratee<T, boolean>): boolean
36
37
  {
37
38
  let index = 0;
38
39
 
39
- // eslint-disable-next-line no-constant-condition
40
40
  while (true)
41
41
  {
42
42
  const result = this._iterator.next();
@@ -51,7 +51,6 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
51
51
  {
52
52
  let index = 0;
53
53
 
54
- // eslint-disable-next-line no-constant-condition
55
54
  while (true)
56
55
  {
57
56
  const result = this._iterator.next();
@@ -118,7 +117,6 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
118
117
  index += 1;
119
118
  }
120
119
 
121
- // eslint-disable-next-line no-constant-condition
122
120
  while (true)
123
121
  {
124
122
  const result = this._iterator.next();
@@ -130,6 +128,92 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
130
128
  }
131
129
  }
132
130
 
131
+ public flatMap<V>(iteratee: Iteratee<T, Iterable<V>>): SmartIterator<V, R>
132
+ {
133
+ const iterator = this._iterator;
134
+
135
+ return new SmartIterator<V, R>(function* ()
136
+ {
137
+ let index = 0;
138
+
139
+ while (true)
140
+ {
141
+ const result = iterator.next();
142
+ if (result.done) { return result.value; }
143
+
144
+ const iterable = iteratee(result.value, index);
145
+ for (const value of iterable)
146
+ {
147
+ yield value;
148
+ }
149
+
150
+ index += 1;
151
+ }
152
+ });
153
+ }
154
+
155
+ public drop(count: number): SmartIterator<T, R | undefined>
156
+ {
157
+ const iterator = this._iterator;
158
+
159
+ return new SmartIterator<T, R | undefined>(function* ()
160
+ {
161
+ let index = 0;
162
+ while (index < count)
163
+ {
164
+ const result = iterator.next();
165
+ if (result.done) { return; }
166
+
167
+ index += 1;
168
+ }
169
+
170
+ while (true)
171
+ {
172
+ const result = iterator.next();
173
+ if (result.done) { return result.value; }
174
+
175
+ yield result.value;
176
+ }
177
+ });
178
+ }
179
+ public take(limit: number): SmartIterator<T, R | undefined>
180
+ {
181
+ const iterator = this._iterator;
182
+
183
+ return new SmartIterator<T, R | undefined>(function* ()
184
+ {
185
+ let index = 0;
186
+ while (index < limit)
187
+ {
188
+ const result = iterator.next();
189
+ if (result.done) { return result.value; }
190
+
191
+ yield result.value;
192
+
193
+ index += 1;
194
+ }
195
+
196
+ return;
197
+ });
198
+ }
199
+
200
+ public find(predicate: Iteratee<T, boolean>): T | undefined;
201
+ public find<S extends T>(predicate: TypeGuardIteratee<T, S>): S | undefined;
202
+ public find(predicate: Iteratee<T, boolean>): T | undefined
203
+ {
204
+ let index = 0;
205
+
206
+ while (true)
207
+ {
208
+ const result = this._iterator.next();
209
+
210
+ if (result.done) { return; }
211
+ if (predicate(result.value, index)) { return result.value; }
212
+
213
+ index += 1;
214
+ }
215
+ }
216
+
133
217
  public enumerate(): SmartIterator<[number, T], R>
134
218
  {
135
219
  return this.map((value, index) => [index, value]);
@@ -160,7 +244,6 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
160
244
  {
161
245
  let index = 0;
162
246
 
163
- // eslint-disable-next-line no-constant-condition
164
247
  while (true)
165
248
  {
166
249
  const result = this._iterator.next();
@@ -169,11 +252,11 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
169
252
  index += 1;
170
253
  }
171
254
  }
255
+
172
256
  public forEach(iteratee: Iteratee<T>): void
173
257
  {
174
258
  let index = 0;
175
259
 
176
- // eslint-disable-next-line no-constant-condition
177
260
  while (true)
178
261
  {
179
262
  const result = this._iterator.next();
@@ -190,12 +273,22 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
190
273
  return this._iterator.next(...values);
191
274
  }
192
275
 
276
+ public groupBy<K extends PropertyKey>(iteratee: Iteratee<T, K>): AggregatedIterator<K, T>
277
+ {
278
+ return new AggregatedIterator(this.map((element, index) =>
279
+ {
280
+ const key = iteratee(element, index);
281
+
282
+ return [key, element] as [K, T];
283
+ }));
284
+ }
285
+
193
286
  public toArray(): T[]
194
287
  {
195
288
  return Array.from(this as Iterable<T>);
196
289
  }
197
290
 
198
- public get [Symbol.toStringTag]() { return "SmartIterator"; }
291
+ public readonly [Symbol.toStringTag]: string = "SmartIterator";
199
292
 
200
293
  public [Symbol.iterator](): SmartIterator<T, R, N> { return this; }
201
294
  }
@@ -1,20 +1,30 @@
1
+
1
2
  import type { MaybePromise } from "../promises/types.js";
2
3
 
4
+ export type MaybeAsyncIterable<T, R = void, N = undefined> = Iterable<T, R, N> | AsyncIterable<T, R, N>;
5
+ export type MaybeAsyncIterator<T, R = void, N = undefined> = Iterator<T, R, N> | AsyncIterator<T, R, N>;
6
+ export type MaybeAsyncGenerator<T, R = void, N = undefined> = Generator<T, R, N> | AsyncGenerator<T, R, N>;
7
+
3
8
  export type GeneratorFunction<T, R = void, N = undefined> = () => Generator<T, R, N>;
4
9
  export type AsyncGeneratorFunction<T, R = void, N = undefined> = () => AsyncGenerator<T, R, N>;
10
+ export type MaybeAsyncGeneratorFunction<T, R = void, N = undefined> = () => MaybeAsyncGenerator<T, R, N>;
5
11
 
6
12
  export type Iteratee<T, R = void> = (value: T, index: number) => R;
13
+ export type AsyncIteratee<T, R = void> = (value: T, index: number) => Promise<R>;
7
14
  export type MaybeAsyncIteratee<T, R = void> = (value: T, index: number) => MaybePromise<R>;
8
15
 
9
16
  export type TypeGuardIteratee<T, R extends T> = (value: T, index: number) => value is R;
10
- export type MaybeAsyncTypeGuardIteratee<T, R extends T> = (value: MaybePromise<T>, index: number) =>
11
- value is MaybePromise<R>;
17
+
18
+ // @ts-expect-error - This is an asyncronous type guard iteratee that guarantees the return value is a promise.
19
+ export type AsyncTypeGuardIteratee<T, R extends T> = (value: T, index: number) => value is Promise<R>;
20
+
21
+ // @ts-expect-error - This may be an asyncronous type guard iteratee that guarantees the return value may be a promise.
22
+ export type MaybeAsyncTypeGuardIteratee<T, R extends T> = (value: T, index: number) => value is MaybePromise<R>;
12
23
 
13
24
  export type Reducer<T, A> = (accumulator: A, value: T, index: number) => A;
25
+ export type AsyncReducer<T, A> = (accumulator: A, value: T, index: number) => Promise<A>;
14
26
  export type MaybeAsyncReducer<T, A> = (accumulator: A, value: T, index: number) => MaybePromise<A>;
15
27
 
16
- export type IterLike<T, R = void, N = undefined> = Iterable<T> | Iterator<T, R, N> | GeneratorFunction<T, R, N>;
17
- export type AsyncIterLike<T, R = void, N = undefined> =
18
- AsyncIterable<T> | AsyncIterator<T, R, N> | AsyncGeneratorFunction<T, R, N>;
19
-
20
- export type MaybeAsyncIterLike<T, R = void, N = undefined> = IterLike<T, R, N> | AsyncIterLike<T, R, N>;
28
+ export type IteratorLike<T, R = void, N = undefined> = Iterable<T> | Iterator<T, R, N>;
29
+ export type AsyncIteratorLike<T, R = void, N = undefined> = AsyncIterable<T> | AsyncIterator<T, R, N>;
30
+ export type MaybeAsyncIteratorLike<T, R = void, N = undefined> = IteratorLike<T, R, N> | AsyncIteratorLike<T, R, N>;
@@ -1,4 +1,3 @@
1
- /* eslint-disable no-trailing-spaces */
2
1
 
3
2
  import { isBrowser } from "../../helpers.js";
4
3
  import { EnvironmentException } from "../exceptions/index.js";
@@ -45,7 +44,7 @@ export default class JSONStorage
45
44
  {
46
45
  return JSON.parse(propertyValue);
47
46
  }
48
- catch (error)
47
+ catch
49
48
  {
50
49
  // eslint-disable-next-line no-console
51
50
  console.warn(
@@ -252,5 +251,5 @@ export default class JSONStorage
252
251
  this._persistent.removeItem(propertyName);
253
252
  }
254
253
 
255
- public get [Symbol.toStringTag]() { return "JSONStorage"; }
254
+ public readonly [Symbol.toStringTag]: string = "JSONStorage";
256
255
  }
@@ -1,3 +1,5 @@
1
1
  export type JSONArray = JSONValue[];
2
- export type JSONObject = { [key: string]: JSONValue };
2
+
3
+ // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
4
+ export interface JSONObject { [key: string]: JSONValue }
3
5
  export type JSONValue = boolean | number | string | null | JSONObject | JSONArray;
@@ -37,5 +37,5 @@ export default class DeferredPromise<T = void, F = T, R = never> extends SmartPr
37
37
  return this;
38
38
  }
39
39
 
40
- public get [Symbol.toStringTag]() { return "DeferredPromise"; }
40
+ public readonly [Symbol.toStringTag]: string = "DeferredPromise";
41
41
  }
@@ -1,5 +1,7 @@
1
1
  import DeferredPromise from "./deferred-promise.js";
2
+ import LongRunningTask from "./long-running-task.js";
2
3
  import SmartPromise from "./smart-promise.js";
4
+ import Thenable from "./thenable.js";
3
5
  import TimedPromise from "./timed-promise.js";
4
6
 
5
- export { DeferredPromise, SmartPromise, TimedPromise };
7
+ export { DeferredPromise, LongRunningTask, SmartPromise, Thenable, TimedPromise };
@@ -0,0 +1,294 @@
1
+ import { yieldToEventLoop } from "../../utils/async.js";
2
+
3
+ import Publisher from "../callbacks/publisher.js";
4
+ import { RuntimeException } from "../exceptions/index.js";
5
+
6
+ import type { MaybeAsyncGeneratorFunction } from "../iterators/types.js";
7
+ import type { FulfilledHandler, PromiseExecutor, RejectedHandler } from "./types.js";
8
+
9
+ export interface LongRunningTaskOptions
10
+ {
11
+ ignoreErrors?: boolean;
12
+ stepIncrement?: number;
13
+ totalSteps?: number | null;
14
+ trackProgress?: boolean;
15
+ }
16
+
17
+ export default class LongRunningTask<T = void> implements Promise<T>
18
+ {
19
+ private static get _DefaultOptions(): Required<LongRunningTaskOptions>
20
+ {
21
+ return {
22
+ ignoreErrors: false,
23
+ stepIncrement: 1,
24
+ totalSteps: null,
25
+ trackProgress: false
26
+ };
27
+ }
28
+
29
+ protected _startTime: number;
30
+ protected _estimatedTime: number;
31
+ protected _endTime?: number;
32
+
33
+ protected _currentStep: number;
34
+ protected _percentage: number;
35
+
36
+ protected _isRunning: boolean;
37
+ protected _hasCompleted: boolean;
38
+ protected _hasFailed: boolean;
39
+
40
+ protected _promise: Promise<T>;
41
+ protected _publisher?: Publisher;
42
+
43
+ public constructor(executor: MaybeAsyncGeneratorFunction<undefined, T>, options?: LongRunningTaskOptions);
44
+ public constructor(executor: MaybeAsyncGeneratorFunction<number, T>, options?: LongRunningTaskOptions);
45
+ public constructor(executor: MaybeAsyncGeneratorFunction<number | undefined, T>, options?: LongRunningTaskOptions)
46
+ {
47
+ this._startTime = 0;
48
+ this._estimatedTime = 0;
49
+
50
+ this._currentStep = 0;
51
+ this._percentage = 0;
52
+
53
+ this._isRunning = true;
54
+ this._hasCompleted = false;
55
+ this._hasFailed = false;
56
+
57
+ const _onFulfilled = (result: T): T =>
58
+ {
59
+ this._estimatedTime = 0;
60
+ this._endTime = Date.now();
61
+
62
+ this._percentage = 100;
63
+
64
+ this._isRunning = false;
65
+ this._hasCompleted = true;
66
+
67
+ return result;
68
+ };
69
+ const _onRejected = (reason: unknown): never =>
70
+ {
71
+ this._endTime = Date.now();
72
+
73
+ this._isRunning = false;
74
+ this._hasFailed = true;
75
+
76
+ throw reason;
77
+ };
78
+
79
+ let _executor: PromiseExecutor<T>;
80
+
81
+ options = { ...LongRunningTask._DefaultOptions, ...options };
82
+ if ((options.trackProgress))
83
+ {
84
+ let _trackProgress: (steps: number | undefined) => void;
85
+
86
+ this._publisher = new Publisher();
87
+
88
+ if (options.totalSteps)
89
+ {
90
+ if (options.stepIncrement)
91
+ {
92
+ _trackProgress = (steps: number | undefined) =>
93
+ {
94
+ if (steps) { this._currentStep += steps; }
95
+ else { this._currentStep += options.stepIncrement!; }
96
+
97
+ this._percentage = (this._currentStep / options.totalSteps!) * 100;
98
+
99
+ const elapsedTime = Date.now() - this._startTime;
100
+ const remainingSteps = options.totalSteps! - this._currentStep;
101
+ this._estimatedTime = (elapsedTime / this._currentStep) * remainingSteps;
102
+ };
103
+ }
104
+ else
105
+ {
106
+ _trackProgress = (steps: number | undefined) =>
107
+ {
108
+ if (steps)
109
+ {
110
+ this._currentStep += steps;
111
+ this._percentage = (this._currentStep / options.totalSteps!) * 100;
112
+
113
+ const elapsedTime = Date.now() - this._startTime;
114
+ const remainingSteps = options.totalSteps! - this._currentStep;
115
+ this._estimatedTime = (elapsedTime / this._currentStep) * remainingSteps;
116
+ }
117
+ };
118
+ }
119
+ }
120
+ else if (options.stepIncrement)
121
+ {
122
+ _trackProgress = (steps: number | undefined) =>
123
+ {
124
+ if (steps) { this._currentStep += steps; }
125
+ else { this._currentStep += options.stepIncrement!; }
126
+ };
127
+ }
128
+ else
129
+ {
130
+ _trackProgress = (steps: number | undefined) =>
131
+ {
132
+ if (steps) { this._currentStep += steps; }
133
+ };
134
+ }
135
+
136
+ if (options.ignoreErrors)
137
+ {
138
+ _executor = async (resolve) =>
139
+ {
140
+ const generator = executor();
141
+ this._startTime = Date.now();
142
+
143
+ while (true)
144
+ {
145
+ try
146
+ {
147
+ const { done, value } = await generator.next();
148
+
149
+ if (done) { return resolve(value); }
150
+ else { _trackProgress(value); }
151
+ }
152
+
153
+ // eslint-disable-next-line no-console
154
+ catch (error) { console.error(error); }
155
+
156
+ this._publisher!.publish("progress");
157
+
158
+ await yieldToEventLoop();
159
+ }
160
+ };
161
+ }
162
+ else
163
+ {
164
+ _executor = async (resolve, reject) =>
165
+ {
166
+ try
167
+ {
168
+ const generator = executor();
169
+ this._startTime = Date.now();
170
+
171
+ while (true)
172
+ {
173
+ const { done, value } = await generator.next();
174
+ if (done) { return resolve(value); }
175
+ else { _trackProgress(value); }
176
+
177
+ this._publisher!.publish("progress");
178
+
179
+ await yieldToEventLoop();
180
+ }
181
+ }
182
+ catch (error) { reject(error); }
183
+ };
184
+ }
185
+ }
186
+ else if (options.ignoreErrors)
187
+ {
188
+ _executor = async (resolve) =>
189
+ {
190
+ const generator = executor();
191
+ this._startTime = Date.now();
192
+
193
+ while (true)
194
+ {
195
+ try
196
+ {
197
+ const { done, value } = await generator.next();
198
+ if (done) { return resolve(value); }
199
+ }
200
+
201
+ // eslint-disable-next-line no-console
202
+ catch (error) { console.error(error); }
203
+
204
+ await yieldToEventLoop();
205
+ }
206
+ };
207
+ }
208
+ else
209
+ {
210
+ _executor = async (resolve, reject) =>
211
+ {
212
+ try
213
+ {
214
+ const generator = executor();
215
+ this._startTime = Date.now();
216
+
217
+ while (true)
218
+ {
219
+ const { done, value } = await generator.next();
220
+ if (done) { return resolve(value); }
221
+
222
+ await yieldToEventLoop();
223
+ }
224
+ }
225
+ catch (error) { reject(error); }
226
+ };
227
+ }
228
+
229
+ this._promise = new Promise(_executor)
230
+ .then(_onFulfilled, _onRejected);
231
+ }
232
+
233
+ public get startTime(): number { return this._startTime; }
234
+ public get elapsedTime(): number
235
+ {
236
+ if (this._isRunning) { return Date.now() - this._startTime; }
237
+
238
+ return this._endTime! - this._startTime;
239
+ }
240
+ public get estimatedTime(): number { return this._estimatedTime; }
241
+ public get endTime(): number
242
+ {
243
+ if (this._isRunning)
244
+ {
245
+ throw new RuntimeException("The task is still running and has no end time yet.");
246
+ }
247
+
248
+ return this._endTime!;
249
+ }
250
+
251
+ public get currentStep(): number { return this._currentStep; }
252
+ public get percentage(): number { return this._percentage; }
253
+
254
+ public get isRunning(): boolean { return this._isRunning; }
255
+ public get hasCompleted(): boolean { return this._hasCompleted; }
256
+ public get hasFailed(): boolean { return this._hasFailed; }
257
+
258
+ public then(onFulfilled?: null): Promise<T>;
259
+ public then<F = T>(onFulfilled: FulfilledHandler<T, F>, onRejected?: null): Promise<F>;
260
+ public then<F = T, R = never>(onFulfilled: FulfilledHandler<T, F>, onRejected: RejectedHandler<unknown, R>)
261
+ : Promise<F | R>;
262
+ public then<F = T, R = never>(
263
+ onFulfilled?: FulfilledHandler<T, F> | null,
264
+ onRejected?: RejectedHandler<unknown, R> | null): Promise<F | R>
265
+ {
266
+ return this._promise.then(onFulfilled, onRejected);
267
+ }
268
+
269
+ public catch(onRejected?: null): Promise<T>;
270
+ public catch<R = never>(onRejected: RejectedHandler<unknown, R>): Promise<T | R>;
271
+ public catch<R = never>(onRejected?: RejectedHandler<unknown, R> | null): Promise<T | R>
272
+ {
273
+ return this._promise.catch(onRejected);
274
+ }
275
+ public finally(onFinally?: (() => void) | null): Promise<T>
276
+ {
277
+ return this._promise.finally(onFinally);
278
+ }
279
+
280
+ public onProgress(callback: () => void): () => void
281
+ {
282
+ if (!(this._publisher))
283
+ {
284
+ throw new RuntimeException(
285
+ "You cannot subscribe to progress events without enabling progress tracking. " +
286
+ "Did you forget to set the `trackProgress` option to `true` when creating the task?"
287
+ );
288
+ }
289
+
290
+ return this._publisher.subscribe("progress", callback);
291
+ }
292
+
293
+ public readonly [Symbol.toStringTag]: string = "LongRunningTask";
294
+ }
@@ -2,6 +2,11 @@ import type { FulfilledHandler, PromiseExecutor, RejectedHandler } from "./types
2
2
 
3
3
  export default class SmartPromise<T = void> implements Promise<T>
4
4
  {
5
+ public static FromPromise<T>(promise: Promise<T>): SmartPromise<T>
6
+ {
7
+ return new SmartPromise((resolve, reject) => promise.then(resolve, reject));
8
+ }
9
+
5
10
  protected _isPending: boolean;
6
11
  protected _isFulfilled: boolean;
7
12
  protected _isRejected: boolean;
@@ -59,5 +64,5 @@ export default class SmartPromise<T = void> implements Promise<T>
59
64
  return this._promise.finally(onFinally);
60
65
  }
61
66
 
62
- public get [Symbol.toStringTag]() { return "SmartPromise"; }
67
+ public readonly [Symbol.toStringTag]: string = "SmartPromise";
63
68
  }
@@ -0,0 +1,97 @@
1
+ export default class Thenable<T> implements Promise<T>
2
+ {
3
+ protected _onFulfilled: (result: T) => T;
4
+ protected _resolve(result: T): T
5
+ {
6
+ return this._onFulfilled(result);
7
+ }
8
+
9
+ public constructor()
10
+ {
11
+ this._onFulfilled = (result: T) => result;
12
+ }
13
+
14
+ public then(onFulfilled?: null): Thenable<T>;
15
+ public then<F = T>(onFulfilled: (result: T) => F, onRejected?: null): Thenable<F>;
16
+ public then<F = T, R = never>(onFulfilled: (result: T) => F, onRejected: (reason: unknown) => R)
17
+ : Thenable<F | R>;
18
+ public then<F = T, R = never>(onFulfilled?: ((result: T) => F) | null, onRejected?: ((reason: unknown) => R) | null)
19
+ : Thenable<F | R>
20
+ {
21
+ if (onRejected)
22
+ {
23
+ const _previousOnFulfilled = this._onFulfilled;
24
+ this._onFulfilled = (result: T) =>
25
+ {
26
+ try
27
+ {
28
+ result = _previousOnFulfilled(result);
29
+
30
+ return (onFulfilled!(result) as unknown) as T;
31
+ }
32
+ catch (error)
33
+ {
34
+ return (onRejected(error) as unknown) as T;
35
+ }
36
+ };
37
+ }
38
+ else if (onFulfilled)
39
+ {
40
+ const _previousOnFulfilled = this._onFulfilled;
41
+ this._onFulfilled = (result: T) =>
42
+ {
43
+ result = _previousOnFulfilled(result);
44
+
45
+ return (onFulfilled(result) as unknown) as T;
46
+ };
47
+ }
48
+
49
+ return (this as unknown) as Thenable<F | R>;
50
+ }
51
+
52
+ public catch(onRejected?: null): Thenable<T>;
53
+ public catch<R = never>(onRejected: (reason: unknown) => R): Thenable<T | R>;
54
+ public catch<R = never>(onRejected?: ((reason: unknown) => R) | null): Thenable<T | R>
55
+ {
56
+ if (onRejected)
57
+ {
58
+ const _previousOnFulfilled = this._onFulfilled;
59
+ this._onFulfilled = (result) =>
60
+ {
61
+ try
62
+ {
63
+ return _previousOnFulfilled(result);
64
+ }
65
+ catch (error)
66
+ {
67
+ return (onRejected(error) as unknown) as T;
68
+ }
69
+ };
70
+ }
71
+
72
+ return this as Thenable<T | R>;
73
+ }
74
+
75
+ public finally(onFinally?: (() => void) | null): Thenable<T>
76
+ {
77
+ if (onFinally)
78
+ {
79
+ const _previousOnFulfilled = this._onFulfilled;
80
+ this._onFulfilled = (result) =>
81
+ {
82
+ try
83
+ {
84
+ return _previousOnFulfilled(result);
85
+ }
86
+ finally
87
+ {
88
+ onFinally();
89
+ }
90
+ };
91
+ }
92
+
93
+ return this;
94
+ }
95
+
96
+ public readonly [Symbol.toStringTag]: string = "Thenable";
97
+ }