@naturalcycles/js-lib 14.69.4 → 14.71.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/dist/error/error.util.d.ts +6 -2
- package/dist/error/error.util.js +34 -18
- package/dist/error/try.d.ts +1 -1
- package/dist/error/try.js +2 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.js +9 -12
- package/dist/promise/pBatch.d.ts +2 -3
- package/dist/promise/pFilter.d.ts +2 -2
- package/dist/promise/pMap.d.ts +2 -3
- package/dist/promise/pMap.js +13 -7
- package/dist/promise/pProps.d.ts +3 -1
- package/dist/promise/pProps.js +4 -5
- package/dist/seq/seq.d.ts +30 -0
- package/dist/seq/seq.js +141 -0
- package/dist/string/json.util.js +4 -1
- package/dist/typeFest.d.ts +0 -30
- package/dist/types.d.ts +13 -1
- package/dist/types.js +9 -1
- package/dist-esm/error/error.util.js +32 -16
- package/dist-esm/error/try.js +2 -0
- package/dist-esm/index.js +4 -3
- package/dist-esm/promise/pMap.js +14 -8
- package/dist-esm/promise/pProps.js +4 -5
- package/dist-esm/seq/seq.js +136 -0
- package/dist-esm/string/json.util.js +4 -1
- package/dist-esm/types.js +8 -0
- package/package.json +1 -1
- package/src/error/error.util.ts +44 -19
- package/src/error/try.ts +4 -1
- package/src/index.ts +14 -18
- package/src/promise/pBatch.ts +2 -3
- package/src/promise/pFilter.ts +2 -2
- package/src/promise/pMap.ts +16 -11
- package/src/promise/pProps.ts +6 -7
- package/src/seq/seq.ts +143 -0
- package/src/string/json.util.ts +5 -1
- package/src/typeFest.ts +0 -32
- package/src/types.ts +22 -1
package/src/promise/pMap.ts
CHANGED
|
@@ -7,8 +7,7 @@ Improvements:
|
|
|
7
7
|
- Compatible with pProps (that had typings issues)
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { ErrorMode } from '..'
|
|
11
|
-
import { AsyncMapper } from '../types'
|
|
10
|
+
import { AbortableAsyncMapper, END, ErrorMode, SKIP } from '..'
|
|
12
11
|
import { AggregatedError } from './AggregatedError'
|
|
13
12
|
|
|
14
13
|
export interface PMapOptions {
|
|
@@ -57,37 +56,38 @@ export interface PMapOptions {
|
|
|
57
56
|
*/
|
|
58
57
|
export async function pMap<IN, OUT>(
|
|
59
58
|
iterable: Iterable<IN | PromiseLike<IN>>,
|
|
60
|
-
mapper:
|
|
59
|
+
mapper: AbortableAsyncMapper<IN, OUT>,
|
|
61
60
|
opt: PMapOptions = {},
|
|
62
61
|
): Promise<OUT[]> {
|
|
63
62
|
return new Promise<OUT[]>((resolve, reject) => {
|
|
64
63
|
const { concurrency = Number.POSITIVE_INFINITY, errorMode = ErrorMode.THROW_IMMEDIATELY } = opt
|
|
65
64
|
|
|
66
|
-
const ret: OUT[] = []
|
|
65
|
+
const ret: (OUT | typeof SKIP)[] = []
|
|
67
66
|
const iterator = iterable[Symbol.iterator]()
|
|
68
67
|
const errors: Error[] = []
|
|
69
|
-
let
|
|
68
|
+
let isSettled = false
|
|
70
69
|
let isIterableDone = false
|
|
71
70
|
let resolvingCount = 0
|
|
72
71
|
let currentIndex = 0
|
|
73
72
|
|
|
74
|
-
const next = () => {
|
|
75
|
-
if (
|
|
73
|
+
const next = (skipped = false) => {
|
|
74
|
+
if (isSettled) {
|
|
76
75
|
return
|
|
77
76
|
}
|
|
78
77
|
|
|
79
78
|
const nextItem = iterator.next()
|
|
80
79
|
const i = currentIndex
|
|
81
|
-
currentIndex++
|
|
80
|
+
if (!skipped) currentIndex++
|
|
82
81
|
|
|
83
82
|
if (nextItem.done) {
|
|
84
83
|
isIterableDone = true
|
|
85
84
|
|
|
86
85
|
if (resolvingCount === 0) {
|
|
86
|
+
const r = ret.filter(r => r !== SKIP) as OUT[]
|
|
87
87
|
if (errors.length && errorMode === ErrorMode.THROW_AGGREGATED) {
|
|
88
|
-
reject(new AggregatedError(errors,
|
|
88
|
+
reject(new AggregatedError(errors, r))
|
|
89
89
|
} else {
|
|
90
|
-
resolve(
|
|
90
|
+
resolve(r)
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -100,13 +100,18 @@ export async function pMap<IN, OUT>(
|
|
|
100
100
|
.then(async element => await mapper(element, i))
|
|
101
101
|
.then(
|
|
102
102
|
value => {
|
|
103
|
+
if (value === END) {
|
|
104
|
+
isSettled = true
|
|
105
|
+
return resolve(ret.filter(r => r !== SKIP) as OUT[])
|
|
106
|
+
}
|
|
107
|
+
|
|
103
108
|
ret[i] = value
|
|
104
109
|
resolvingCount--
|
|
105
110
|
next()
|
|
106
111
|
},
|
|
107
112
|
err => {
|
|
108
113
|
if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
|
|
109
|
-
|
|
114
|
+
isSettled = true
|
|
110
115
|
reject(err)
|
|
111
116
|
} else {
|
|
112
117
|
errors.push(err)
|
package/src/promise/pProps.ts
CHANGED
|
@@ -10,6 +10,9 @@ Improvements:
|
|
|
10
10
|
|
|
11
11
|
import { pMap, PMapOptions } from './pMap'
|
|
12
12
|
|
|
13
|
+
// todo: remove when eslint starts to know about Awaited
|
|
14
|
+
/* eslint-disable no-undef */
|
|
15
|
+
|
|
13
16
|
/**
|
|
14
17
|
* Promise.all for Object instead of Array.
|
|
15
18
|
* Supports concurrency.
|
|
@@ -17,13 +20,9 @@ import { pMap, PMapOptions } from './pMap'
|
|
|
17
20
|
export async function pProps<T>(
|
|
18
21
|
input: { [K in keyof T]: T[K] | Promise<T[K]> },
|
|
19
22
|
opt?: PMapOptions,
|
|
20
|
-
): Promise<T> {
|
|
23
|
+
): Promise<{ [K in keyof T]: Awaited<T[K]> }> {
|
|
24
|
+
const r = {} as { [K in keyof T]: Awaited<T[K]> }
|
|
21
25
|
const keys = Object.keys(input) as (keyof T)[]
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const r = {} as T
|
|
25
|
-
values.forEach((v, i) => {
|
|
26
|
-
r[keys[i]!] = v
|
|
27
|
-
})
|
|
26
|
+
await pMap(Object.values(input), (v, i) => (r[keys[i]!] = v), opt)
|
|
28
27
|
return r
|
|
29
28
|
}
|
package/src/seq/seq.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { AbortableMapper, AbortablePredicate, END, SKIP } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Inspired by Kotlin Sequences.
|
|
5
|
+
* Similar to arrays, but with lazy evaluation, abortable.
|
|
6
|
+
* Can be useful when it's not feasible/performant to create an array of values to iterate upfront
|
|
7
|
+
* (e.g to construct 1000 Dayjs instances only to find that 2 of them were needed).
|
|
8
|
+
*
|
|
9
|
+
* @experimental
|
|
10
|
+
*/
|
|
11
|
+
export class Seq<T> implements Iterable<T> {
|
|
12
|
+
private constructor(initialValue: T | typeof END, private nextFn: AbortableMapper<T, T>) {
|
|
13
|
+
this.currentValue = initialValue
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
[Symbol.iterator](): Iterator<T> {
|
|
17
|
+
return {
|
|
18
|
+
next: () => {
|
|
19
|
+
const value = this.next()
|
|
20
|
+
return value === END ? { done: true, value: undefined } : { value }
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static create<T>(initialValue: T | typeof END, nextFn: AbortableMapper<T, T>): Seq<T> {
|
|
26
|
+
return new Seq(initialValue, nextFn)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static range(minIncl: number, maxExcl: number, step = 1): Seq<number> {
|
|
30
|
+
const max = maxExcl - step
|
|
31
|
+
return new Seq(minIncl, n => (n < max ? n + step : END))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static from<T>(a: Iterable<T>): Seq<T> {
|
|
35
|
+
const it = a[Symbol.iterator]()
|
|
36
|
+
const v = it.next()
|
|
37
|
+
if (v.done) return new Seq<any>(END, () => {})
|
|
38
|
+
|
|
39
|
+
return new Seq(v.value, () => {
|
|
40
|
+
const v = it.next()
|
|
41
|
+
if (v.done) return END
|
|
42
|
+
return v.value
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static empty<T = any>(): Seq<T> {
|
|
47
|
+
return new Seq(END as any, () => {})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private currentValue: T | typeof END
|
|
51
|
+
private sentInitialValue = false
|
|
52
|
+
private i = -1
|
|
53
|
+
|
|
54
|
+
next(): T | typeof END {
|
|
55
|
+
if (this.currentValue === END) return END
|
|
56
|
+
|
|
57
|
+
this.i++
|
|
58
|
+
|
|
59
|
+
let v: T | typeof SKIP | typeof END
|
|
60
|
+
|
|
61
|
+
if (!this.sentInitialValue) {
|
|
62
|
+
this.sentInitialValue = true
|
|
63
|
+
v = this.currentValue
|
|
64
|
+
} else {
|
|
65
|
+
v = this.nextFn(this.currentValue, this.i)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// console.log(`_seq`, v)
|
|
69
|
+
|
|
70
|
+
if (v === SKIP) return this.next()
|
|
71
|
+
|
|
72
|
+
return (this.currentValue = v)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Chainable functions - return another (chained) Sequence
|
|
76
|
+
// map<OUT>(mapper: Mapper<T, OUT | typeof SKIP | typeof END>): Seq<OUT> {
|
|
77
|
+
// if (this.currentValue === END) return this as any
|
|
78
|
+
//
|
|
79
|
+
// // Iterate until first valid value, to have as `initialValue` of the new Sequence
|
|
80
|
+
// let v: OUT | typeof SKIP | typeof END
|
|
81
|
+
//
|
|
82
|
+
// while (true) {
|
|
83
|
+
// v = mapper(this.currentValue, ++this.i)
|
|
84
|
+
// if (v === SKIP) continue
|
|
85
|
+
// if (v === END) return this as any
|
|
86
|
+
// }
|
|
87
|
+
//
|
|
88
|
+
// return new Seq<OUT>(v as OUT, (current, i) => {
|
|
89
|
+
// const v = mapper(current, i)
|
|
90
|
+
//
|
|
91
|
+
// })
|
|
92
|
+
// }
|
|
93
|
+
|
|
94
|
+
// Final functions - return final value, not a chained sequence
|
|
95
|
+
find(predicate: AbortablePredicate<T>): T | undefined {
|
|
96
|
+
do {
|
|
97
|
+
const v = this.next()
|
|
98
|
+
if (v === END) return // not found, end of sequence
|
|
99
|
+
const r = predicate(v, this.i)
|
|
100
|
+
if (r === END) return
|
|
101
|
+
if (r) return v
|
|
102
|
+
// otherwise proceed
|
|
103
|
+
} while (true) // eslint-disable-line no-constant-condition
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
some(predicate: AbortablePredicate<T>): boolean {
|
|
107
|
+
do {
|
|
108
|
+
const v = this.next()
|
|
109
|
+
if (v === END) return false
|
|
110
|
+
const r = predicate(v, this.i)
|
|
111
|
+
if (r === END) return false
|
|
112
|
+
if (r) return true
|
|
113
|
+
} while (true) // eslint-disable-line no-constant-condition
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
every(predicate: AbortablePredicate<T>): boolean {
|
|
117
|
+
do {
|
|
118
|
+
const v = this.next()
|
|
119
|
+
if (v === END) return true
|
|
120
|
+
const r = predicate(v, this.i)
|
|
121
|
+
if (r === END) return true
|
|
122
|
+
if (!r) return false
|
|
123
|
+
} while (true) // eslint-disable-line no-constant-condition
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
toArray(): T[] {
|
|
127
|
+
const a: T[] = []
|
|
128
|
+
|
|
129
|
+
// eslint-disable-next-line no-constant-condition
|
|
130
|
+
while (true) {
|
|
131
|
+
const v = this.next()
|
|
132
|
+
if (v === END) return a
|
|
133
|
+
a.push(v)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Convenience function to create a Sequence.
|
|
140
|
+
*/
|
|
141
|
+
export function _seq<T>(initialValue: T | typeof END, nextFn: AbortableMapper<T, T>): Seq<T> {
|
|
142
|
+
return Seq.create(initialValue, nextFn)
|
|
143
|
+
}
|
package/src/string/json.util.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// const possibleJsonStartTokens = ['{', '[', '"']
|
|
2
|
+
const DETECT_JSON = /^\s*[{["\-\d]/
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Attempts to parse object as JSON.
|
|
3
6
|
* Returns original object if JSON parse failed (silently).
|
|
@@ -6,7 +9,8 @@ export function _jsonParseIfPossible(
|
|
|
6
9
|
obj: any,
|
|
7
10
|
reviver?: (this: any, key: string, value: any) => any,
|
|
8
11
|
): any {
|
|
9
|
-
if
|
|
12
|
+
// Optimization: only try to parse if it looks like JSON: starts with a json possible character
|
|
13
|
+
if (typeof obj === 'string' && obj && DETECT_JSON.test(obj)) {
|
|
10
14
|
try {
|
|
11
15
|
return JSON.parse(obj, reviver)
|
|
12
16
|
} catch {}
|
package/src/typeFest.ts
CHANGED
|
@@ -151,38 +151,6 @@ export type Merge<FirstType, SecondType> = Simplify<Merge_<FirstType, SecondType
|
|
|
151
151
|
*/
|
|
152
152
|
export type Promisable<T> = T | PromiseLike<T>
|
|
153
153
|
|
|
154
|
-
/**
|
|
155
|
-
Returns the type that is wrapped inside a `Promise` type.
|
|
156
|
-
If the type is a nested Promise, it is unwrapped recursively until a non-Promise type is obtained.
|
|
157
|
-
If the type is not a `Promise`, the type itself is returned.
|
|
158
|
-
|
|
159
|
-
@example
|
|
160
|
-
```
|
|
161
|
-
import {PromiseValue} from 'type-fest';
|
|
162
|
-
|
|
163
|
-
type AsyncData = Promise<string>;
|
|
164
|
-
let asyncData: PromiseValue<AsyncData> = Promise.resolve('ABC');
|
|
165
|
-
|
|
166
|
-
type Data = PromiseValue<AsyncData>;
|
|
167
|
-
let data: Data = await asyncData;
|
|
168
|
-
|
|
169
|
-
// Here's an example that shows how this type reacts to non-Promise types.
|
|
170
|
-
type SyncData = PromiseValue<string>;
|
|
171
|
-
let syncData: SyncData = getSyncData();
|
|
172
|
-
|
|
173
|
-
// Here's an example that shows how this type reacts to recursive Promise types.
|
|
174
|
-
type RecursiveAsyncData = Promise<Promise<string> >;
|
|
175
|
-
let recursiveAsyncData: PromiseValue<RecursiveAsyncData> = Promise.resolve(Promise.resolve('ABC'));
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
@category Utilities
|
|
179
|
-
*/
|
|
180
|
-
export type PromiseValue<PromiseType, Otherwise = PromiseType> = PromiseType extends Promise<
|
|
181
|
-
infer Value
|
|
182
|
-
>
|
|
183
|
-
? { 0: PromiseValue<Value>; 1: Value }[PromiseType extends Promise<unknown> ? 0 : 1]
|
|
184
|
-
: Otherwise
|
|
185
|
-
|
|
186
154
|
/**
|
|
187
155
|
Extract the keys from a type where the value type of the key extends the given `Condition`.
|
|
188
156
|
Internally this is used for the `ConditionalPick` and `ConditionalExcept` types.
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Merge } from './typeFest'
|
|
1
|
+
import { Merge, Promisable } from './typeFest'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Map from String to String (or <T>).
|
|
@@ -45,6 +45,16 @@ export interface AnyObjectWithId extends AnyObject, ObjectWithId {}
|
|
|
45
45
|
*/
|
|
46
46
|
export type AnyFunction = (...args: any[]) => any
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Symbol to indicate END of Sequence.
|
|
50
|
+
*/
|
|
51
|
+
export const END = Symbol('END')
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Symbol to indicate SKIP of item (e.g in AbortableMapper)
|
|
55
|
+
*/
|
|
56
|
+
export const SKIP = Symbol('SKIP')
|
|
57
|
+
|
|
48
58
|
/**
|
|
49
59
|
* Function which is called for every item in `input`. Expected to return a `Promise` or value.
|
|
50
60
|
*/
|
|
@@ -62,6 +72,17 @@ export const _noop = (..._args: any[]): undefined => undefined
|
|
|
62
72
|
export type Predicate<T> = (item: T, index: number) => boolean
|
|
63
73
|
export type AsyncPredicate<T> = (item: T, index: number) => boolean | PromiseLike<boolean>
|
|
64
74
|
|
|
75
|
+
export type AbortablePredicate<T> = (item: T, i: number) => boolean | typeof END
|
|
76
|
+
export type AbortableAsyncPredicate<T> = (item: T, i: number) => Promisable<boolean | typeof END>
|
|
77
|
+
export type AbortableMapper<IN = any, OUT = any> = (
|
|
78
|
+
input: IN,
|
|
79
|
+
i: number,
|
|
80
|
+
) => OUT | typeof SKIP | typeof END
|
|
81
|
+
export type AbortableAsyncMapper<IN = any, OUT = any> = (
|
|
82
|
+
input: IN,
|
|
83
|
+
i: number,
|
|
84
|
+
) => Promisable<OUT | typeof SKIP | typeof END>
|
|
85
|
+
|
|
65
86
|
export const _passthroughPredicate: Predicate<any> = () => true
|
|
66
87
|
export const _passNothingPredicate: Predicate<any> = () => false
|
|
67
88
|
|