@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
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._memoFnAsync = void 0;
|
|
4
|
+
const time_util_1 = require("../time/time.util");
|
|
5
|
+
const decorator_util_1 = require("./decorator.util");
|
|
6
|
+
const memo_util_1 = require("./memo.util");
|
|
7
|
+
/**
|
|
8
|
+
* Only supports Sync functions.
|
|
9
|
+
* To support Async functions - use _memoFnAsync
|
|
10
|
+
*/
|
|
11
|
+
function _memoFnAsync(fn, opt = {}) {
|
|
12
|
+
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheRejections = true, cacheFactory = () => new memo_util_1.MapMemoCache(), cacheKeyFn = memo_util_1.jsonMemoSerializer, } = opt;
|
|
13
|
+
const cache = cacheFactory();
|
|
14
|
+
const fnName = fn.name;
|
|
15
|
+
const memoizedFn = async function (...args) {
|
|
16
|
+
const ctx = this;
|
|
17
|
+
const cacheKey = cacheKeyFn(args);
|
|
18
|
+
let value;
|
|
19
|
+
try {
|
|
20
|
+
value = await cache.get(cacheKey);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
logger.error(err);
|
|
24
|
+
}
|
|
25
|
+
if (value !== undefined) {
|
|
26
|
+
if (logHit) {
|
|
27
|
+
logger.log(`${fnName}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) memoFnAsync hit`);
|
|
28
|
+
}
|
|
29
|
+
if (value instanceof Error) {
|
|
30
|
+
throw value;
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
const started = Date.now();
|
|
35
|
+
try {
|
|
36
|
+
value = await fn.apply(ctx, args);
|
|
37
|
+
void (async () => {
|
|
38
|
+
try {
|
|
39
|
+
await cache.set(cacheKey, value);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
logger.error(err);
|
|
43
|
+
}
|
|
44
|
+
})();
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
if (cacheRejections) {
|
|
49
|
+
void (async () => {
|
|
50
|
+
try {
|
|
51
|
+
await cache.set(cacheKey, err);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
logger.error(err);
|
|
55
|
+
}
|
|
56
|
+
})();
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
if (logMiss) {
|
|
62
|
+
logger.log(`${fnName}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) memoFnAsync miss (${(0, time_util_1._since)(started)})`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
Object.assign(memoizedFn, { cache });
|
|
67
|
+
return memoizedFn;
|
|
68
|
+
}
|
|
69
|
+
exports._memoFnAsync = _memoFnAsync;
|
|
@@ -37,7 +37,7 @@ const memoSimple = (opt = {}) => (target, key, descriptor) => {
|
|
|
37
37
|
}
|
|
38
38
|
*/
|
|
39
39
|
const cache = new memo_util_1.MapMemoCache();
|
|
40
|
-
const { logHit, logMiss,
|
|
40
|
+
const { logHit, logMiss, logArgs = true, logger = console } = opt;
|
|
41
41
|
const keyStr = String(key);
|
|
42
42
|
const methodSignature = (0, decorator_util_1._getTargetMethodSignature)(target, keyStr);
|
|
43
43
|
descriptor.value = function (...args) {
|
|
@@ -45,14 +45,14 @@ const memoSimple = (opt = {}) => (target, key, descriptor) => {
|
|
|
45
45
|
const cacheKey = (0, memo_util_1.jsonMemoSerializer)(args);
|
|
46
46
|
if (cache.has(cacheKey)) {
|
|
47
47
|
if (logHit) {
|
|
48
|
-
logger.log(`${methodSignature}(${(0, decorator_util_1._getArgsSignature)(args,
|
|
48
|
+
logger.log(`${methodSignature}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) @memo hit`);
|
|
49
49
|
}
|
|
50
50
|
return cache.get(cacheKey);
|
|
51
51
|
}
|
|
52
52
|
const d = Date.now();
|
|
53
53
|
const res = originalFn.apply(ctx, args);
|
|
54
54
|
if (logMiss) {
|
|
55
|
-
logger.log(`${methodSignature}(${(0, decorator_util_1._getArgsSignature)(args,
|
|
55
|
+
logger.log(`${methodSignature}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) @memo miss (${Date.now() - d} ms)`);
|
|
56
56
|
}
|
|
57
57
|
cache.set(cacheKey, res);
|
|
58
58
|
return res;
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from './decorators/memo.decorator';
|
|
|
11
11
|
export * from './decorators/asyncMemo.decorator';
|
|
12
12
|
import { MemoCache, AsyncMemoCache } from './decorators/memo.util';
|
|
13
13
|
export * from './decorators/memoFn';
|
|
14
|
+
export * from './decorators/memoFnAsync';
|
|
14
15
|
export * from './decorators/retry.decorator';
|
|
15
16
|
export * from './decorators/timeout.decorator';
|
|
16
17
|
export * from './error/app.error';
|
|
@@ -51,7 +52,7 @@ export * from './string/string.util';
|
|
|
51
52
|
import { JsonStringifyFunction, StringifyAnyOptions, _stringifyAny } from './string/stringifyAny';
|
|
52
53
|
export * from './time/time.util';
|
|
53
54
|
import { Class, ConditionalExcept, ConditionalPick, Merge, Promisable, ReadonlyDeep, Simplify } from './typeFest';
|
|
54
|
-
import { AsyncMapper, AsyncPredicate, BaseDBEntity, CreatedUpdated, CreatedUpdatedId, ObjectWithId, AnyObjectWithId, Saved, Unsaved, BatchResult, InstanceId, IsoDate, IsoDateTime, KeyValueTuple, Mapper, ObjectMapper, ObjectPredicate, Predicate, PromiseMap, AnyObject, AnyFunction, Reviver, SavedDBEntity, StringMap, UnixTimestamp, ValueOf, ValuesOf, AbortableMapper, AbortableAsyncPredicate, AbortableAsyncMapper, AbortablePredicate, END, SKIP, _noop, _objectKeys, _passNothingPredicate, _passthroughMapper, _passthroughPredicate, _passUndefinedMapper, _stringMapEntries, _stringMapValues } from './types';
|
|
55
|
+
import { AsyncMapper, AsyncPredicate, BaseDBEntity, CreatedUpdated, CreatedUpdatedId, ObjectWithId, AnyObjectWithId, Saved, Unsaved, BatchResult, InstanceId, IsoDate, IsoDateTime, KeyValueTuple, Mapper, ObjectMapper, ObjectPredicate, Predicate, PromiseMap, AnyObject, AnyFunction, Reviver, SavedDBEntity, StringMap, UnixTimestamp, Integer, ValueOf, ValuesOf, AbortableMapper, AbortableAsyncPredicate, AbortableAsyncMapper, AbortablePredicate, END, SKIP, _noop, _objectKeys, _passNothingPredicate, _passthroughMapper, _passthroughPredicate, _passUndefinedMapper, _stringMapEntries, _stringMapValues } from './types';
|
|
55
56
|
export * from './unit/size.util';
|
|
56
57
|
import { is } from './vendor/is';
|
|
57
58
|
import { CommonLogLevel, CommonLogFunction, CommonLogger, commonLoggerMinLevel, commonLoggerNoop, commonLogLevelNumber, commonLoggerPipe, commonLoggerPrefix, CommonLogWithLevelFunction, commonLoggerCreate } from './log/commonLogger';
|
|
@@ -59,5 +60,5 @@ export * from './string/safeJsonStringify';
|
|
|
59
60
|
import { PQueue, PQueueCfg } from './promise/pQueue';
|
|
60
61
|
export * from './seq/seq';
|
|
61
62
|
export * from './math/stack.util';
|
|
62
|
-
export type { AbortableMapper, AbortablePredicate, AbortableAsyncPredicate, AbortableAsyncMapper, PQueueCfg, MemoCache, AsyncMemoCache, PromiseDecoratorCfg, PromiseDecoratorResp, ErrorData, ErrorObject, HttpErrorData, HttpErrorResponse, Admin401ErrorData, Admin403ErrorData, StringMap, PromiseMap, AnyObject, AnyFunction, ValuesOf, ValueOf, KeyValueTuple, ObjectMapper, ObjectPredicate, InstanceId, IsoDate, IsoDateTime, Reviver, PMapOptions, Mapper, AsyncMapper, Predicate, AsyncPredicate, BatchResult, DeferredPromise, PRetryOptions, PTimeoutOptions, TryCatchOptions, StringifyAnyOptions, JsonStringifyFunction, Merge, ReadonlyDeep, Promisable, Simplify, ConditionalPick, ConditionalExcept, Class, UnixTimestamp, BaseDBEntity, SavedDBEntity, Saved, Unsaved, CreatedUpdated, CreatedUpdatedId, ObjectWithId, AnyObjectWithId, JsonSchema, JsonSchemaAny, JsonSchemaOneOf, JsonSchemaAllOf, JsonSchemaAnyOf, JsonSchemaNot, JsonSchemaRef, JsonSchemaConst, JsonSchemaEnum, JsonSchemaString, JsonSchemaNumber, JsonSchemaBoolean, JsonSchemaNull, JsonSchemaRootObject, JsonSchemaObject, JsonSchemaArray, JsonSchemaTuple, JsonSchemaBuilder, CommonLogLevel, CommonLogWithLevelFunction, CommonLogFunction, CommonLogger, };
|
|
63
|
+
export type { AbortableMapper, AbortablePredicate, AbortableAsyncPredicate, AbortableAsyncMapper, PQueueCfg, MemoCache, AsyncMemoCache, PromiseDecoratorCfg, PromiseDecoratorResp, ErrorData, ErrorObject, HttpErrorData, HttpErrorResponse, Admin401ErrorData, Admin403ErrorData, StringMap, PromiseMap, AnyObject, AnyFunction, ValuesOf, ValueOf, KeyValueTuple, ObjectMapper, ObjectPredicate, InstanceId, IsoDate, IsoDateTime, Reviver, PMapOptions, Mapper, AsyncMapper, Predicate, AsyncPredicate, BatchResult, DeferredPromise, PRetryOptions, PTimeoutOptions, TryCatchOptions, StringifyAnyOptions, JsonStringifyFunction, Merge, ReadonlyDeep, Promisable, Simplify, ConditionalPick, ConditionalExcept, Class, UnixTimestamp, Integer, BaseDBEntity, SavedDBEntity, Saved, Unsaved, CreatedUpdated, CreatedUpdatedId, ObjectWithId, AnyObjectWithId, JsonSchema, JsonSchemaAny, JsonSchemaOneOf, JsonSchemaAllOf, JsonSchemaAnyOf, JsonSchemaNot, JsonSchemaRef, JsonSchemaConst, JsonSchemaEnum, JsonSchemaString, JsonSchemaNumber, JsonSchemaBoolean, JsonSchemaNull, JsonSchemaRootObject, JsonSchemaObject, JsonSchemaArray, JsonSchemaTuple, JsonSchemaBuilder, CommonLogLevel, CommonLogWithLevelFunction, CommonLogFunction, CommonLogger, };
|
|
63
64
|
export { is, _createPromiseDecorator, _stringMapValues, _stringMapEntries, _objectKeys, pMap, _passthroughMapper, _passUndefinedMapper, _passthroughPredicate, _passNothingPredicate, _noop, ErrorMode, pDefer, AggregatedError, pRetry, pRetryFn, pTimeout, pTimeoutFn, _tryCatch, _TryCatch, _stringifyAny, jsonSchema, JsonSchemaAnyBuilder, commonLoggerMinLevel, commonLoggerNoop, commonLogLevelNumber, commonLoggerPipe, commonLoggerPrefix, commonLoggerCreate, PQueue, END, SKIP, };
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "_createPromiseDecorator", { enumerable: true, ge
|
|
|
15
15
|
(0, tslib_1.__exportStar)(require("./decorators/memo.decorator"), exports);
|
|
16
16
|
(0, tslib_1.__exportStar)(require("./decorators/asyncMemo.decorator"), exports);
|
|
17
17
|
(0, tslib_1.__exportStar)(require("./decorators/memoFn"), exports);
|
|
18
|
+
(0, tslib_1.__exportStar)(require("./decorators/memoFnAsync"), exports);
|
|
18
19
|
(0, tslib_1.__exportStar)(require("./decorators/retry.decorator"), exports);
|
|
19
20
|
(0, tslib_1.__exportStar)(require("./decorators/timeout.decorator"), exports);
|
|
20
21
|
(0, tslib_1.__exportStar)(require("./error/app.error"), exports);
|
package/dist/types.d.ts
CHANGED
|
@@ -124,6 +124,10 @@ export declare type IsoDateTime = string;
|
|
|
124
124
|
* @example 1628945450
|
|
125
125
|
*/
|
|
126
126
|
export declare type UnixTimestamp = number;
|
|
127
|
+
/**
|
|
128
|
+
* Same as `number`, but with semantic meaning that it's an Integer.
|
|
129
|
+
*/
|
|
130
|
+
export declare type Integer = number;
|
|
127
131
|
/**
|
|
128
132
|
* Base interface for any Entity that was saved to DB.
|
|
129
133
|
*/
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { __asyncValues } from "tslib";
|
|
2
1
|
import { _since } from '../time/time.util';
|
|
3
2
|
import { _getArgsSignature, _getMethodSignature, _getTargetMethodSignature } from './decorator.util';
|
|
4
|
-
import { jsonMemoSerializer } from './memo.util';
|
|
3
|
+
import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
5
4
|
/**
|
|
6
5
|
* Like @_Memo, but allowing async MemoCache implementation.
|
|
7
6
|
*
|
|
@@ -9,18 +8,17 @@ import { jsonMemoSerializer } from './memo.util';
|
|
|
9
8
|
* Return `null` instead (it'll be cached).
|
|
10
9
|
*/
|
|
11
10
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
12
|
-
export const _AsyncMemo = (opt) => (target, key, descriptor) => {
|
|
11
|
+
export const _AsyncMemo = (opt = {}) => (target, key, descriptor) => {
|
|
13
12
|
if (typeof descriptor.value !== 'function') {
|
|
14
13
|
throw new TypeError('Memoization can be applied only to methods');
|
|
15
14
|
}
|
|
16
15
|
const originalFn = descriptor.value;
|
|
17
16
|
// Map from "instance" of the Class where @_AsyncMemo is applied to AsyncMemoCache instance.
|
|
18
17
|
const cache = new Map();
|
|
19
|
-
const { logHit = false, logMiss = false,
|
|
18
|
+
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, cacheRejections = true, } = opt;
|
|
20
19
|
const keyStr = String(key);
|
|
21
20
|
const methodSignature = _getTargetMethodSignature(target, keyStr);
|
|
22
21
|
descriptor.value = async function (...args) {
|
|
23
|
-
var e_1, _a;
|
|
24
22
|
const ctx = this;
|
|
25
23
|
const cacheKey = cacheKeyFn(args);
|
|
26
24
|
if (!cache.has(ctx)) {
|
|
@@ -30,23 +28,7 @@ export const _AsyncMemo = (opt) => (target, key, descriptor) => {
|
|
|
30
28
|
}
|
|
31
29
|
let value;
|
|
32
30
|
try {
|
|
33
|
-
|
|
34
|
-
for (var _b = __asyncValues(cache.get(ctx)), _c; _c = await _b.next(), !_c.done;) {
|
|
35
|
-
const cacheLayer = _c.value;
|
|
36
|
-
value = await cacheLayer.get(cacheKey);
|
|
37
|
-
if (value !== undefined) {
|
|
38
|
-
// it's a hit!
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
44
|
-
finally {
|
|
45
|
-
try {
|
|
46
|
-
if (_c && !_c.done && (_a = _b.return)) await _a.call(_b);
|
|
47
|
-
}
|
|
48
|
-
finally { if (e_1) throw e_1.error; }
|
|
49
|
-
}
|
|
31
|
+
value = await cache.get(ctx).get(cacheKey);
|
|
50
32
|
}
|
|
51
33
|
catch (err) {
|
|
52
34
|
// log error, but don't throw, treat it as a "miss"
|
|
@@ -55,42 +37,56 @@ export const _AsyncMemo = (opt) => (target, key, descriptor) => {
|
|
|
55
37
|
if (value !== undefined) {
|
|
56
38
|
// hit!
|
|
57
39
|
if (logHit) {
|
|
58
|
-
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args,
|
|
40
|
+
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, logArgs)}) @_AsyncMemo hit`);
|
|
59
41
|
}
|
|
60
|
-
|
|
42
|
+
if (value instanceof Error) {
|
|
43
|
+
throw value;
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
61
46
|
}
|
|
62
47
|
// Here we know it's a MISS, let's execute the real method
|
|
63
48
|
const started = Date.now();
|
|
64
49
|
try {
|
|
65
50
|
value = await originalFn.apply(ctx, args);
|
|
66
|
-
|
|
67
|
-
|
|
51
|
+
// Save the value in the Cache, without awaiting it
|
|
52
|
+
// This is to support both sync and async functions
|
|
53
|
+
void (async () => {
|
|
54
|
+
try {
|
|
55
|
+
await cache.get(ctx).set(cacheKey, value);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
68
58
|
// log and ignore the error
|
|
69
59
|
logger.error(err);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
72
62
|
return value;
|
|
73
63
|
}
|
|
74
64
|
catch (err) {
|
|
75
|
-
if (
|
|
65
|
+
if (cacheRejections) {
|
|
76
66
|
// We put it to cache as raw Error, not Promise.reject(err)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
67
|
+
// This is to support both sync and async functions
|
|
68
|
+
void (async () => {
|
|
69
|
+
try {
|
|
70
|
+
await cache.get(ctx).set(cacheKey, err);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
// log and ignore the error
|
|
74
|
+
logger.error(err);
|
|
75
|
+
}
|
|
76
|
+
})();
|
|
81
77
|
}
|
|
82
78
|
throw err;
|
|
83
79
|
}
|
|
84
80
|
finally {
|
|
85
81
|
if (logMiss) {
|
|
86
|
-
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args,
|
|
82
|
+
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, logArgs)}) @_AsyncMemo miss (${_since(started)})`);
|
|
87
83
|
}
|
|
88
84
|
}
|
|
89
85
|
};
|
|
90
86
|
descriptor.value.dropCache = async () => {
|
|
91
87
|
logger.log(`${methodSignature} @_AsyncMemo.dropCache()`);
|
|
92
88
|
try {
|
|
93
|
-
await Promise.all([...cache.values()].
|
|
89
|
+
await Promise.all([...cache.values()].map(c => c.clear()));
|
|
94
90
|
cache.clear();
|
|
95
91
|
}
|
|
96
92
|
catch (err) {
|
|
@@ -19,14 +19,14 @@ export function _createPromiseDecorator(cfg, decoratorParams = {}) {
|
|
|
19
19
|
const key = String(propertyKey);
|
|
20
20
|
const methodSignature = _getTargetMethodSignature(target, key);
|
|
21
21
|
pd.value = async function (...args) {
|
|
22
|
+
var _a, _b;
|
|
22
23
|
// console.log(`@${cfg.decoratorName} called inside function`)
|
|
23
24
|
const started = Date.now();
|
|
24
|
-
|
|
25
|
+
try {
|
|
25
26
|
// Before function
|
|
26
|
-
.then(() => {
|
|
27
27
|
// console.log(`@${cfg.decoratorName} Before`)
|
|
28
28
|
if (cfg.beforeFn) {
|
|
29
|
-
|
|
29
|
+
await cfg.beforeFn({
|
|
30
30
|
decoratorParams,
|
|
31
31
|
args,
|
|
32
32
|
key,
|
|
@@ -35,11 +35,8 @@ export function _createPromiseDecorator(cfg, decoratorParams = {}) {
|
|
|
35
35
|
started,
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
-
})
|
|
39
38
|
// Original function
|
|
40
|
-
|
|
41
|
-
.then(res => {
|
|
42
|
-
var _a;
|
|
39
|
+
let res = await originalMethod.apply(this, args);
|
|
43
40
|
// console.log(`${cfg.decoratorName} After`)
|
|
44
41
|
const resp = {
|
|
45
42
|
decoratorParams,
|
|
@@ -54,9 +51,8 @@ export function _createPromiseDecorator(cfg, decoratorParams = {}) {
|
|
|
54
51
|
}
|
|
55
52
|
(_a = cfg.finallyFn) === null || _a === void 0 ? void 0 : _a.call(cfg, resp);
|
|
56
53
|
return res;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
var _a;
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
60
56
|
console.error(`@${decoratorName} ${methodSignature} catch:`, err);
|
|
61
57
|
const resp = {
|
|
62
58
|
decoratorParams,
|
|
@@ -71,14 +67,11 @@ export function _createPromiseDecorator(cfg, decoratorParams = {}) {
|
|
|
71
67
|
cfg.catchFn(Object.assign(Object.assign({}, resp), { err }));
|
|
72
68
|
handled = true;
|
|
73
69
|
}
|
|
74
|
-
(
|
|
70
|
+
(_b = cfg.finallyFn) === null || _b === void 0 ? void 0 : _b.call(cfg, resp);
|
|
75
71
|
if (!handled) {
|
|
76
72
|
throw err; // rethrow
|
|
77
73
|
}
|
|
78
|
-
}
|
|
79
|
-
// es2018 only
|
|
80
|
-
// .finally(() => {})
|
|
81
|
-
);
|
|
74
|
+
}
|
|
82
75
|
};
|
|
83
76
|
return pd;
|
|
84
77
|
};
|
|
@@ -19,8 +19,8 @@ export function _getTargetMethodSignature(target, keyStr) {
|
|
|
19
19
|
* returns:
|
|
20
20
|
* a, b, c
|
|
21
21
|
*/
|
|
22
|
-
export function _getArgsSignature(args = [],
|
|
23
|
-
if (
|
|
22
|
+
export function _getArgsSignature(args = [], logArgs = true) {
|
|
23
|
+
if (!logArgs)
|
|
24
24
|
return '';
|
|
25
25
|
return args
|
|
26
26
|
.map(arg => {
|
|
@@ -20,13 +20,13 @@ export function _LogMethod(opt = {}) {
|
|
|
20
20
|
_assert(typeof descriptor.value === 'function', '@_LogMethod can be applied only to methods');
|
|
21
21
|
const originalFn = descriptor.value;
|
|
22
22
|
const keyStr = String(key);
|
|
23
|
-
const { avg,
|
|
23
|
+
const { avg, logArgs = true, logStart, logResult, logResultLength = true, logger = console, } = opt;
|
|
24
24
|
let { logResultFn } = opt;
|
|
25
25
|
if (!logResultFn) {
|
|
26
26
|
if (logResult) {
|
|
27
27
|
logResultFn = r => ['result:', _stringifyAny(r)];
|
|
28
28
|
}
|
|
29
|
-
else if (
|
|
29
|
+
else if (logResultLength) {
|
|
30
30
|
logResultFn = r => (Array.isArray(r) ? [`result: ${r.length} items`] : []);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -38,7 +38,7 @@ export function _LogMethod(opt = {}) {
|
|
|
38
38
|
// e.g `NameOfYourClass.methodName`
|
|
39
39
|
// or `NameOfYourClass(instanceId).methodName`
|
|
40
40
|
const methodSignature = _getMethodSignature(ctx, keyStr);
|
|
41
|
-
const argsStr = _getArgsSignature(args,
|
|
41
|
+
const argsStr = _getArgsSignature(args, logArgs);
|
|
42
42
|
const callSignature = `${methodSignature}(${argsStr}) #${++count}`;
|
|
43
43
|
if (logStart)
|
|
44
44
|
logger.log(`>> ${callSignature}`);
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
// Based on:
|
|
2
|
-
// https://github.com/mgechev/memo-decorator/blob/master/index.ts
|
|
3
|
-
// http://decodize.com/blog/2012/08/27/javascript-memoization-caching-results-for-better-performance/
|
|
4
|
-
// http://inlehmansterms.net/2015/03/01/javascript-memoization/
|
|
5
|
-
// https://community.risingstack.com/the-worlds-fastest-javascript-memoization-library/
|
|
6
1
|
import { _since } from '../time/time.util';
|
|
7
2
|
import { _getArgsSignature, _getMethodSignature, _getTargetMethodSignature } from './decorator.util';
|
|
8
3
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
@@ -15,6 +10,15 @@ import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
|
15
10
|
* If you don't want it that way - you can use a static method, then there will be only one "instance".
|
|
16
11
|
*
|
|
17
12
|
* Supports dropping it's cache by calling .dropCache() method of decorated function (useful in unit testing).
|
|
13
|
+
*
|
|
14
|
+
* Doesn't support Async functions, use @_AsyncMemo instead!
|
|
15
|
+
* (or, it will simply return the [unresolved] Promise further, without awaiting it)
|
|
16
|
+
*
|
|
17
|
+
* Based on:
|
|
18
|
+
* https://github.com/mgechev/memo-decorator/blob/master/index.ts
|
|
19
|
+
* http://decodize.com/blog/2012/08/27/javascript-memoization-caching-results-for-better-performance/
|
|
20
|
+
* http://inlehmansterms.net/2015/03/01/javascript-memoization/
|
|
21
|
+
* https://community.risingstack.com/the-worlds-fastest-javascript-memoization-library/
|
|
18
22
|
*/
|
|
19
23
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
20
24
|
export const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
@@ -30,62 +34,52 @@ export const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
|
30
34
|
// UPD: tests show that normal Map also doesn't leak (to be tested further)
|
|
31
35
|
// Normal Map is needed to allow .dropCache()
|
|
32
36
|
const cache = new Map();
|
|
33
|
-
const { logHit = false, logMiss = false,
|
|
34
|
-
const awaitPromise = Boolean(noCacheRejected || noCacheResolved);
|
|
37
|
+
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, cacheErrors = true, } = opt;
|
|
35
38
|
const keyStr = String(key);
|
|
36
39
|
const methodSignature = _getTargetMethodSignature(target, keyStr);
|
|
37
40
|
descriptor.value = function (...args) {
|
|
38
41
|
const ctx = this;
|
|
39
42
|
const cacheKey = cacheKeyFn(args);
|
|
43
|
+
let value;
|
|
40
44
|
if (!cache.has(ctx)) {
|
|
41
45
|
cache.set(ctx, cacheFactory());
|
|
42
46
|
}
|
|
43
47
|
else if (cache.get(ctx).has(cacheKey)) {
|
|
44
48
|
if (logHit) {
|
|
45
|
-
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args,
|
|
46
|
-
}
|
|
47
|
-
const res = cache.get(ctx).get(cacheKey);
|
|
48
|
-
if (awaitPromise) {
|
|
49
|
-
return res instanceof Error ? Promise.reject(res) : Promise.resolve(res);
|
|
49
|
+
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, logArgs)}) @_Memo hit`);
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
value = cache.get(ctx).get(cacheKey);
|
|
52
|
+
if (value instanceof Error) {
|
|
53
|
+
throw value;
|
|
53
54
|
}
|
|
55
|
+
return value;
|
|
54
56
|
}
|
|
55
57
|
const started = Date.now();
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// console.log('REJECTED', err)
|
|
71
|
-
if (logMiss) {
|
|
72
|
-
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, noLogArgs)}) @_Memo miss rejected (${_since(started)})`);
|
|
58
|
+
try {
|
|
59
|
+
value = originalFn.apply(ctx, args);
|
|
60
|
+
try {
|
|
61
|
+
cache.get(ctx).set(cacheKey, value);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
logger.error(err);
|
|
65
|
+
}
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
if (cacheErrors) {
|
|
70
|
+
try {
|
|
71
|
+
cache.get(ctx).set(cacheKey, err);
|
|
73
72
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// So, we'll need to check if it's instanceof Error to reject it or resolve
|
|
77
|
-
// Wrap as Error if it's not Error
|
|
78
|
-
cache.get(ctx).set(cacheKey, err instanceof Error ? err : new Error(err));
|
|
73
|
+
catch (err) {
|
|
74
|
+
logger.error(err);
|
|
79
75
|
}
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
}
|
|
77
|
+
throw err;
|
|
82
78
|
}
|
|
83
|
-
|
|
79
|
+
finally {
|
|
84
80
|
if (logMiss) {
|
|
85
|
-
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args,
|
|
81
|
+
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, logArgs)}) @_Memo miss (${_since(started)})`);
|
|
86
82
|
}
|
|
87
|
-
cache.get(ctx).set(cacheKey, res);
|
|
88
|
-
return res;
|
|
89
83
|
}
|
|
90
84
|
};
|
|
91
85
|
descriptor.value.dropCache = () => {
|
|
@@ -1,60 +1,55 @@
|
|
|
1
1
|
import { _since } from '../time/time.util';
|
|
2
2
|
import { _getArgsSignature } from './decorator.util';
|
|
3
3
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
4
|
+
/**
|
|
5
|
+
* Only supports Sync functions.
|
|
6
|
+
* To support Async functions - use _memoFnAsync.
|
|
7
|
+
* Technically, you can use it with Async functions, but it'll return the Promise without awaiting it.
|
|
8
|
+
*/
|
|
4
9
|
export function _memoFn(fn, opt = {}) {
|
|
5
|
-
const { logHit = false, logMiss = false,
|
|
10
|
+
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheErrors = true, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, } = opt;
|
|
6
11
|
const cache = cacheFactory();
|
|
7
|
-
const awaitPromise = Boolean(noCacheRejected || noCacheResolved);
|
|
8
12
|
const fnName = fn.name;
|
|
9
13
|
const memoizedFn = function (...args) {
|
|
10
14
|
const ctx = this;
|
|
11
15
|
const cacheKey = cacheKeyFn(args);
|
|
16
|
+
let value;
|
|
12
17
|
if (cache.has(cacheKey)) {
|
|
13
18
|
if (logHit) {
|
|
14
|
-
logger.log(`${fnName}(${_getArgsSignature(args,
|
|
19
|
+
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFn hit`);
|
|
15
20
|
}
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
return res;
|
|
21
|
+
value = cache.get(cacheKey);
|
|
22
|
+
if (value instanceof Error) {
|
|
23
|
+
throw value;
|
|
22
24
|
}
|
|
25
|
+
return value;
|
|
23
26
|
}
|
|
24
27
|
const started = Date.now();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// console.log('REJECTED', err)
|
|
40
|
-
if (logMiss) {
|
|
41
|
-
logger.log(`${fnName}(${_getArgsSignature(args, noLogArgs)}) memoFn miss rejected (${_since(started)})`);
|
|
28
|
+
try {
|
|
29
|
+
value = fn.apply(ctx, args);
|
|
30
|
+
try {
|
|
31
|
+
cache.set(cacheKey, value);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
logger.error(err);
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
if (cacheErrors) {
|
|
40
|
+
try {
|
|
41
|
+
cache.set(cacheKey, err);
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// So, we'll need to check if it's instanceof Error to reject it or resolve
|
|
46
|
-
// Wrap as Error if it's not Error
|
|
47
|
-
cache.set(cacheKey, err instanceof Error ? err : new Error(err));
|
|
43
|
+
catch (err) {
|
|
44
|
+
logger.error(err);
|
|
48
45
|
}
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
}
|
|
47
|
+
throw err;
|
|
51
48
|
}
|
|
52
|
-
|
|
49
|
+
finally {
|
|
53
50
|
if (logMiss) {
|
|
54
|
-
logger.log(`${fnName}(${_getArgsSignature(args,
|
|
51
|
+
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFn miss (${_since(started)})`);
|
|
55
52
|
}
|
|
56
|
-
cache.set(cacheKey, res);
|
|
57
|
-
return res;
|
|
58
53
|
}
|
|
59
54
|
};
|
|
60
55
|
Object.assign(memoizedFn, { cache });
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { _since } from '../time/time.util';
|
|
2
|
+
import { _getArgsSignature } from './decorator.util';
|
|
3
|
+
import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
4
|
+
/**
|
|
5
|
+
* Only supports Sync functions.
|
|
6
|
+
* To support Async functions - use _memoFnAsync
|
|
7
|
+
*/
|
|
8
|
+
export function _memoFnAsync(fn, opt = {}) {
|
|
9
|
+
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheRejections = true, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, } = opt;
|
|
10
|
+
const cache = cacheFactory();
|
|
11
|
+
const fnName = fn.name;
|
|
12
|
+
const memoizedFn = async function (...args) {
|
|
13
|
+
const ctx = this;
|
|
14
|
+
const cacheKey = cacheKeyFn(args);
|
|
15
|
+
let value;
|
|
16
|
+
try {
|
|
17
|
+
value = await cache.get(cacheKey);
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
logger.error(err);
|
|
21
|
+
}
|
|
22
|
+
if (value !== undefined) {
|
|
23
|
+
if (logHit) {
|
|
24
|
+
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFnAsync hit`);
|
|
25
|
+
}
|
|
26
|
+
if (value instanceof Error) {
|
|
27
|
+
throw value;
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
const started = Date.now();
|
|
32
|
+
try {
|
|
33
|
+
value = await fn.apply(ctx, args);
|
|
34
|
+
void (async () => {
|
|
35
|
+
try {
|
|
36
|
+
await cache.set(cacheKey, value);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
logger.error(err);
|
|
40
|
+
}
|
|
41
|
+
})();
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (cacheRejections) {
|
|
46
|
+
void (async () => {
|
|
47
|
+
try {
|
|
48
|
+
await cache.set(cacheKey, err);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
logger.error(err);
|
|
52
|
+
}
|
|
53
|
+
})();
|
|
54
|
+
}
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
if (logMiss) {
|
|
59
|
+
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFnAsync miss (${_since(started)})`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
Object.assign(memoizedFn, { cache });
|
|
64
|
+
return memoizedFn;
|
|
65
|
+
}
|