@naturalcycles/js-lib 14.84.2 → 14.87.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/decorators/asyncMemo.decorator.d.ts +35 -9
- package/dist/decorators/asyncMemo.decorator.js +30 -22
- package/dist/decorators/createPromiseDecorator.d.ts +2 -2
- package/dist/decorators/createPromiseDecorator.js +6 -12
- package/dist/decorators/decorator.util.d.ts +1 -1
- package/dist/decorators/decorator.util.js +2 -2
- package/dist/decorators/logMethod.decorator.d.ts +6 -4
- package/dist/decorators/logMethod.decorator.js +3 -3
- package/dist/decorators/memo.decorator.d.ts +29 -25
- package/dist/decorators/memo.decorator.js +36 -42
- package/dist/decorators/memo.util.d.ts +6 -6
- package/dist/decorators/memoFn.d.ts +5 -0
- package/dist/decorators/memoFn.js +32 -37
- package/dist/decorators/memoFnAsync.d.ts +10 -0
- package/dist/decorators/memoFnAsync.js +69 -0
- package/dist/decorators/memoSimple.decorator.d.ts +1 -1
- package/dist/decorators/memoSimple.decorator.js +3 -3
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -0
- package/dist/types.d.ts +4 -0
- package/dist-esm/decorators/asyncMemo.decorator.js +31 -35
- package/dist-esm/decorators/createPromiseDecorator.js +8 -15
- package/dist-esm/decorators/decorator.util.js +2 -2
- package/dist-esm/decorators/logMethod.decorator.js +3 -3
- package/dist-esm/decorators/memo.decorator.js +36 -42
- package/dist-esm/decorators/memoFn.js +32 -37
- package/dist-esm/decorators/memoFnAsync.js +65 -0
- package/dist-esm/decorators/memoSimple.decorator.js +3 -3
- package/dist-esm/index.js +1 -0
- package/package.json +1 -1
- package/src/decorators/asyncMemo.decorator.ts +80 -49
- package/src/decorators/createPromiseDecorator.ts +64 -70
- package/src/decorators/decorator.util.ts +2 -2
- package/src/decorators/logMethod.decorator.ts +16 -7
- package/src/decorators/memo.decorator.ts +61 -90
- package/src/decorators/memo.util.ts +7 -7
- package/src/decorators/memoFn.ts +33 -51
- package/src/decorators/memoFnAsync.ts +91 -0
- package/src/decorators/memoSimple.decorator.ts +4 -4
- package/src/index.ts +3 -0
- package/src/types.ts +5 -0
|
@@ -34,7 +34,7 @@ export const memoSimple = (opt = {}) => (target, key, descriptor) => {
|
|
|
34
34
|
}
|
|
35
35
|
*/
|
|
36
36
|
const cache = new MapMemoCache();
|
|
37
|
-
const { logHit, logMiss,
|
|
37
|
+
const { logHit, logMiss, logArgs = true, logger = console } = opt;
|
|
38
38
|
const keyStr = String(key);
|
|
39
39
|
const methodSignature = _getTargetMethodSignature(target, keyStr);
|
|
40
40
|
descriptor.value = function (...args) {
|
|
@@ -42,14 +42,14 @@ export const memoSimple = (opt = {}) => (target, key, descriptor) => {
|
|
|
42
42
|
const cacheKey = jsonMemoSerializer(args);
|
|
43
43
|
if (cache.has(cacheKey)) {
|
|
44
44
|
if (logHit) {
|
|
45
|
-
logger.log(`${methodSignature}(${_getArgsSignature(args,
|
|
45
|
+
logger.log(`${methodSignature}(${_getArgsSignature(args, logArgs)}) @memo hit`);
|
|
46
46
|
}
|
|
47
47
|
return cache.get(cacheKey);
|
|
48
48
|
}
|
|
49
49
|
const d = Date.now();
|
|
50
50
|
const res = originalFn.apply(ctx, args);
|
|
51
51
|
if (logMiss) {
|
|
52
|
-
logger.log(`${methodSignature}(${_getArgsSignature(args,
|
|
52
|
+
logger.log(`${methodSignature}(${_getArgsSignature(args, logArgs)}) @memo miss (${Date.now() - d} ms)`);
|
|
53
53
|
}
|
|
54
54
|
cache.set(cacheKey, res);
|
|
55
55
|
return res;
|
package/dist-esm/index.js
CHANGED
|
@@ -10,6 +10,7 @@ export * from './decorators/logMethod.decorator';
|
|
|
10
10
|
export * from './decorators/memo.decorator';
|
|
11
11
|
export * from './decorators/asyncMemo.decorator';
|
|
12
12
|
export * from './decorators/memoFn';
|
|
13
|
+
export * from './decorators/memoFnAsync';
|
|
13
14
|
export * from './decorators/retry.decorator';
|
|
14
15
|
export * from './decorators/timeout.decorator';
|
|
15
16
|
export * from './error/app.error';
|
package/package.json
CHANGED
|
@@ -1,25 +1,54 @@
|
|
|
1
|
+
import { CommonLogger } from '../log/commonLogger'
|
|
1
2
|
import { _since } from '../time/time.util'
|
|
2
|
-
import { Merge } from '../typeFest'
|
|
3
3
|
import { AnyObject } from '../types'
|
|
4
4
|
import { _getArgsSignature, _getMethodSignature, _getTargetMethodSignature } from './decorator.util'
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
5
|
+
import { AsyncMemoCache, jsonMemoSerializer, MapMemoCache } from './memo.util'
|
|
6
|
+
|
|
7
|
+
export interface AsyncMemoOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Provide a custom implementation of MemoCache.
|
|
10
|
+
* Function that creates an instance of `MemoCache`.
|
|
11
|
+
* e.g LRUMemoCache from `@naturalcycles/nodejs-lib`.
|
|
12
|
+
*/
|
|
13
|
+
cacheFactory?: () => AsyncMemoCache
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Provide a custom implementation of CacheKey function.
|
|
17
|
+
*/
|
|
18
|
+
cacheKeyFn?: (args: any[]) => any
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Default true.
|
|
22
|
+
*
|
|
23
|
+
* Set to `false` to skip caching rejected promises (errors).
|
|
24
|
+
*
|
|
25
|
+
* True will ensure "max 1 execution", but will "remember" rejection.
|
|
26
|
+
* False will allow >1 execution in case of errors.
|
|
27
|
+
*/
|
|
28
|
+
cacheRejections?: boolean
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Default to false
|
|
32
|
+
*/
|
|
33
|
+
logHit?: boolean
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Default to false
|
|
37
|
+
*/
|
|
38
|
+
logMiss?: boolean
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Set to `false` to skip logging method arguments.
|
|
42
|
+
*
|
|
43
|
+
* Defaults to true.
|
|
44
|
+
*/
|
|
45
|
+
logArgs?: boolean
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Default to `console`
|
|
49
|
+
*/
|
|
50
|
+
logger?: CommonLogger
|
|
51
|
+
}
|
|
23
52
|
|
|
24
53
|
/**
|
|
25
54
|
* Like @_Memo, but allowing async MemoCache implementation.
|
|
@@ -29,7 +58,7 @@ export type AsyncMemoOptions = Merge<
|
|
|
29
58
|
*/
|
|
30
59
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
31
60
|
export const _AsyncMemo =
|
|
32
|
-
(opt: AsyncMemoOptions): MethodDecorator =>
|
|
61
|
+
(opt: AsyncMemoOptions = {}): MethodDecorator =>
|
|
33
62
|
(target, key, descriptor) => {
|
|
34
63
|
if (typeof descriptor.value !== 'function') {
|
|
35
64
|
throw new TypeError('Memoization can be applied only to methods')
|
|
@@ -38,17 +67,16 @@ export const _AsyncMemo =
|
|
|
38
67
|
const originalFn = descriptor.value
|
|
39
68
|
|
|
40
69
|
// Map from "instance" of the Class where @_AsyncMemo is applied to AsyncMemoCache instance.
|
|
41
|
-
const cache = new Map<AnyObject, AsyncMemoCache
|
|
70
|
+
const cache = new Map<AnyObject, AsyncMemoCache>()
|
|
42
71
|
|
|
43
72
|
const {
|
|
44
73
|
logHit = false,
|
|
45
74
|
logMiss = false,
|
|
46
|
-
|
|
75
|
+
logArgs = true,
|
|
47
76
|
logger = console,
|
|
48
|
-
cacheFactory,
|
|
77
|
+
cacheFactory = () => new MapMemoCache(),
|
|
49
78
|
cacheKeyFn = jsonMemoSerializer,
|
|
50
|
-
|
|
51
|
-
noCacheResolved = false,
|
|
79
|
+
cacheRejections = true,
|
|
52
80
|
} = opt
|
|
53
81
|
|
|
54
82
|
const keyStr = String(key)
|
|
@@ -68,13 +96,7 @@ export const _AsyncMemo =
|
|
|
68
96
|
let value: any
|
|
69
97
|
|
|
70
98
|
try {
|
|
71
|
-
|
|
72
|
-
value = await cacheLayer.get(cacheKey)
|
|
73
|
-
if (value !== undefined) {
|
|
74
|
-
// it's a hit!
|
|
75
|
-
break
|
|
76
|
-
}
|
|
77
|
-
}
|
|
99
|
+
value = await cache.get(ctx)!.get(cacheKey)
|
|
78
100
|
} catch (err) {
|
|
79
101
|
// log error, but don't throw, treat it as a "miss"
|
|
80
102
|
logger.error(err)
|
|
@@ -86,12 +108,16 @@ export const _AsyncMemo =
|
|
|
86
108
|
logger.log(
|
|
87
109
|
`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(
|
|
88
110
|
args,
|
|
89
|
-
|
|
111
|
+
logArgs,
|
|
90
112
|
)}) @_AsyncMemo hit`,
|
|
91
113
|
)
|
|
92
114
|
}
|
|
93
115
|
|
|
94
|
-
|
|
116
|
+
if (value instanceof Error) {
|
|
117
|
+
throw value
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return value
|
|
95
121
|
}
|
|
96
122
|
|
|
97
123
|
// Here we know it's a MISS, let's execute the real method
|
|
@@ -100,25 +126,30 @@ export const _AsyncMemo =
|
|
|
100
126
|
try {
|
|
101
127
|
value = await originalFn.apply(ctx, args)
|
|
102
128
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
129
|
+
// Save the value in the Cache, without awaiting it
|
|
130
|
+
// This is to support both sync and async functions
|
|
131
|
+
void (async () => {
|
|
132
|
+
try {
|
|
133
|
+
await cache.get(ctx)!.set(cacheKey, value)
|
|
134
|
+
} catch (err) {
|
|
135
|
+
// log and ignore the error
|
|
136
|
+
logger.error(err)
|
|
137
|
+
}
|
|
138
|
+
})()
|
|
111
139
|
|
|
112
140
|
return value
|
|
113
141
|
} catch (err) {
|
|
114
|
-
if (
|
|
142
|
+
if (cacheRejections) {
|
|
115
143
|
// We put it to cache as raw Error, not Promise.reject(err)
|
|
116
|
-
|
|
117
|
-
|
|
144
|
+
// This is to support both sync and async functions
|
|
145
|
+
void (async () => {
|
|
146
|
+
try {
|
|
147
|
+
await cache.get(ctx)!.set(cacheKey, err)
|
|
148
|
+
} catch (err) {
|
|
118
149
|
// log and ignore the error
|
|
119
150
|
logger.error(err)
|
|
120
|
-
}
|
|
121
|
-
)
|
|
151
|
+
}
|
|
152
|
+
})()
|
|
122
153
|
}
|
|
123
154
|
|
|
124
155
|
throw err
|
|
@@ -127,7 +158,7 @@ export const _AsyncMemo =
|
|
|
127
158
|
logger.log(
|
|
128
159
|
`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(
|
|
129
160
|
args,
|
|
130
|
-
|
|
161
|
+
logArgs,
|
|
131
162
|
)}) @_AsyncMemo miss (${_since(started)})`,
|
|
132
163
|
)
|
|
133
164
|
}
|
|
@@ -136,7 +167,7 @@ export const _AsyncMemo =
|
|
|
136
167
|
;(descriptor.value as any).dropCache = async () => {
|
|
137
168
|
logger.log(`${methodSignature} @_AsyncMemo.dropCache()`)
|
|
138
169
|
try {
|
|
139
|
-
await Promise.all([...cache.values()].
|
|
170
|
+
await Promise.all([...cache.values()].map(c => c.clear()))
|
|
140
171
|
cache.clear()
|
|
141
172
|
} catch (err) {
|
|
142
173
|
logger.error(err)
|
|
@@ -5,9 +5,9 @@ export interface PromiseDecoratorCfg<RES = any, PARAMS = any> {
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Called BEFORE the original function.
|
|
8
|
-
*
|
|
8
|
+
* If Promise is returned - it will be awaited.
|
|
9
9
|
*/
|
|
10
|
-
beforeFn?: (r: PromiseDecoratorResp<PARAMS>) => void
|
|
10
|
+
beforeFn?: (r: PromiseDecoratorResp<PARAMS>) => void | Promise<void>
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Called just AFTER the original function.
|
|
@@ -73,77 +73,71 @@ export function _createPromiseDecorator<RES = any, PARAMS = any>(
|
|
|
73
73
|
// console.log(`@${cfg.decoratorName} called inside function`)
|
|
74
74
|
const started = Date.now()
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
decoratorName,
|
|
88
|
-
started,
|
|
89
|
-
})
|
|
90
|
-
}
|
|
76
|
+
try {
|
|
77
|
+
// Before function
|
|
78
|
+
// console.log(`@${cfg.decoratorName} Before`)
|
|
79
|
+
if (cfg.beforeFn) {
|
|
80
|
+
await cfg.beforeFn({
|
|
81
|
+
decoratorParams,
|
|
82
|
+
args,
|
|
83
|
+
key,
|
|
84
|
+
target,
|
|
85
|
+
decoratorName,
|
|
86
|
+
started,
|
|
91
87
|
})
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
cfg.finallyFn?.(resp)
|
|
113
|
-
|
|
114
|
-
return res
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Original function
|
|
91
|
+
let res = await originalMethod.apply(this, args)
|
|
92
|
+
|
|
93
|
+
// console.log(`${cfg.decoratorName} After`)
|
|
94
|
+
const resp: PromiseDecoratorResp<PARAMS> = {
|
|
95
|
+
decoratorParams,
|
|
96
|
+
args,
|
|
97
|
+
key,
|
|
98
|
+
target,
|
|
99
|
+
decoratorName,
|
|
100
|
+
started,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (cfg.thenFn) {
|
|
104
|
+
res = cfg.thenFn({
|
|
105
|
+
...resp,
|
|
106
|
+
res,
|
|
115
107
|
})
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (!handled) {
|
|
141
|
-
throw err // rethrow
|
|
142
|
-
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
cfg.finallyFn?.(resp)
|
|
111
|
+
|
|
112
|
+
return res
|
|
113
|
+
} catch (err) {
|
|
114
|
+
console.error(`@${decoratorName} ${methodSignature} catch:`, err)
|
|
115
|
+
|
|
116
|
+
const resp: PromiseDecoratorResp<PARAMS> = {
|
|
117
|
+
decoratorParams,
|
|
118
|
+
args,
|
|
119
|
+
key,
|
|
120
|
+
target,
|
|
121
|
+
decoratorName,
|
|
122
|
+
started,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let handled = false
|
|
126
|
+
|
|
127
|
+
if (cfg.catchFn) {
|
|
128
|
+
cfg.catchFn({
|
|
129
|
+
...resp,
|
|
130
|
+
err,
|
|
143
131
|
})
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
132
|
+
handled = true
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
cfg.finallyFn?.(resp)
|
|
136
|
+
|
|
137
|
+
if (!handled) {
|
|
138
|
+
throw err // rethrow
|
|
139
|
+
}
|
|
140
|
+
}
|
|
147
141
|
}
|
|
148
142
|
|
|
149
143
|
return pd
|
|
@@ -23,8 +23,8 @@ export function _getTargetMethodSignature(target: AnyObject, keyStr: string): st
|
|
|
23
23
|
* returns:
|
|
24
24
|
* a, b, c
|
|
25
25
|
*/
|
|
26
|
-
export function _getArgsSignature(args: any[] = [],
|
|
27
|
-
if (
|
|
26
|
+
export function _getArgsSignature(args: any[] = [], logArgs = true): string {
|
|
27
|
+
if (!logArgs) return ''
|
|
28
28
|
|
|
29
29
|
return args
|
|
30
30
|
.map(arg => {
|
|
@@ -16,14 +16,16 @@ export interface LogMethodOptions {
|
|
|
16
16
|
avg?: number
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Defaults to true.
|
|
20
|
+
* Set to false to skip logging method arguments
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
|
+
logArgs?: boolean
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
+
* Defaults to true.
|
|
26
|
+
* Set to false to skip logging result length when result is an array.
|
|
25
27
|
*/
|
|
26
|
-
|
|
28
|
+
logResultLength?: boolean
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Also log on method start.
|
|
@@ -74,12 +76,19 @@ export function _LogMethod(opt: LogMethodOptions = {}): MethodDecorator {
|
|
|
74
76
|
const originalFn = descriptor.value
|
|
75
77
|
const keyStr = String(key)
|
|
76
78
|
|
|
77
|
-
const {
|
|
79
|
+
const {
|
|
80
|
+
avg,
|
|
81
|
+
logArgs = true,
|
|
82
|
+
logStart,
|
|
83
|
+
logResult,
|
|
84
|
+
logResultLength = true,
|
|
85
|
+
logger = console,
|
|
86
|
+
} = opt
|
|
78
87
|
let { logResultFn } = opt
|
|
79
88
|
if (!logResultFn) {
|
|
80
89
|
if (logResult) {
|
|
81
90
|
logResultFn = r => ['result:', _stringifyAny(r)]
|
|
82
|
-
} else if (
|
|
91
|
+
} else if (logResultLength) {
|
|
83
92
|
logResultFn = r => (Array.isArray(r) ? [`result: ${r.length} items`] : [])
|
|
84
93
|
}
|
|
85
94
|
}
|
|
@@ -94,7 +103,7 @@ export function _LogMethod(opt: LogMethodOptions = {}): MethodDecorator {
|
|
|
94
103
|
// e.g `NameOfYourClass.methodName`
|
|
95
104
|
// or `NameOfYourClass(instanceId).methodName`
|
|
96
105
|
const methodSignature = _getMethodSignature(ctx, keyStr)
|
|
97
|
-
const argsStr = _getArgsSignature(args,
|
|
106
|
+
const argsStr = _getArgsSignature(args, logArgs)
|
|
98
107
|
const callSignature = `${methodSignature}(${argsStr}) #${++count}`
|
|
99
108
|
if (logStart) logger.log(`>> ${callSignature}`)
|
|
100
109
|
|