@naturalcycles/js-lib 15.69.0 → 15.71.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/asyncManager.d.ts +50 -0
- package/dist/asyncManager.js +83 -0
- package/dist/browser/adminService.js +0 -1
- package/dist/decorators/asyncMemo.decorator.js +0 -1
- package/dist/decorators/debounce.decorator.js +0 -2
- package/dist/decorators/logMethod.decorator.js +0 -1
- package/dist/decorators/memo.decorator.js +0 -1
- package/dist/decorators/retry.decorator.js +0 -1
- package/dist/decorators/swarmSafe.decorator.js +0 -1
- package/dist/decorators/timeout.decorator.js +0 -1
- package/dist/error/tryCatch.js +0 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/number/number.util.js +0 -1
- package/dist/object/keySortedMap.js +0 -1
- package/dist/object/lazyKeySortedMap.js +0 -1
- package/dist/promise/pProps.js +0 -2
- package/dist/string/safeJsonStringify.js +0 -1
- package/dist/types.d.ts +8 -0
- package/package.json +2 -2
- package/src/asyncManager.ts +96 -0
- package/src/browser/adminService.ts +0 -1
- package/src/browser/analytics.util.ts +0 -2
- package/src/decorators/asyncMemo.decorator.ts +0 -1
- package/src/decorators/debounce.decorator.ts +0 -2
- package/src/decorators/logMethod.decorator.ts +0 -1
- package/src/decorators/memo.decorator.ts +0 -1
- package/src/decorators/retry.decorator.ts +0 -1
- package/src/decorators/swarmSafe.decorator.ts +0 -1
- package/src/decorators/timeout.decorator.ts +0 -1
- package/src/error/tryCatch.ts +0 -1
- package/src/index.ts +1 -0
- package/src/number/number.util.ts +0 -1
- package/src/object/keySortedMap.ts +0 -1
- package/src/object/lazyKeySortedMap.ts +0 -1
- package/src/promise/pProps.ts +1 -5
- package/src/string/safeJsonStringify.ts +0 -1
- package/src/types.ts +10 -2
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { CommonLogger } from './log/commonLogger.js';
|
|
2
|
+
import type { NumberOfMilliseconds } from './types.js';
|
|
3
|
+
declare class AsyncManagerImpl {
|
|
4
|
+
logger: CommonLogger;
|
|
5
|
+
pendingOps: Set<Promise<unknown>>;
|
|
6
|
+
private onErrorHooks;
|
|
7
|
+
runInBackground(promise: Promise<unknown>): void;
|
|
8
|
+
onError(fn: OnErrorHook): void;
|
|
9
|
+
/**
|
|
10
|
+
* Resolves when all pending operations settle.
|
|
11
|
+
* They may resolve or reject, allDone will never throw.
|
|
12
|
+
* Errors (rejections) are reported to onErrorHooks (instead).
|
|
13
|
+
*
|
|
14
|
+
* If timeout is specified - it resolves if timeout has reached.
|
|
15
|
+
*/
|
|
16
|
+
allDone(timeout?: NumberOfMilliseconds): Promise<void>;
|
|
17
|
+
reset(): void;
|
|
18
|
+
private fireOnErrorHooks;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Singleton which keeps track of async operations - "voided promise-returning functions"
|
|
22
|
+
* that should run in parallel to the main request.
|
|
23
|
+
*
|
|
24
|
+
* It is an alternative to do `void doSomeAnalytics()`, which should run in parallel
|
|
25
|
+
* and not block the request (not slow down nor fail the request on analytics api failure).
|
|
26
|
+
*
|
|
27
|
+
* At the same time, `void doSomeAnalytics()` gets completely detached and untracked,
|
|
28
|
+
* nothing awaits it, its rejection becomes unhandledRejection (and may kill Node.js process).
|
|
29
|
+
*
|
|
30
|
+
* With AsyncManager, you instead register all those "voided" calls like this:
|
|
31
|
+
*
|
|
32
|
+
* AsyncManager.runInBackground(doSomeAnalytics())
|
|
33
|
+
*
|
|
34
|
+
* Then, in a few places you may be interested to ensure that all async operations have been finished.
|
|
35
|
+
* The places can be:
|
|
36
|
+
* - Graceful shutdown of a backend service
|
|
37
|
+
* - Before the end of runScript
|
|
38
|
+
* - At the end of each unit test, to make sure async ops don't leak
|
|
39
|
+
*
|
|
40
|
+
* You ensure no pending async operations like this:
|
|
41
|
+
*
|
|
42
|
+
* await AsyncManager.allDone()
|
|
43
|
+
*
|
|
44
|
+
* which never throws, but instead awaits all operations to be settled.
|
|
45
|
+
*
|
|
46
|
+
* @experimental
|
|
47
|
+
*/
|
|
48
|
+
export declare const AsyncManager: AsyncManagerImpl;
|
|
49
|
+
export type OnErrorHook = (err: Error) => any;
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
class AsyncManagerImpl {
|
|
2
|
+
logger = console;
|
|
3
|
+
pendingOps = new Set();
|
|
4
|
+
onErrorHooks = [];
|
|
5
|
+
runInBackground(promise) {
|
|
6
|
+
const wrappedPromise = promise
|
|
7
|
+
.catch(err => this.fireOnErrorHooks(err))
|
|
8
|
+
.finally(() => this.pendingOps.delete(wrappedPromise));
|
|
9
|
+
this.pendingOps.add(wrappedPromise);
|
|
10
|
+
}
|
|
11
|
+
onError(fn) {
|
|
12
|
+
this.onErrorHooks.push(fn);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Resolves when all pending operations settle.
|
|
16
|
+
* They may resolve or reject, allDone will never throw.
|
|
17
|
+
* Errors (rejections) are reported to onErrorHooks (instead).
|
|
18
|
+
*
|
|
19
|
+
* If timeout is specified - it resolves if timeout has reached.
|
|
20
|
+
*/
|
|
21
|
+
async allDone(timeout) {
|
|
22
|
+
const { size } = this.pendingOps;
|
|
23
|
+
if (!size)
|
|
24
|
+
return;
|
|
25
|
+
const { logger } = this;
|
|
26
|
+
const started = Date.now();
|
|
27
|
+
if (timeout) {
|
|
28
|
+
const result = await Promise.race([
|
|
29
|
+
Promise.allSettled(this.pendingOps),
|
|
30
|
+
new Promise(resolve => setTimeout(resolve, timeout, 'timeout')),
|
|
31
|
+
]);
|
|
32
|
+
if (result === 'timeout') {
|
|
33
|
+
logger.warn(`AsyncManager.allDone timed out after ${timeout} ms with ${this.pendingOps.size} pending op(s)`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
await Promise.allSettled(this.pendingOps);
|
|
39
|
+
}
|
|
40
|
+
logger.log(`AsyncManager.allDone for ${size} op(s) in ${Date.now() - started} ms`);
|
|
41
|
+
}
|
|
42
|
+
reset() {
|
|
43
|
+
this.pendingOps.clear();
|
|
44
|
+
this.onErrorHooks = [];
|
|
45
|
+
}
|
|
46
|
+
fireOnErrorHooks(err) {
|
|
47
|
+
if (this.onErrorHooks.length) {
|
|
48
|
+
this.onErrorHooks.forEach(hook => hook(err));
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.logger.error('AsyncManager unhandled rejection:', err);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Singleton which keeps track of async operations - "voided promise-returning functions"
|
|
57
|
+
* that should run in parallel to the main request.
|
|
58
|
+
*
|
|
59
|
+
* It is an alternative to do `void doSomeAnalytics()`, which should run in parallel
|
|
60
|
+
* and not block the request (not slow down nor fail the request on analytics api failure).
|
|
61
|
+
*
|
|
62
|
+
* At the same time, `void doSomeAnalytics()` gets completely detached and untracked,
|
|
63
|
+
* nothing awaits it, its rejection becomes unhandledRejection (and may kill Node.js process).
|
|
64
|
+
*
|
|
65
|
+
* With AsyncManager, you instead register all those "voided" calls like this:
|
|
66
|
+
*
|
|
67
|
+
* AsyncManager.runInBackground(doSomeAnalytics())
|
|
68
|
+
*
|
|
69
|
+
* Then, in a few places you may be interested to ensure that all async operations have been finished.
|
|
70
|
+
* The places can be:
|
|
71
|
+
* - Graceful shutdown of a backend service
|
|
72
|
+
* - Before the end of runScript
|
|
73
|
+
* - At the end of each unit test, to make sure async ops don't leak
|
|
74
|
+
*
|
|
75
|
+
* You ensure no pending async operations like this:
|
|
76
|
+
*
|
|
77
|
+
* await AsyncManager.allDone()
|
|
78
|
+
*
|
|
79
|
+
* which never throws, but instead awaits all operations to be settled.
|
|
80
|
+
*
|
|
81
|
+
* @experimental
|
|
82
|
+
*/
|
|
83
|
+
export const AsyncManager = new AsyncManagerImpl();
|
|
@@ -10,7 +10,6 @@ import { jsonMemoSerializer } from './memo.util.js';
|
|
|
10
10
|
*
|
|
11
11
|
* @experimental consider normal `@_Memo` for most of the cases, it's stable and predictable
|
|
12
12
|
*/
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
14
13
|
export const _AsyncMemo = (opt) => (target, key, descriptor) => {
|
|
15
14
|
_assertTypeOf(descriptor.value, 'function', 'Memoization can be applied only to methods');
|
|
16
15
|
const originalFn = descriptor.value;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { _debounce, _throttle } from './debounce.js';
|
|
2
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
3
2
|
export function _Debounce(wait, opt = {}) {
|
|
4
3
|
return (_target, _key, descriptor) => {
|
|
5
4
|
const originalFn = descriptor.value;
|
|
@@ -7,7 +6,6 @@ export function _Debounce(wait, opt = {}) {
|
|
|
7
6
|
return descriptor;
|
|
8
7
|
};
|
|
9
8
|
}
|
|
10
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
11
9
|
export function _Throttle(wait, opt = {}) {
|
|
12
10
|
return (_target, _key, descriptor) => {
|
|
13
11
|
const originalFn = descriptor.value;
|
|
@@ -16,7 +16,6 @@ import { _getArgsSignature, _getMethodSignature } from './decorator.util.js';
|
|
|
16
16
|
* >> asyncMethod()
|
|
17
17
|
* << asyncMethodThrow() took 10 ms ERROR: MyError
|
|
18
18
|
*/
|
|
19
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
20
19
|
export function _LogMethod(opt = {}) {
|
|
21
20
|
return (_target, key, descriptor) => {
|
|
22
21
|
_assert(typeof descriptor.value === 'function', '@_LogMethod can be applied only to methods');
|
|
@@ -22,7 +22,6 @@ import { jsonMemoSerializer, MapMemoCache } from './memo.util.js';
|
|
|
22
22
|
* http://inlehmansterms.net/2015/03/01/javascript-memoization/
|
|
23
23
|
* https://community.risingstack.com/the-worlds-fastest-javascript-memoization-library/
|
|
24
24
|
*/
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
26
25
|
export const _Memo = (opt = {}) => (target, key, descriptor) => {
|
|
27
26
|
_assertTypeOf(descriptor.value, 'function', 'Memoization can be applied only to methods');
|
|
28
27
|
const originalFn = descriptor.value;
|
|
@@ -7,7 +7,6 @@ import { _getTargetMethodSignature } from './decorator.util.js';
|
|
|
7
7
|
* Does not support `cacheKey`.
|
|
8
8
|
* So, the same Promise is returned, regardless of the arguments.
|
|
9
9
|
*/
|
|
10
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
11
10
|
export const _SwarmSafe = () => (target, key, descriptor) => {
|
|
12
11
|
if (typeof descriptor.value !== 'function') {
|
|
13
12
|
throw new TypeError('@_SwarmSafe can be applied only to methods');
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { _assert } from '../error/assert.js';
|
|
2
2
|
import { pTimeout } from '../promise/pTimeout.js';
|
|
3
3
|
import { _getMethodSignature } from './decorator.util.js';
|
|
4
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
5
4
|
export function _Timeout(opt) {
|
|
6
5
|
return (_target, key, descriptor) => {
|
|
7
6
|
_assert(typeof descriptor.value === 'function', '@_Timeout can be applied only to methods');
|
package/dist/error/tryCatch.js
CHANGED
|
@@ -34,7 +34,6 @@ export function _tryCatch(fn, opt = {}) {
|
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
38
37
|
export const _TryCatch = (opt = {}) => (_target, _key, descriptor) => {
|
|
39
38
|
const originalFn = descriptor.value;
|
|
40
39
|
descriptor.value = _tryCatch(originalFn, opt);
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/promise/pProps.js
CHANGED
|
@@ -14,7 +14,5 @@
|
|
|
14
14
|
*/
|
|
15
15
|
export async function pProps(input) {
|
|
16
16
|
const keys = Object.keys(input);
|
|
17
|
-
// `as any` here is added to make it compile when `noUncheckedIndexedAccess` is false
|
|
18
|
-
// oxlint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
19
17
|
return Object.fromEntries((await Promise.all(Object.values(input))).map((v, i) => [keys[i], v]));
|
|
20
18
|
}
|
|
@@ -13,7 +13,6 @@ export function _safeJsonStringify(obj, replacer, spaces, cycleReplacer) {
|
|
|
13
13
|
return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces);
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
// oxlint-disable no-unused-expressions
|
|
17
16
|
function serializer(replacer, cycleReplacer) {
|
|
18
17
|
const stack = [];
|
|
19
18
|
const keys = [];
|
package/dist/types.d.ts
CHANGED
|
@@ -84,6 +84,14 @@ export type AnyAsyncFunction<T = any> = (...args: any[]) => Promise<T>;
|
|
|
84
84
|
export type AsyncFunction<T = any> = () => Promise<T>;
|
|
85
85
|
export type AnyPromisableFunction<T = any> = (...args: any[]) => Promisable<T>;
|
|
86
86
|
export type PromisableFunction<T = any> = () => Promisable<T>;
|
|
87
|
+
export type AnySyncFunction<T = any> = (...args: any[]) => NotPromise<T>;
|
|
88
|
+
export type SyncFunction<T = any> = () => NotPromise<T>;
|
|
89
|
+
/**
|
|
90
|
+
* Compile-time guard against accidentally returning a Promise from a sync context.
|
|
91
|
+
* Example usage: `fn: () => NotPromise<T>`
|
|
92
|
+
* This would fail if fn returns a Promise (e.g an async function).
|
|
93
|
+
*/
|
|
94
|
+
export type NotPromise<T> = [T] extends [PromiseLike<any>] ? never : T;
|
|
87
95
|
/**
|
|
88
96
|
* A function that lazily calculates something.
|
|
89
97
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/js-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.71.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"tslib": "^2"
|
|
7
7
|
},
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"@types/crypto-js": "^4",
|
|
18
18
|
"@types/node": "^25",
|
|
19
19
|
"@types/semver": "^7",
|
|
20
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
20
|
+
"@typescript/native-preview": "7.0.0-dev.20260301.1",
|
|
21
21
|
"crypto-js": "^4",
|
|
22
22
|
"dayjs": "^1",
|
|
23
23
|
"@naturalcycles/dev-lib": "18.4.2"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { CommonLogger } from './log/commonLogger.js'
|
|
2
|
+
import type { NumberOfMilliseconds } from './types.js'
|
|
3
|
+
|
|
4
|
+
class AsyncManagerImpl {
|
|
5
|
+
logger: CommonLogger = console
|
|
6
|
+
|
|
7
|
+
pendingOps = new Set<Promise<unknown>>()
|
|
8
|
+
|
|
9
|
+
private onErrorHooks: OnErrorHook[] = []
|
|
10
|
+
|
|
11
|
+
runInBackground(promise: Promise<unknown>): void {
|
|
12
|
+
const wrappedPromise = promise
|
|
13
|
+
.catch(err => this.fireOnErrorHooks(err))
|
|
14
|
+
.finally(() => this.pendingOps.delete(wrappedPromise))
|
|
15
|
+
this.pendingOps.add(wrappedPromise)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
onError(fn: OnErrorHook): void {
|
|
19
|
+
this.onErrorHooks.push(fn)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolves when all pending operations settle.
|
|
24
|
+
* They may resolve or reject, allDone will never throw.
|
|
25
|
+
* Errors (rejections) are reported to onErrorHooks (instead).
|
|
26
|
+
*
|
|
27
|
+
* If timeout is specified - it resolves if timeout has reached.
|
|
28
|
+
*/
|
|
29
|
+
async allDone(timeout?: NumberOfMilliseconds): Promise<void> {
|
|
30
|
+
const { size } = this.pendingOps
|
|
31
|
+
if (!size) return
|
|
32
|
+
const { logger } = this
|
|
33
|
+
|
|
34
|
+
const started = Date.now()
|
|
35
|
+
if (timeout) {
|
|
36
|
+
const result = await Promise.race([
|
|
37
|
+
Promise.allSettled(this.pendingOps),
|
|
38
|
+
new Promise<'timeout'>(resolve => setTimeout(resolve, timeout, 'timeout')),
|
|
39
|
+
])
|
|
40
|
+
if (result === 'timeout') {
|
|
41
|
+
logger.warn(
|
|
42
|
+
`AsyncManager.allDone timed out after ${timeout} ms with ${this.pendingOps.size} pending op(s)`,
|
|
43
|
+
)
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
await Promise.allSettled(this.pendingOps)
|
|
48
|
+
}
|
|
49
|
+
logger.log(`AsyncManager.allDone for ${size} op(s) in ${Date.now() - started} ms`)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
reset(): void {
|
|
53
|
+
this.pendingOps.clear()
|
|
54
|
+
this.onErrorHooks = []
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private fireOnErrorHooks(err: any): void {
|
|
58
|
+
if (this.onErrorHooks.length) {
|
|
59
|
+
this.onErrorHooks.forEach(hook => hook(err))
|
|
60
|
+
} else {
|
|
61
|
+
this.logger.error('AsyncManager unhandled rejection:', err)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Singleton which keeps track of async operations - "voided promise-returning functions"
|
|
68
|
+
* that should run in parallel to the main request.
|
|
69
|
+
*
|
|
70
|
+
* It is an alternative to do `void doSomeAnalytics()`, which should run in parallel
|
|
71
|
+
* and not block the request (not slow down nor fail the request on analytics api failure).
|
|
72
|
+
*
|
|
73
|
+
* At the same time, `void doSomeAnalytics()` gets completely detached and untracked,
|
|
74
|
+
* nothing awaits it, its rejection becomes unhandledRejection (and may kill Node.js process).
|
|
75
|
+
*
|
|
76
|
+
* With AsyncManager, you instead register all those "voided" calls like this:
|
|
77
|
+
*
|
|
78
|
+
* AsyncManager.runInBackground(doSomeAnalytics())
|
|
79
|
+
*
|
|
80
|
+
* Then, in a few places you may be interested to ensure that all async operations have been finished.
|
|
81
|
+
* The places can be:
|
|
82
|
+
* - Graceful shutdown of a backend service
|
|
83
|
+
* - Before the end of runScript
|
|
84
|
+
* - At the end of each unit test, to make sure async ops don't leak
|
|
85
|
+
*
|
|
86
|
+
* You ensure no pending async operations like this:
|
|
87
|
+
*
|
|
88
|
+
* await AsyncManager.allDone()
|
|
89
|
+
*
|
|
90
|
+
* which never throws, but instead awaits all operations to be settled.
|
|
91
|
+
*
|
|
92
|
+
* @experimental
|
|
93
|
+
*/
|
|
94
|
+
export const AsyncManager = new AsyncManagerImpl()
|
|
95
|
+
|
|
96
|
+
export type OnErrorHook = (err: Error) => any
|
|
@@ -43,7 +43,6 @@ export interface AsyncMemoInstance {
|
|
|
43
43
|
*
|
|
44
44
|
* @experimental consider normal `@_Memo` for most of the cases, it's stable and predictable
|
|
45
45
|
*/
|
|
46
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
47
46
|
export const _AsyncMemo =
|
|
48
47
|
<FN>(opt: AsyncMemoOptions<FN>): MethodDecorator<FN> =>
|
|
49
48
|
(target, key, descriptor) => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { DebounceOptions, ThrottleOptions } from './debounce.js'
|
|
2
2
|
import { _debounce, _throttle } from './debounce.js'
|
|
3
3
|
|
|
4
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
5
4
|
export function _Debounce(wait: number, opt: DebounceOptions = {}): MethodDecorator {
|
|
6
5
|
return (_target, _key, descriptor) => {
|
|
7
6
|
const originalFn = descriptor.value
|
|
@@ -10,7 +9,6 @@ export function _Debounce(wait: number, opt: DebounceOptions = {}): MethodDecora
|
|
|
10
9
|
}
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
14
12
|
export function _Throttle(wait: number, opt: ThrottleOptions = {}): MethodDecorator {
|
|
15
13
|
return (_target, _key, descriptor) => {
|
|
16
14
|
const originalFn = descriptor.value
|
|
@@ -71,7 +71,6 @@ export interface LogMethodOptions {
|
|
|
71
71
|
* >> asyncMethod()
|
|
72
72
|
* << asyncMethodThrow() took 10 ms ERROR: MyError
|
|
73
73
|
*/
|
|
74
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
75
74
|
export function _LogMethod(opt: LogMethodOptions = {}): MethodDecorator {
|
|
76
75
|
return (_target, key, descriptor) => {
|
|
77
76
|
_assert(typeof descriptor.value === 'function', '@_LogMethod can be applied only to methods')
|
|
@@ -56,7 +56,6 @@ export interface MemoInstance {
|
|
|
56
56
|
* http://inlehmansterms.net/2015/03/01/javascript-memoization/
|
|
57
57
|
* https://community.risingstack.com/the-worlds-fastest-javascript-memoization-library/
|
|
58
58
|
*/
|
|
59
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
60
59
|
export const _Memo =
|
|
61
60
|
<FN>(opt: MemoOptions<FN> = {}): MethodDecorator<FN> =>
|
|
62
61
|
(target, key, descriptor) => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { PRetryOptions } from '../promise/pRetry.js'
|
|
2
2
|
import { pRetryFn } from '../promise/pRetry.js'
|
|
3
3
|
|
|
4
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
5
4
|
export function _Retry(opt: PRetryOptions = {}): MethodDecorator {
|
|
6
5
|
return (_target, _key, descriptor) => {
|
|
7
6
|
const originalFn = descriptor.value
|
|
@@ -9,7 +9,6 @@ import { _getTargetMethodSignature } from './decorator.util.js'
|
|
|
9
9
|
* Does not support `cacheKey`.
|
|
10
10
|
* So, the same Promise is returned, regardless of the arguments.
|
|
11
11
|
*/
|
|
12
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
13
12
|
export const _SwarmSafe = (): MethodDecorator => (target, key, descriptor) => {
|
|
14
13
|
if (typeof descriptor.value !== 'function') {
|
|
15
14
|
throw new TypeError('@_SwarmSafe can be applied only to methods')
|
|
@@ -3,7 +3,6 @@ import type { PTimeoutOptions } from '../promise/pTimeout.js'
|
|
|
3
3
|
import { pTimeout } from '../promise/pTimeout.js'
|
|
4
4
|
import { _getMethodSignature } from './decorator.util.js'
|
|
5
5
|
|
|
6
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
7
6
|
export function _Timeout(opt: PTimeoutOptions): MethodDecorator {
|
|
8
7
|
return (_target, key, descriptor) => {
|
|
9
8
|
_assert(typeof descriptor.value === 'function', '@_Timeout can be applied only to methods')
|
package/src/error/tryCatch.ts
CHANGED
|
@@ -65,7 +65,6 @@ export function _tryCatch<T extends AnyFunction>(fn: T, opt: TryCatchOptions = {
|
|
|
65
65
|
} as any
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
69
68
|
export const _TryCatch =
|
|
70
69
|
(opt: TryCatchOptions = {}): MethodDecorator =>
|
|
71
70
|
(_target, _key, descriptor) => {
|
package/src/index.ts
CHANGED
|
@@ -50,7 +50,6 @@ export function _isBetween<T extends number | string>(
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export function _clamp(x: number, minIncl: number, maxIncl: number): number {
|
|
53
|
-
// oxlint-disable-next-line unicorn/prefer-math-min-max
|
|
54
53
|
return x <= minIncl ? minIncl : x >= maxIncl ? maxIncl : x
|
|
55
54
|
}
|
|
56
55
|
|
|
@@ -25,7 +25,6 @@ export interface KeySortedMapOptions<K> {
|
|
|
25
25
|
// oxlint-disable-next-line no-unsafe-declaration-merging -- Map<K,V> workaround for oxlint TS2420 false positive
|
|
26
26
|
export interface KeySortedMap<K, V> extends Map<K, V> {}
|
|
27
27
|
|
|
28
|
-
// oxlint-disable-next-line no-unsafe-declaration-merging -- Map<K,V> workaround for oxlint TS2420 false positive
|
|
29
28
|
export class KeySortedMap<K, V> {
|
|
30
29
|
private readonly map: Map<K, V>
|
|
31
30
|
readonly #sortedKeys: K[]
|
|
@@ -20,7 +20,6 @@ export interface LazyKeySortedMapOptions<K> {
|
|
|
20
20
|
// oxlint-disable-next-line no-unsafe-declaration-merging -- Map<K,V> workaround for oxlint TS2420 false positive
|
|
21
21
|
export interface LazyKeySortedMap<K, V> extends Map<K, V> {}
|
|
22
22
|
|
|
23
|
-
// oxlint-disable-next-line no-unsafe-declaration-merging -- Map<K,V> workaround for oxlint TS2420 false positive
|
|
24
23
|
export class LazyKeySortedMap<K, V> {
|
|
25
24
|
private readonly map: Map<K, V>
|
|
26
25
|
private readonly maybeSortedKeys: K[]
|
package/src/promise/pProps.ts
CHANGED
|
@@ -16,9 +16,5 @@ export async function pProps<T>(input: { [K in keyof T]: T[K] | Promise<T[K]> })
|
|
|
16
16
|
[K in keyof T]: Awaited<T[K]>
|
|
17
17
|
}> {
|
|
18
18
|
const keys = Object.keys(input)
|
|
19
|
-
|
|
20
|
-
// oxlint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
21
|
-
return Object.fromEntries(
|
|
22
|
-
(await Promise.all(Object.values(input))).map((v, i) => [keys[i], v]),
|
|
23
|
-
) as any
|
|
19
|
+
return Object.fromEntries((await Promise.all(Object.values(input))).map((v, i) => [keys[i], v]))
|
|
24
20
|
}
|
package/src/types.ts
CHANGED
|
@@ -105,6 +105,16 @@ export type AnyAsyncFunction<T = any> = (...args: any[]) => Promise<T>
|
|
|
105
105
|
export type AsyncFunction<T = any> = () => Promise<T>
|
|
106
106
|
export type AnyPromisableFunction<T = any> = (...args: any[]) => Promisable<T>
|
|
107
107
|
export type PromisableFunction<T = any> = () => Promisable<T>
|
|
108
|
+
export type AnySyncFunction<T = any> = (...args: any[]) => NotPromise<T>
|
|
109
|
+
export type SyncFunction<T = any> = () => NotPromise<T>
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Compile-time guard against accidentally returning a Promise from a sync context.
|
|
113
|
+
* Example usage: `fn: () => NotPromise<T>`
|
|
114
|
+
* This would fail if fn returns a Promise (e.g an async function).
|
|
115
|
+
*/
|
|
116
|
+
export type NotPromise<T> = [T] extends [PromiseLike<any>] ? never : T
|
|
117
|
+
|
|
108
118
|
/**
|
|
109
119
|
* A function that lazily calculates something.
|
|
110
120
|
*/
|
|
@@ -511,8 +521,6 @@ type ReadonlyObjectDeep<ObjectType extends object> = {
|
|
|
511
521
|
readonly [KeyType in keyof ObjectType]: ReadonlyDeep<ObjectType[KeyType]>
|
|
512
522
|
}
|
|
513
523
|
|
|
514
|
-
// oxlint-enable
|
|
515
|
-
|
|
516
524
|
/**
|
|
517
525
|
Makes one property of T required instead of optional.
|
|
518
526
|
@example
|