@nu-art/ts-common 0.400.4 → 0.400.6
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/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +2 -4
- package/testing/workspace-creator.d.ts +5 -3
- package/testing/workspace-creator.js +29 -11
- package/utils/debounce.d.ts +32 -0
- package/utils/debounce.js +115 -0
- package/utils/number-tools.d.ts +1 -0
- package/utils/number-tools.js +3 -0
- package/utils/tools.d.ts +1 -0
- package/utils/tools.js +11 -0
package/index.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export * from './utils/ui-tools.js';
|
|
|
42
42
|
export * from './utils/json-tools.js';
|
|
43
43
|
export * from './utils/promise-tools.js';
|
|
44
44
|
export * from './utils/url-tools.js';
|
|
45
|
+
export * from './utils/debounce.js';
|
|
45
46
|
export * from './validator/validator-core.js';
|
|
46
47
|
export * from './validator/validators.js';
|
|
47
48
|
export * from './validator/type-validators.js';
|
package/index.js
CHANGED
|
@@ -59,6 +59,7 @@ export * from './utils/ui-tools.js';
|
|
|
59
59
|
export * from './utils/json-tools.js';
|
|
60
60
|
export * from './utils/promise-tools.js';
|
|
61
61
|
export * from './utils/url-tools.js';
|
|
62
|
+
export * from './utils/debounce.js';
|
|
62
63
|
export * from './validator/validator-core.js';
|
|
63
64
|
export * from './validator/validators.js';
|
|
64
65
|
export * from './validator/type-validators.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nu-art/ts-common",
|
|
3
|
-
"version": "0.400.
|
|
3
|
+
"version": "0.400.6",
|
|
4
4
|
"description": "js and ts infra",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -36,9 +36,7 @@
|
|
|
36
36
|
"node-forge": "^1.2.1",
|
|
37
37
|
"uuid": "^9.0.0",
|
|
38
38
|
"csv-parser": "^2.3.3",
|
|
39
|
-
"
|
|
40
|
-
"jose": "^5.0.0",
|
|
41
|
-
"pbkdf2": "3.1.2"
|
|
39
|
+
"jose": "^5.0.0"
|
|
42
40
|
},
|
|
43
41
|
"devDependencies": {
|
|
44
42
|
"@types/mocha": "^10.0.6",
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Logger } from '../core/logger/Logger.js';
|
|
2
|
+
import { StringMap } from '../utils/index.js';
|
|
2
3
|
export declare class TestWorkspaceCreator extends Logger {
|
|
3
4
|
private readonly pathToFixtures;
|
|
4
5
|
private readonly pathToWorkspace;
|
|
5
6
|
constructor(pathToFixtures: string, pathToWorkspace: string);
|
|
6
|
-
setupWorkspace(fixtures: string[], relativePath?: string, clean?: boolean): void
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
setupWorkspace(fixtures: string[], relativePath?: string, clean?: boolean): Promise<void>;
|
|
8
|
+
setupWorkspace(fixtures: string[], params: StringMap, relativePath?: string, clean?: boolean): Promise<void>;
|
|
9
|
+
clearWorkspace(relativePathInWorkspace?: string): Promise<void>;
|
|
10
|
+
extractFixture(pathToFixture: string, relativePathInWorkspace?: string, params?: StringMap): Promise<void>;
|
|
9
11
|
}
|
|
10
12
|
export declare function setupWorkspace(pathToWorkspaceFile: string, outputRootDir: string, clean?: boolean): void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync,
|
|
1
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
|
|
2
2
|
import { dirname, resolve } from 'path';
|
|
3
3
|
import { Logger } from '../core/logger/Logger.js';
|
|
4
|
+
import { FileSystemUtils } from '../utils/FileSystemUtils.js';
|
|
4
5
|
const FILE_HEADER_REGEX = /^\/\/ file: (.+)$/gm;
|
|
5
6
|
const INNER_FILE_HEADER_REGEX = /^(\/\/)*?\/\/\/\/ file:/gm;
|
|
6
7
|
export class TestWorkspaceCreator extends Logger {
|
|
@@ -11,20 +12,38 @@ export class TestWorkspaceCreator extends Logger {
|
|
|
11
12
|
this.pathToFixtures = pathToFixtures;
|
|
12
13
|
this.pathToWorkspace = pathToWorkspace;
|
|
13
14
|
}
|
|
14
|
-
setupWorkspace(fixtures, relativePath
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
async setupWorkspace(fixtures, params, relativePath, clean) {
|
|
16
|
+
let _clean = clean;
|
|
17
|
+
let _relativePath;
|
|
18
|
+
let _params;
|
|
19
|
+
if (typeof relativePath === 'boolean') {
|
|
20
|
+
_clean = relativePath;
|
|
21
|
+
}
|
|
22
|
+
else if (typeof relativePath === 'string') {
|
|
23
|
+
_relativePath = relativePath;
|
|
24
|
+
}
|
|
25
|
+
if (typeof params === 'string') {
|
|
26
|
+
_relativePath = params;
|
|
27
|
+
_params = {};
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
_params = params;
|
|
31
|
+
}
|
|
32
|
+
_relativePath = _relativePath ?? '';
|
|
33
|
+
_clean = _clean ?? true;
|
|
34
|
+
if (_clean)
|
|
35
|
+
await this.clearWorkspace(_relativePath);
|
|
17
36
|
for (const fixture of fixtures) {
|
|
18
|
-
this.extractFixture(resolve(this.pathToFixtures, fixture),
|
|
37
|
+
await this.extractFixture(resolve(this.pathToFixtures, fixture), _relativePath, _params);
|
|
19
38
|
}
|
|
20
39
|
}
|
|
21
|
-
clearWorkspace(relativePathInWorkspace = '') {
|
|
40
|
+
async clearWorkspace(relativePathInWorkspace = '') {
|
|
22
41
|
const path = resolve(this.pathToWorkspace, relativePathInWorkspace);
|
|
23
42
|
this.logWarning(`Deleting folder: ${path}`);
|
|
24
|
-
|
|
43
|
+
await FileSystemUtils.folder.delete(path);
|
|
25
44
|
}
|
|
26
|
-
extractFixture(pathToFixture, relativePathInWorkspace = '') {
|
|
27
|
-
const content =
|
|
45
|
+
async extractFixture(pathToFixture, relativePathInWorkspace = '', params = {}) {
|
|
46
|
+
const content = await FileSystemUtils.file.template.read(pathToFixture, params);
|
|
28
47
|
const matches = [...content.matchAll(FILE_HEADER_REGEX)];
|
|
29
48
|
if (matches.length === 0) {
|
|
30
49
|
this.logError('No matching file headers found. Aborting.');
|
|
@@ -43,8 +62,7 @@ export class TestWorkspaceCreator extends Logger {
|
|
|
43
62
|
.slice(startIndex + currentMatch[0].length, endIndex)
|
|
44
63
|
.trimStart();
|
|
45
64
|
fileContent = fileContent.replace(INNER_FILE_HEADER_REGEX, '\$1// file:');
|
|
46
|
-
|
|
47
|
-
writeFileSync(targetPath, fileContent);
|
|
65
|
+
await FileSystemUtils.file.write(targetPath, fileContent);
|
|
48
66
|
this.logVerbose(`Wrote: ${targetPath}`);
|
|
49
67
|
}
|
|
50
68
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Logger } from '../core/logger/Logger.js';
|
|
2
|
+
export declare class Debounce<Args extends any[], Response> extends Logger {
|
|
3
|
+
private action;
|
|
4
|
+
private readonly waitMs;
|
|
5
|
+
private readonly maxWaitMs?;
|
|
6
|
+
private triggerIndex;
|
|
7
|
+
private waitTimer?;
|
|
8
|
+
private maxTimer?;
|
|
9
|
+
private latestArgs;
|
|
10
|
+
private actionInProgress;
|
|
11
|
+
private triggerPending;
|
|
12
|
+
constructor(action: (...args: Args) => Response, waitMs: number, maxWaitMs?: number);
|
|
13
|
+
private validate_WaitMs;
|
|
14
|
+
private validate_MaxWaitMs;
|
|
15
|
+
private clearTimers;
|
|
16
|
+
private executeAction;
|
|
17
|
+
/** Schedule the action to be performed (fire-and-forget). */
|
|
18
|
+
trigger(...args: Args): void;
|
|
19
|
+
/**
|
|
20
|
+
* Returns whether an action is currently scheduled
|
|
21
|
+
*/
|
|
22
|
+
isScheduled(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Run immediately if scheduled and return the action's value.
|
|
25
|
+
* - Returns Response when something was scheduled, else undefined.
|
|
26
|
+
* - If action throws sync here, it throws to the caller.
|
|
27
|
+
* - If action returns a rejected Promise, caller handles it.
|
|
28
|
+
*/
|
|
29
|
+
flush(): Response | undefined;
|
|
30
|
+
/** Cancel any scheduled run. (Does not cancel in-flight.) */
|
|
31
|
+
clear(): void;
|
|
32
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { BadImplementationException } from '../core/exceptions/exceptions.js';
|
|
2
|
+
import { Logger } from '../core/logger/Logger.js';
|
|
3
|
+
import { isPromise } from './promise-tools.js';
|
|
4
|
+
import { exists } from './tools.js';
|
|
5
|
+
export class Debounce extends Logger {
|
|
6
|
+
action;
|
|
7
|
+
waitMs;
|
|
8
|
+
maxWaitMs;
|
|
9
|
+
triggerIndex = 0;
|
|
10
|
+
waitTimer;
|
|
11
|
+
maxTimer;
|
|
12
|
+
latestArgs = [];
|
|
13
|
+
actionInProgress = false;
|
|
14
|
+
triggerPending = false;
|
|
15
|
+
constructor(action, waitMs, maxWaitMs) {
|
|
16
|
+
super('Debounce');
|
|
17
|
+
this.validate_WaitMs(waitMs);
|
|
18
|
+
this.validate_MaxWaitMs(waitMs, maxWaitMs);
|
|
19
|
+
this.action = action;
|
|
20
|
+
this.waitMs = waitMs;
|
|
21
|
+
this.maxWaitMs = maxWaitMs;
|
|
22
|
+
}
|
|
23
|
+
//######################### Validation #########################
|
|
24
|
+
validate_WaitMs(waitMs) {
|
|
25
|
+
if (waitMs < 0)
|
|
26
|
+
throw new BadImplementationException('waitMs value must be >= 0');
|
|
27
|
+
}
|
|
28
|
+
validate_MaxWaitMs(waitMs, maxWaitMs) {
|
|
29
|
+
if (!exists(maxWaitMs))
|
|
30
|
+
return;
|
|
31
|
+
if (maxWaitMs <= 0)
|
|
32
|
+
throw new BadImplementationException('maxWaitMs value must be > 0');
|
|
33
|
+
if (maxWaitMs < waitMs)
|
|
34
|
+
this.logWarning('maxWaitMs < waitMs; the \'max\' may never matter');
|
|
35
|
+
}
|
|
36
|
+
//######################### Internal Logic #########################
|
|
37
|
+
clearTimers() {
|
|
38
|
+
if (this.waitTimer) {
|
|
39
|
+
clearTimeout(this.waitTimer);
|
|
40
|
+
this.waitTimer = undefined;
|
|
41
|
+
}
|
|
42
|
+
if (this.maxTimer) {
|
|
43
|
+
clearTimeout(this.maxTimer);
|
|
44
|
+
this.maxTimer = undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
executeAction(triggerIndex, immediate) {
|
|
48
|
+
if (triggerIndex !== this.triggerIndex)
|
|
49
|
+
return;
|
|
50
|
+
this.triggerIndex++;
|
|
51
|
+
const args = this.latestArgs ?? [];
|
|
52
|
+
this.clearTimers();
|
|
53
|
+
const result = this.action(...args);
|
|
54
|
+
if (immediate)
|
|
55
|
+
return result;
|
|
56
|
+
//Set up chain triggering if the action is asynchronous.
|
|
57
|
+
if (isPromise(result)) {
|
|
58
|
+
this.actionInProgress = true;
|
|
59
|
+
Promise.resolve(result).finally(() => {
|
|
60
|
+
this.actionInProgress = false;
|
|
61
|
+
//Immediately execute the action again if debounce was triggered during the current run
|
|
62
|
+
if (this.triggerPending) {
|
|
63
|
+
this.triggerPending = false;
|
|
64
|
+
void this.executeAction(this.triggerIndex);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//######################### Public Logic #########################
|
|
70
|
+
/** Schedule the action to be performed (fire-and-forget). */
|
|
71
|
+
trigger(...args) {
|
|
72
|
+
this.latestArgs = args;
|
|
73
|
+
//If action is in progress, queue up immediate trigger
|
|
74
|
+
if (this.actionInProgress) {
|
|
75
|
+
this.triggerPending = true;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Normal debounce behavior while idle:
|
|
79
|
+
//Reset waitTimer
|
|
80
|
+
if (this.waitTimer)
|
|
81
|
+
clearTimeout(this.waitTimer);
|
|
82
|
+
const triggerIndex = this.triggerIndex;
|
|
83
|
+
this.waitTimer = setTimeout(() => {
|
|
84
|
+
void this.executeAction(triggerIndex);
|
|
85
|
+
}, this.waitMs);
|
|
86
|
+
//Set maxWaitTimer if configured and does not currently exist
|
|
87
|
+
if (exists(this.maxWaitMs) && !exists(this.maxTimer))
|
|
88
|
+
this.maxTimer = setTimeout(() => {
|
|
89
|
+
void this.executeAction(triggerIndex);
|
|
90
|
+
}, this.maxWaitMs);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Returns whether an action is currently scheduled
|
|
94
|
+
*/
|
|
95
|
+
isScheduled() {
|
|
96
|
+
return exists(this.waitTimer ?? this.maxTimer);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Run immediately if scheduled and return the action's value.
|
|
100
|
+
* - Returns Response when something was scheduled, else undefined.
|
|
101
|
+
* - If action throws sync here, it throws to the caller.
|
|
102
|
+
* - If action returns a rejected Promise, caller handles it.
|
|
103
|
+
*/
|
|
104
|
+
flush() {
|
|
105
|
+
if (!this.isScheduled())
|
|
106
|
+
return void this.logInfo('Not scheduled');
|
|
107
|
+
return this.executeAction(this.triggerIndex, true);
|
|
108
|
+
}
|
|
109
|
+
/** Cancel any scheduled run. (Does not cancel in-flight.) */
|
|
110
|
+
clear() {
|
|
111
|
+
this.clearTimers();
|
|
112
|
+
this.triggerIndex++;
|
|
113
|
+
this.triggerPending = false;
|
|
114
|
+
}
|
|
115
|
+
}
|
package/utils/number-tools.d.ts
CHANGED
package/utils/number-tools.js
CHANGED
|
@@ -20,3 +20,6 @@ export const roundNumber = (number, digits) => {
|
|
|
20
20
|
return Math.round(number * multiple) / multiple;
|
|
21
21
|
};
|
|
22
22
|
export const clamp = (min, num, max) => Math.min(Math.max(num, min), max);
|
|
23
|
+
export const numberInRange = (number, range) => {
|
|
24
|
+
return range[0] <= number && number <= range[1];
|
|
25
|
+
};
|
package/utils/tools.d.ts
CHANGED
package/utils/tools.js
CHANGED
|
@@ -63,3 +63,14 @@ export function freeze(item) {
|
|
|
63
63
|
export const logicalXOR = (a, b) => {
|
|
64
64
|
return (a && !b) || (!a && b);
|
|
65
65
|
};
|
|
66
|
+
export function createLockedAsyncFunction(fn) {
|
|
67
|
+
let inProgress = false;
|
|
68
|
+
return () => {
|
|
69
|
+
if (inProgress)
|
|
70
|
+
return;
|
|
71
|
+
inProgress = true;
|
|
72
|
+
Promise.resolve(fn()).finally(() => {
|
|
73
|
+
inProgress = false;
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
}
|