@naturalcycles/js-lib 14.85.0 → 14.88.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 +5 -2
- package/dist/decorators/asyncMemo.decorator.js +5 -2
- package/dist/decorators/createPromiseDecorator.d.ts +2 -2
- package/dist/decorators/createPromiseDecorator.js +6 -12
- package/dist/decorators/memo.decorator.d.ts +5 -2
- package/dist/decorators/memo.decorator.js +19 -5
- package/dist/decorators/memoFn.js +19 -5
- package/dist/decorators/memoFnAsync.js +4 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -0
- package/dist/string/leven.d.ts +6 -0
- package/dist/string/leven.js +75 -0
- package/dist/types.d.ts +4 -0
- package/dist-esm/decorators/asyncMemo.decorator.js +5 -2
- package/dist-esm/decorators/createPromiseDecorator.js +8 -15
- package/dist-esm/decorators/memo.decorator.js +19 -5
- package/dist-esm/decorators/memoFn.js +19 -5
- package/dist-esm/decorators/memoFnAsync.js +4 -1
- package/dist-esm/index.js +1 -0
- package/dist-esm/string/leven.js +71 -0
- package/package.json +1 -1
- package/src/decorators/asyncMemo.decorator.ts +11 -4
- package/src/decorators/createPromiseDecorator.ts +64 -70
- package/src/decorators/memo.decorator.ts +24 -8
- package/src/decorators/memoFn.ts +19 -6
- package/src/decorators/memoFnAsync.ts +5 -1
- package/src/index.ts +3 -0
- package/src/string/leven.ts +86 -0
- package/src/types.ts +5 -0
|
@@ -12,9 +12,12 @@ export interface AsyncMemoOptions {
|
|
|
12
12
|
*/
|
|
13
13
|
cacheKeyFn?: (args: any[]) => any;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Default true.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
17
|
+
* Set to `false` to skip caching rejected promises (errors).
|
|
18
|
+
*
|
|
19
|
+
* True will ensure "max 1 execution", but will "remember" rejection.
|
|
20
|
+
* False will allow >1 execution in case of errors.
|
|
18
21
|
*/
|
|
19
22
|
cacheRejections?: boolean;
|
|
20
23
|
/**
|
|
@@ -18,7 +18,7 @@ const _AsyncMemo = (opt = {}) => (target, key, descriptor) => {
|
|
|
18
18
|
const originalFn = descriptor.value;
|
|
19
19
|
// Map from "instance" of the Class where @_AsyncMemo is applied to AsyncMemoCache instance.
|
|
20
20
|
const cache = new Map();
|
|
21
|
-
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheFactory = () => new memo_util_1.MapMemoCache(), cacheKeyFn = memo_util_1.jsonMemoSerializer, cacheRejections =
|
|
21
|
+
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheFactory = () => new memo_util_1.MapMemoCache(), cacheKeyFn = memo_util_1.jsonMemoSerializer, cacheRejections = true, } = opt;
|
|
22
22
|
const keyStr = String(key);
|
|
23
23
|
const methodSignature = (0, decorator_util_1._getTargetMethodSignature)(target, keyStr);
|
|
24
24
|
descriptor.value = async function (...args) {
|
|
@@ -42,7 +42,10 @@ const _AsyncMemo = (opt = {}) => (target, key, descriptor) => {
|
|
|
42
42
|
if (logHit) {
|
|
43
43
|
logger.log(`${(0, decorator_util_1._getMethodSignature)(ctx, keyStr)}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) @_AsyncMemo hit`);
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
if (value instanceof Error) {
|
|
46
|
+
throw value;
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
46
49
|
}
|
|
47
50
|
// Here we know it's a MISS, let's execute the real method
|
|
48
51
|
const started = Date.now();
|
|
@@ -2,9 +2,9 @@ export interface PromiseDecoratorCfg<RES = any, PARAMS = any> {
|
|
|
2
2
|
decoratorName: string;
|
|
3
3
|
/**
|
|
4
4
|
* Called BEFORE the original function.
|
|
5
|
-
*
|
|
5
|
+
* If Promise is returned - it will be awaited.
|
|
6
6
|
*/
|
|
7
|
-
beforeFn?: (r: PromiseDecoratorResp<PARAMS>) => void
|
|
7
|
+
beforeFn?: (r: PromiseDecoratorResp<PARAMS>) => void | Promise<void>;
|
|
8
8
|
/**
|
|
9
9
|
* Called just AFTER the original function.
|
|
10
10
|
* The output of this hook will be passed further,
|
|
@@ -24,12 +24,11 @@ function _createPromiseDecorator(cfg, decoratorParams = {}) {
|
|
|
24
24
|
pd.value = async function (...args) {
|
|
25
25
|
// console.log(`@${cfg.decoratorName} called inside function`)
|
|
26
26
|
const started = Date.now();
|
|
27
|
-
|
|
27
|
+
try {
|
|
28
28
|
// Before function
|
|
29
|
-
.then(() => {
|
|
30
29
|
// console.log(`@${cfg.decoratorName} Before`)
|
|
31
30
|
if (cfg.beforeFn) {
|
|
32
|
-
|
|
31
|
+
await cfg.beforeFn({
|
|
33
32
|
decoratorParams,
|
|
34
33
|
args,
|
|
35
34
|
key,
|
|
@@ -38,10 +37,8 @@ function _createPromiseDecorator(cfg, decoratorParams = {}) {
|
|
|
38
37
|
started,
|
|
39
38
|
});
|
|
40
39
|
}
|
|
41
|
-
})
|
|
42
40
|
// Original function
|
|
43
|
-
|
|
44
|
-
.then(res => {
|
|
41
|
+
let res = await originalMethod.apply(this, args);
|
|
45
42
|
// console.log(`${cfg.decoratorName} After`)
|
|
46
43
|
const resp = {
|
|
47
44
|
decoratorParams,
|
|
@@ -59,8 +56,8 @@ function _createPromiseDecorator(cfg, decoratorParams = {}) {
|
|
|
59
56
|
}
|
|
60
57
|
cfg.finallyFn?.(resp);
|
|
61
58
|
return res;
|
|
62
|
-
}
|
|
63
|
-
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
64
61
|
console.error(`@${decoratorName} ${methodSignature} catch:`, err);
|
|
65
62
|
const resp = {
|
|
66
63
|
decoratorParams,
|
|
@@ -82,10 +79,7 @@ function _createPromiseDecorator(cfg, decoratorParams = {}) {
|
|
|
82
79
|
if (!handled) {
|
|
83
80
|
throw err; // rethrow
|
|
84
81
|
}
|
|
85
|
-
}
|
|
86
|
-
// es2018 only
|
|
87
|
-
// .finally(() => {})
|
|
88
|
-
);
|
|
82
|
+
}
|
|
89
83
|
};
|
|
90
84
|
return pd;
|
|
91
85
|
};
|
|
@@ -12,8 +12,11 @@ export interface MemoOptions {
|
|
|
12
12
|
*/
|
|
13
13
|
cacheKeyFn?: (args: any[]) => any;
|
|
14
14
|
/**
|
|
15
|
-
* Defaults to
|
|
16
|
-
* Set to
|
|
15
|
+
* Defaults to true.
|
|
16
|
+
* Set to false to skip caching errors.
|
|
17
|
+
*
|
|
18
|
+
* True will ensure "max 1 execution", but will "remember" errors.
|
|
19
|
+
* False will allow >1 execution in case of errors.
|
|
17
20
|
*/
|
|
18
21
|
cacheErrors?: boolean;
|
|
19
22
|
/**
|
|
@@ -37,12 +37,13 @@ const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
|
37
37
|
// UPD: tests show that normal Map also doesn't leak (to be tested further)
|
|
38
38
|
// Normal Map is needed to allow .dropCache()
|
|
39
39
|
const cache = new Map();
|
|
40
|
-
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheFactory = () => new memo_util_1.MapMemoCache(), cacheKeyFn = memo_util_1.jsonMemoSerializer, cacheErrors =
|
|
40
|
+
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheFactory = () => new memo_util_1.MapMemoCache(), cacheKeyFn = memo_util_1.jsonMemoSerializer, cacheErrors = true, } = opt;
|
|
41
41
|
const keyStr = String(key);
|
|
42
42
|
const methodSignature = (0, decorator_util_1._getTargetMethodSignature)(target, keyStr);
|
|
43
43
|
descriptor.value = function (...args) {
|
|
44
44
|
const ctx = this;
|
|
45
45
|
const cacheKey = cacheKeyFn(args);
|
|
46
|
+
let value;
|
|
46
47
|
if (!cache.has(ctx)) {
|
|
47
48
|
cache.set(ctx, cacheFactory());
|
|
48
49
|
}
|
|
@@ -50,18 +51,31 @@ const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
|
50
51
|
if (logHit) {
|
|
51
52
|
logger.log(`${(0, decorator_util_1._getMethodSignature)(ctx, keyStr)}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) @_Memo hit`);
|
|
52
53
|
}
|
|
53
|
-
|
|
54
|
+
value = cache.get(ctx).get(cacheKey);
|
|
55
|
+
if (value instanceof Error) {
|
|
56
|
+
throw value;
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
54
59
|
}
|
|
55
60
|
const started = Date.now();
|
|
56
|
-
let value;
|
|
57
61
|
try {
|
|
58
62
|
value = originalFn.apply(ctx, args);
|
|
59
|
-
|
|
63
|
+
try {
|
|
64
|
+
cache.get(ctx).set(cacheKey, value);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
logger.error(err);
|
|
68
|
+
}
|
|
60
69
|
return value;
|
|
61
70
|
}
|
|
62
71
|
catch (err) {
|
|
63
72
|
if (cacheErrors) {
|
|
64
|
-
|
|
73
|
+
try {
|
|
74
|
+
cache.get(ctx).set(cacheKey, err);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
logger.error(err);
|
|
78
|
+
}
|
|
65
79
|
}
|
|
66
80
|
throw err;
|
|
67
81
|
}
|
|
@@ -10,28 +10,42 @@ const memo_util_1 = require("./memo.util");
|
|
|
10
10
|
* Technically, you can use it with Async functions, but it'll return the Promise without awaiting it.
|
|
11
11
|
*/
|
|
12
12
|
function _memoFn(fn, opt = {}) {
|
|
13
|
-
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheErrors =
|
|
13
|
+
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheErrors = true, cacheFactory = () => new memo_util_1.MapMemoCache(), cacheKeyFn = memo_util_1.jsonMemoSerializer, } = opt;
|
|
14
14
|
const cache = cacheFactory();
|
|
15
15
|
const fnName = fn.name;
|
|
16
16
|
const memoizedFn = function (...args) {
|
|
17
17
|
const ctx = this;
|
|
18
18
|
const cacheKey = cacheKeyFn(args);
|
|
19
|
+
let value;
|
|
19
20
|
if (cache.has(cacheKey)) {
|
|
20
21
|
if (logHit) {
|
|
21
22
|
logger.log(`${fnName}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) memoFn hit`);
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
+
value = cache.get(cacheKey);
|
|
25
|
+
if (value instanceof Error) {
|
|
26
|
+
throw value;
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
24
29
|
}
|
|
25
30
|
const started = Date.now();
|
|
26
|
-
let value;
|
|
27
31
|
try {
|
|
28
32
|
value = fn.apply(ctx, args);
|
|
29
|
-
|
|
33
|
+
try {
|
|
34
|
+
cache.set(cacheKey, value);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
logger.error(err);
|
|
38
|
+
}
|
|
30
39
|
return value;
|
|
31
40
|
}
|
|
32
41
|
catch (err) {
|
|
33
42
|
if (cacheErrors) {
|
|
34
|
-
|
|
43
|
+
try {
|
|
44
|
+
cache.set(cacheKey, err);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
logger.error(err);
|
|
48
|
+
}
|
|
35
49
|
}
|
|
36
50
|
throw err;
|
|
37
51
|
}
|
|
@@ -9,7 +9,7 @@ const memo_util_1 = require("./memo.util");
|
|
|
9
9
|
* To support Async functions - use _memoFnAsync
|
|
10
10
|
*/
|
|
11
11
|
function _memoFnAsync(fn, opt = {}) {
|
|
12
|
-
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheRejections =
|
|
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
13
|
const cache = cacheFactory();
|
|
14
14
|
const fnName = fn.name;
|
|
15
15
|
const memoizedFn = async function (...args) {
|
|
@@ -26,6 +26,9 @@ function _memoFnAsync(fn, opt = {}) {
|
|
|
26
26
|
if (logHit) {
|
|
27
27
|
logger.log(`${fnName}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) memoFnAsync hit`);
|
|
28
28
|
}
|
|
29
|
+
if (value instanceof Error) {
|
|
30
|
+
throw value;
|
|
31
|
+
}
|
|
29
32
|
return value;
|
|
30
33
|
}
|
|
31
34
|
const started = Date.now();
|
package/dist/index.d.ts
CHANGED
|
@@ -52,7 +52,7 @@ export * from './string/string.util';
|
|
|
52
52
|
import { JsonStringifyFunction, StringifyAnyOptions, _stringifyAny } from './string/stringifyAny';
|
|
53
53
|
export * from './time/time.util';
|
|
54
54
|
import { Class, ConditionalExcept, ConditionalPick, Merge, Promisable, ReadonlyDeep, Simplify } from './typeFest';
|
|
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, 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';
|
|
56
56
|
export * from './unit/size.util';
|
|
57
57
|
import { is } from './vendor/is';
|
|
58
58
|
import { CommonLogLevel, CommonLogFunction, CommonLogger, commonLoggerMinLevel, commonLoggerNoop, commonLogLevelNumber, commonLoggerPipe, commonLoggerPrefix, CommonLogWithLevelFunction, commonLoggerCreate } from './log/commonLogger';
|
|
@@ -60,5 +60,6 @@ export * from './string/safeJsonStringify';
|
|
|
60
60
|
import { PQueue, PQueueCfg } from './promise/pQueue';
|
|
61
61
|
export * from './seq/seq';
|
|
62
62
|
export * from './math/stack.util';
|
|
63
|
-
export
|
|
63
|
+
export * from './string/leven';
|
|
64
|
+
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, };
|
|
64
65
|
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
|
@@ -92,3 +92,4 @@ const pQueue_1 = require("./promise/pQueue");
|
|
|
92
92
|
Object.defineProperty(exports, "PQueue", { enumerable: true, get: function () { return pQueue_1.PQueue; } });
|
|
93
93
|
(0, tslib_1.__exportStar)(require("./seq/seq"), exports);
|
|
94
94
|
(0, tslib_1.__exportStar)(require("./math/stack.util"), exports);
|
|
95
|
+
(0, tslib_1.__exportStar)(require("./string/leven"), exports);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._leven = void 0;
|
|
4
|
+
const array = [];
|
|
5
|
+
const characterCodeCache = [];
|
|
6
|
+
/* eslint-disable unicorn/prefer-code-point, no-bitwise */
|
|
7
|
+
/**
|
|
8
|
+
* Modified version of: https://github.com/sindresorhus/leven/
|
|
9
|
+
*
|
|
10
|
+
* Returns a Levenshtein distance between first and second word.
|
|
11
|
+
*/
|
|
12
|
+
function _leven(first, second) {
|
|
13
|
+
if (first === second) {
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
const swap = first;
|
|
17
|
+
// Swapping the strings if `a` is longer than `b` so we know which one is the
|
|
18
|
+
// shortest & which one is the longest
|
|
19
|
+
if (first.length > second.length) {
|
|
20
|
+
first = second;
|
|
21
|
+
second = swap;
|
|
22
|
+
}
|
|
23
|
+
let firstLength = first.length;
|
|
24
|
+
let secondLength = second.length;
|
|
25
|
+
// Performing suffix trimming:
|
|
26
|
+
// We can linearly drop suffix common to both strings since they
|
|
27
|
+
// don't increase distance at all
|
|
28
|
+
// Note: `~-` is the bitwise way to perform a `- 1` operation
|
|
29
|
+
while (firstLength > 0 && first.charCodeAt(~-firstLength) === second.charCodeAt(~-secondLength)) {
|
|
30
|
+
firstLength--;
|
|
31
|
+
secondLength--;
|
|
32
|
+
}
|
|
33
|
+
// Performing prefix trimming
|
|
34
|
+
// We can linearly drop prefix common to both strings since they
|
|
35
|
+
// don't increase distance at all
|
|
36
|
+
let start = 0;
|
|
37
|
+
while (start < firstLength && first.charCodeAt(start) === second.charCodeAt(start)) {
|
|
38
|
+
start++;
|
|
39
|
+
}
|
|
40
|
+
firstLength -= start;
|
|
41
|
+
secondLength -= start;
|
|
42
|
+
if (firstLength === 0) {
|
|
43
|
+
return secondLength;
|
|
44
|
+
}
|
|
45
|
+
let bCharacterCode;
|
|
46
|
+
let result;
|
|
47
|
+
let temporary;
|
|
48
|
+
let temporary2;
|
|
49
|
+
let index = 0;
|
|
50
|
+
let index2 = 0;
|
|
51
|
+
while (index < firstLength) {
|
|
52
|
+
characterCodeCache[index] = first.charCodeAt(start + index);
|
|
53
|
+
array[index] = ++index;
|
|
54
|
+
}
|
|
55
|
+
while (index2 < secondLength) {
|
|
56
|
+
bCharacterCode = second.charCodeAt(start + index2);
|
|
57
|
+
temporary = index2++;
|
|
58
|
+
result = index2;
|
|
59
|
+
for (index = 0; index < firstLength; index++) {
|
|
60
|
+
temporary2 = bCharacterCode === characterCodeCache[index] ? temporary : temporary + 1;
|
|
61
|
+
temporary = array[index];
|
|
62
|
+
// eslint-disable-next-line no-multi-assign
|
|
63
|
+
result = array[index] =
|
|
64
|
+
temporary > result
|
|
65
|
+
? temporary2 > result
|
|
66
|
+
? result + 1
|
|
67
|
+
: temporary2
|
|
68
|
+
: temporary2 > temporary
|
|
69
|
+
? temporary + 1
|
|
70
|
+
: temporary2;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
exports._leven = _leven;
|
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
|
*/
|
|
@@ -15,7 +15,7 @@ export const _AsyncMemo = (opt = {}) => (target, key, descriptor) => {
|
|
|
15
15
|
const originalFn = descriptor.value;
|
|
16
16
|
// Map from "instance" of the Class where @_AsyncMemo is applied to AsyncMemoCache instance.
|
|
17
17
|
const cache = new Map();
|
|
18
|
-
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, cacheRejections =
|
|
18
|
+
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, cacheRejections = true, } = opt;
|
|
19
19
|
const keyStr = String(key);
|
|
20
20
|
const methodSignature = _getTargetMethodSignature(target, keyStr);
|
|
21
21
|
descriptor.value = async function (...args) {
|
|
@@ -39,7 +39,10 @@ export const _AsyncMemo = (opt = {}) => (target, key, descriptor) => {
|
|
|
39
39
|
if (logHit) {
|
|
40
40
|
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, logArgs)}) @_AsyncMemo hit`);
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
if (value instanceof Error) {
|
|
43
|
+
throw value;
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
43
46
|
}
|
|
44
47
|
// Here we know it's a MISS, let's execute the real method
|
|
45
48
|
const started = Date.now();
|
|
@@ -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
|
};
|
|
@@ -34,12 +34,13 @@ export const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
|
34
34
|
// UPD: tests show that normal Map also doesn't leak (to be tested further)
|
|
35
35
|
// Normal Map is needed to allow .dropCache()
|
|
36
36
|
const cache = new Map();
|
|
37
|
-
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, cacheErrors =
|
|
37
|
+
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, cacheErrors = true, } = opt;
|
|
38
38
|
const keyStr = String(key);
|
|
39
39
|
const methodSignature = _getTargetMethodSignature(target, keyStr);
|
|
40
40
|
descriptor.value = function (...args) {
|
|
41
41
|
const ctx = this;
|
|
42
42
|
const cacheKey = cacheKeyFn(args);
|
|
43
|
+
let value;
|
|
43
44
|
if (!cache.has(ctx)) {
|
|
44
45
|
cache.set(ctx, cacheFactory());
|
|
45
46
|
}
|
|
@@ -47,18 +48,31 @@ export const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
|
47
48
|
if (logHit) {
|
|
48
49
|
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, logArgs)}) @_Memo hit`);
|
|
49
50
|
}
|
|
50
|
-
|
|
51
|
+
value = cache.get(ctx).get(cacheKey);
|
|
52
|
+
if (value instanceof Error) {
|
|
53
|
+
throw value;
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
51
56
|
}
|
|
52
57
|
const started = Date.now();
|
|
53
|
-
let value;
|
|
54
58
|
try {
|
|
55
59
|
value = originalFn.apply(ctx, args);
|
|
56
|
-
|
|
60
|
+
try {
|
|
61
|
+
cache.get(ctx).set(cacheKey, value);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
logger.error(err);
|
|
65
|
+
}
|
|
57
66
|
return value;
|
|
58
67
|
}
|
|
59
68
|
catch (err) {
|
|
60
69
|
if (cacheErrors) {
|
|
61
|
-
|
|
70
|
+
try {
|
|
71
|
+
cache.get(ctx).set(cacheKey, err);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
logger.error(err);
|
|
75
|
+
}
|
|
62
76
|
}
|
|
63
77
|
throw err;
|
|
64
78
|
}
|
|
@@ -7,28 +7,42 @@ import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
|
7
7
|
* Technically, you can use it with Async functions, but it'll return the Promise without awaiting it.
|
|
8
8
|
*/
|
|
9
9
|
export function _memoFn(fn, opt = {}) {
|
|
10
|
-
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheErrors =
|
|
10
|
+
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheErrors = true, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, } = opt;
|
|
11
11
|
const cache = cacheFactory();
|
|
12
12
|
const fnName = fn.name;
|
|
13
13
|
const memoizedFn = function (...args) {
|
|
14
14
|
const ctx = this;
|
|
15
15
|
const cacheKey = cacheKeyFn(args);
|
|
16
|
+
let value;
|
|
16
17
|
if (cache.has(cacheKey)) {
|
|
17
18
|
if (logHit) {
|
|
18
19
|
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFn hit`);
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
+
value = cache.get(cacheKey);
|
|
22
|
+
if (value instanceof Error) {
|
|
23
|
+
throw value;
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
21
26
|
}
|
|
22
27
|
const started = Date.now();
|
|
23
|
-
let value;
|
|
24
28
|
try {
|
|
25
29
|
value = fn.apply(ctx, args);
|
|
26
|
-
|
|
30
|
+
try {
|
|
31
|
+
cache.set(cacheKey, value);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
logger.error(err);
|
|
35
|
+
}
|
|
27
36
|
return value;
|
|
28
37
|
}
|
|
29
38
|
catch (err) {
|
|
30
39
|
if (cacheErrors) {
|
|
31
|
-
|
|
40
|
+
try {
|
|
41
|
+
cache.set(cacheKey, err);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
logger.error(err);
|
|
45
|
+
}
|
|
32
46
|
}
|
|
33
47
|
throw err;
|
|
34
48
|
}
|
|
@@ -6,7 +6,7 @@ import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
|
6
6
|
* To support Async functions - use _memoFnAsync
|
|
7
7
|
*/
|
|
8
8
|
export function _memoFnAsync(fn, opt = {}) {
|
|
9
|
-
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheRejections =
|
|
9
|
+
const { logHit = false, logMiss = false, logArgs = true, logger = console, cacheRejections = true, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, } = opt;
|
|
10
10
|
const cache = cacheFactory();
|
|
11
11
|
const fnName = fn.name;
|
|
12
12
|
const memoizedFn = async function (...args) {
|
|
@@ -23,6 +23,9 @@ export function _memoFnAsync(fn, opt = {}) {
|
|
|
23
23
|
if (logHit) {
|
|
24
24
|
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFnAsync hit`);
|
|
25
25
|
}
|
|
26
|
+
if (value instanceof Error) {
|
|
27
|
+
throw value;
|
|
28
|
+
}
|
|
26
29
|
return value;
|
|
27
30
|
}
|
|
28
31
|
const started = Date.now();
|
package/dist-esm/index.js
CHANGED
|
@@ -56,4 +56,5 @@ export * from './string/safeJsonStringify';
|
|
|
56
56
|
import { PQueue } from './promise/pQueue';
|
|
57
57
|
export * from './seq/seq';
|
|
58
58
|
export * from './math/stack.util';
|
|
59
|
+
export * from './string/leven';
|
|
59
60
|
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, };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const array = [];
|
|
2
|
+
const characterCodeCache = [];
|
|
3
|
+
/* eslint-disable unicorn/prefer-code-point, no-bitwise */
|
|
4
|
+
/**
|
|
5
|
+
* Modified version of: https://github.com/sindresorhus/leven/
|
|
6
|
+
*
|
|
7
|
+
* Returns a Levenshtein distance between first and second word.
|
|
8
|
+
*/
|
|
9
|
+
export function _leven(first, second) {
|
|
10
|
+
if (first === second) {
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
const swap = first;
|
|
14
|
+
// Swapping the strings if `a` is longer than `b` so we know which one is the
|
|
15
|
+
// shortest & which one is the longest
|
|
16
|
+
if (first.length > second.length) {
|
|
17
|
+
first = second;
|
|
18
|
+
second = swap;
|
|
19
|
+
}
|
|
20
|
+
let firstLength = first.length;
|
|
21
|
+
let secondLength = second.length;
|
|
22
|
+
// Performing suffix trimming:
|
|
23
|
+
// We can linearly drop suffix common to both strings since they
|
|
24
|
+
// don't increase distance at all
|
|
25
|
+
// Note: `~-` is the bitwise way to perform a `- 1` operation
|
|
26
|
+
while (firstLength > 0 && first.charCodeAt(~-firstLength) === second.charCodeAt(~-secondLength)) {
|
|
27
|
+
firstLength--;
|
|
28
|
+
secondLength--;
|
|
29
|
+
}
|
|
30
|
+
// Performing prefix trimming
|
|
31
|
+
// We can linearly drop prefix common to both strings since they
|
|
32
|
+
// don't increase distance at all
|
|
33
|
+
let start = 0;
|
|
34
|
+
while (start < firstLength && first.charCodeAt(start) === second.charCodeAt(start)) {
|
|
35
|
+
start++;
|
|
36
|
+
}
|
|
37
|
+
firstLength -= start;
|
|
38
|
+
secondLength -= start;
|
|
39
|
+
if (firstLength === 0) {
|
|
40
|
+
return secondLength;
|
|
41
|
+
}
|
|
42
|
+
let bCharacterCode;
|
|
43
|
+
let result;
|
|
44
|
+
let temporary;
|
|
45
|
+
let temporary2;
|
|
46
|
+
let index = 0;
|
|
47
|
+
let index2 = 0;
|
|
48
|
+
while (index < firstLength) {
|
|
49
|
+
characterCodeCache[index] = first.charCodeAt(start + index);
|
|
50
|
+
array[index] = ++index;
|
|
51
|
+
}
|
|
52
|
+
while (index2 < secondLength) {
|
|
53
|
+
bCharacterCode = second.charCodeAt(start + index2);
|
|
54
|
+
temporary = index2++;
|
|
55
|
+
result = index2;
|
|
56
|
+
for (index = 0; index < firstLength; index++) {
|
|
57
|
+
temporary2 = bCharacterCode === characterCodeCache[index] ? temporary : temporary + 1;
|
|
58
|
+
temporary = array[index];
|
|
59
|
+
// eslint-disable-next-line no-multi-assign
|
|
60
|
+
result = array[index] =
|
|
61
|
+
temporary > result
|
|
62
|
+
? temporary2 > result
|
|
63
|
+
? result + 1
|
|
64
|
+
: temporary2
|
|
65
|
+
: temporary2 > temporary
|
|
66
|
+
? temporary + 1
|
|
67
|
+
: temporary2;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -18,9 +18,12 @@ export interface AsyncMemoOptions {
|
|
|
18
18
|
cacheKeyFn?: (args: any[]) => any
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* Default true.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
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.
|
|
24
27
|
*/
|
|
25
28
|
cacheRejections?: boolean
|
|
26
29
|
|
|
@@ -73,7 +76,7 @@ export const _AsyncMemo =
|
|
|
73
76
|
logger = console,
|
|
74
77
|
cacheFactory = () => new MapMemoCache(),
|
|
75
78
|
cacheKeyFn = jsonMemoSerializer,
|
|
76
|
-
cacheRejections =
|
|
79
|
+
cacheRejections = true,
|
|
77
80
|
} = opt
|
|
78
81
|
|
|
79
82
|
const keyStr = String(key)
|
|
@@ -110,7 +113,11 @@ export const _AsyncMemo =
|
|
|
110
113
|
)
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
|
|
116
|
+
if (value instanceof Error) {
|
|
117
|
+
throw value
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return value
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
// Here we know it's a MISS, let's execute the real method
|
|
@@ -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
|
|
@@ -18,8 +18,11 @@ export interface MemoOptions {
|
|
|
18
18
|
cacheKeyFn?: (args: any[]) => any
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* Defaults to
|
|
22
|
-
* Set to
|
|
21
|
+
* Defaults to true.
|
|
22
|
+
* Set to false to skip caching errors.
|
|
23
|
+
*
|
|
24
|
+
* True will ensure "max 1 execution", but will "remember" errors.
|
|
25
|
+
* False will allow >1 execution in case of errors.
|
|
23
26
|
*/
|
|
24
27
|
cacheErrors?: boolean
|
|
25
28
|
|
|
@@ -89,7 +92,7 @@ export const _Memo =
|
|
|
89
92
|
logger = console,
|
|
90
93
|
cacheFactory = () => new MapMemoCache(),
|
|
91
94
|
cacheKeyFn = jsonMemoSerializer,
|
|
92
|
-
cacheErrors =
|
|
95
|
+
cacheErrors = true,
|
|
93
96
|
} = opt
|
|
94
97
|
|
|
95
98
|
const keyStr = String(key)
|
|
@@ -97,8 +100,8 @@ export const _Memo =
|
|
|
97
100
|
|
|
98
101
|
descriptor.value = function (this: typeof target, ...args: any[]): any {
|
|
99
102
|
const ctx = this
|
|
100
|
-
|
|
101
103
|
const cacheKey = cacheKeyFn(args)
|
|
104
|
+
let value: any
|
|
102
105
|
|
|
103
106
|
if (!cache.has(ctx)) {
|
|
104
107
|
cache.set(ctx, cacheFactory())
|
|
@@ -109,21 +112,34 @@ export const _Memo =
|
|
|
109
112
|
)
|
|
110
113
|
}
|
|
111
114
|
|
|
112
|
-
|
|
115
|
+
value = cache.get(ctx)!.get(cacheKey)
|
|
116
|
+
|
|
117
|
+
if (value instanceof Error) {
|
|
118
|
+
throw value
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return value
|
|
113
122
|
}
|
|
114
123
|
|
|
115
124
|
const started = Date.now()
|
|
116
|
-
let value: any
|
|
117
125
|
|
|
118
126
|
try {
|
|
119
127
|
value = originalFn.apply(ctx, args)
|
|
120
128
|
|
|
121
|
-
|
|
129
|
+
try {
|
|
130
|
+
cache.get(ctx)!.set(cacheKey, value)
|
|
131
|
+
} catch (err) {
|
|
132
|
+
logger.error(err)
|
|
133
|
+
}
|
|
122
134
|
|
|
123
135
|
return value
|
|
124
136
|
} catch (err) {
|
|
125
137
|
if (cacheErrors) {
|
|
126
|
-
|
|
138
|
+
try {
|
|
139
|
+
cache.get(ctx)!.set(cacheKey, err)
|
|
140
|
+
} catch (err) {
|
|
141
|
+
logger.error(err)
|
|
142
|
+
}
|
|
127
143
|
}
|
|
128
144
|
|
|
129
145
|
throw err
|
package/src/decorators/memoFn.ts
CHANGED
|
@@ -21,7 +21,7 @@ export function _memoFn<T extends (...args: any[]) => any>(
|
|
|
21
21
|
logMiss = false,
|
|
22
22
|
logArgs = true,
|
|
23
23
|
logger = console,
|
|
24
|
-
cacheErrors =
|
|
24
|
+
cacheErrors = true,
|
|
25
25
|
cacheFactory = () => new MapMemoCache(),
|
|
26
26
|
cacheKeyFn = jsonMemoSerializer,
|
|
27
27
|
} = opt
|
|
@@ -32,28 +32,41 @@ export function _memoFn<T extends (...args: any[]) => any>(
|
|
|
32
32
|
const memoizedFn = function (this: any, ...args: any[]): T {
|
|
33
33
|
const ctx = this
|
|
34
34
|
const cacheKey = cacheKeyFn(args)
|
|
35
|
+
let value: any
|
|
35
36
|
|
|
36
37
|
if (cache.has(cacheKey)) {
|
|
37
38
|
if (logHit) {
|
|
38
39
|
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFn hit`)
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
value = cache.get(cacheKey)
|
|
43
|
+
|
|
44
|
+
if (value instanceof Error) {
|
|
45
|
+
throw value
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return value
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
const started = Date.now()
|
|
45
52
|
|
|
46
|
-
let value: any
|
|
47
|
-
|
|
48
53
|
try {
|
|
49
54
|
value = fn.apply(ctx, args)
|
|
50
55
|
|
|
51
|
-
|
|
56
|
+
try {
|
|
57
|
+
cache.set(cacheKey, value)
|
|
58
|
+
} catch (err) {
|
|
59
|
+
logger.error(err)
|
|
60
|
+
}
|
|
52
61
|
|
|
53
62
|
return value
|
|
54
63
|
} catch (err) {
|
|
55
64
|
if (cacheErrors) {
|
|
56
|
-
|
|
65
|
+
try {
|
|
66
|
+
cache.set(cacheKey, err)
|
|
67
|
+
} catch (err) {
|
|
68
|
+
logger.error(err)
|
|
69
|
+
}
|
|
57
70
|
}
|
|
58
71
|
|
|
59
72
|
throw err
|
|
@@ -20,7 +20,7 @@ export function _memoFnAsync<T extends (...args: any[]) => Promise<any>>(
|
|
|
20
20
|
logMiss = false,
|
|
21
21
|
logArgs = true,
|
|
22
22
|
logger = console,
|
|
23
|
-
cacheRejections =
|
|
23
|
+
cacheRejections = true,
|
|
24
24
|
cacheFactory = () => new MapMemoCache(),
|
|
25
25
|
cacheKeyFn = jsonMemoSerializer,
|
|
26
26
|
} = opt
|
|
@@ -44,6 +44,10 @@ export function _memoFnAsync<T extends (...args: any[]) => Promise<any>>(
|
|
|
44
44
|
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFnAsync hit`)
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
if (value instanceof Error) {
|
|
48
|
+
throw value
|
|
49
|
+
}
|
|
50
|
+
|
|
47
51
|
return value
|
|
48
52
|
}
|
|
49
53
|
|
package/src/index.ts
CHANGED
|
@@ -119,6 +119,7 @@ import {
|
|
|
119
119
|
SavedDBEntity,
|
|
120
120
|
StringMap,
|
|
121
121
|
UnixTimestamp,
|
|
122
|
+
Integer,
|
|
122
123
|
ValueOf,
|
|
123
124
|
ValuesOf,
|
|
124
125
|
AbortableMapper,
|
|
@@ -154,6 +155,7 @@ export * from './string/safeJsonStringify'
|
|
|
154
155
|
import { PQueue, PQueueCfg } from './promise/pQueue'
|
|
155
156
|
export * from './seq/seq'
|
|
156
157
|
export * from './math/stack.util'
|
|
158
|
+
export * from './string/leven'
|
|
157
159
|
|
|
158
160
|
export type {
|
|
159
161
|
AbortableMapper,
|
|
@@ -204,6 +206,7 @@ export type {
|
|
|
204
206
|
ConditionalExcept,
|
|
205
207
|
Class,
|
|
206
208
|
UnixTimestamp,
|
|
209
|
+
Integer,
|
|
207
210
|
BaseDBEntity,
|
|
208
211
|
SavedDBEntity,
|
|
209
212
|
Saved,
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const array: number[] = []
|
|
2
|
+
const characterCodeCache: number[] = []
|
|
3
|
+
|
|
4
|
+
/* eslint-disable unicorn/prefer-code-point, no-bitwise */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Modified version of: https://github.com/sindresorhus/leven/
|
|
8
|
+
*
|
|
9
|
+
* Returns a Levenshtein distance between first and second word.
|
|
10
|
+
*/
|
|
11
|
+
export function _leven(first: string, second: string): number {
|
|
12
|
+
if (first === second) {
|
|
13
|
+
return 0
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const swap = first
|
|
17
|
+
|
|
18
|
+
// Swapping the strings if `a` is longer than `b` so we know which one is the
|
|
19
|
+
// shortest & which one is the longest
|
|
20
|
+
if (first.length > second.length) {
|
|
21
|
+
first = second
|
|
22
|
+
second = swap
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let firstLength = first.length
|
|
26
|
+
let secondLength = second.length
|
|
27
|
+
|
|
28
|
+
// Performing suffix trimming:
|
|
29
|
+
// We can linearly drop suffix common to both strings since they
|
|
30
|
+
// don't increase distance at all
|
|
31
|
+
// Note: `~-` is the bitwise way to perform a `- 1` operation
|
|
32
|
+
while (firstLength > 0 && first.charCodeAt(~-firstLength) === second.charCodeAt(~-secondLength)) {
|
|
33
|
+
firstLength--
|
|
34
|
+
secondLength--
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Performing prefix trimming
|
|
38
|
+
// We can linearly drop prefix common to both strings since they
|
|
39
|
+
// don't increase distance at all
|
|
40
|
+
let start = 0
|
|
41
|
+
|
|
42
|
+
while (start < firstLength && first.charCodeAt(start) === second.charCodeAt(start)) {
|
|
43
|
+
start++
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
firstLength -= start
|
|
47
|
+
secondLength -= start
|
|
48
|
+
|
|
49
|
+
if (firstLength === 0) {
|
|
50
|
+
return secondLength
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let bCharacterCode
|
|
54
|
+
let result: number
|
|
55
|
+
let temporary: number
|
|
56
|
+
let temporary2: number
|
|
57
|
+
let index = 0
|
|
58
|
+
let index2 = 0
|
|
59
|
+
|
|
60
|
+
while (index < firstLength) {
|
|
61
|
+
characterCodeCache[index] = first.charCodeAt(start + index)
|
|
62
|
+
array[index] = ++index
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
while (index2 < secondLength) {
|
|
66
|
+
bCharacterCode = second.charCodeAt(start + index2)
|
|
67
|
+
temporary = index2++
|
|
68
|
+
result = index2
|
|
69
|
+
|
|
70
|
+
for (index = 0; index < firstLength; index++) {
|
|
71
|
+
temporary2 = bCharacterCode === characterCodeCache[index] ? temporary : temporary + 1
|
|
72
|
+
temporary = array[index]!
|
|
73
|
+
// eslint-disable-next-line no-multi-assign
|
|
74
|
+
result = array[index] =
|
|
75
|
+
temporary > result
|
|
76
|
+
? temporary2 > result
|
|
77
|
+
? result + 1
|
|
78
|
+
: temporary2
|
|
79
|
+
: temporary2 > temporary
|
|
80
|
+
? temporary + 1
|
|
81
|
+
: temporary2
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return result!
|
|
86
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -166,6 +166,11 @@ export type IsoDateTime = string
|
|
|
166
166
|
*/
|
|
167
167
|
export type UnixTimestamp = number
|
|
168
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Same as `number`, but with semantic meaning that it's an Integer.
|
|
171
|
+
*/
|
|
172
|
+
export type Integer = number
|
|
173
|
+
|
|
169
174
|
/**
|
|
170
175
|
* Base interface for any Entity that was saved to DB.
|
|
171
176
|
*/
|