@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.
Files changed (40) hide show
  1. package/dist/array/array.util.js +11 -11
  2. package/dist/decorators/asyncMemo.decorator.d.ts +22 -20
  3. package/dist/decorators/asyncMemo.decorator.js +81 -58
  4. package/dist/decorators/memo.decorator.d.ts +20 -13
  5. package/dist/decorators/memo.decorator.js +43 -41
  6. package/dist/decorators/memo.util.d.ts +33 -14
  7. package/dist/decorators/memo.util.js +27 -1
  8. package/dist/decorators/memoFn.d.ts +2 -0
  9. package/dist/decorators/memoFn.js +8 -24
  10. package/dist/decorators/memoFnAsync.d.ts +2 -3
  11. package/dist/decorators/memoFnAsync.js +14 -32
  12. package/dist/form.util.js +3 -1
  13. package/dist/http/fetcher.js +2 -2
  14. package/dist/math/math.util.js +2 -2
  15. package/dist/object/object.util.js +6 -3
  16. package/dist/types.d.ts +5 -0
  17. package/dist/types.js +6 -1
  18. package/dist-esm/array/array.util.js +11 -11
  19. package/dist-esm/decorators/asyncMemo.decorator.js +80 -58
  20. package/dist-esm/decorators/memo.decorator.js +41 -40
  21. package/dist-esm/decorators/memo.util.js +26 -1
  22. package/dist-esm/decorators/memoFn.js +8 -24
  23. package/dist-esm/decorators/memoFnAsync.js +14 -32
  24. package/dist-esm/form.util.js +3 -1
  25. package/dist-esm/http/fetcher.js +2 -2
  26. package/dist-esm/math/math.util.js +2 -2
  27. package/dist-esm/object/object.util.js +6 -3
  28. package/dist-esm/types.js +5 -0
  29. package/package.json +1 -1
  30. package/src/array/array.util.ts +13 -11
  31. package/src/decorators/asyncMemo.decorator.ts +106 -80
  32. package/src/decorators/memo.decorator.ts +55 -51
  33. package/src/decorators/memo.util.ts +49 -19
  34. package/src/decorators/memoFn.ts +9 -27
  35. package/src/decorators/memoFnAsync.ts +12 -31
  36. package/src/form.util.ts +3 -1
  37. package/src/http/fetcher.ts +2 -2
  38. package/src/math/math.util.ts +2 -2
  39. package/src/object/object.util.ts +7 -3
  40. 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
- get: (k: KEY) => VALUE | Error | undefined
15
- set: (k: KEY, v: VALUE | Error) => void
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 `.dropCache()` is called.
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
- * `undefined` value returned indicates the ABSENCE of value in the Cache.
29
- * This also means that you CANNOT store `undefined` value in the Cache, as it'll be treated as a MISS.
30
- * You CAN store `null` value instead, it will be treated as a HIT.
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) => Promisable<VALUE | Error | undefined>
33
- set: (k: KEY, v: VALUE | Error) => Promisable<void>
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 `.dropCache()` is called.
41
+ * Clear is only called when `_getAsyncMemo().clear()` is called.
37
42
  * Otherwise the Cache is "persistent" (never cleared).
38
43
  */
39
- clear: () => Promisable<void>
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
- implements MemoCache<KEY, VALUE>, AsyncMemoCache<KEY, VALUE>
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 | Error | undefined {
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 | Error): void {
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
+ }
@@ -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
- value = cache.get(cacheKey)
34
-
35
- if (value instanceof Error) {
36
- throw value
37
- }
38
-
39
- return value
33
+ return cache.get(cacheKey)
40
34
  }
41
35
 
42
- try {
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
- return value
38
+ try {
39
+ cache.set(cacheKey, value)
52
40
  } catch (err) {
53
- if (cacheErrors) {
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
- * Only supports Sync functions.
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 !== undefined) {
38
- if (value instanceof Error) {
39
- throw value
40
- }
41
-
36
+ if (value !== MISS) {
42
37
  return value
43
38
  }
44
39
 
45
- try {
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
- return value
57
- } catch (err) {
58
- if (cacheRejections) {
59
- void (async () => {
60
- try {
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
- throw err
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
- Object.entries(obj).forEach(([k, v]) => fd.append(k, v))
11
+ for (const [k, v] of Object.entries(obj)) {
12
+ fd.append(k, v)
13
+ }
12
14
  return fd
13
15
  }
14
16
 
@@ -82,7 +82,7 @@ export class Fetcher {
82
82
  this.cfg = this.normalizeCfg(cfg)
83
83
 
84
84
  // Dynamically create all helper methods
85
- HTTP_METHODS.forEach(method => {
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
  /**
@@ -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
- pcs.forEach(pc => {
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
- Object.keys(target)
444
- .filter(k => !(k in source))
445
- .forEach(k => delete target[k])
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
  */