@naturalcycles/js-lib 14.215.0 → 14.217.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/array/array.util.js +11 -11
- package/dist/decorators/asyncMemo.decorator.d.ts +22 -20
- package/dist/decorators/asyncMemo.decorator.js +81 -58
- package/dist/decorators/memo.decorator.d.ts +20 -13
- package/dist/decorators/memo.decorator.js +43 -41
- package/dist/decorators/memo.util.d.ts +33 -14
- package/dist/decorators/memo.util.js +27 -1
- package/dist/decorators/memoFn.d.ts +2 -0
- package/dist/decorators/memoFn.js +8 -24
- package/dist/decorators/memoFnAsync.d.ts +2 -3
- package/dist/decorators/memoFnAsync.js +14 -32
- package/dist/form.util.js +3 -1
- package/dist/http/fetcher.js +2 -2
- package/dist/math/math.util.js +2 -2
- package/dist/object/object.util.js +6 -3
- package/dist/types.d.ts +5 -0
- package/dist/types.js +6 -1
- package/dist-esm/array/array.util.js +11 -11
- package/dist-esm/decorators/asyncMemo.decorator.js +80 -58
- package/dist-esm/decorators/memo.decorator.js +41 -40
- package/dist-esm/decorators/memo.util.js +26 -1
- package/dist-esm/decorators/memoFn.js +8 -24
- package/dist-esm/decorators/memoFnAsync.js +14 -32
- package/dist-esm/form.util.js +3 -1
- package/dist-esm/http/fetcher.js +2 -2
- package/dist-esm/math/math.util.js +2 -2
- package/dist-esm/object/object.util.js +6 -3
- package/dist-esm/types.js +5 -0
- package/package.json +1 -1
- package/src/array/array.util.ts +13 -11
- package/src/decorators/asyncMemo.decorator.ts +106 -80
- package/src/decorators/memo.decorator.ts +55 -51
- package/src/decorators/memo.util.ts +49 -19
- package/src/decorators/memoFn.ts +9 -27
- package/src/decorators/memoFnAsync.ts +12 -31
- package/src/form.util.ts +3 -1
- package/src/http/fetcher.ts +2 -2
- package/src/math/math.util.ts +2 -2
- package/src/object/object.util.ts +7 -3
- package/src/types.ts +6 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { _isPrimitive } from '..'
|
|
2
|
-
import type { Promisable } from '../typeFest'
|
|
1
|
+
import { _isPrimitive, MISS, pDelay } from '..'
|
|
3
2
|
|
|
4
3
|
export type MemoSerializer = (args: any[]) => any
|
|
5
4
|
|
|
@@ -11,11 +10,17 @@ export const jsonMemoSerializer: MemoSerializer = args => {
|
|
|
11
10
|
|
|
12
11
|
export interface MemoCache<KEY = any, VALUE = any> {
|
|
13
12
|
has: (k: KEY) => boolean
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
/**
|
|
14
|
+
* `get` return signature doesn't contain `undefined`,
|
|
15
|
+
* because `undefined` is a valid VALUE to store in the Cache.
|
|
16
|
+
* `undefined` does NOT mean cache miss.
|
|
17
|
+
* Cache misses are checked by calling `has` method instead.
|
|
18
|
+
*/
|
|
19
|
+
get: (k: KEY) => VALUE
|
|
20
|
+
set: (k: KEY, v: VALUE) => void
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
|
-
* Clear is only called when
|
|
23
|
+
* Clear is only called when `_getMemoCache().clear()` is called.
|
|
19
24
|
* Otherwise the Cache is "persistent" (never cleared).
|
|
20
25
|
*/
|
|
21
26
|
clear: () => void
|
|
@@ -25,18 +30,18 @@ export interface AsyncMemoCache<KEY = any, VALUE = any> {
|
|
|
25
30
|
// `has` method is removed, because it is assumed that it has a cost and it's best to avoid doing both `has` and then `get`
|
|
26
31
|
// has(k: any): Promise<boolean>
|
|
27
32
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
33
|
+
* MISS symbol indicates the ABSENCE of value in the Cache.
|
|
34
|
+
* You can safely store `undefined` or `null` values in the Cache,
|
|
35
|
+
* they will not be interpreted as a cache miss, because there is a special MISS symbol for that.
|
|
31
36
|
*/
|
|
32
|
-
get: (k: KEY) =>
|
|
33
|
-
set: (k: KEY, v: VALUE
|
|
37
|
+
get: (k: KEY) => Promise<VALUE | typeof MISS>
|
|
38
|
+
set: (k: KEY, v: VALUE) => Promise<void>
|
|
34
39
|
|
|
35
40
|
/**
|
|
36
|
-
* Clear is only called when
|
|
41
|
+
* Clear is only called when `_getAsyncMemo().clear()` is called.
|
|
37
42
|
* Otherwise the Cache is "persistent" (never cleared).
|
|
38
43
|
*/
|
|
39
|
-
clear: () =>
|
|
44
|
+
clear: () => Promise<void>
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
// SingleValueMemoCache and ObjectMemoCache are example-only, not used in production code
|
|
@@ -85,20 +90,18 @@ export class ObjectMemoCache implements MemoCache {
|
|
|
85
90
|
}
|
|
86
91
|
*/
|
|
87
92
|
|
|
88
|
-
export class MapMemoCache<KEY = any, VALUE = any>
|
|
89
|
-
|
|
90
|
-
{
|
|
91
|
-
private m = new Map<KEY, VALUE | Error>()
|
|
93
|
+
export class MapMemoCache<KEY = any, VALUE = any> implements MemoCache<KEY, VALUE> {
|
|
94
|
+
private m = new Map<KEY, VALUE>()
|
|
92
95
|
|
|
93
96
|
has(k: KEY): boolean {
|
|
94
97
|
return this.m.has(k)
|
|
95
98
|
}
|
|
96
99
|
|
|
97
|
-
get(k: KEY): VALUE
|
|
98
|
-
return this.m.get(k)
|
|
100
|
+
get(k: KEY): VALUE {
|
|
101
|
+
return this.m.get(k)!
|
|
99
102
|
}
|
|
100
103
|
|
|
101
|
-
set(k: KEY, v: VALUE
|
|
104
|
+
set(k: KEY, v: VALUE): void {
|
|
102
105
|
this.m.set(k, v)
|
|
103
106
|
}
|
|
104
107
|
|
|
@@ -106,3 +109,30 @@ export class MapMemoCache<KEY = any, VALUE = any>
|
|
|
106
109
|
this.m.clear()
|
|
107
110
|
}
|
|
108
111
|
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Implementation of AsyncMemoCache backed by a synchronous Map.
|
|
115
|
+
* Doesn't have a practical use except testing,
|
|
116
|
+
* because the point of AsyncMemoCache is to have an **async** backed cache.
|
|
117
|
+
*/
|
|
118
|
+
export class MapAsyncMemoCache<KEY = any, VALUE = any> implements AsyncMemoCache<KEY, VALUE> {
|
|
119
|
+
constructor(private delay = 0) {}
|
|
120
|
+
|
|
121
|
+
private m = new Map<KEY, VALUE>()
|
|
122
|
+
|
|
123
|
+
async get(k: KEY): Promise<VALUE | typeof MISS> {
|
|
124
|
+
await pDelay(this.delay)
|
|
125
|
+
if (!this.m.has(k)) return MISS
|
|
126
|
+
return this.m.get(k)!
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async set(k: KEY, v: VALUE): Promise<void> {
|
|
130
|
+
await pDelay(this.delay)
|
|
131
|
+
this.m.set(k, v)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async clear(): Promise<void> {
|
|
135
|
+
await pDelay(this.delay)
|
|
136
|
+
this.m.clear()
|
|
137
|
+
}
|
|
138
|
+
}
|
package/src/decorators/memoFn.ts
CHANGED
|
@@ -10,6 +10,8 @@ export interface MemoizedFunction {
|
|
|
10
10
|
* Only supports Sync functions.
|
|
11
11
|
* To support Async functions - use _memoFnAsync.
|
|
12
12
|
* Technically, you can use it with Async functions, but it'll return the Promise without awaiting it.
|
|
13
|
+
*
|
|
14
|
+
* @experimental
|
|
13
15
|
*/
|
|
14
16
|
export function _memoFn<T extends (...args: any[]) => any>(
|
|
15
17
|
fn: T,
|
|
@@ -17,7 +19,6 @@ export function _memoFn<T extends (...args: any[]) => any>(
|
|
|
17
19
|
): T & MemoizedFunction {
|
|
18
20
|
const {
|
|
19
21
|
logger = console,
|
|
20
|
-
cacheErrors = true,
|
|
21
22
|
cacheFactory = () => new MapMemoCache(),
|
|
22
23
|
cacheKeyFn = jsonMemoSerializer,
|
|
23
24
|
} = opt
|
|
@@ -27,39 +28,20 @@ export function _memoFn<T extends (...args: any[]) => any>(
|
|
|
27
28
|
const memoizedFn = function (this: any, ...args: any[]): T {
|
|
28
29
|
const ctx = this
|
|
29
30
|
const cacheKey = cacheKeyFn(args)
|
|
30
|
-
let value: any
|
|
31
31
|
|
|
32
32
|
if (cache.has(cacheKey)) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (value instanceof Error) {
|
|
36
|
-
throw value
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return value
|
|
33
|
+
return cache.get(cacheKey)
|
|
40
34
|
}
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
value = fn.apply(ctx, args)
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
cache.set(cacheKey, value)
|
|
47
|
-
} catch (err) {
|
|
48
|
-
logger.error(err)
|
|
49
|
-
}
|
|
36
|
+
const value = fn.apply(ctx, args)
|
|
50
37
|
|
|
51
|
-
|
|
38
|
+
try {
|
|
39
|
+
cache.set(cacheKey, value)
|
|
52
40
|
} catch (err) {
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
cache.set(cacheKey, err)
|
|
56
|
-
} catch (err) {
|
|
57
|
-
logger.error(err)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
throw err
|
|
41
|
+
logger.error(err)
|
|
62
42
|
}
|
|
43
|
+
|
|
44
|
+
return value
|
|
63
45
|
}
|
|
64
46
|
|
|
65
47
|
Object.assign(memoizedFn, { cache })
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MISS } from '../types'
|
|
1
2
|
import type { AsyncMemoOptions } from './asyncMemo.decorator'
|
|
2
3
|
import type { AsyncMemoCache } from './memo.util'
|
|
3
4
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util'
|
|
@@ -7,16 +8,14 @@ export interface MemoizedAsyncFunction {
|
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
* To support Async functions - use _memoFnAsync
|
|
11
|
+
* @experimental
|
|
12
12
|
*/
|
|
13
13
|
export function _memoFnAsync<T extends (...args: any[]) => Promise<any>>(
|
|
14
14
|
fn: T,
|
|
15
|
-
opt: AsyncMemoOptions
|
|
15
|
+
opt: AsyncMemoOptions,
|
|
16
16
|
): T & MemoizedAsyncFunction {
|
|
17
17
|
const {
|
|
18
18
|
logger = console,
|
|
19
|
-
cacheRejections = true,
|
|
20
19
|
cacheFactory = () => new MapMemoCache(),
|
|
21
20
|
cacheKeyFn = jsonMemoSerializer,
|
|
22
21
|
} = opt
|
|
@@ -34,39 +33,21 @@ export function _memoFnAsync<T extends (...args: any[]) => Promise<any>>(
|
|
|
34
33
|
logger.error(err)
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
if (value !==
|
|
38
|
-
if (value instanceof Error) {
|
|
39
|
-
throw value
|
|
40
|
-
}
|
|
41
|
-
|
|
36
|
+
if (value !== MISS) {
|
|
42
37
|
return value
|
|
43
38
|
}
|
|
44
39
|
|
|
45
|
-
|
|
46
|
-
value = await fn.apply(ctx, args)
|
|
47
|
-
|
|
48
|
-
void (async () => {
|
|
49
|
-
try {
|
|
50
|
-
await cache.set(cacheKey, value)
|
|
51
|
-
} catch (err) {
|
|
52
|
-
logger.error(err)
|
|
53
|
-
}
|
|
54
|
-
})()
|
|
40
|
+
value = await fn.apply(ctx, args)
|
|
55
41
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
await cache.set(cacheKey, err)
|
|
62
|
-
} catch (err) {
|
|
63
|
-
logger.error(err)
|
|
64
|
-
}
|
|
65
|
-
})()
|
|
42
|
+
void (async () => {
|
|
43
|
+
try {
|
|
44
|
+
await cache.set(cacheKey, value)
|
|
45
|
+
} catch (err) {
|
|
46
|
+
logger.error(err)
|
|
66
47
|
}
|
|
48
|
+
})()
|
|
67
49
|
|
|
68
|
-
|
|
69
|
-
}
|
|
50
|
+
return value
|
|
70
51
|
}
|
|
71
52
|
|
|
72
53
|
Object.assign(memoizedFn, { cache })
|
package/src/form.util.ts
CHANGED
|
@@ -8,7 +8,9 @@ import type { AnyObject } from './types'
|
|
|
8
8
|
*/
|
|
9
9
|
export function objectToFormData(obj: AnyObject = {}): FormData {
|
|
10
10
|
const fd = new FormData()
|
|
11
|
-
|
|
11
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
12
|
+
fd.append(k, v)
|
|
13
|
+
}
|
|
12
14
|
return fd
|
|
13
15
|
}
|
|
14
16
|
|
package/src/http/fetcher.ts
CHANGED
|
@@ -82,7 +82,7 @@ export class Fetcher {
|
|
|
82
82
|
this.cfg = this.normalizeCfg(cfg)
|
|
83
83
|
|
|
84
84
|
// Dynamically create all helper methods
|
|
85
|
-
|
|
85
|
+
for (const method of HTTP_METHODS) {
|
|
86
86
|
const m = method.toLowerCase()
|
|
87
87
|
|
|
88
88
|
// responseType=void
|
|
@@ -114,7 +114,7 @@ export class Fetcher {
|
|
|
114
114
|
...opt,
|
|
115
115
|
})
|
|
116
116
|
}
|
|
117
|
-
}
|
|
117
|
+
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/**
|
package/src/math/math.util.ts
CHANGED
|
@@ -62,7 +62,7 @@ export function _percentiles(values: number[], pcs: number[]): Record<number, nu
|
|
|
62
62
|
|
|
63
63
|
const sorted = _sortNumbers(values)
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
for (const pc of pcs) {
|
|
66
66
|
// Floating pos in the range of [0; length - 1]
|
|
67
67
|
const pos = ((values.length - 1) * pc) / 100
|
|
68
68
|
const dec = pos % 1
|
|
@@ -70,7 +70,7 @@ export function _percentiles(values: number[], pcs: number[]): Record<number, nu
|
|
|
70
70
|
const ceilPos = Math.ceil(pos)
|
|
71
71
|
|
|
72
72
|
r[pc] = _averageWeighted([sorted[floorPos]!, sorted[ceilPos]!], [1 - dec, dec])
|
|
73
|
-
}
|
|
73
|
+
}
|
|
74
74
|
|
|
75
75
|
return r
|
|
76
76
|
}
|
|
@@ -440,7 +440,11 @@ export function _deepFreeze(o: any): void {
|
|
|
440
440
|
*/
|
|
441
441
|
export function _objectAssignExact<T extends AnyObject>(target: T, source: T): void {
|
|
442
442
|
Object.assign(target, source)
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
443
|
+
|
|
444
|
+
for (const k of Object.keys(target)) {
|
|
445
|
+
if (!(k in source)) {
|
|
446
|
+
// consider setting it to undefined maybe?
|
|
447
|
+
delete target[k]
|
|
448
|
+
}
|
|
449
|
+
}
|
|
446
450
|
}
|
package/src/types.ts
CHANGED
|
@@ -101,6 +101,12 @@ export const END = Symbol('END')
|
|
|
101
101
|
*/
|
|
102
102
|
export const SKIP = Symbol('SKIP')
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Symbol to indicate cache miss.
|
|
106
|
+
* To distinguish from cache returning `undefined` or `null`.
|
|
107
|
+
*/
|
|
108
|
+
export const MISS = Symbol('MISS')
|
|
109
|
+
|
|
104
110
|
/**
|
|
105
111
|
* Function which is called for every item in `input`. Expected to return a `Promise` or value.
|
|
106
112
|
*/
|