@naturalcycles/js-lib 14.213.0 → 14.214.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/array/array.util.d.ts +3 -0
- package/dist/array/array.util.js +3 -0
- package/dist/decorators/asyncMemo.decorator.d.ts +4 -14
- package/dist/decorators/asyncMemo.decorator.js +5 -11
- package/dist/decorators/memo.decorator.d.ts +0 -13
- package/dist/decorators/memo.decorator.js +1 -11
- package/dist/decorators/memoFn.js +1 -13
- package/dist/decorators/memoFnAsync.js +1 -13
- package/dist/decorators/memoSimple.decorator.d.ts +0 -3
- package/dist/decorators/memoSimple.decorator.js +1 -8
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/object/map2.d.ts +15 -0
- package/dist/object/map2.js +25 -0
- package/dist/object/object.util.d.ts +3 -1
- package/dist/object/set2.d.ts +11 -0
- package/dist/object/set2.js +19 -0
- package/dist/string/stringify.js +6 -0
- package/dist-esm/array/array.util.js +3 -0
- package/dist-esm/decorators/asyncMemo.decorator.js +6 -12
- package/dist-esm/decorators/memo.decorator.js +2 -12
- package/dist-esm/decorators/memoFn.js +1 -13
- package/dist-esm/decorators/memoFnAsync.js +1 -13
- package/dist-esm/decorators/memoSimple.decorator.js +2 -9
- package/dist-esm/index.js +2 -0
- package/dist-esm/object/map2.js +21 -0
- package/dist-esm/object/set2.js +15 -0
- package/dist-esm/string/stringify.js +6 -0
- package/package.json +1 -1
- package/src/array/array.util.ts +3 -0
- package/src/decorators/asyncMemo.decorator.ts +5 -42
- package/src/decorators/memo.decorator.ts +1 -37
- package/src/decorators/memoFn.ts +0 -18
- package/src/decorators/memoFnAsync.ts +0 -18
- package/src/decorators/memoSimple.decorator.ts +2 -20
- package/src/index.ts +2 -0
- package/src/object/map2.ts +25 -0
- package/src/object/object.util.ts +3 -1
- package/src/object/set2.ts +18 -0
- package/src/string/stringify.ts +6 -0
- package/dist/lodash.types.d.ts +0 -4
- package/dist/lodash.types.js +0 -2
- package/dist-esm/lodash.types.js +0 -1
- package/src/lodash.types.ts +0 -6
|
@@ -104,6 +104,9 @@ export declare function _sortBy<T>(items: T[], mapper: Mapper<T, any>, mutate?:
|
|
|
104
104
|
export declare function _sortDescBy<T>(items: T[], mapper: Mapper<T, any>, mutate?: boolean): T[];
|
|
105
105
|
/**
|
|
106
106
|
* Like items.find(), but it tries to find from the END of the array.
|
|
107
|
+
*
|
|
108
|
+
* Node 18+ supports native array.findLast() - use that.
|
|
109
|
+
* iOS Safari only has it since 15.4
|
|
107
110
|
*/
|
|
108
111
|
export declare function _findLast<T>(items: T[], predicate: Predicate<T>): T | undefined;
|
|
109
112
|
export declare function _takeWhile<T>(items: T[], predicate: Predicate<T>): T[];
|
package/dist/array/array.util.js
CHANGED
|
@@ -173,6 +173,9 @@ function _sortDescBy(items, mapper, mutate = false) {
|
|
|
173
173
|
exports._sortDescBy = _sortDescBy;
|
|
174
174
|
/**
|
|
175
175
|
* Like items.find(), but it tries to find from the END of the array.
|
|
176
|
+
*
|
|
177
|
+
* Node 18+ supports native array.findLast() - use that.
|
|
178
|
+
* iOS Safari only has it since 15.4
|
|
176
179
|
*/
|
|
177
180
|
function _findLast(items, predicate) {
|
|
178
181
|
return [...items].reverse().find(predicate);
|
|
@@ -20,20 +20,6 @@ export interface AsyncMemoOptions {
|
|
|
20
20
|
* False will allow >1 execution in case of errors.
|
|
21
21
|
*/
|
|
22
22
|
cacheRejections?: boolean;
|
|
23
|
-
/**
|
|
24
|
-
* Default to false
|
|
25
|
-
*/
|
|
26
|
-
logHit?: boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Default to false
|
|
29
|
-
*/
|
|
30
|
-
logMiss?: boolean;
|
|
31
|
-
/**
|
|
32
|
-
* Set to `false` to skip logging method arguments.
|
|
33
|
-
*
|
|
34
|
-
* Defaults to true.
|
|
35
|
-
*/
|
|
36
|
-
logArgs?: boolean;
|
|
37
23
|
/**
|
|
38
24
|
* Default to `console`
|
|
39
25
|
*/
|
|
@@ -42,6 +28,10 @@ export interface AsyncMemoOptions {
|
|
|
42
28
|
/**
|
|
43
29
|
* Like @_Memo, but allowing async MemoCache implementation.
|
|
44
30
|
*
|
|
31
|
+
* Important: it awaits the method to return the result before caching it.
|
|
32
|
+
*
|
|
33
|
+
* todo: test for "swarm requests", it should return "the same promise" and not cause a swarm origin hit
|
|
34
|
+
*
|
|
45
35
|
* Method CANNOT return `undefined`, as undefined will always be treated as cache MISS and retried.
|
|
46
36
|
* Return `null` instead (it'll be cached).
|
|
47
37
|
*/
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports._AsyncMemo = void 0;
|
|
4
|
-
const time_util_1 = require("../time/time.util");
|
|
5
4
|
const decorator_util_1 = require("./decorator.util");
|
|
6
5
|
const memo_util_1 = require("./memo.util");
|
|
7
6
|
/**
|
|
8
7
|
* Like @_Memo, but allowing async MemoCache implementation.
|
|
9
8
|
*
|
|
9
|
+
* Important: it awaits the method to return the result before caching it.
|
|
10
|
+
*
|
|
11
|
+
* todo: test for "swarm requests", it should return "the same promise" and not cause a swarm origin hit
|
|
12
|
+
*
|
|
10
13
|
* Method CANNOT return `undefined`, as undefined will always be treated as cache MISS and retried.
|
|
11
14
|
* Return `null` instead (it'll be cached).
|
|
12
15
|
*/
|
|
@@ -18,7 +21,7 @@ const _AsyncMemo = (opt = {}) => (target, key, descriptor) => {
|
|
|
18
21
|
const originalFn = descriptor.value;
|
|
19
22
|
// Map from "instance" of the Class where @_AsyncMemo is applied to AsyncMemoCache instance.
|
|
20
23
|
const cache = new Map();
|
|
21
|
-
const {
|
|
24
|
+
const { logger = console, cacheFactory = () => new memo_util_1.MapMemoCache(), cacheKeyFn = memo_util_1.jsonMemoSerializer, cacheRejections = true, } = opt;
|
|
22
25
|
const keyStr = String(key);
|
|
23
26
|
const methodSignature = (0, decorator_util_1._getTargetMethodSignature)(target, keyStr);
|
|
24
27
|
descriptor.value = async function (...args) {
|
|
@@ -39,16 +42,12 @@ const _AsyncMemo = (opt = {}) => (target, key, descriptor) => {
|
|
|
39
42
|
}
|
|
40
43
|
if (value !== undefined) {
|
|
41
44
|
// hit!
|
|
42
|
-
if (logHit) {
|
|
43
|
-
logger.log(`${(0, decorator_util_1._getMethodSignature)(ctx, keyStr)}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) @_AsyncMemo hit`);
|
|
44
|
-
}
|
|
45
45
|
if (value instanceof Error) {
|
|
46
46
|
throw value;
|
|
47
47
|
}
|
|
48
48
|
return value;
|
|
49
49
|
}
|
|
50
50
|
// Here we know it's a MISS, let's execute the real method
|
|
51
|
-
const started = Date.now();
|
|
52
51
|
try {
|
|
53
52
|
value = await originalFn.apply(ctx, args);
|
|
54
53
|
// Save the value in the Cache, without awaiting it
|
|
@@ -80,11 +79,6 @@ const _AsyncMemo = (opt = {}) => (target, key, descriptor) => {
|
|
|
80
79
|
}
|
|
81
80
|
throw err;
|
|
82
81
|
}
|
|
83
|
-
finally {
|
|
84
|
-
if (logMiss) {
|
|
85
|
-
logger.log(`${(0, decorator_util_1._getMethodSignature)(ctx, keyStr)}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) @_AsyncMemo miss (${(0, time_util_1._since)(started)})`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
82
|
};
|
|
89
83
|
descriptor.value.dropCache = async () => {
|
|
90
84
|
logger.log(`${methodSignature} @_AsyncMemo.dropCache()`);
|
|
@@ -19,19 +19,6 @@ export interface MemoOptions {
|
|
|
19
19
|
* False will allow >1 execution in case of errors.
|
|
20
20
|
*/
|
|
21
21
|
cacheErrors?: boolean;
|
|
22
|
-
/**
|
|
23
|
-
* Default to false
|
|
24
|
-
*/
|
|
25
|
-
logHit?: boolean;
|
|
26
|
-
/**
|
|
27
|
-
* Default to false
|
|
28
|
-
*/
|
|
29
|
-
logMiss?: boolean;
|
|
30
|
-
/**
|
|
31
|
-
* Defaults to true.
|
|
32
|
-
* Set to false to skip logging method arguments.
|
|
33
|
-
*/
|
|
34
|
-
logArgs?: boolean;
|
|
35
22
|
/**
|
|
36
23
|
* Default to `console`
|
|
37
24
|
*/
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports._Memo = void 0;
|
|
4
|
-
const time_util_1 = require("../time/time.util");
|
|
5
4
|
const decorator_util_1 = require("./decorator.util");
|
|
6
5
|
const memo_util_1 = require("./memo.util");
|
|
7
6
|
/**
|
|
@@ -37,7 +36,7 @@ const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
|
37
36
|
// UPD: tests show that normal Map also doesn't leak (to be tested further)
|
|
38
37
|
// Normal Map is needed to allow .dropCache()
|
|
39
38
|
const cache = new Map();
|
|
40
|
-
const {
|
|
39
|
+
const { logger = console, cacheFactory = () => new memo_util_1.MapMemoCache(), cacheKeyFn = memo_util_1.jsonMemoSerializer, cacheErrors = true, } = opt;
|
|
41
40
|
const keyStr = String(key);
|
|
42
41
|
const methodSignature = (0, decorator_util_1._getTargetMethodSignature)(target, keyStr);
|
|
43
42
|
descriptor.value = function (...args) {
|
|
@@ -48,16 +47,12 @@ const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
|
48
47
|
cache.set(ctx, cacheFactory());
|
|
49
48
|
}
|
|
50
49
|
else if (cache.get(ctx).has(cacheKey)) {
|
|
51
|
-
if (logHit) {
|
|
52
|
-
logger.log(`${(0, decorator_util_1._getMethodSignature)(ctx, keyStr)}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) @_Memo hit`);
|
|
53
|
-
}
|
|
54
50
|
value = cache.get(ctx).get(cacheKey);
|
|
55
51
|
if (value instanceof Error) {
|
|
56
52
|
throw value;
|
|
57
53
|
}
|
|
58
54
|
return value;
|
|
59
55
|
}
|
|
60
|
-
const started = Date.now();
|
|
61
56
|
try {
|
|
62
57
|
value = originalFn.apply(ctx, args);
|
|
63
58
|
try {
|
|
@@ -79,11 +74,6 @@ const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
|
79
74
|
}
|
|
80
75
|
throw err;
|
|
81
76
|
}
|
|
82
|
-
finally {
|
|
83
|
-
if (logMiss) {
|
|
84
|
-
logger.log(`${(0, decorator_util_1._getMethodSignature)(ctx, keyStr)}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) @_Memo miss (${(0, time_util_1._since)(started)})`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
77
|
};
|
|
88
78
|
descriptor.value.dropCache = () => {
|
|
89
79
|
logger.log(`${methodSignature} @_Memo.dropCache()`);
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports._memoFn = void 0;
|
|
4
|
-
const time_util_1 = require("../time/time.util");
|
|
5
|
-
const decorator_util_1 = require("./decorator.util");
|
|
6
4
|
const memo_util_1 = require("./memo.util");
|
|
7
5
|
/**
|
|
8
6
|
* Only supports Sync functions.
|
|
@@ -10,24 +8,19 @@ const memo_util_1 = require("./memo.util");
|
|
|
10
8
|
* Technically, you can use it with Async functions, but it'll return the Promise without awaiting it.
|
|
11
9
|
*/
|
|
12
10
|
function _memoFn(fn, opt = {}) {
|
|
13
|
-
const {
|
|
11
|
+
const { logger = console, cacheErrors = true, cacheFactory = () => new memo_util_1.MapMemoCache(), cacheKeyFn = memo_util_1.jsonMemoSerializer, } = opt;
|
|
14
12
|
const cache = cacheFactory();
|
|
15
|
-
const fnName = fn.name;
|
|
16
13
|
const memoizedFn = function (...args) {
|
|
17
14
|
const ctx = this;
|
|
18
15
|
const cacheKey = cacheKeyFn(args);
|
|
19
16
|
let value;
|
|
20
17
|
if (cache.has(cacheKey)) {
|
|
21
|
-
if (logHit) {
|
|
22
|
-
logger.log(`${fnName}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) memoFn hit`);
|
|
23
|
-
}
|
|
24
18
|
value = cache.get(cacheKey);
|
|
25
19
|
if (value instanceof Error) {
|
|
26
20
|
throw value;
|
|
27
21
|
}
|
|
28
22
|
return value;
|
|
29
23
|
}
|
|
30
|
-
const started = Date.now();
|
|
31
24
|
try {
|
|
32
25
|
value = fn.apply(ctx, args);
|
|
33
26
|
try {
|
|
@@ -49,11 +42,6 @@ function _memoFn(fn, opt = {}) {
|
|
|
49
42
|
}
|
|
50
43
|
throw err;
|
|
51
44
|
}
|
|
52
|
-
finally {
|
|
53
|
-
if (logMiss) {
|
|
54
|
-
logger.log(`${fnName}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) memoFn miss (${(0, time_util_1._since)(started)})`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
45
|
};
|
|
58
46
|
Object.assign(memoizedFn, { cache });
|
|
59
47
|
return memoizedFn;
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports._memoFnAsync = void 0;
|
|
4
|
-
const time_util_1 = require("../time/time.util");
|
|
5
|
-
const decorator_util_1 = require("./decorator.util");
|
|
6
4
|
const memo_util_1 = require("./memo.util");
|
|
7
5
|
/**
|
|
8
6
|
* Only supports Sync functions.
|
|
9
7
|
* To support Async functions - use _memoFnAsync
|
|
10
8
|
*/
|
|
11
9
|
function _memoFnAsync(fn, opt = {}) {
|
|
12
|
-
const {
|
|
10
|
+
const { logger = console, cacheRejections = true, cacheFactory = () => new memo_util_1.MapMemoCache(), cacheKeyFn = memo_util_1.jsonMemoSerializer, } = opt;
|
|
13
11
|
const cache = cacheFactory();
|
|
14
|
-
const fnName = fn.name;
|
|
15
12
|
const memoizedFn = async function (...args) {
|
|
16
13
|
const ctx = this;
|
|
17
14
|
const cacheKey = cacheKeyFn(args);
|
|
@@ -23,15 +20,11 @@ function _memoFnAsync(fn, opt = {}) {
|
|
|
23
20
|
logger.error(err);
|
|
24
21
|
}
|
|
25
22
|
if (value !== undefined) {
|
|
26
|
-
if (logHit) {
|
|
27
|
-
logger.log(`${fnName}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) memoFnAsync hit`);
|
|
28
|
-
}
|
|
29
23
|
if (value instanceof Error) {
|
|
30
24
|
throw value;
|
|
31
25
|
}
|
|
32
26
|
return value;
|
|
33
27
|
}
|
|
34
|
-
const started = Date.now();
|
|
35
28
|
try {
|
|
36
29
|
value = await fn.apply(ctx, args);
|
|
37
30
|
void (async () => {
|
|
@@ -57,11 +50,6 @@ function _memoFnAsync(fn, opt = {}) {
|
|
|
57
50
|
}
|
|
58
51
|
throw err;
|
|
59
52
|
}
|
|
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
53
|
};
|
|
66
54
|
Object.assign(memoizedFn, { cache });
|
|
67
55
|
return memoizedFn;
|
|
@@ -37,23 +37,16 @@ const memoSimple = (opt = {}) => (target, key, descriptor) => {
|
|
|
37
37
|
}
|
|
38
38
|
*/
|
|
39
39
|
const cache = new memo_util_1.MapMemoCache();
|
|
40
|
-
const {
|
|
40
|
+
const { 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) {
|
|
44
44
|
const ctx = this;
|
|
45
45
|
const cacheKey = (0, memo_util_1.jsonMemoSerializer)(args);
|
|
46
46
|
if (cache.has(cacheKey)) {
|
|
47
|
-
if (logHit) {
|
|
48
|
-
logger.log(`${methodSignature}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) @memo hit`);
|
|
49
|
-
}
|
|
50
47
|
return cache.get(cacheKey);
|
|
51
48
|
}
|
|
52
|
-
const d = Date.now();
|
|
53
49
|
const res = originalFn.apply(ctx, args);
|
|
54
|
-
if (logMiss) {
|
|
55
|
-
logger.log(`${methodSignature}(${(0, decorator_util_1._getArgsSignature)(args, logArgs)}) @memo miss (${Date.now() - d} ms)`);
|
|
56
|
-
}
|
|
57
50
|
cache.set(cacheKey, res);
|
|
58
51
|
return res;
|
|
59
52
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ export * from './object/deepEquals';
|
|
|
35
35
|
export * from './object/object.util';
|
|
36
36
|
export * from './object/sortObject';
|
|
37
37
|
export * from './object/sortObjectDeep';
|
|
38
|
+
export * from './object/map2';
|
|
39
|
+
export * from './object/set2';
|
|
38
40
|
export * from './promise/pDefer';
|
|
39
41
|
export * from './promise/pDelay';
|
|
40
42
|
export * from './promise/pFilter';
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,8 @@ tslib_1.__exportStar(require("./object/deepEquals"), exports);
|
|
|
39
39
|
tslib_1.__exportStar(require("./object/object.util"), exports);
|
|
40
40
|
tslib_1.__exportStar(require("./object/sortObject"), exports);
|
|
41
41
|
tslib_1.__exportStar(require("./object/sortObjectDeep"), exports);
|
|
42
|
+
tslib_1.__exportStar(require("./object/map2"), exports);
|
|
43
|
+
tslib_1.__exportStar(require("./object/set2"), exports);
|
|
42
44
|
tslib_1.__exportStar(require("./promise/pDefer"), exports);
|
|
43
45
|
tslib_1.__exportStar(require("./promise/pDelay"), exports);
|
|
44
46
|
tslib_1.__exportStar(require("./promise/pFilter"), exports);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Like Map, but serializes to JSON as an object.
|
|
3
|
+
*
|
|
4
|
+
* Fixes the "issue" of stock Map being json-serialized as `{}`.
|
|
5
|
+
*
|
|
6
|
+
* @experimental
|
|
7
|
+
*/
|
|
8
|
+
export declare class Map2<K = any, V = any> extends Map<K, V> {
|
|
9
|
+
/**
|
|
10
|
+
* Convenience way to create Map2 from object.
|
|
11
|
+
*/
|
|
12
|
+
static of<V>(obj: Record<any, V>): Map2<string, V>;
|
|
13
|
+
toObject(): Record<string, V>;
|
|
14
|
+
toJSON(): Record<string, V>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Map2 = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Like Map, but serializes to JSON as an object.
|
|
6
|
+
*
|
|
7
|
+
* Fixes the "issue" of stock Map being json-serialized as `{}`.
|
|
8
|
+
*
|
|
9
|
+
* @experimental
|
|
10
|
+
*/
|
|
11
|
+
class Map2 extends Map {
|
|
12
|
+
/**
|
|
13
|
+
* Convenience way to create Map2 from object.
|
|
14
|
+
*/
|
|
15
|
+
static of(obj) {
|
|
16
|
+
return new Map2(Object.entries(obj));
|
|
17
|
+
}
|
|
18
|
+
toObject() {
|
|
19
|
+
return Object.fromEntries(this);
|
|
20
|
+
}
|
|
21
|
+
toJSON() {
|
|
22
|
+
return Object.fromEntries(this);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.Map2 = Map2;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { PropertyPath } from '../lodash.types';
|
|
2
1
|
import { KeyValueTuple, Reviver, SKIP } from '../types';
|
|
3
2
|
import type { AnyObject, ObjectMapper, ObjectPredicate, ValueOf } from '../types';
|
|
4
3
|
/**
|
|
@@ -146,6 +145,8 @@ export declare function _invertMap<K, V>(m: ReadonlyMap<K, V>): Map<V, K>;
|
|
|
146
145
|
* _get(obj, 'unknown.path') // undefined
|
|
147
146
|
*/
|
|
148
147
|
export declare function _get<T extends AnyObject>(obj?: T, path?: string): unknown;
|
|
148
|
+
type Many<T> = T | readonly T[];
|
|
149
|
+
type PropertyPath = Many<PropertyKey>;
|
|
149
150
|
/**
|
|
150
151
|
* Sets the value at path of object. If a portion of path doesn’t exist it’s created. Arrays are created for
|
|
151
152
|
* missing index properties while objects are created for all other missing properties.
|
|
@@ -207,3 +208,4 @@ export declare function _deepFreeze(o: any): void;
|
|
|
207
208
|
* To make mutation extra clear - function returns void (unlike Object.assign).
|
|
208
209
|
*/
|
|
209
210
|
export declare function _objectAssignExact<T extends AnyObject>(target: T, source: T): void;
|
|
211
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Set2 = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Like Set, but serializes to JSON as an array.
|
|
6
|
+
*
|
|
7
|
+
* Fixes the "issue" of stock Set being json-serialized as `{}`.
|
|
8
|
+
*
|
|
9
|
+
* @experimental
|
|
10
|
+
*/
|
|
11
|
+
class Set2 extends Set {
|
|
12
|
+
toArray() {
|
|
13
|
+
return [...this];
|
|
14
|
+
}
|
|
15
|
+
toJSON() {
|
|
16
|
+
return [...this];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.Set2 = Set2;
|
package/dist/string/stringify.js
CHANGED
|
@@ -111,6 +111,12 @@ function _stringify(obj, opt = {}) {
|
|
|
111
111
|
//
|
|
112
112
|
// Other
|
|
113
113
|
//
|
|
114
|
+
if (obj instanceof Map) {
|
|
115
|
+
obj = Object.fromEntries(obj);
|
|
116
|
+
}
|
|
117
|
+
else if (obj instanceof Set) {
|
|
118
|
+
obj = [...obj];
|
|
119
|
+
}
|
|
114
120
|
try {
|
|
115
121
|
const { stringifyFn = globalStringifyFunction } = opt;
|
|
116
122
|
s = stringifyFn(obj, undefined, 2);
|
|
@@ -160,6 +160,9 @@ export function _sortDescBy(items, mapper, mutate = false) {
|
|
|
160
160
|
}
|
|
161
161
|
/**
|
|
162
162
|
* Like items.find(), but it tries to find from the END of the array.
|
|
163
|
+
*
|
|
164
|
+
* Node 18+ supports native array.findLast() - use that.
|
|
165
|
+
* iOS Safari only has it since 15.4
|
|
163
166
|
*/
|
|
164
167
|
export function _findLast(items, predicate) {
|
|
165
168
|
return [...items].reverse().find(predicate);
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { _getArgsSignature, _getMethodSignature, _getTargetMethodSignature } from './decorator.util';
|
|
1
|
+
import { _getTargetMethodSignature } from './decorator.util';
|
|
3
2
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
4
3
|
/**
|
|
5
4
|
* Like @_Memo, but allowing async MemoCache implementation.
|
|
6
5
|
*
|
|
6
|
+
* Important: it awaits the method to return the result before caching it.
|
|
7
|
+
*
|
|
8
|
+
* todo: test for "swarm requests", it should return "the same promise" and not cause a swarm origin hit
|
|
9
|
+
*
|
|
7
10
|
* Method CANNOT return `undefined`, as undefined will always be treated as cache MISS and retried.
|
|
8
11
|
* Return `null` instead (it'll be cached).
|
|
9
12
|
*/
|
|
@@ -15,7 +18,7 @@ export const _AsyncMemo = (opt = {}) => (target, key, descriptor) => {
|
|
|
15
18
|
const originalFn = descriptor.value;
|
|
16
19
|
// Map from "instance" of the Class where @_AsyncMemo is applied to AsyncMemoCache instance.
|
|
17
20
|
const cache = new Map();
|
|
18
|
-
const {
|
|
21
|
+
const { logger = console, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, cacheRejections = true, } = opt;
|
|
19
22
|
const keyStr = String(key);
|
|
20
23
|
const methodSignature = _getTargetMethodSignature(target, keyStr);
|
|
21
24
|
descriptor.value = async function (...args) {
|
|
@@ -36,16 +39,12 @@ export const _AsyncMemo = (opt = {}) => (target, key, descriptor) => {
|
|
|
36
39
|
}
|
|
37
40
|
if (value !== undefined) {
|
|
38
41
|
// hit!
|
|
39
|
-
if (logHit) {
|
|
40
|
-
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, logArgs)}) @_AsyncMemo hit`);
|
|
41
|
-
}
|
|
42
42
|
if (value instanceof Error) {
|
|
43
43
|
throw value;
|
|
44
44
|
}
|
|
45
45
|
return value;
|
|
46
46
|
}
|
|
47
47
|
// Here we know it's a MISS, let's execute the real method
|
|
48
|
-
const started = Date.now();
|
|
49
48
|
try {
|
|
50
49
|
value = await originalFn.apply(ctx, args);
|
|
51
50
|
// Save the value in the Cache, without awaiting it
|
|
@@ -77,11 +76,6 @@ export const _AsyncMemo = (opt = {}) => (target, key, descriptor) => {
|
|
|
77
76
|
}
|
|
78
77
|
throw err;
|
|
79
78
|
}
|
|
80
|
-
finally {
|
|
81
|
-
if (logMiss) {
|
|
82
|
-
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, logArgs)}) @_AsyncMemo miss (${_since(started)})`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
79
|
};
|
|
86
80
|
descriptor.value.dropCache = async () => {
|
|
87
81
|
logger.log(`${methodSignature} @_AsyncMemo.dropCache()`);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { _getArgsSignature, _getMethodSignature, _getTargetMethodSignature } from './decorator.util';
|
|
1
|
+
import { _getTargetMethodSignature } from './decorator.util';
|
|
3
2
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
4
3
|
/**
|
|
5
4
|
* Memoizes the method of the class, so it caches the output and returns the cached version if the "key"
|
|
@@ -34,7 +33,7 @@ export const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
|
34
33
|
// UPD: tests show that normal Map also doesn't leak (to be tested further)
|
|
35
34
|
// Normal Map is needed to allow .dropCache()
|
|
36
35
|
const cache = new Map();
|
|
37
|
-
const {
|
|
36
|
+
const { logger = console, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, cacheErrors = true, } = opt;
|
|
38
37
|
const keyStr = String(key);
|
|
39
38
|
const methodSignature = _getTargetMethodSignature(target, keyStr);
|
|
40
39
|
descriptor.value = function (...args) {
|
|
@@ -45,16 +44,12 @@ export const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
|
45
44
|
cache.set(ctx, cacheFactory());
|
|
46
45
|
}
|
|
47
46
|
else if (cache.get(ctx).has(cacheKey)) {
|
|
48
|
-
if (logHit) {
|
|
49
|
-
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, logArgs)}) @_Memo hit`);
|
|
50
|
-
}
|
|
51
47
|
value = cache.get(ctx).get(cacheKey);
|
|
52
48
|
if (value instanceof Error) {
|
|
53
49
|
throw value;
|
|
54
50
|
}
|
|
55
51
|
return value;
|
|
56
52
|
}
|
|
57
|
-
const started = Date.now();
|
|
58
53
|
try {
|
|
59
54
|
value = originalFn.apply(ctx, args);
|
|
60
55
|
try {
|
|
@@ -76,11 +71,6 @@ export const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
|
76
71
|
}
|
|
77
72
|
throw err;
|
|
78
73
|
}
|
|
79
|
-
finally {
|
|
80
|
-
if (logMiss) {
|
|
81
|
-
logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, logArgs)}) @_Memo miss (${_since(started)})`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
74
|
};
|
|
85
75
|
descriptor.value.dropCache = () => {
|
|
86
76
|
logger.log(`${methodSignature} @_Memo.dropCache()`);
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { _since } from '../time/time.util';
|
|
2
|
-
import { _getArgsSignature } from './decorator.util';
|
|
3
1
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
4
2
|
/**
|
|
5
3
|
* Only supports Sync functions.
|
|
@@ -7,24 +5,19 @@ import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
|
7
5
|
* Technically, you can use it with Async functions, but it'll return the Promise without awaiting it.
|
|
8
6
|
*/
|
|
9
7
|
export function _memoFn(fn, opt = {}) {
|
|
10
|
-
const {
|
|
8
|
+
const { logger = console, cacheErrors = true, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, } = opt;
|
|
11
9
|
const cache = cacheFactory();
|
|
12
|
-
const fnName = fn.name;
|
|
13
10
|
const memoizedFn = function (...args) {
|
|
14
11
|
const ctx = this;
|
|
15
12
|
const cacheKey = cacheKeyFn(args);
|
|
16
13
|
let value;
|
|
17
14
|
if (cache.has(cacheKey)) {
|
|
18
|
-
if (logHit) {
|
|
19
|
-
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFn hit`);
|
|
20
|
-
}
|
|
21
15
|
value = cache.get(cacheKey);
|
|
22
16
|
if (value instanceof Error) {
|
|
23
17
|
throw value;
|
|
24
18
|
}
|
|
25
19
|
return value;
|
|
26
20
|
}
|
|
27
|
-
const started = Date.now();
|
|
28
21
|
try {
|
|
29
22
|
value = fn.apply(ctx, args);
|
|
30
23
|
try {
|
|
@@ -46,11 +39,6 @@ export function _memoFn(fn, opt = {}) {
|
|
|
46
39
|
}
|
|
47
40
|
throw err;
|
|
48
41
|
}
|
|
49
|
-
finally {
|
|
50
|
-
if (logMiss) {
|
|
51
|
-
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFn miss (${_since(started)})`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
42
|
};
|
|
55
43
|
Object.assign(memoizedFn, { cache });
|
|
56
44
|
return memoizedFn;
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import { _since } from '../time/time.util';
|
|
2
|
-
import { _getArgsSignature } from './decorator.util';
|
|
3
1
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
4
2
|
/**
|
|
5
3
|
* Only supports Sync functions.
|
|
6
4
|
* To support Async functions - use _memoFnAsync
|
|
7
5
|
*/
|
|
8
6
|
export function _memoFnAsync(fn, opt = {}) {
|
|
9
|
-
const {
|
|
7
|
+
const { logger = console, cacheRejections = true, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, } = opt;
|
|
10
8
|
const cache = cacheFactory();
|
|
11
|
-
const fnName = fn.name;
|
|
12
9
|
const memoizedFn = async function (...args) {
|
|
13
10
|
const ctx = this;
|
|
14
11
|
const cacheKey = cacheKeyFn(args);
|
|
@@ -20,15 +17,11 @@ export function _memoFnAsync(fn, opt = {}) {
|
|
|
20
17
|
logger.error(err);
|
|
21
18
|
}
|
|
22
19
|
if (value !== undefined) {
|
|
23
|
-
if (logHit) {
|
|
24
|
-
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFnAsync hit`);
|
|
25
|
-
}
|
|
26
20
|
if (value instanceof Error) {
|
|
27
21
|
throw value;
|
|
28
22
|
}
|
|
29
23
|
return value;
|
|
30
24
|
}
|
|
31
|
-
const started = Date.now();
|
|
32
25
|
try {
|
|
33
26
|
value = await fn.apply(ctx, args);
|
|
34
27
|
void (async () => {
|
|
@@ -54,11 +47,6 @@ export function _memoFnAsync(fn, opt = {}) {
|
|
|
54
47
|
}
|
|
55
48
|
throw err;
|
|
56
49
|
}
|
|
57
|
-
finally {
|
|
58
|
-
if (logMiss) {
|
|
59
|
-
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFnAsync miss (${_since(started)})`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
50
|
};
|
|
63
51
|
Object.assign(memoizedFn, { cache });
|
|
64
52
|
return memoizedFn;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// http://decodize.com/blog/2012/08/27/javascript-memoization-caching-results-for-better-performance/
|
|
4
4
|
// http://inlehmansterms.net/2015/03/01/javascript-memoization/
|
|
5
5
|
// https://community.risingstack.com/the-worlds-fastest-javascript-memoization-library/
|
|
6
|
-
import {
|
|
6
|
+
import { _getTargetMethodSignature } from './decorator.util';
|
|
7
7
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util';
|
|
8
8
|
// memoSimple decorator is NOT exported. Only used in benchmarks currently
|
|
9
9
|
/**
|
|
@@ -34,23 +34,16 @@ export const memoSimple = (opt = {}) => (target, key, descriptor) => {
|
|
|
34
34
|
}
|
|
35
35
|
*/
|
|
36
36
|
const cache = new MapMemoCache();
|
|
37
|
-
const {
|
|
37
|
+
const { logger = console } = 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 = jsonMemoSerializer(args);
|
|
43
43
|
if (cache.has(cacheKey)) {
|
|
44
|
-
if (logHit) {
|
|
45
|
-
logger.log(`${methodSignature}(${_getArgsSignature(args, logArgs)}) @memo hit`);
|
|
46
|
-
}
|
|
47
44
|
return cache.get(cacheKey);
|
|
48
45
|
}
|
|
49
|
-
const d = Date.now();
|
|
50
46
|
const res = originalFn.apply(ctx, args);
|
|
51
|
-
if (logMiss) {
|
|
52
|
-
logger.log(`${methodSignature}(${_getArgsSignature(args, logArgs)}) @memo miss (${Date.now() - d} ms)`);
|
|
53
|
-
}
|
|
54
47
|
cache.set(cacheKey, res);
|
|
55
48
|
return res;
|
|
56
49
|
};
|
package/dist-esm/index.js
CHANGED
|
@@ -35,6 +35,8 @@ export * from './object/deepEquals';
|
|
|
35
35
|
export * from './object/object.util';
|
|
36
36
|
export * from './object/sortObject';
|
|
37
37
|
export * from './object/sortObjectDeep';
|
|
38
|
+
export * from './object/map2';
|
|
39
|
+
export * from './object/set2';
|
|
38
40
|
export * from './promise/pDefer';
|
|
39
41
|
export * from './promise/pDelay';
|
|
40
42
|
export * from './promise/pFilter';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Like Map, but serializes to JSON as an object.
|
|
3
|
+
*
|
|
4
|
+
* Fixes the "issue" of stock Map being json-serialized as `{}`.
|
|
5
|
+
*
|
|
6
|
+
* @experimental
|
|
7
|
+
*/
|
|
8
|
+
export class Map2 extends Map {
|
|
9
|
+
/**
|
|
10
|
+
* Convenience way to create Map2 from object.
|
|
11
|
+
*/
|
|
12
|
+
static of(obj) {
|
|
13
|
+
return new Map2(Object.entries(obj));
|
|
14
|
+
}
|
|
15
|
+
toObject() {
|
|
16
|
+
return Object.fromEntries(this);
|
|
17
|
+
}
|
|
18
|
+
toJSON() {
|
|
19
|
+
return Object.fromEntries(this);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Like Set, but serializes to JSON as an array.
|
|
3
|
+
*
|
|
4
|
+
* Fixes the "issue" of stock Set being json-serialized as `{}`.
|
|
5
|
+
*
|
|
6
|
+
* @experimental
|
|
7
|
+
*/
|
|
8
|
+
export class Set2 extends Set {
|
|
9
|
+
toArray() {
|
|
10
|
+
return [...this];
|
|
11
|
+
}
|
|
12
|
+
toJSON() {
|
|
13
|
+
return [...this];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -107,6 +107,12 @@ export function _stringify(obj, opt = {}) {
|
|
|
107
107
|
//
|
|
108
108
|
// Other
|
|
109
109
|
//
|
|
110
|
+
if (obj instanceof Map) {
|
|
111
|
+
obj = Object.fromEntries(obj);
|
|
112
|
+
}
|
|
113
|
+
else if (obj instanceof Set) {
|
|
114
|
+
obj = [...obj];
|
|
115
|
+
}
|
|
110
116
|
try {
|
|
111
117
|
const { stringifyFn = globalStringifyFunction } = opt;
|
|
112
118
|
s = stringifyFn(obj, undefined, 2);
|
package/package.json
CHANGED
package/src/array/array.util.ts
CHANGED
|
@@ -196,6 +196,9 @@ export function _sortDescBy<T>(items: T[], mapper: Mapper<T, any>, mutate = fals
|
|
|
196
196
|
|
|
197
197
|
/**
|
|
198
198
|
* Like items.find(), but it tries to find from the END of the array.
|
|
199
|
+
*
|
|
200
|
+
* Node 18+ supports native array.findLast() - use that.
|
|
201
|
+
* iOS Safari only has it since 15.4
|
|
199
202
|
*/
|
|
200
203
|
export function _findLast<T>(items: T[], predicate: Predicate<T>): T | undefined {
|
|
201
204
|
return [...items].reverse().find(predicate)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { CommonLogger } from '../log/commonLogger'
|
|
2
|
-
import { _since } from '../time/time.util'
|
|
3
2
|
import type { AnyObject } from '../types'
|
|
4
|
-
import {
|
|
3
|
+
import { _getTargetMethodSignature } from './decorator.util'
|
|
5
4
|
import type { AsyncMemoCache } from './memo.util'
|
|
6
5
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util'
|
|
7
6
|
|
|
@@ -28,23 +27,6 @@ export interface AsyncMemoOptions {
|
|
|
28
27
|
*/
|
|
29
28
|
cacheRejections?: boolean
|
|
30
29
|
|
|
31
|
-
/**
|
|
32
|
-
* Default to false
|
|
33
|
-
*/
|
|
34
|
-
logHit?: boolean
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Default to false
|
|
38
|
-
*/
|
|
39
|
-
logMiss?: boolean
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Set to `false` to skip logging method arguments.
|
|
43
|
-
*
|
|
44
|
-
* Defaults to true.
|
|
45
|
-
*/
|
|
46
|
-
logArgs?: boolean
|
|
47
|
-
|
|
48
30
|
/**
|
|
49
31
|
* Default to `console`
|
|
50
32
|
*/
|
|
@@ -54,6 +36,10 @@ export interface AsyncMemoOptions {
|
|
|
54
36
|
/**
|
|
55
37
|
* Like @_Memo, but allowing async MemoCache implementation.
|
|
56
38
|
*
|
|
39
|
+
* Important: it awaits the method to return the result before caching it.
|
|
40
|
+
*
|
|
41
|
+
* todo: test for "swarm requests", it should return "the same promise" and not cause a swarm origin hit
|
|
42
|
+
*
|
|
57
43
|
* Method CANNOT return `undefined`, as undefined will always be treated as cache MISS and retried.
|
|
58
44
|
* Return `null` instead (it'll be cached).
|
|
59
45
|
*/
|
|
@@ -71,9 +57,6 @@ export const _AsyncMemo =
|
|
|
71
57
|
const cache = new Map<AnyObject, AsyncMemoCache>()
|
|
72
58
|
|
|
73
59
|
const {
|
|
74
|
-
logHit = false,
|
|
75
|
-
logMiss = false,
|
|
76
|
-
logArgs = true,
|
|
77
60
|
logger = console,
|
|
78
61
|
cacheFactory = () => new MapMemoCache(),
|
|
79
62
|
cacheKeyFn = jsonMemoSerializer,
|
|
@@ -105,15 +88,6 @@ export const _AsyncMemo =
|
|
|
105
88
|
|
|
106
89
|
if (value !== undefined) {
|
|
107
90
|
// hit!
|
|
108
|
-
if (logHit) {
|
|
109
|
-
logger.log(
|
|
110
|
-
`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(
|
|
111
|
-
args,
|
|
112
|
-
logArgs,
|
|
113
|
-
)}) @_AsyncMemo hit`,
|
|
114
|
-
)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
91
|
if (value instanceof Error) {
|
|
118
92
|
throw value
|
|
119
93
|
}
|
|
@@ -122,8 +96,6 @@ export const _AsyncMemo =
|
|
|
122
96
|
}
|
|
123
97
|
|
|
124
98
|
// Here we know it's a MISS, let's execute the real method
|
|
125
|
-
const started = Date.now()
|
|
126
|
-
|
|
127
99
|
try {
|
|
128
100
|
value = await originalFn.apply(ctx, args)
|
|
129
101
|
|
|
@@ -154,15 +126,6 @@ export const _AsyncMemo =
|
|
|
154
126
|
}
|
|
155
127
|
|
|
156
128
|
throw err
|
|
157
|
-
} finally {
|
|
158
|
-
if (logMiss) {
|
|
159
|
-
logger.log(
|
|
160
|
-
`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(
|
|
161
|
-
args,
|
|
162
|
-
logArgs,
|
|
163
|
-
)}) @_AsyncMemo miss (${_since(started)})`,
|
|
164
|
-
)
|
|
165
|
-
}
|
|
166
129
|
}
|
|
167
130
|
} as any
|
|
168
131
|
;(descriptor.value as any).dropCache = async () => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { CommonLogger } from '../log/commonLogger'
|
|
2
|
-
import { _since } from '../time/time.util'
|
|
3
2
|
import type { AnyObject } from '../types'
|
|
4
|
-
import {
|
|
3
|
+
import { _getTargetMethodSignature } from './decorator.util'
|
|
5
4
|
import type { MemoCache } from './memo.util'
|
|
6
5
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util'
|
|
7
6
|
|
|
@@ -27,21 +26,6 @@ export interface MemoOptions {
|
|
|
27
26
|
*/
|
|
28
27
|
cacheErrors?: boolean
|
|
29
28
|
|
|
30
|
-
/**
|
|
31
|
-
* Default to false
|
|
32
|
-
*/
|
|
33
|
-
logHit?: boolean
|
|
34
|
-
/**
|
|
35
|
-
* Default to false
|
|
36
|
-
*/
|
|
37
|
-
logMiss?: boolean
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Defaults to true.
|
|
41
|
-
* Set to false to skip logging method arguments.
|
|
42
|
-
*/
|
|
43
|
-
logArgs?: boolean
|
|
44
|
-
|
|
45
29
|
/**
|
|
46
30
|
* Default to `console`
|
|
47
31
|
*/
|
|
@@ -87,9 +71,6 @@ export const _Memo =
|
|
|
87
71
|
const cache = new Map<AnyObject, MemoCache>()
|
|
88
72
|
|
|
89
73
|
const {
|
|
90
|
-
logHit = false,
|
|
91
|
-
logMiss = false,
|
|
92
|
-
logArgs = true,
|
|
93
74
|
logger = console,
|
|
94
75
|
cacheFactory = () => new MapMemoCache(),
|
|
95
76
|
cacheKeyFn = jsonMemoSerializer,
|
|
@@ -107,12 +88,6 @@ export const _Memo =
|
|
|
107
88
|
if (!cache.has(ctx)) {
|
|
108
89
|
cache.set(ctx, cacheFactory())
|
|
109
90
|
} else if (cache.get(ctx)!.has(cacheKey)) {
|
|
110
|
-
if (logHit) {
|
|
111
|
-
logger.log(
|
|
112
|
-
`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, logArgs)}) @_Memo hit`,
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
91
|
value = cache.get(ctx)!.get(cacheKey)
|
|
117
92
|
|
|
118
93
|
if (value instanceof Error) {
|
|
@@ -122,8 +97,6 @@ export const _Memo =
|
|
|
122
97
|
return value
|
|
123
98
|
}
|
|
124
99
|
|
|
125
|
-
const started = Date.now()
|
|
126
|
-
|
|
127
100
|
try {
|
|
128
101
|
value = originalFn.apply(ctx, args)
|
|
129
102
|
|
|
@@ -144,15 +117,6 @@ export const _Memo =
|
|
|
144
117
|
}
|
|
145
118
|
|
|
146
119
|
throw err
|
|
147
|
-
} finally {
|
|
148
|
-
if (logMiss) {
|
|
149
|
-
logger.log(
|
|
150
|
-
`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(
|
|
151
|
-
args,
|
|
152
|
-
logArgs,
|
|
153
|
-
)}) @_Memo miss (${_since(started)})`,
|
|
154
|
-
)
|
|
155
|
-
}
|
|
156
120
|
}
|
|
157
121
|
} as any
|
|
158
122
|
;(descriptor.value as any).dropCache = () => {
|
package/src/decorators/memoFn.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { _since } from '../time/time.util'
|
|
2
|
-
import { _getArgsSignature } from './decorator.util'
|
|
3
1
|
import type { MemoOptions } from './memo.decorator'
|
|
4
2
|
import type { MemoCache } from './memo.util'
|
|
5
3
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util'
|
|
@@ -18,9 +16,6 @@ export function _memoFn<T extends (...args: any[]) => any>(
|
|
|
18
16
|
opt: MemoOptions = {},
|
|
19
17
|
): T & MemoizedFunction {
|
|
20
18
|
const {
|
|
21
|
-
logHit = false,
|
|
22
|
-
logMiss = false,
|
|
23
|
-
logArgs = true,
|
|
24
19
|
logger = console,
|
|
25
20
|
cacheErrors = true,
|
|
26
21
|
cacheFactory = () => new MapMemoCache(),
|
|
@@ -28,7 +23,6 @@ export function _memoFn<T extends (...args: any[]) => any>(
|
|
|
28
23
|
} = opt
|
|
29
24
|
|
|
30
25
|
const cache = cacheFactory()
|
|
31
|
-
const fnName = fn.name
|
|
32
26
|
|
|
33
27
|
const memoizedFn = function (this: any, ...args: any[]): T {
|
|
34
28
|
const ctx = this
|
|
@@ -36,10 +30,6 @@ export function _memoFn<T extends (...args: any[]) => any>(
|
|
|
36
30
|
let value: any
|
|
37
31
|
|
|
38
32
|
if (cache.has(cacheKey)) {
|
|
39
|
-
if (logHit) {
|
|
40
|
-
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFn hit`)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
33
|
value = cache.get(cacheKey)
|
|
44
34
|
|
|
45
35
|
if (value instanceof Error) {
|
|
@@ -49,8 +39,6 @@ export function _memoFn<T extends (...args: any[]) => any>(
|
|
|
49
39
|
return value
|
|
50
40
|
}
|
|
51
41
|
|
|
52
|
-
const started = Date.now()
|
|
53
|
-
|
|
54
42
|
try {
|
|
55
43
|
value = fn.apply(ctx, args)
|
|
56
44
|
|
|
@@ -71,12 +59,6 @@ export function _memoFn<T extends (...args: any[]) => any>(
|
|
|
71
59
|
}
|
|
72
60
|
|
|
73
61
|
throw err
|
|
74
|
-
} finally {
|
|
75
|
-
if (logMiss) {
|
|
76
|
-
logger.log(
|
|
77
|
-
`${fnName}(${_getArgsSignature(args, logArgs)}) memoFn miss (${_since(started)})`,
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
62
|
}
|
|
81
63
|
}
|
|
82
64
|
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { _since } from '../time/time.util'
|
|
2
1
|
import type { AsyncMemoOptions } from './asyncMemo.decorator'
|
|
3
|
-
import { _getArgsSignature } from './decorator.util'
|
|
4
2
|
import type { AsyncMemoCache } from './memo.util'
|
|
5
3
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util'
|
|
6
4
|
|
|
@@ -17,9 +15,6 @@ export function _memoFnAsync<T extends (...args: any[]) => Promise<any>>(
|
|
|
17
15
|
opt: AsyncMemoOptions = {},
|
|
18
16
|
): T & MemoizedAsyncFunction {
|
|
19
17
|
const {
|
|
20
|
-
logHit = false,
|
|
21
|
-
logMiss = false,
|
|
22
|
-
logArgs = true,
|
|
23
18
|
logger = console,
|
|
24
19
|
cacheRejections = true,
|
|
25
20
|
cacheFactory = () => new MapMemoCache(),
|
|
@@ -27,7 +22,6 @@ export function _memoFnAsync<T extends (...args: any[]) => Promise<any>>(
|
|
|
27
22
|
} = opt
|
|
28
23
|
|
|
29
24
|
const cache = cacheFactory()
|
|
30
|
-
const fnName = fn.name
|
|
31
25
|
|
|
32
26
|
const memoizedFn = async function (this: any, ...args: any[]): Promise<any> {
|
|
33
27
|
const ctx = this
|
|
@@ -41,10 +35,6 @@ export function _memoFnAsync<T extends (...args: any[]) => Promise<any>>(
|
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
if (value !== undefined) {
|
|
44
|
-
if (logHit) {
|
|
45
|
-
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFnAsync hit`)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
38
|
if (value instanceof Error) {
|
|
49
39
|
throw value
|
|
50
40
|
}
|
|
@@ -52,8 +42,6 @@ export function _memoFnAsync<T extends (...args: any[]) => Promise<any>>(
|
|
|
52
42
|
return value
|
|
53
43
|
}
|
|
54
44
|
|
|
55
|
-
const started = Date.now()
|
|
56
|
-
|
|
57
45
|
try {
|
|
58
46
|
value = await fn.apply(ctx, args)
|
|
59
47
|
|
|
@@ -78,12 +66,6 @@ export function _memoFnAsync<T extends (...args: any[]) => Promise<any>>(
|
|
|
78
66
|
}
|
|
79
67
|
|
|
80
68
|
throw err
|
|
81
|
-
} finally {
|
|
82
|
-
if (logMiss) {
|
|
83
|
-
logger.log(
|
|
84
|
-
`${fnName}(${_getArgsSignature(args, logArgs)}) memoFnAsync miss (${_since(started)})`,
|
|
85
|
-
)
|
|
86
|
-
}
|
|
87
69
|
}
|
|
88
70
|
}
|
|
89
71
|
|
|
@@ -12,14 +12,11 @@ Benchmark shows similar perf for ObjectCache and MapCache.
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type { CommonLogger } from '../log/commonLogger'
|
|
15
|
-
import {
|
|
15
|
+
import { _getTargetMethodSignature } from './decorator.util'
|
|
16
16
|
import type { MemoCache } from './memo.util'
|
|
17
17
|
import { jsonMemoSerializer, MapMemoCache } from './memo.util'
|
|
18
18
|
|
|
19
19
|
export interface MemoOpts {
|
|
20
|
-
logHit?: boolean
|
|
21
|
-
logMiss?: boolean
|
|
22
|
-
logArgs?: boolean
|
|
23
20
|
logger?: CommonLogger
|
|
24
21
|
}
|
|
25
22
|
|
|
@@ -58,7 +55,7 @@ export const memoSimple =
|
|
|
58
55
|
*/
|
|
59
56
|
const cache: MemoCache = new MapMemoCache()
|
|
60
57
|
|
|
61
|
-
const {
|
|
58
|
+
const { logger = console } = opt
|
|
62
59
|
const keyStr = String(key)
|
|
63
60
|
const methodSignature = _getTargetMethodSignature(target, keyStr)
|
|
64
61
|
|
|
@@ -67,26 +64,11 @@ export const memoSimple =
|
|
|
67
64
|
const cacheKey = jsonMemoSerializer(args)
|
|
68
65
|
|
|
69
66
|
if (cache.has(cacheKey)) {
|
|
70
|
-
if (logHit) {
|
|
71
|
-
logger.log(`${methodSignature}(${_getArgsSignature(args, logArgs)}) @memo hit`)
|
|
72
|
-
}
|
|
73
67
|
return cache.get(cacheKey)
|
|
74
68
|
}
|
|
75
69
|
|
|
76
|
-
const d = Date.now()
|
|
77
|
-
|
|
78
70
|
const res: any = originalFn.apply(ctx, args)
|
|
79
|
-
|
|
80
|
-
if (logMiss) {
|
|
81
|
-
logger.log(
|
|
82
|
-
`${methodSignature}(${_getArgsSignature(args, logArgs)}) @memo miss (${
|
|
83
|
-
Date.now() - d
|
|
84
|
-
} ms)`,
|
|
85
|
-
)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
71
|
cache.set(cacheKey, res)
|
|
89
|
-
|
|
90
72
|
return res
|
|
91
73
|
} as any
|
|
92
74
|
;(descriptor.value as any).dropCache = () => {
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,8 @@ export * from './object/deepEquals'
|
|
|
35
35
|
export * from './object/object.util'
|
|
36
36
|
export * from './object/sortObject'
|
|
37
37
|
export * from './object/sortObjectDeep'
|
|
38
|
+
export * from './object/map2'
|
|
39
|
+
export * from './object/set2'
|
|
38
40
|
export * from './promise/pDefer'
|
|
39
41
|
export * from './promise/pDelay'
|
|
40
42
|
export * from './promise/pFilter'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Like Map, but serializes to JSON as an object.
|
|
3
|
+
*
|
|
4
|
+
* Fixes the "issue" of stock Map being json-serialized as `{}`.
|
|
5
|
+
*
|
|
6
|
+
* @experimental
|
|
7
|
+
*/
|
|
8
|
+
export class Map2<K = any, V = any> extends Map<K, V> {
|
|
9
|
+
/**
|
|
10
|
+
* Convenience way to create Map2 from object.
|
|
11
|
+
*/
|
|
12
|
+
static of<V>(obj: Record<any, V>): Map2<string, V> {
|
|
13
|
+
return new Map2(Object.entries(obj))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
toObject(): Record<string, V> {
|
|
17
|
+
return Object.fromEntries(this)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
toJSON(): Record<string, V> {
|
|
21
|
+
return Object.fromEntries(this)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// consider more helpful .toString() ?
|
|
25
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { _isEmpty, _isObject } from '../is.util'
|
|
2
|
-
import type { PropertyPath } from '../lodash.types'
|
|
3
2
|
import { _objectEntries, KeyValueTuple, Reviver, SKIP } from '../types'
|
|
4
3
|
import type { AnyObject, ObjectMapper, ObjectPredicate, ValueOf } from '../types'
|
|
5
4
|
|
|
@@ -328,6 +327,9 @@ export function _get<T extends AnyObject>(obj = {} as T, path = ''): unknown {
|
|
|
328
327
|
.reduce((o, p) => o?.[p], obj)
|
|
329
328
|
}
|
|
330
329
|
|
|
330
|
+
type Many<T> = T | readonly T[]
|
|
331
|
+
type PropertyPath = Many<PropertyKey>
|
|
332
|
+
|
|
331
333
|
/**
|
|
332
334
|
* Sets the value at path of object. If a portion of path doesn’t exist it’s created. Arrays are created for
|
|
333
335
|
* missing index properties while objects are created for all other missing properties.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Like Set, but serializes to JSON as an array.
|
|
3
|
+
*
|
|
4
|
+
* Fixes the "issue" of stock Set being json-serialized as `{}`.
|
|
5
|
+
*
|
|
6
|
+
* @experimental
|
|
7
|
+
*/
|
|
8
|
+
export class Set2<T = any> extends Set<T> {
|
|
9
|
+
toArray(): T[] {
|
|
10
|
+
return [...this]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
toJSON(): T[] {
|
|
14
|
+
return [...this]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// consider more helpful .toString() ?
|
|
18
|
+
}
|
package/src/string/stringify.ts
CHANGED
|
@@ -158,6 +158,12 @@ export function _stringify(obj: any, opt: StringifyOptions = {}): string {
|
|
|
158
158
|
//
|
|
159
159
|
// Other
|
|
160
160
|
//
|
|
161
|
+
if (obj instanceof Map) {
|
|
162
|
+
obj = Object.fromEntries(obj)
|
|
163
|
+
} else if (obj instanceof Set) {
|
|
164
|
+
obj = [...obj]
|
|
165
|
+
}
|
|
166
|
+
|
|
161
167
|
try {
|
|
162
168
|
const { stringifyFn = globalStringifyFunction } = opt
|
|
163
169
|
|
package/dist/lodash.types.d.ts
DELETED
package/dist/lodash.types.js
DELETED
package/dist-esm/lodash.types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|