@naturalcycles/js-lib 14.72.0 → 14.75.1
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/dist/math/math.util.d.ts +4 -0
- package/dist/math/math.util.js +18 -1
- package/dist/math/stack.util.d.ts +32 -3
- package/dist/math/stack.util.js +55 -4
- package/dist/number/number.util.d.ts +1 -1
- package/dist/number/number.util.js +1 -1
- package/dist/seq/seq.d.ts +29 -7
- package/dist/seq/seq.js +116 -9
- package/dist/string/stringifyAny.js +12 -10
- package/dist-esm/math/math.util.js +16 -0
- package/dist-esm/math/stack.util.js +52 -2
- package/dist-esm/number/number.util.js +1 -1
- package/dist-esm/seq/seq.js +114 -8
- package/dist-esm/string/stringifyAny.js +12 -10
- package/package.json +1 -1
- package/src/math/math.util.ts +21 -0
- package/src/math/stack.util.ts +62 -3
- package/src/number/number.util.ts +1 -1
- package/src/seq/seq.ts +134 -13
- package/src/string/stringifyAny.ts +13 -9
package/dist/math/math.util.d.ts
CHANGED
|
@@ -24,6 +24,10 @@ export declare function _averageWeighted(values: number[], weights: number[]): n
|
|
|
24
24
|
* // 3
|
|
25
25
|
*/
|
|
26
26
|
export declare function _percentile(values: number[], pc: number): number;
|
|
27
|
+
/**
|
|
28
|
+
* A tiny bit more efficient function than calling _percentile individually.
|
|
29
|
+
*/
|
|
30
|
+
export declare function _percentiles(values: number[], pcs: number[]): Record<number, number>;
|
|
27
31
|
/**
|
|
28
32
|
* @example
|
|
29
33
|
*
|
package/dist/math/math.util.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports._median = exports._percentile = exports._averageWeighted = exports._average = void 0;
|
|
3
|
+
exports._median = exports._percentiles = exports._percentile = exports._averageWeighted = exports._average = void 0;
|
|
4
4
|
const number_util_1 = require("../number/number.util");
|
|
5
5
|
/**
|
|
6
6
|
* @returns Average of the array of numbers
|
|
@@ -45,6 +45,23 @@ function _percentile(values, pc) {
|
|
|
45
45
|
return _averageWeighted([sorted[floorPos], sorted[ceilPos]], [1 - dec, dec]);
|
|
46
46
|
}
|
|
47
47
|
exports._percentile = _percentile;
|
|
48
|
+
/**
|
|
49
|
+
* A tiny bit more efficient function than calling _percentile individually.
|
|
50
|
+
*/
|
|
51
|
+
function _percentiles(values, pcs) {
|
|
52
|
+
const r = {};
|
|
53
|
+
const sorted = (0, number_util_1._sortNumbers)(values);
|
|
54
|
+
pcs.forEach(pc => {
|
|
55
|
+
// Floating pos in the range of [0; length - 1]
|
|
56
|
+
const pos = ((values.length - 1) * pc) / 100;
|
|
57
|
+
const dec = pos % 1;
|
|
58
|
+
const floorPos = Math.floor(pos);
|
|
59
|
+
const ceilPos = Math.ceil(pos);
|
|
60
|
+
r[pc] = _averageWeighted([sorted[floorPos], sorted[ceilPos]], [1 - dec, dec]);
|
|
61
|
+
});
|
|
62
|
+
return r;
|
|
63
|
+
}
|
|
64
|
+
exports._percentiles = _percentiles;
|
|
48
65
|
/**
|
|
49
66
|
* @example
|
|
50
67
|
*
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Implements a "round-robin" Stack with a limited size.
|
|
2
|
+
* Implements a "round-robin" Stack ("first-in last-out" aka FILO) with a limited size.
|
|
3
3
|
* Like an array of a fixed size. When it runs out of space - it starts writing on top of itself
|
|
4
4
|
* from index 0.
|
|
5
|
+
*
|
|
6
|
+
*
|
|
5
7
|
*/
|
|
6
|
-
export declare class
|
|
8
|
+
export declare class Stack<T> {
|
|
7
9
|
readonly size: number;
|
|
8
10
|
constructor(size: number);
|
|
9
11
|
/**
|
|
@@ -12,10 +14,37 @@ export declare class SizeLimitedStack<T> {
|
|
|
12
14
|
*/
|
|
13
15
|
private nextIndex;
|
|
14
16
|
readonly items: T[];
|
|
15
|
-
push(item: T):
|
|
17
|
+
push(item: T): this;
|
|
18
|
+
/**
|
|
19
|
+
* Fill (overwrite) the whole Stack (all its items) with the passed `item`.
|
|
20
|
+
*/
|
|
21
|
+
fill(item: T): this;
|
|
16
22
|
/**
|
|
17
23
|
* Returns last items in the right order.
|
|
18
24
|
* Unlike raw `items` property that returns "items buffer" as-is (not ordered properly).
|
|
19
25
|
*/
|
|
20
26
|
get itemsOrdered(): T[];
|
|
21
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Fixed-size FILO stack of Numbers.
|
|
30
|
+
* Has convenience stat methods, e.g percentile, avg, etc.
|
|
31
|
+
*/
|
|
32
|
+
export declare class NumberStack extends Stack<number> {
|
|
33
|
+
avg(): number;
|
|
34
|
+
/**
|
|
35
|
+
* Returns null if Stack is empty.
|
|
36
|
+
*/
|
|
37
|
+
avgOrNull(): number | null;
|
|
38
|
+
median(): number;
|
|
39
|
+
medianOrNull(): number | null;
|
|
40
|
+
/**
|
|
41
|
+
* `pc` is a number from 0 to 100 inclusive.
|
|
42
|
+
*/
|
|
43
|
+
percentile(pc: number): number;
|
|
44
|
+
/**
|
|
45
|
+
* `pc` is a number from 0 to 100 inclusive.
|
|
46
|
+
* Returns null if Stack is empty.
|
|
47
|
+
*/
|
|
48
|
+
percentileOrNull(pc: number): number | null;
|
|
49
|
+
percentiles(pcs: number[]): Record<number, number>;
|
|
50
|
+
}
|
package/dist/math/stack.util.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.NumberStack = exports.Stack = void 0;
|
|
4
|
+
const index_1 = require("../index");
|
|
4
5
|
/**
|
|
5
|
-
* Implements a "round-robin" Stack with a limited size.
|
|
6
|
+
* Implements a "round-robin" Stack ("first-in last-out" aka FILO) with a limited size.
|
|
6
7
|
* Like an array of a fixed size. When it runs out of space - it starts writing on top of itself
|
|
7
8
|
* from index 0.
|
|
9
|
+
*
|
|
10
|
+
*
|
|
8
11
|
*/
|
|
9
|
-
class
|
|
12
|
+
class Stack {
|
|
10
13
|
constructor(size) {
|
|
11
14
|
this.size = size;
|
|
12
15
|
/**
|
|
@@ -19,6 +22,14 @@ class SizeLimitedStack {
|
|
|
19
22
|
push(item) {
|
|
20
23
|
this.items[this.nextIndex] = item;
|
|
21
24
|
this.nextIndex = this.nextIndex === this.size - 1 ? 0 : this.nextIndex + 1;
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Fill (overwrite) the whole Stack (all its items) with the passed `item`.
|
|
29
|
+
*/
|
|
30
|
+
fill(item) {
|
|
31
|
+
(0, index_1._range)(this.size).forEach(i => (this.items[i] = item));
|
|
32
|
+
return this;
|
|
22
33
|
}
|
|
23
34
|
/**
|
|
24
35
|
* Returns last items in the right order.
|
|
@@ -33,4 +44,44 @@ class SizeLimitedStack {
|
|
|
33
44
|
return [...this.items.slice(this.nextIndex), ...this.items.slice(0, this.nextIndex)];
|
|
34
45
|
}
|
|
35
46
|
}
|
|
36
|
-
exports.
|
|
47
|
+
exports.Stack = Stack;
|
|
48
|
+
/**
|
|
49
|
+
* Fixed-size FILO stack of Numbers.
|
|
50
|
+
* Has convenience stat methods, e.g percentile, avg, etc.
|
|
51
|
+
*/
|
|
52
|
+
class NumberStack extends Stack {
|
|
53
|
+
avg() {
|
|
54
|
+
// _assert(this.items.length, 'NumberStack.avg cannot be called on empty stack')
|
|
55
|
+
return (0, index_1._average)(this.items);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Returns null if Stack is empty.
|
|
59
|
+
*/
|
|
60
|
+
avgOrNull() {
|
|
61
|
+
return this.items.length === 0 ? null : (0, index_1._average)(this.items);
|
|
62
|
+
}
|
|
63
|
+
median() {
|
|
64
|
+
return (0, index_1._percentile)(this.items, 50);
|
|
65
|
+
}
|
|
66
|
+
medianOrNull() {
|
|
67
|
+
return this.items.length === 0 ? null : (0, index_1._percentile)(this.items, 50);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* `pc` is a number from 0 to 100 inclusive.
|
|
71
|
+
*/
|
|
72
|
+
percentile(pc) {
|
|
73
|
+
// _assert(this.items.length, 'NumberStack.percentile cannot be called on empty stack')
|
|
74
|
+
return (0, index_1._percentile)(this.items, pc);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* `pc` is a number from 0 to 100 inclusive.
|
|
78
|
+
* Returns null if Stack is empty.
|
|
79
|
+
*/
|
|
80
|
+
percentileOrNull(pc) {
|
|
81
|
+
return this.items.length === 0 ? null : (0, index_1._percentile)(this.items, pc);
|
|
82
|
+
}
|
|
83
|
+
percentiles(pcs) {
|
|
84
|
+
return (0, index_1._percentiles)(this.items, pcs);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
exports.NumberStack = NumberStack;
|
|
@@ -44,7 +44,7 @@ export declare function _toFixed(n: number, fractionDigits: number): number;
|
|
|
44
44
|
* _toPrecision(1634.56, 1)
|
|
45
45
|
* // 2000
|
|
46
46
|
*
|
|
47
|
-
* _toPrecision(
|
|
47
|
+
* _toPrecision(1634.56, 2)
|
|
48
48
|
* // 1600
|
|
49
49
|
*/
|
|
50
50
|
export declare function _toPrecision(n: number, precision: number): number;
|
package/dist/seq/seq.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbortableMapper, AbortablePredicate, END } from '../types';
|
|
1
|
+
import { AbortableAsyncMapper, AbortableAsyncPredicate, AbortableMapper, AbortablePredicate, END } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Inspired by Kotlin Sequences.
|
|
4
4
|
* Similar to arrays, but with lazy evaluation, abortable.
|
|
@@ -7,14 +7,14 @@ import { AbortableMapper, AbortablePredicate, END } from '../types';
|
|
|
7
7
|
*
|
|
8
8
|
* @experimental
|
|
9
9
|
*/
|
|
10
|
-
export declare class
|
|
10
|
+
export declare class Sequence<T> implements Iterable<T> {
|
|
11
11
|
private nextFn;
|
|
12
12
|
private constructor();
|
|
13
13
|
[Symbol.iterator](): Iterator<T>;
|
|
14
|
-
static create<T>(initialValue: T | typeof END, nextFn: AbortableMapper<T, T>):
|
|
15
|
-
static range(minIncl: number, maxExcl: number, step?: number):
|
|
16
|
-
static from<T>(a: Iterable<T>):
|
|
17
|
-
static empty<T = any>():
|
|
14
|
+
static create<T>(initialValue: T | typeof END, nextFn: AbortableMapper<T, T>): Sequence<T>;
|
|
15
|
+
static range(minIncl: number, maxExcl: number, step?: number): Sequence<number>;
|
|
16
|
+
static from<T>(a: Iterable<T>): Sequence<T>;
|
|
17
|
+
static empty<T = any>(): Sequence<T>;
|
|
18
18
|
private currentValue;
|
|
19
19
|
private sentInitialValue;
|
|
20
20
|
private i;
|
|
@@ -27,4 +27,26 @@ export declare class Seq<T> implements Iterable<T> {
|
|
|
27
27
|
/**
|
|
28
28
|
* Convenience function to create a Sequence.
|
|
29
29
|
*/
|
|
30
|
-
export declare function _seq<T>(initialValue: T | typeof END, nextFn: AbortableMapper<T, T>):
|
|
30
|
+
export declare function _seq<T>(initialValue: T | typeof END, nextFn: AbortableMapper<T, T>): Sequence<T>;
|
|
31
|
+
/**
|
|
32
|
+
* Experimental.
|
|
33
|
+
* Feasibility to be proven.
|
|
34
|
+
*
|
|
35
|
+
* @experimental
|
|
36
|
+
*/
|
|
37
|
+
export declare class AsyncSequence<T> implements AsyncIterable<T> {
|
|
38
|
+
private nextFn;
|
|
39
|
+
private constructor();
|
|
40
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
41
|
+
static create<T>(initialValue: T | typeof END, nextFn: AbortableAsyncMapper<T, T>): AsyncSequence<T>;
|
|
42
|
+
static from<T>(a: AsyncIterable<T>): Promise<AsyncSequence<T>>;
|
|
43
|
+
static empty<T = any>(): AsyncSequence<T>;
|
|
44
|
+
private currentValue;
|
|
45
|
+
private sentInitialValue;
|
|
46
|
+
private i;
|
|
47
|
+
next(): Promise<T | typeof END>;
|
|
48
|
+
find(predicate: AbortableAsyncPredicate<T>): Promise<T | undefined>;
|
|
49
|
+
some(predicate: AbortableAsyncPredicate<T>): Promise<boolean>;
|
|
50
|
+
every(predicate: AbortableAsyncPredicate<T>): Promise<boolean>;
|
|
51
|
+
toArray(): Promise<T[]>;
|
|
52
|
+
}
|
package/dist/seq/seq.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports._seq = exports.
|
|
3
|
+
exports.AsyncSequence = exports._seq = exports.Sequence = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
5
|
/**
|
|
6
6
|
* Inspired by Kotlin Sequences.
|
|
@@ -10,7 +10,7 @@ const types_1 = require("../types");
|
|
|
10
10
|
*
|
|
11
11
|
* @experimental
|
|
12
12
|
*/
|
|
13
|
-
class
|
|
13
|
+
class Sequence {
|
|
14
14
|
constructor(initialValue, nextFn) {
|
|
15
15
|
this.nextFn = nextFn;
|
|
16
16
|
this.sentInitialValue = false;
|
|
@@ -26,18 +26,18 @@ class Seq {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
static create(initialValue, nextFn) {
|
|
29
|
-
return new
|
|
29
|
+
return new Sequence(initialValue, nextFn);
|
|
30
30
|
}
|
|
31
31
|
static range(minIncl, maxExcl, step = 1) {
|
|
32
32
|
const max = maxExcl - step;
|
|
33
|
-
return new
|
|
33
|
+
return new Sequence(minIncl, n => (n < max ? n + step : types_1.END));
|
|
34
34
|
}
|
|
35
35
|
static from(a) {
|
|
36
36
|
const it = a[Symbol.iterator]();
|
|
37
37
|
const v = it.next();
|
|
38
38
|
if (v.done)
|
|
39
|
-
return new
|
|
40
|
-
return new
|
|
39
|
+
return new Sequence(types_1.END, () => { });
|
|
40
|
+
return new Sequence(v.value, () => {
|
|
41
41
|
const v = it.next();
|
|
42
42
|
if (v.done)
|
|
43
43
|
return types_1.END;
|
|
@@ -45,7 +45,7 @@ class Seq {
|
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
static empty() {
|
|
48
|
-
return new
|
|
48
|
+
return new Sequence(types_1.END, () => { });
|
|
49
49
|
}
|
|
50
50
|
next() {
|
|
51
51
|
if (this.currentValue === types_1.END)
|
|
@@ -131,11 +131,118 @@ class Seq {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
|
-
exports.
|
|
134
|
+
exports.Sequence = Sequence;
|
|
135
135
|
/**
|
|
136
136
|
* Convenience function to create a Sequence.
|
|
137
137
|
*/
|
|
138
138
|
function _seq(initialValue, nextFn) {
|
|
139
|
-
return
|
|
139
|
+
return Sequence.create(initialValue, nextFn);
|
|
140
140
|
}
|
|
141
141
|
exports._seq = _seq;
|
|
142
|
+
/* eslint-disable no-await-in-loop */
|
|
143
|
+
/**
|
|
144
|
+
* Experimental.
|
|
145
|
+
* Feasibility to be proven.
|
|
146
|
+
*
|
|
147
|
+
* @experimental
|
|
148
|
+
*/
|
|
149
|
+
class AsyncSequence {
|
|
150
|
+
constructor(initialValue, nextFn) {
|
|
151
|
+
this.nextFn = nextFn;
|
|
152
|
+
this.sentInitialValue = false;
|
|
153
|
+
this.i = -1;
|
|
154
|
+
this.currentValue = initialValue;
|
|
155
|
+
}
|
|
156
|
+
[Symbol.asyncIterator]() {
|
|
157
|
+
return {
|
|
158
|
+
next: async () => {
|
|
159
|
+
const value = await this.next();
|
|
160
|
+
return value === types_1.END ? { done: true, value: undefined } : { value };
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
static create(initialValue, nextFn) {
|
|
165
|
+
return new AsyncSequence(initialValue, nextFn);
|
|
166
|
+
}
|
|
167
|
+
static async from(a) {
|
|
168
|
+
const it = a[Symbol.asyncIterator]();
|
|
169
|
+
const v = await it.next();
|
|
170
|
+
if (v.done)
|
|
171
|
+
return new AsyncSequence(types_1.END, () => { });
|
|
172
|
+
return new AsyncSequence(v.value, async () => {
|
|
173
|
+
const v = await it.next();
|
|
174
|
+
if (v.done)
|
|
175
|
+
return types_1.END;
|
|
176
|
+
return v.value;
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
static empty() {
|
|
180
|
+
return new AsyncSequence(types_1.END, () => { });
|
|
181
|
+
}
|
|
182
|
+
async next() {
|
|
183
|
+
if (this.currentValue === types_1.END)
|
|
184
|
+
return types_1.END;
|
|
185
|
+
this.i++;
|
|
186
|
+
let v;
|
|
187
|
+
if (!this.sentInitialValue) {
|
|
188
|
+
this.sentInitialValue = true;
|
|
189
|
+
v = this.currentValue;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
v = await this.nextFn(this.currentValue, this.i);
|
|
193
|
+
}
|
|
194
|
+
// console.log(`_seq`, v)
|
|
195
|
+
if (v === types_1.SKIP)
|
|
196
|
+
return await this.next();
|
|
197
|
+
return (this.currentValue = v);
|
|
198
|
+
}
|
|
199
|
+
// Final functions - return final value, not a chained sequence
|
|
200
|
+
async find(predicate) {
|
|
201
|
+
do {
|
|
202
|
+
const v = await this.next();
|
|
203
|
+
if (v === types_1.END)
|
|
204
|
+
return; // not found, end of sequence
|
|
205
|
+
const r = await predicate(v, this.i);
|
|
206
|
+
if (r === types_1.END)
|
|
207
|
+
return;
|
|
208
|
+
if (r)
|
|
209
|
+
return v;
|
|
210
|
+
// otherwise proceed
|
|
211
|
+
} while (true); // eslint-disable-line no-constant-condition
|
|
212
|
+
}
|
|
213
|
+
async some(predicate) {
|
|
214
|
+
do {
|
|
215
|
+
const v = await this.next();
|
|
216
|
+
if (v === types_1.END)
|
|
217
|
+
return false;
|
|
218
|
+
const r = await predicate(v, this.i);
|
|
219
|
+
if (r === types_1.END)
|
|
220
|
+
return false;
|
|
221
|
+
if (r)
|
|
222
|
+
return true;
|
|
223
|
+
} while (true); // eslint-disable-line no-constant-condition
|
|
224
|
+
}
|
|
225
|
+
async every(predicate) {
|
|
226
|
+
do {
|
|
227
|
+
const v = await this.next();
|
|
228
|
+
if (v === types_1.END)
|
|
229
|
+
return true;
|
|
230
|
+
const r = await predicate(v, this.i);
|
|
231
|
+
if (r === types_1.END)
|
|
232
|
+
return true;
|
|
233
|
+
if (!r)
|
|
234
|
+
return false;
|
|
235
|
+
} while (true); // eslint-disable-line no-constant-condition
|
|
236
|
+
}
|
|
237
|
+
async toArray() {
|
|
238
|
+
const a = [];
|
|
239
|
+
// eslint-disable-next-line no-constant-condition
|
|
240
|
+
while (true) {
|
|
241
|
+
const v = await this.next();
|
|
242
|
+
if (v === types_1.END)
|
|
243
|
+
return a;
|
|
244
|
+
a.push(v);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
exports.AsyncSequence = AsyncSequence;
|
|
@@ -44,17 +44,19 @@ function _stringifyAny(obj, opt = {}) {
|
|
|
44
44
|
//
|
|
45
45
|
// Error or ErrorObject
|
|
46
46
|
//
|
|
47
|
+
// Omit "default" error name as non-informative
|
|
48
|
+
// UPD: no, it's still important to understand that we're dealing with Error and not just some string
|
|
49
|
+
// if (obj?.name === 'Error') {
|
|
50
|
+
// s = obj.message
|
|
51
|
+
// }
|
|
52
|
+
s = [obj.name, obj.message].join(': ');
|
|
47
53
|
if (opt.includeErrorStack && obj.stack) {
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
// if (obj?.name === 'Error') {
|
|
55
|
-
// s = obj.message
|
|
56
|
-
// }
|
|
57
|
-
s = [obj.name, obj.message].join(': ');
|
|
54
|
+
// Here we're using the previously-generated "title line" (e.g "Error: some_message"),
|
|
55
|
+
// concatenating it with the Stack (but without the title line of the Stack)
|
|
56
|
+
// This is to fix the rare error (happened with Got) where `err.message` was changed,
|
|
57
|
+
// but err.stack had "old" err.message
|
|
58
|
+
// This should "fix" that
|
|
59
|
+
s = [s, ...obj.stack.split('\n').slice(1)].join('\n');
|
|
58
60
|
}
|
|
59
61
|
if ((0, error_util_1._isErrorObject)(obj)) {
|
|
60
62
|
if ((0, error_util_1._isHttpErrorObject)(obj)) {
|
|
@@ -39,6 +39,22 @@ export function _percentile(values, pc) {
|
|
|
39
39
|
const ceilPos = Math.ceil(pos);
|
|
40
40
|
return _averageWeighted([sorted[floorPos], sorted[ceilPos]], [1 - dec, dec]);
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* A tiny bit more efficient function than calling _percentile individually.
|
|
44
|
+
*/
|
|
45
|
+
export function _percentiles(values, pcs) {
|
|
46
|
+
const r = {};
|
|
47
|
+
const sorted = _sortNumbers(values);
|
|
48
|
+
pcs.forEach(pc => {
|
|
49
|
+
// Floating pos in the range of [0; length - 1]
|
|
50
|
+
const pos = ((values.length - 1) * pc) / 100;
|
|
51
|
+
const dec = pos % 1;
|
|
52
|
+
const floorPos = Math.floor(pos);
|
|
53
|
+
const ceilPos = Math.ceil(pos);
|
|
54
|
+
r[pc] = _averageWeighted([sorted[floorPos], sorted[ceilPos]], [1 - dec, dec]);
|
|
55
|
+
});
|
|
56
|
+
return r;
|
|
57
|
+
}
|
|
42
58
|
/**
|
|
43
59
|
* @example
|
|
44
60
|
*
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { _average, _percentile, _percentiles, _range } from '../index';
|
|
1
2
|
/**
|
|
2
|
-
* Implements a "round-robin" Stack with a limited size.
|
|
3
|
+
* Implements a "round-robin" Stack ("first-in last-out" aka FILO) with a limited size.
|
|
3
4
|
* Like an array of a fixed size. When it runs out of space - it starts writing on top of itself
|
|
4
5
|
* from index 0.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
5
8
|
*/
|
|
6
|
-
export class
|
|
9
|
+
export class Stack {
|
|
7
10
|
constructor(size) {
|
|
8
11
|
this.size = size;
|
|
9
12
|
/**
|
|
@@ -16,6 +19,14 @@ export class SizeLimitedStack {
|
|
|
16
19
|
push(item) {
|
|
17
20
|
this.items[this.nextIndex] = item;
|
|
18
21
|
this.nextIndex = this.nextIndex === this.size - 1 ? 0 : this.nextIndex + 1;
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Fill (overwrite) the whole Stack (all its items) with the passed `item`.
|
|
26
|
+
*/
|
|
27
|
+
fill(item) {
|
|
28
|
+
_range(this.size).forEach(i => (this.items[i] = item));
|
|
29
|
+
return this;
|
|
19
30
|
}
|
|
20
31
|
/**
|
|
21
32
|
* Returns last items in the right order.
|
|
@@ -30,3 +41,42 @@ export class SizeLimitedStack {
|
|
|
30
41
|
return [...this.items.slice(this.nextIndex), ...this.items.slice(0, this.nextIndex)];
|
|
31
42
|
}
|
|
32
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Fixed-size FILO stack of Numbers.
|
|
46
|
+
* Has convenience stat methods, e.g percentile, avg, etc.
|
|
47
|
+
*/
|
|
48
|
+
export class NumberStack extends Stack {
|
|
49
|
+
avg() {
|
|
50
|
+
// _assert(this.items.length, 'NumberStack.avg cannot be called on empty stack')
|
|
51
|
+
return _average(this.items);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Returns null if Stack is empty.
|
|
55
|
+
*/
|
|
56
|
+
avgOrNull() {
|
|
57
|
+
return this.items.length === 0 ? null : _average(this.items);
|
|
58
|
+
}
|
|
59
|
+
median() {
|
|
60
|
+
return _percentile(this.items, 50);
|
|
61
|
+
}
|
|
62
|
+
medianOrNull() {
|
|
63
|
+
return this.items.length === 0 ? null : _percentile(this.items, 50);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* `pc` is a number from 0 to 100 inclusive.
|
|
67
|
+
*/
|
|
68
|
+
percentile(pc) {
|
|
69
|
+
// _assert(this.items.length, 'NumberStack.percentile cannot be called on empty stack')
|
|
70
|
+
return _percentile(this.items, pc);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* `pc` is a number from 0 to 100 inclusive.
|
|
74
|
+
* Returns null if Stack is empty.
|
|
75
|
+
*/
|
|
76
|
+
percentileOrNull(pc) {
|
|
77
|
+
return this.items.length === 0 ? null : _percentile(this.items, pc);
|
|
78
|
+
}
|
|
79
|
+
percentiles(pcs) {
|
|
80
|
+
return _percentiles(this.items, pcs);
|
|
81
|
+
}
|
|
82
|
+
}
|
package/dist-esm/seq/seq.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { END, SKIP } from '../types';
|
|
1
|
+
import { END, SKIP, } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Inspired by Kotlin Sequences.
|
|
4
4
|
* Similar to arrays, but with lazy evaluation, abortable.
|
|
@@ -7,7 +7,7 @@ import { END, SKIP } from '../types';
|
|
|
7
7
|
*
|
|
8
8
|
* @experimental
|
|
9
9
|
*/
|
|
10
|
-
export class
|
|
10
|
+
export class Sequence {
|
|
11
11
|
constructor(initialValue, nextFn) {
|
|
12
12
|
this.nextFn = nextFn;
|
|
13
13
|
this.sentInitialValue = false;
|
|
@@ -23,18 +23,18 @@ export class Seq {
|
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
static create(initialValue, nextFn) {
|
|
26
|
-
return new
|
|
26
|
+
return new Sequence(initialValue, nextFn);
|
|
27
27
|
}
|
|
28
28
|
static range(minIncl, maxExcl, step = 1) {
|
|
29
29
|
const max = maxExcl - step;
|
|
30
|
-
return new
|
|
30
|
+
return new Sequence(minIncl, n => (n < max ? n + step : END));
|
|
31
31
|
}
|
|
32
32
|
static from(a) {
|
|
33
33
|
const it = a[Symbol.iterator]();
|
|
34
34
|
const v = it.next();
|
|
35
35
|
if (v.done)
|
|
36
|
-
return new
|
|
37
|
-
return new
|
|
36
|
+
return new Sequence(END, () => { });
|
|
37
|
+
return new Sequence(v.value, () => {
|
|
38
38
|
const v = it.next();
|
|
39
39
|
if (v.done)
|
|
40
40
|
return END;
|
|
@@ -42,7 +42,7 @@ export class Seq {
|
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
static empty() {
|
|
45
|
-
return new
|
|
45
|
+
return new Sequence(END, () => { });
|
|
46
46
|
}
|
|
47
47
|
next() {
|
|
48
48
|
if (this.currentValue === END)
|
|
@@ -132,5 +132,111 @@ export class Seq {
|
|
|
132
132
|
* Convenience function to create a Sequence.
|
|
133
133
|
*/
|
|
134
134
|
export function _seq(initialValue, nextFn) {
|
|
135
|
-
return
|
|
135
|
+
return Sequence.create(initialValue, nextFn);
|
|
136
|
+
}
|
|
137
|
+
/* eslint-disable no-await-in-loop */
|
|
138
|
+
/**
|
|
139
|
+
* Experimental.
|
|
140
|
+
* Feasibility to be proven.
|
|
141
|
+
*
|
|
142
|
+
* @experimental
|
|
143
|
+
*/
|
|
144
|
+
export class AsyncSequence {
|
|
145
|
+
constructor(initialValue, nextFn) {
|
|
146
|
+
this.nextFn = nextFn;
|
|
147
|
+
this.sentInitialValue = false;
|
|
148
|
+
this.i = -1;
|
|
149
|
+
this.currentValue = initialValue;
|
|
150
|
+
}
|
|
151
|
+
[Symbol.asyncIterator]() {
|
|
152
|
+
return {
|
|
153
|
+
next: async () => {
|
|
154
|
+
const value = await this.next();
|
|
155
|
+
return value === END ? { done: true, value: undefined } : { value };
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
static create(initialValue, nextFn) {
|
|
160
|
+
return new AsyncSequence(initialValue, nextFn);
|
|
161
|
+
}
|
|
162
|
+
static async from(a) {
|
|
163
|
+
const it = a[Symbol.asyncIterator]();
|
|
164
|
+
const v = await it.next();
|
|
165
|
+
if (v.done)
|
|
166
|
+
return new AsyncSequence(END, () => { });
|
|
167
|
+
return new AsyncSequence(v.value, async () => {
|
|
168
|
+
const v = await it.next();
|
|
169
|
+
if (v.done)
|
|
170
|
+
return END;
|
|
171
|
+
return v.value;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
static empty() {
|
|
175
|
+
return new AsyncSequence(END, () => { });
|
|
176
|
+
}
|
|
177
|
+
async next() {
|
|
178
|
+
if (this.currentValue === END)
|
|
179
|
+
return END;
|
|
180
|
+
this.i++;
|
|
181
|
+
let v;
|
|
182
|
+
if (!this.sentInitialValue) {
|
|
183
|
+
this.sentInitialValue = true;
|
|
184
|
+
v = this.currentValue;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
v = await this.nextFn(this.currentValue, this.i);
|
|
188
|
+
}
|
|
189
|
+
// console.log(`_seq`, v)
|
|
190
|
+
if (v === SKIP)
|
|
191
|
+
return await this.next();
|
|
192
|
+
return (this.currentValue = v);
|
|
193
|
+
}
|
|
194
|
+
// Final functions - return final value, not a chained sequence
|
|
195
|
+
async find(predicate) {
|
|
196
|
+
do {
|
|
197
|
+
const v = await this.next();
|
|
198
|
+
if (v === END)
|
|
199
|
+
return; // not found, end of sequence
|
|
200
|
+
const r = await predicate(v, this.i);
|
|
201
|
+
if (r === END)
|
|
202
|
+
return;
|
|
203
|
+
if (r)
|
|
204
|
+
return v;
|
|
205
|
+
// otherwise proceed
|
|
206
|
+
} while (true); // eslint-disable-line no-constant-condition
|
|
207
|
+
}
|
|
208
|
+
async some(predicate) {
|
|
209
|
+
do {
|
|
210
|
+
const v = await this.next();
|
|
211
|
+
if (v === END)
|
|
212
|
+
return false;
|
|
213
|
+
const r = await predicate(v, this.i);
|
|
214
|
+
if (r === END)
|
|
215
|
+
return false;
|
|
216
|
+
if (r)
|
|
217
|
+
return true;
|
|
218
|
+
} while (true); // eslint-disable-line no-constant-condition
|
|
219
|
+
}
|
|
220
|
+
async every(predicate) {
|
|
221
|
+
do {
|
|
222
|
+
const v = await this.next();
|
|
223
|
+
if (v === END)
|
|
224
|
+
return true;
|
|
225
|
+
const r = await predicate(v, this.i);
|
|
226
|
+
if (r === END)
|
|
227
|
+
return true;
|
|
228
|
+
if (!r)
|
|
229
|
+
return false;
|
|
230
|
+
} while (true); // eslint-disable-line no-constant-condition
|
|
231
|
+
}
|
|
232
|
+
async toArray() {
|
|
233
|
+
const a = [];
|
|
234
|
+
// eslint-disable-next-line no-constant-condition
|
|
235
|
+
while (true) {
|
|
236
|
+
const v = await this.next();
|
|
237
|
+
if (v === END)
|
|
238
|
+
return a;
|
|
239
|
+
a.push(v);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
136
242
|
}
|
|
@@ -41,17 +41,19 @@ export function _stringifyAny(obj, opt = {}) {
|
|
|
41
41
|
//
|
|
42
42
|
// Error or ErrorObject
|
|
43
43
|
//
|
|
44
|
+
// Omit "default" error name as non-informative
|
|
45
|
+
// UPD: no, it's still important to understand that we're dealing with Error and not just some string
|
|
46
|
+
// if (obj?.name === 'Error') {
|
|
47
|
+
// s = obj.message
|
|
48
|
+
// }
|
|
49
|
+
s = [obj.name, obj.message].join(': ');
|
|
44
50
|
if (opt.includeErrorStack && obj.stack) {
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
// if (obj?.name === 'Error') {
|
|
52
|
-
// s = obj.message
|
|
53
|
-
// }
|
|
54
|
-
s = [obj.name, obj.message].join(': ');
|
|
51
|
+
// Here we're using the previously-generated "title line" (e.g "Error: some_message"),
|
|
52
|
+
// concatenating it with the Stack (but without the title line of the Stack)
|
|
53
|
+
// This is to fix the rare error (happened with Got) where `err.message` was changed,
|
|
54
|
+
// but err.stack had "old" err.message
|
|
55
|
+
// This should "fix" that
|
|
56
|
+
s = [s, ...obj.stack.split('\n').slice(1)].join('\n');
|
|
55
57
|
}
|
|
56
58
|
if (_isErrorObject(obj)) {
|
|
57
59
|
if (_isHttpErrorObject(obj)) {
|
package/package.json
CHANGED
package/src/math/math.util.ts
CHANGED
|
@@ -47,6 +47,27 @@ export function _percentile(values: number[], pc: number): number {
|
|
|
47
47
|
return _averageWeighted([sorted[floorPos]!, sorted[ceilPos]!], [1 - dec, dec])
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* A tiny bit more efficient function than calling _percentile individually.
|
|
52
|
+
*/
|
|
53
|
+
export function _percentiles(values: number[], pcs: number[]): Record<number, number> {
|
|
54
|
+
const r = {} as Record<number, number>
|
|
55
|
+
|
|
56
|
+
const sorted = _sortNumbers(values)
|
|
57
|
+
|
|
58
|
+
pcs.forEach(pc => {
|
|
59
|
+
// Floating pos in the range of [0; length - 1]
|
|
60
|
+
const pos = ((values.length - 1) * pc) / 100
|
|
61
|
+
const dec = pos % 1
|
|
62
|
+
const floorPos = Math.floor(pos)
|
|
63
|
+
const ceilPos = Math.ceil(pos)
|
|
64
|
+
|
|
65
|
+
r[pc] = _averageWeighted([sorted[floorPos]!, sorted[ceilPos]!], [1 - dec, dec])
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
return r
|
|
69
|
+
}
|
|
70
|
+
|
|
50
71
|
/**
|
|
51
72
|
* @example
|
|
52
73
|
*
|
package/src/math/stack.util.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { _average, _percentile, _percentiles, _range } from '../index'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
* Implements a "round-robin" Stack with a limited size.
|
|
4
|
+
* Implements a "round-robin" Stack ("first-in last-out" aka FILO) with a limited size.
|
|
3
5
|
* Like an array of a fixed size. When it runs out of space - it starts writing on top of itself
|
|
4
6
|
* from index 0.
|
|
7
|
+
*
|
|
8
|
+
*
|
|
5
9
|
*/
|
|
6
|
-
export class
|
|
10
|
+
export class Stack<T> {
|
|
7
11
|
constructor(public readonly size: number) {}
|
|
8
12
|
|
|
9
13
|
/**
|
|
@@ -14,9 +18,18 @@ export class SizeLimitedStack<T> {
|
|
|
14
18
|
|
|
15
19
|
readonly items: T[] = []
|
|
16
20
|
|
|
17
|
-
push(item: T):
|
|
21
|
+
push(item: T): this {
|
|
18
22
|
this.items[this.nextIndex] = item
|
|
19
23
|
this.nextIndex = this.nextIndex === this.size - 1 ? 0 : this.nextIndex + 1
|
|
24
|
+
return this
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Fill (overwrite) the whole Stack (all its items) with the passed `item`.
|
|
29
|
+
*/
|
|
30
|
+
fill(item: T): this {
|
|
31
|
+
_range(this.size).forEach(i => (this.items[i] = item))
|
|
32
|
+
return this
|
|
20
33
|
}
|
|
21
34
|
|
|
22
35
|
/**
|
|
@@ -33,3 +46,49 @@ export class SizeLimitedStack<T> {
|
|
|
33
46
|
return [...this.items.slice(this.nextIndex), ...this.items.slice(0, this.nextIndex)]
|
|
34
47
|
}
|
|
35
48
|
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Fixed-size FILO stack of Numbers.
|
|
52
|
+
* Has convenience stat methods, e.g percentile, avg, etc.
|
|
53
|
+
*/
|
|
54
|
+
export class NumberStack extends Stack<number> {
|
|
55
|
+
avg(): number {
|
|
56
|
+
// _assert(this.items.length, 'NumberStack.avg cannot be called on empty stack')
|
|
57
|
+
return _average(this.items)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Returns null if Stack is empty.
|
|
62
|
+
*/
|
|
63
|
+
avgOrNull(): number | null {
|
|
64
|
+
return this.items.length === 0 ? null : _average(this.items)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
median(): number {
|
|
68
|
+
return _percentile(this.items, 50)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
medianOrNull(): number | null {
|
|
72
|
+
return this.items.length === 0 ? null : _percentile(this.items, 50)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* `pc` is a number from 0 to 100 inclusive.
|
|
77
|
+
*/
|
|
78
|
+
percentile(pc: number): number {
|
|
79
|
+
// _assert(this.items.length, 'NumberStack.percentile cannot be called on empty stack')
|
|
80
|
+
return _percentile(this.items, pc)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* `pc` is a number from 0 to 100 inclusive.
|
|
85
|
+
* Returns null if Stack is empty.
|
|
86
|
+
*/
|
|
87
|
+
percentileOrNull(pc: number): number | null {
|
|
88
|
+
return this.items.length === 0 ? null : _percentile(this.items, pc)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
percentiles(pcs: number[]): Record<number, number> {
|
|
92
|
+
return _percentiles(this.items, pcs)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -65,7 +65,7 @@ export function _toFixed(n: number, fractionDigits: number): number {
|
|
|
65
65
|
* _toPrecision(1634.56, 1)
|
|
66
66
|
* // 2000
|
|
67
67
|
*
|
|
68
|
-
* _toPrecision(
|
|
68
|
+
* _toPrecision(1634.56, 2)
|
|
69
69
|
* // 1600
|
|
70
70
|
*/
|
|
71
71
|
export function _toPrecision(n: number, precision: number): number {
|
package/src/seq/seq.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AbortableAsyncMapper,
|
|
3
|
+
AbortableAsyncPredicate,
|
|
4
|
+
AbortableMapper,
|
|
5
|
+
AbortablePredicate,
|
|
6
|
+
END,
|
|
7
|
+
SKIP,
|
|
8
|
+
} from '../types'
|
|
2
9
|
|
|
3
10
|
/**
|
|
4
11
|
* Inspired by Kotlin Sequences.
|
|
@@ -8,7 +15,7 @@ import { AbortableMapper, AbortablePredicate, END, SKIP } from '../types'
|
|
|
8
15
|
*
|
|
9
16
|
* @experimental
|
|
10
17
|
*/
|
|
11
|
-
export class
|
|
18
|
+
export class Sequence<T> implements Iterable<T> {
|
|
12
19
|
private constructor(initialValue: T | typeof END, private nextFn: AbortableMapper<T, T>) {
|
|
13
20
|
this.currentValue = initialValue
|
|
14
21
|
}
|
|
@@ -22,29 +29,29 @@ export class Seq<T> implements Iterable<T> {
|
|
|
22
29
|
}
|
|
23
30
|
}
|
|
24
31
|
|
|
25
|
-
static create<T>(initialValue: T | typeof END, nextFn: AbortableMapper<T, T>):
|
|
26
|
-
return new
|
|
32
|
+
static create<T>(initialValue: T | typeof END, nextFn: AbortableMapper<T, T>): Sequence<T> {
|
|
33
|
+
return new Sequence(initialValue, nextFn)
|
|
27
34
|
}
|
|
28
35
|
|
|
29
|
-
static range(minIncl: number, maxExcl: number, step = 1):
|
|
36
|
+
static range(minIncl: number, maxExcl: number, step = 1): Sequence<number> {
|
|
30
37
|
const max = maxExcl - step
|
|
31
|
-
return new
|
|
38
|
+
return new Sequence(minIncl, n => (n < max ? n + step : END))
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
static from<T>(a: Iterable<T>):
|
|
41
|
+
static from<T>(a: Iterable<T>): Sequence<T> {
|
|
35
42
|
const it = a[Symbol.iterator]()
|
|
36
43
|
const v = it.next()
|
|
37
|
-
if (v.done) return new
|
|
44
|
+
if (v.done) return new Sequence<any>(END, () => {})
|
|
38
45
|
|
|
39
|
-
return new
|
|
46
|
+
return new Sequence(v.value, () => {
|
|
40
47
|
const v = it.next()
|
|
41
48
|
if (v.done) return END
|
|
42
49
|
return v.value
|
|
43
50
|
})
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
static empty<T = any>():
|
|
47
|
-
return new
|
|
53
|
+
static empty<T = any>(): Sequence<T> {
|
|
54
|
+
return new Sequence(END as any, () => {})
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
private currentValue: T | typeof END
|
|
@@ -138,6 +145,120 @@ export class Seq<T> implements Iterable<T> {
|
|
|
138
145
|
/**
|
|
139
146
|
* Convenience function to create a Sequence.
|
|
140
147
|
*/
|
|
141
|
-
export function _seq<T>(initialValue: T | typeof END, nextFn: AbortableMapper<T, T>):
|
|
142
|
-
return
|
|
148
|
+
export function _seq<T>(initialValue: T | typeof END, nextFn: AbortableMapper<T, T>): Sequence<T> {
|
|
149
|
+
return Sequence.create(initialValue, nextFn)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* eslint-disable no-await-in-loop */
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Experimental.
|
|
156
|
+
* Feasibility to be proven.
|
|
157
|
+
*
|
|
158
|
+
* @experimental
|
|
159
|
+
*/
|
|
160
|
+
export class AsyncSequence<T> implements AsyncIterable<T> {
|
|
161
|
+
private constructor(initialValue: T | typeof END, private nextFn: AbortableAsyncMapper<T, T>) {
|
|
162
|
+
this.currentValue = initialValue
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
[Symbol.asyncIterator](): AsyncIterator<T> {
|
|
166
|
+
return {
|
|
167
|
+
next: async () => {
|
|
168
|
+
const value = await this.next()
|
|
169
|
+
return value === END ? { done: true, value: undefined } : { value }
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
static create<T>(
|
|
175
|
+
initialValue: T | typeof END,
|
|
176
|
+
nextFn: AbortableAsyncMapper<T, T>,
|
|
177
|
+
): AsyncSequence<T> {
|
|
178
|
+
return new AsyncSequence(initialValue, nextFn)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
static async from<T>(a: AsyncIterable<T>): Promise<AsyncSequence<T>> {
|
|
182
|
+
const it = a[Symbol.asyncIterator]()
|
|
183
|
+
const v = await it.next()
|
|
184
|
+
if (v.done) return new AsyncSequence<any>(END, () => {})
|
|
185
|
+
|
|
186
|
+
return new AsyncSequence(v.value, async () => {
|
|
187
|
+
const v = await it.next()
|
|
188
|
+
if (v.done) return END
|
|
189
|
+
return v.value
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
static empty<T = any>(): AsyncSequence<T> {
|
|
194
|
+
return new AsyncSequence(END as any, () => {})
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private currentValue: T | typeof END
|
|
198
|
+
private sentInitialValue = false
|
|
199
|
+
private i = -1
|
|
200
|
+
|
|
201
|
+
async next(): Promise<T | typeof END> {
|
|
202
|
+
if (this.currentValue === END) return END
|
|
203
|
+
|
|
204
|
+
this.i++
|
|
205
|
+
|
|
206
|
+
let v: T | typeof SKIP | typeof END
|
|
207
|
+
|
|
208
|
+
if (!this.sentInitialValue) {
|
|
209
|
+
this.sentInitialValue = true
|
|
210
|
+
v = this.currentValue
|
|
211
|
+
} else {
|
|
212
|
+
v = await this.nextFn(this.currentValue, this.i)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// console.log(`_seq`, v)
|
|
216
|
+
|
|
217
|
+
if (v === SKIP) return await this.next()
|
|
218
|
+
|
|
219
|
+
return (this.currentValue = v)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Final functions - return final value, not a chained sequence
|
|
223
|
+
async find(predicate: AbortableAsyncPredicate<T>): Promise<T | undefined> {
|
|
224
|
+
do {
|
|
225
|
+
const v = await this.next()
|
|
226
|
+
if (v === END) return // not found, end of sequence
|
|
227
|
+
const r = await predicate(v, this.i)
|
|
228
|
+
if (r === END) return
|
|
229
|
+
if (r) return v
|
|
230
|
+
// otherwise proceed
|
|
231
|
+
} while (true) // eslint-disable-line no-constant-condition
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async some(predicate: AbortableAsyncPredicate<T>): Promise<boolean> {
|
|
235
|
+
do {
|
|
236
|
+
const v = await this.next()
|
|
237
|
+
if (v === END) return false
|
|
238
|
+
const r = await predicate(v, this.i)
|
|
239
|
+
if (r === END) return false
|
|
240
|
+
if (r) return true
|
|
241
|
+
} while (true) // eslint-disable-line no-constant-condition
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async every(predicate: AbortableAsyncPredicate<T>): Promise<boolean> {
|
|
245
|
+
do {
|
|
246
|
+
const v = await this.next()
|
|
247
|
+
if (v === END) return true
|
|
248
|
+
const r = await predicate(v, this.i)
|
|
249
|
+
if (r === END) return true
|
|
250
|
+
if (!r) return false
|
|
251
|
+
} while (true) // eslint-disable-line no-constant-condition
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async toArray(): Promise<T[]> {
|
|
255
|
+
const a: T[] = []
|
|
256
|
+
|
|
257
|
+
// eslint-disable-next-line no-constant-condition
|
|
258
|
+
while (true) {
|
|
259
|
+
const v = await this.next()
|
|
260
|
+
if (v === END) return a
|
|
261
|
+
a.push(v)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
143
264
|
}
|
|
@@ -76,16 +76,20 @@ export function _stringifyAny(obj: any, opt: StringifyAnyOptions = {}): string {
|
|
|
76
76
|
// Error or ErrorObject
|
|
77
77
|
//
|
|
78
78
|
|
|
79
|
+
// Omit "default" error name as non-informative
|
|
80
|
+
// UPD: no, it's still important to understand that we're dealing with Error and not just some string
|
|
81
|
+
// if (obj?.name === 'Error') {
|
|
82
|
+
// s = obj.message
|
|
83
|
+
// }
|
|
84
|
+
s = [obj.name, obj.message].join(': ')
|
|
85
|
+
|
|
79
86
|
if (opt.includeErrorStack && obj.stack) {
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
// s = obj.message
|
|
87
|
-
// }
|
|
88
|
-
s = [obj.name, obj.message].join(': ')
|
|
87
|
+
// Here we're using the previously-generated "title line" (e.g "Error: some_message"),
|
|
88
|
+
// concatenating it with the Stack (but without the title line of the Stack)
|
|
89
|
+
// This is to fix the rare error (happened with Got) where `err.message` was changed,
|
|
90
|
+
// but err.stack had "old" err.message
|
|
91
|
+
// This should "fix" that
|
|
92
|
+
s = [s, ...obj.stack.split('\n').slice(1)].join('\n')
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
if (_isErrorObject(obj)) {
|