@vpmedia/simplify 1.53.0 → 1.55.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/CHANGELOG.md +52 -0
- package/package.json +16 -16
- package/src/index.js +9 -19
- package/src/logging/Logger.js +1 -1
- package/src/logging/OpenTelemetryLogHandler.js +1 -1
- package/src/logging/util.js +2 -0
- package/src/logging/util.test.js +4 -1
- package/src/pagelifecycle/util.js +17 -21
- package/src/util/async.js +20 -0
- package/src/util/{delayPromise.test.js → async.test.js} +1 -1
- package/src/util/{getErrorDetails.test.js → error.test.js} +1 -1
- package/src/util/{fetchRetry.js → fetch.js} +6 -4
- package/src/util/number.js +116 -0
- package/src/util/number.test.js +115 -0
- package/src/util/object.js +97 -0
- package/src/util/object.test.js +205 -0
- package/src/util/{getURLParam.js → query.js} +12 -2
- package/src/util/{sanitizeURLParam.test.js → query.test.js} +18 -1
- package/src/util/{serverDataToState.js → state.js} +1 -1
- package/src/util/{serverDataToState.test.js → state.test.js} +1 -1
- package/src/util/string.js +54 -0
- package/src/util/{capitalize.test.js → string.test.js} +19 -1
- package/src/util/validate.js +272 -0
- package/src/util/validate.test.js +325 -0
- package/tsconfig.build.json +24 -0
- package/types/index.d.ts +9 -19
- package/types/logging/util.d.ts.map +1 -1
- package/types/pagelifecycle/util.d.ts +1 -1
- package/types/pagelifecycle/util.d.ts.map +1 -1
- package/types/util/async.d.ts +3 -0
- package/types/util/async.d.ts.map +1 -0
- package/types/util/{getErrorDetails.d.ts → error.d.ts} +1 -1
- package/types/util/error.d.ts.map +1 -0
- package/types/util/{fetchRetry.d.ts → fetch.d.ts} +1 -1
- package/types/util/fetch.d.ts.map +1 -0
- package/types/util/number.d.ts +13 -0
- package/types/util/number.d.ts.map +1 -0
- package/types/util/object.d.ts +6 -0
- package/types/util/object.d.ts.map +1 -0
- package/types/util/{getURLParam.d.ts → query.d.ts} +2 -1
- package/types/util/query.d.ts.map +1 -0
- package/types/util/{serverDataToState.d.ts → state.d.ts} +1 -1
- package/types/util/state.d.ts.map +1 -0
- package/types/util/string.d.ts +5 -0
- package/types/util/string.d.ts.map +1 -0
- package/types/util/validate.d.ts +54 -0
- package/types/util/validate.d.ts.map +1 -0
- package/src/util/addLeadingZero.js +0 -16
- package/src/util/addLeadingZero.test.js +0 -11
- package/src/util/capitalize.js +0 -15
- package/src/util/deepMerge.js +0 -24
- package/src/util/deepMerge.test.js +0 -103
- package/src/util/deg2rad.js +0 -8
- package/src/util/deg2rad.test.js +0 -5
- package/src/util/delayPromise.js +0 -6
- package/src/util/fixFloatPrecision.js +0 -16
- package/src/util/fixFloatPrecision.test.js +0 -27
- package/src/util/getObjArrayPropSum.js +0 -11
- package/src/util/getObjValueByPath.js +0 -20
- package/src/util/getObjValueByPath.test.js +0 -51
- package/src/util/getRandomInt.js +0 -9
- package/src/util/getRandomInt.test.js +0 -24
- package/src/util/getURLParam.test.js +0 -18
- package/src/util/loadJSON.js +0 -10
- package/src/util/purgeObject.js +0 -13
- package/src/util/purgeObject.test.js +0 -8
- package/src/util/safeFloat.js +0 -31
- package/src/util/safeFloat.test.js +0 -13
- package/src/util/sanitizeURLParam.js +0 -11
- package/src/util/saveAsFile.js +0 -14
- package/src/util/setObjValueByPath.js +0 -26
- package/src/util/setObjValueByPath.test.js +0 -47
- package/src/util/underscoreToCamelCase.js +0 -10
- package/src/util/underscoreToCamelCase.test.js +0 -7
- package/typedefs/global.d.ts +0 -5
- package/types/util/addLeadingZero.d.ts +0 -2
- package/types/util/addLeadingZero.d.ts.map +0 -1
- package/types/util/capitalize.d.ts +0 -2
- package/types/util/capitalize.d.ts.map +0 -1
- package/types/util/deepMerge.d.ts +0 -2
- package/types/util/deepMerge.d.ts.map +0 -1
- package/types/util/deg2rad.d.ts +0 -2
- package/types/util/deg2rad.d.ts.map +0 -1
- package/types/util/delayPromise.d.ts +0 -2
- package/types/util/delayPromise.d.ts.map +0 -1
- package/types/util/fetchRetry.d.ts.map +0 -1
- package/types/util/fixFloatPrecision.d.ts +0 -7
- package/types/util/fixFloatPrecision.d.ts.map +0 -1
- package/types/util/getErrorDetails.d.ts.map +0 -1
- package/types/util/getObjArrayPropSum.d.ts +0 -2
- package/types/util/getObjArrayPropSum.d.ts.map +0 -1
- package/types/util/getObjValueByPath.d.ts +0 -2
- package/types/util/getObjValueByPath.d.ts.map +0 -1
- package/types/util/getRandomInt.d.ts +0 -2
- package/types/util/getRandomInt.d.ts.map +0 -1
- package/types/util/getURLParam.d.ts.map +0 -1
- package/types/util/loadJSON.d.ts +0 -7
- package/types/util/loadJSON.d.ts.map +0 -1
- package/types/util/purgeObject.d.ts +0 -2
- package/types/util/purgeObject.d.ts.map +0 -1
- package/types/util/safeFloat.d.ts +0 -22
- package/types/util/safeFloat.d.ts.map +0 -1
- package/types/util/sanitizeURLParam.d.ts +0 -2
- package/types/util/sanitizeURLParam.d.ts.map +0 -1
- package/types/util/saveAsFile.d.ts +0 -2
- package/types/util/saveAsFile.d.ts.map +0 -1
- package/types/util/serverDataToState.d.ts.map +0 -1
- package/types/util/setObjValueByPath.d.ts +0 -2
- package/types/util/setObjValueByPath.d.ts.map +0 -1
- package/types/util/underscoreToCamelCase.d.ts +0 -2
- package/types/util/underscoreToCamelCase.d.ts.map +0 -1
- /package/src/util/{getErrorDetails.js → error.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,55 @@
|
|
|
1
|
+
## [1.55.0] - 2026-01-12
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- Added type guard helpers
|
|
6
|
+
- Improve validate utils
|
|
7
|
+
- Improve type check validators
|
|
8
|
+
|
|
9
|
+
### 💼 Other
|
|
10
|
+
|
|
11
|
+
- *(deps)* Bump dependency versions
|
|
12
|
+
- *(deps)* Bump dependency versions
|
|
13
|
+
- *(deps)* Bump dependency versions
|
|
14
|
+
- *(deps)* Bump dependency versions
|
|
15
|
+
- *(deps)* Bump dependency versions
|
|
16
|
+
- *(deps)* Bump dependency versions
|
|
17
|
+
- *(deps)* Bump dependency versions
|
|
18
|
+
- *(deps)* Bump dependency versions
|
|
19
|
+
- *(deps)* Bump dependency versions
|
|
20
|
+
|
|
21
|
+
### 🚜 Refactor
|
|
22
|
+
|
|
23
|
+
- Improve naming conventions
|
|
24
|
+
- Improve runtime type checking utils
|
|
25
|
+
- Improve type checker validators
|
|
26
|
+
- Improved validators
|
|
27
|
+
- Merged util methods to more compact structure
|
|
28
|
+
|
|
29
|
+
### ⚙️ Miscellaneous Tasks
|
|
30
|
+
|
|
31
|
+
- Release
|
|
32
|
+
- Cleanup
|
|
33
|
+
- Improve linting with oxlint
|
|
34
|
+
- Fixed lint errors
|
|
35
|
+
- Fixed lint errors
|
|
36
|
+
- *(release)* V1.55.0
|
|
37
|
+
## [1.54.0] - 2025-12-29
|
|
38
|
+
|
|
39
|
+
### 💼 Other
|
|
40
|
+
|
|
41
|
+
- *(deps)* Bump dependency versions
|
|
42
|
+
- *(deps)* Bump dependency versions
|
|
43
|
+
- *(deps)* Bump dependency versions
|
|
44
|
+
- *(deps)* Bump dependency versions
|
|
45
|
+
- *(deps)* Bump dependency versions
|
|
46
|
+
- *(deps)* Bump dependency versions
|
|
47
|
+
- *(deps)* Bumped package versions
|
|
48
|
+
|
|
49
|
+
### ⚙️ Miscellaneous Tasks
|
|
50
|
+
|
|
51
|
+
- Release
|
|
52
|
+
- *(release)* V1.54.0
|
|
1
53
|
## [1.53.0] - 2025-12-17
|
|
2
54
|
|
|
3
55
|
### 💼 Other
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vpmedia/simplify",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.55.0",
|
|
4
4
|
"description": "@vpmedia/simplify",
|
|
5
5
|
"author": "Andras Csizmadia <andras@vpmedia.hu> (www.vpmedia.hu)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,25 +22,25 @@
|
|
|
22
22
|
"eventemitter3": "^5.0.1"
|
|
23
23
|
},
|
|
24
24
|
"optionalDependencies": {
|
|
25
|
-
"@sentry/browser": "^10.
|
|
25
|
+
"@sentry/browser": "^10.32.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@commitlint/cli": "^20.
|
|
29
|
-
"@commitlint/config-conventional": "^20.
|
|
28
|
+
"@commitlint/cli": "^20.3.1",
|
|
29
|
+
"@commitlint/config-conventional": "^20.3.1",
|
|
30
30
|
"@eslint/js": "^9.39.2",
|
|
31
|
-
"@types/node": "^25.0.
|
|
31
|
+
"@types/node": "^25.0.5",
|
|
32
32
|
"@vitest/coverage-v8": "^4.0.16",
|
|
33
33
|
"eslint": "^9.39.2",
|
|
34
|
-
"eslint-plugin-jsdoc": "^
|
|
35
|
-
"eslint-plugin-oxlint": "^1.
|
|
34
|
+
"eslint-plugin-jsdoc": "^62.0.0",
|
|
35
|
+
"eslint-plugin-oxlint": "^1.38.0",
|
|
36
36
|
"eslint-plugin-unicorn": "^62.0.0",
|
|
37
|
-
"globals": "^
|
|
38
|
-
"jsdom": "^27.
|
|
39
|
-
"oxlint": "^1.
|
|
40
|
-
"oxlint-tsgolint": "^0.
|
|
37
|
+
"globals": "^17.0.0",
|
|
38
|
+
"jsdom": "^27.4.0",
|
|
39
|
+
"oxlint": "^1.38.0",
|
|
40
|
+
"oxlint-tsgolint": "^0.11.0",
|
|
41
41
|
"prettier": "^3.7.4",
|
|
42
42
|
"typescript": "^5.9.3",
|
|
43
|
-
"typescript-eslint": "^8.
|
|
43
|
+
"typescript-eslint": "^8.52.0",
|
|
44
44
|
"vitest": "^4.0.16"
|
|
45
45
|
},
|
|
46
46
|
"browserslist": [
|
|
@@ -50,11 +50,11 @@
|
|
|
50
50
|
"iOS >= 14"
|
|
51
51
|
],
|
|
52
52
|
"scripts": {
|
|
53
|
-
"build": "
|
|
54
|
-
"check": "pnpm lint && pnpm test && pnpm typecheck",
|
|
53
|
+
"build": "rm -rf types && tsc -p ./tsconfig.build.json",
|
|
54
|
+
"check": "pnpm build && pnpm lint && pnpm test && pnpm typecheck",
|
|
55
55
|
"format": "prettier --write \"./**/*.{js,jsx,mjs,cjs,ts,tsx,json,css}\"",
|
|
56
|
-
"lint": "eslint
|
|
56
|
+
"lint": "oxlint src && eslint",
|
|
57
57
|
"test": "vitest --coverage",
|
|
58
|
-
"typecheck": "
|
|
58
|
+
"typecheck": "tsc"
|
|
59
59
|
}
|
|
60
60
|
}
|
package/src/index.js
CHANGED
|
@@ -11,22 +11,12 @@ export * from './pagelifecycle/const.js';
|
|
|
11
11
|
export * from './pagelifecycle/event.js';
|
|
12
12
|
export * from './pagelifecycle/typedef.js';
|
|
13
13
|
export * from './pagelifecycle/util.js';
|
|
14
|
-
export
|
|
15
|
-
export
|
|
16
|
-
export {
|
|
17
|
-
export
|
|
18
|
-
export
|
|
19
|
-
export
|
|
20
|
-
export
|
|
21
|
-
export
|
|
22
|
-
export
|
|
23
|
-
export { getRandomInt } from './util/getRandomInt.js';
|
|
24
|
-
export { getURLParam } from './util/getURLParam.js';
|
|
25
|
-
export { loadJSON } from './util/loadJSON.js';
|
|
26
|
-
export { purgeObject } from './util/purgeObject.js';
|
|
27
|
-
export { addFloat, fixFloat, subFloat } from './util/safeFloat.js';
|
|
28
|
-
export { sanitizeURLParam } from './util/sanitizeURLParam.js';
|
|
29
|
-
export { saveAsFile } from './util/saveAsFile.js';
|
|
30
|
-
export { serverDataToState } from './util/serverDataToState.js';
|
|
31
|
-
export { setObjValueByPath } from './util/setObjValueByPath.js';
|
|
32
|
-
export { underscoreToCamelCase } from './util/underscoreToCamelCase.js';
|
|
14
|
+
export * from './util/async.js';
|
|
15
|
+
export * from './util/error.js';
|
|
16
|
+
export { FetchError, fetchRetry, HTTP_0_ANY } from './util/fetch.js';
|
|
17
|
+
export * from './util/number.js';
|
|
18
|
+
export * from './util/object.js';
|
|
19
|
+
export * from './util/query.js';
|
|
20
|
+
export * from './util/state.js';
|
|
21
|
+
export * from './util/string.js';
|
|
22
|
+
export * from './util/validate.js';
|
package/src/logging/Logger.js
CHANGED
|
@@ -7,7 +7,7 @@ export class OpenTelemetryLogHandler extends AbstractLogHandler {
|
|
|
7
7
|
* @param {number} level - Log handler level.
|
|
8
8
|
* @param {(logger: import('./Logger.js').Logger, timestamp: number, level: number, message: string, extra: object | null | undefined, error: Error | null | undefined) => void} emitter - Log handler emitter.
|
|
9
9
|
*/
|
|
10
|
-
constructor(level
|
|
10
|
+
constructor(level, emitter) {
|
|
11
11
|
super(level);
|
|
12
12
|
this.emitter = emitter;
|
|
13
13
|
}
|
package/src/logging/util.js
CHANGED
package/src/logging/util.test.js
CHANGED
|
@@ -12,11 +12,14 @@ import {
|
|
|
12
12
|
LOG_LEVEL_SILENT,
|
|
13
13
|
LOG_LEVEL_WARNING,
|
|
14
14
|
} from './const.js';
|
|
15
|
+
import { Logger } from './Logger.js';
|
|
15
16
|
import { formatLogMessage, getLogLevelName } from './util.js';
|
|
16
17
|
|
|
17
18
|
test('formatLogMessage()', () => {
|
|
18
19
|
expect(
|
|
19
|
-
formatLogMessage(
|
|
20
|
+
formatLogMessage(new Logger('loggerName'), Date.now(), LOG_LEVEL_INFO, 'logMessage').endsWith(
|
|
21
|
+
'[loggerName] logMessage'
|
|
22
|
+
)
|
|
20
23
|
).toBe(true);
|
|
21
24
|
});
|
|
22
25
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* oxlint-disable prefer-await-to-callbacks */
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Page lifecycle helper.
|
|
3
5
|
* @see https://developer.chrome.com/docs/web-platform/page-lifecycle-api
|
|
@@ -28,19 +30,8 @@ let currentDocumentState = null;
|
|
|
28
30
|
|
|
29
31
|
let isInitialized = false;
|
|
30
32
|
|
|
31
|
-
/** @type {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Add callback for a specific state change.
|
|
36
|
-
* @param {import('./typedef.js').DocumentState | import('./typedef.js').PageLifecycleState} state - Callback state condition.
|
|
37
|
-
* @param {() => void} callback - Callback function.
|
|
38
|
-
*/
|
|
39
|
-
export const addPageLifecycleCallback = (state, callback) => {
|
|
40
|
-
const stateCallbacks = callbacks[state] ?? [];
|
|
41
|
-
stateCallbacks.push(callback);
|
|
42
|
-
callbacks[state] = stateCallbacks;
|
|
43
|
-
};
|
|
33
|
+
/** @type {Record<string, (() => void)[]>} */
|
|
34
|
+
const callbacks = {};
|
|
44
35
|
|
|
45
36
|
/**
|
|
46
37
|
* Run callbacks for a specific state change.
|
|
@@ -135,17 +126,13 @@ export const initPageLifecycle = () => {
|
|
|
135
126
|
* Returns the current page lifecycle state.
|
|
136
127
|
* @returns {string | null | undefined} Current page lifecycle state.
|
|
137
128
|
*/
|
|
138
|
-
export const getPageLifecycleState = () =>
|
|
139
|
-
return currentPageLifecycleState;
|
|
140
|
-
};
|
|
129
|
+
export const getPageLifecycleState = () => currentPageLifecycleState;
|
|
141
130
|
|
|
142
131
|
/**
|
|
143
132
|
* Returns the current document state.
|
|
144
133
|
* @returns {import('./typedef.js').DocumentState | null | undefined} Current document state.
|
|
145
134
|
*/
|
|
146
|
-
export const getDocumentState = () =>
|
|
147
|
-
return currentDocumentState;
|
|
148
|
-
};
|
|
135
|
+
export const getDocumentState = () => currentDocumentState;
|
|
149
136
|
|
|
150
137
|
/**
|
|
151
138
|
* Returns the event emitter instance.
|
|
@@ -162,6 +149,15 @@ export const getPageLifecycleEventEmitter = () => {
|
|
|
162
149
|
* Returns the page lifecycle observer initialized state.
|
|
163
150
|
* @returns {boolean} Page lifecycle observer initialized flag.
|
|
164
151
|
*/
|
|
165
|
-
export const isPageLifecycleInitialized = () =>
|
|
166
|
-
|
|
152
|
+
export const isPageLifecycleInitialized = () => isInitialized;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Add callback for a specific state change.
|
|
156
|
+
* @param {import('./typedef.js').DocumentState | import('./typedef.js').PageLifecycleState} state - Callback state condition.
|
|
157
|
+
* @param {() => void} callback - Callback function.
|
|
158
|
+
*/
|
|
159
|
+
export const addPageLifecycleCallback = (state, callback) => {
|
|
160
|
+
const stateCallbacks = callbacks[state] ?? [];
|
|
161
|
+
stateCallbacks.push(callback);
|
|
162
|
+
callbacks[state] = stateCallbacks;
|
|
167
163
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a promise with delayed resolve.
|
|
3
|
+
* @param {number} delayMS - Promise resolve delay in milliseconds.
|
|
4
|
+
* @returns {Promise<void>} Delayed resolve promise.
|
|
5
|
+
*/
|
|
6
|
+
export const delayPromise = (delayMS) =>
|
|
7
|
+
new Promise((resolve) => {
|
|
8
|
+
setTimeout(resolve, delayMS);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Load JSON file using a fetch GET request.
|
|
13
|
+
* @param {string} url - URL to load.
|
|
14
|
+
* @returns {Promise<string>} The parsed JSON data.
|
|
15
|
+
*/
|
|
16
|
+
export const loadJSON = async (url) => {
|
|
17
|
+
const response = await fetch(url);
|
|
18
|
+
const json = await response.json();
|
|
19
|
+
return JSON.stringify(json);
|
|
20
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* oxlint-disable no-await-in-loop */
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
HTTP_401_UNAUTHORIZED,
|
|
3
5
|
HTTP_403_FORBIDDEN,
|
|
@@ -5,8 +7,8 @@ import {
|
|
|
5
7
|
HTTP_422_UNPROCESSABLE_ENTITY,
|
|
6
8
|
} from '../const/http_status.js';
|
|
7
9
|
import { Logger } from '../logging/Logger.js';
|
|
8
|
-
import { delayPromise } from './
|
|
9
|
-
import { getErrorDetails } from './
|
|
10
|
+
import { delayPromise } from './async.js';
|
|
11
|
+
import { getErrorDetails } from './error.js';
|
|
10
12
|
|
|
11
13
|
const logger = new Logger('fetch');
|
|
12
14
|
|
|
@@ -37,11 +39,11 @@ export class FetchError extends Error {
|
|
|
37
39
|
* @returns {Promise<Response>} Fetch result.
|
|
38
40
|
*/
|
|
39
41
|
export const fetchRetry = async (resource, fetchOptions, retryOptions) => {
|
|
40
|
-
retryOptions
|
|
42
|
+
retryOptions ??= {};
|
|
41
43
|
retryOptions.timeout = Math.max(retryOptions.timeout ?? 30000, 1);
|
|
42
44
|
retryOptions.delay = Math.max(retryOptions.delay ?? 1000, 1);
|
|
43
45
|
retryOptions.numTries = Math.max(retryOptions.numTries ?? 1, 1);
|
|
44
|
-
retryOptions.statusExcludes
|
|
46
|
+
retryOptions.statusExcludes ??= [
|
|
45
47
|
HTTP_401_UNAUTHORIZED,
|
|
46
48
|
HTTP_403_FORBIDDEN,
|
|
47
49
|
HTTP_405_METHOD_NOT_ALLOWED,
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts degrees to radians.
|
|
3
|
+
* @param {number} deg - Degree value.
|
|
4
|
+
* @returns {number} Radian value.
|
|
5
|
+
*/
|
|
6
|
+
export const deg2rad = (deg) => deg * (Math.PI / 180);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Returns random integer in range.
|
|
10
|
+
* @param {number} min - Min value.
|
|
11
|
+
* @param {number} max - Max value.
|
|
12
|
+
* @returns {number} Random integer in given range.
|
|
13
|
+
*/
|
|
14
|
+
export const getRandomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Fixes floating point number (0.20000000000000004 -> 0.2).
|
|
18
|
+
* @param {number | string} value - Number to fix.
|
|
19
|
+
* @returns {number} The fixed number.
|
|
20
|
+
*/
|
|
21
|
+
export const fixFloatPrecision = (value) => {
|
|
22
|
+
// Handle string inputs by converting to number first
|
|
23
|
+
if (typeof value === 'string') {
|
|
24
|
+
value = Number(value);
|
|
25
|
+
}
|
|
26
|
+
if (value >= 0 && value < 0.00000000001) {
|
|
27
|
+
const valuePlusOne = value + 1;
|
|
28
|
+
return Number.parseFloat(valuePlusOne.toPrecision(12)) - 1;
|
|
29
|
+
}
|
|
30
|
+
return Number.parseFloat(value.toPrecision(12));
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Convenience method for floating point precision handling.
|
|
35
|
+
* @param {number} value - The number to process.
|
|
36
|
+
* @param {number} p - The precision. Defaults to 2.
|
|
37
|
+
* @returns {number} The processed value.
|
|
38
|
+
*/
|
|
39
|
+
export const fixFloat = (value, p = 2) => Number.parseFloat(value.toFixed(p));
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Adds two value with floating point precision.
|
|
43
|
+
* @param {number} a - The number a.
|
|
44
|
+
* @param {number} b - The number b.
|
|
45
|
+
* @returns {number} The processed value.
|
|
46
|
+
*/
|
|
47
|
+
export const addFloat = (a, b) => {
|
|
48
|
+
const p = 100;
|
|
49
|
+
return fixFloat((a * p + b * p) / p);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Substracts two value with floating point precision.
|
|
54
|
+
* @param {number} a - The number a.
|
|
55
|
+
* @param {number} b - The number b.
|
|
56
|
+
* @returns {number} The processed value.
|
|
57
|
+
*/
|
|
58
|
+
export const subFloat = (a, b) => {
|
|
59
|
+
const p = 100;
|
|
60
|
+
return fixFloat((a * p - b * p) / p);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Value greater than check.
|
|
65
|
+
* @param {number} value - Input value.
|
|
66
|
+
* @param {number} min - Limit that `value` must be greater than.
|
|
67
|
+
* @returns {boolean} `true` is check success.
|
|
68
|
+
* @private
|
|
69
|
+
*/
|
|
70
|
+
export const isGt = (value, min) => value > min;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Value greater than check.
|
|
74
|
+
* @param {number} value - Input value.
|
|
75
|
+
* @param {number} min - Limit that `value` must be greater or equal than.
|
|
76
|
+
* @returns {boolean} `true` is check success.
|
|
77
|
+
* @private
|
|
78
|
+
*/
|
|
79
|
+
export const isGtOrEq = (value, min) => value >= min;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Value less than check.
|
|
83
|
+
* @param {number} value - Input value.
|
|
84
|
+
* @param {number} min - Limit that `value` must be greater than.
|
|
85
|
+
* @returns {boolean} `true` is check success.
|
|
86
|
+
* @private
|
|
87
|
+
*/
|
|
88
|
+
export const isLe = (value, min) => value < min;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Value less than check.
|
|
92
|
+
* @param {number} value - Input value.
|
|
93
|
+
* @param {number} min - Limit that `value` must be greater than.
|
|
94
|
+
* @returns {boolean} `true` is check success.
|
|
95
|
+
* @private
|
|
96
|
+
*/
|
|
97
|
+
export const isLeOrEq = (value, min) => value <= min;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Value greater than check.
|
|
101
|
+
* @param {number} value - Input value.
|
|
102
|
+
* @param {number} min - Limit `value` must be greater or equal than.
|
|
103
|
+
* @param {number} max - Limit `value` must be less or equal than.
|
|
104
|
+
* @returns {boolean} `true` is check success.
|
|
105
|
+
* @private
|
|
106
|
+
*/
|
|
107
|
+
export const isInRange = (value, min, max) => value >= min && value <= max;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Value equal check.
|
|
111
|
+
* @param {number} value - Input value.
|
|
112
|
+
* @param {number} expected - `expected` that `value` must equal.
|
|
113
|
+
* @returns {boolean} `true` is check success.
|
|
114
|
+
* @private
|
|
115
|
+
*/
|
|
116
|
+
export const isEq = (value, expected) => value === expected;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addFloat,
|
|
3
|
+
fixFloat,
|
|
4
|
+
fixFloatPrecision,
|
|
5
|
+
getRandomInt,
|
|
6
|
+
isEq,
|
|
7
|
+
isGt,
|
|
8
|
+
isGtOrEq,
|
|
9
|
+
isInRange,
|
|
10
|
+
isLe,
|
|
11
|
+
isLeOrEq,
|
|
12
|
+
subFloat,
|
|
13
|
+
deg2rad,
|
|
14
|
+
} from './number.js';
|
|
15
|
+
|
|
16
|
+
test('Converts degrees to radians', () => {
|
|
17
|
+
expect(deg2rad(90)).toBe(1.5707963267948966);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('fixFloatPrecision', () => {
|
|
21
|
+
test('Fixes float precision issues', () => {
|
|
22
|
+
expect(fixFloatPrecision(0.20000000000000004)).toBe(0.2);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('Handles zero', () => {
|
|
26
|
+
expect(fixFloatPrecision(0)).toBe(0);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('Handles negative numbers', () => {
|
|
30
|
+
expect(fixFloatPrecision(-0.20000000000000004)).toBe(-0.2);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('Handles very small numbers', () => {
|
|
34
|
+
expect(fixFloatPrecision(0.0000000000001)).toBe(0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('Handles integer numbers', () => {
|
|
38
|
+
expect(fixFloatPrecision(5)).toBe(5);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('Handles string input', () => {
|
|
42
|
+
expect(fixFloatPrecision('5.123456789')).toBe(5.123456789);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('getRandomInt', () => {
|
|
47
|
+
test('Returns random integer within range when min equals max', () => {
|
|
48
|
+
expect(getRandomInt(1, 1)).toBe(1);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('Returns random integer within range when min is less than max', () => {
|
|
52
|
+
const result = getRandomInt(1, 10);
|
|
53
|
+
expect(result).toBeGreaterThanOrEqual(1);
|
|
54
|
+
expect(result).toBeLessThanOrEqual(10);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('Works with negative numbers', () => {
|
|
58
|
+
const result = getRandomInt(-5, -1);
|
|
59
|
+
expect(result).toBeGreaterThanOrEqual(-5);
|
|
60
|
+
expect(result).toBeLessThanOrEqual(-1);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('Works with zero range', () => {
|
|
64
|
+
const result = getRandomInt(0, 0);
|
|
65
|
+
expect(result).toBe(0);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('fixFloat()', () => {
|
|
70
|
+
expect(fixFloat(0.20000000000000004)).toBe(0.2);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('addFloat()', () => {
|
|
74
|
+
expect(addFloat(0.20000000000000004, 0.1000001)).toBe(0.3);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('subFloat()', () => {
|
|
78
|
+
expect(subFloat(0.20000000000000004, 0.1000001)).toBe(0.1);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('number', () => {
|
|
82
|
+
test('isEq', () => {
|
|
83
|
+
expect(isEq(1, 0)).toBe(false);
|
|
84
|
+
expect(isEq(1, 1)).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('isGt', () => {
|
|
88
|
+
expect(isGt(1, 0)).toBe(true);
|
|
89
|
+
expect(isGt(1, 1)).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('isGtOrEq', () => {
|
|
93
|
+
expect(isGtOrEq(1, 0)).toBe(true);
|
|
94
|
+
expect(isGtOrEq(1, 1)).toBe(true);
|
|
95
|
+
expect(isGtOrEq(1, 2)).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('isGtOrEq', () => {
|
|
99
|
+
expect(isInRange(1, 0, 2)).toBe(true);
|
|
100
|
+
expect(isInRange(1, 0, 1)).toBe(true);
|
|
101
|
+
expect(isInRange(2, 0, 1)).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('isLe', () => {
|
|
105
|
+
expect(isLe(1, 0)).toBe(false);
|
|
106
|
+
expect(isLe(0, 0)).toBe(false);
|
|
107
|
+
expect(isLe(0, 1)).toBe(true);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('isLeOrEq', () => {
|
|
111
|
+
expect(isLeOrEq(1, 0)).toBe(false);
|
|
112
|
+
expect(isLeOrEq(0, 0)).toBe(true);
|
|
113
|
+
expect(isLeOrEq(0, 1)).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purges object properties to free up memory.
|
|
3
|
+
* @param {object} target - The target object.
|
|
4
|
+
*/
|
|
5
|
+
export const purgeObject = (target) => {
|
|
6
|
+
if (!target) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const reference = target;
|
|
10
|
+
for (const entry of Object.keys(target)) {
|
|
11
|
+
reference[entry] = null;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Merge two objects.
|
|
17
|
+
* @param {object} target - Target merge object.
|
|
18
|
+
* @param {object} source - Source merge object.
|
|
19
|
+
* @returns {object} Merged result object.
|
|
20
|
+
*/
|
|
21
|
+
export const deepMerge = (target, source) => {
|
|
22
|
+
if (typeof target !== 'object' || target === null) {
|
|
23
|
+
return source;
|
|
24
|
+
}
|
|
25
|
+
if (typeof source !== 'object' || source === null) {
|
|
26
|
+
return target;
|
|
27
|
+
}
|
|
28
|
+
for (const key of Object.keys(source)) {
|
|
29
|
+
if (key !== '__proto__' && key !== 'constructor') {
|
|
30
|
+
if (typeof source[key] === 'object' && source[key] !== null) {
|
|
31
|
+
if (!target[key] || typeof target[key] !== 'object') {
|
|
32
|
+
target[key] = {};
|
|
33
|
+
}
|
|
34
|
+
deepMerge(target[key], source[key]);
|
|
35
|
+
} else {
|
|
36
|
+
target[key] = source[key];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return target;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns the sum value of an array of objects field.
|
|
46
|
+
* @param {object[]} arr - The list of input objects.
|
|
47
|
+
* @param {string} prop - The object property key.
|
|
48
|
+
* @returns {number} The sum value.
|
|
49
|
+
*/
|
|
50
|
+
export const getObjArrayPropSum = (arr, prop) => arr.reduce((accumulator, object) => accumulator + object[prop], 0);
|
|
51
|
+
/**
|
|
52
|
+
* Get object value by path.
|
|
53
|
+
* @param {object} obj - The source object to get the value from.
|
|
54
|
+
* @param {string} path - The path to the property in dot notation (e.g. 'a.b.c').
|
|
55
|
+
* @returns {object | null} The value at the specified path or null if not found.
|
|
56
|
+
*/
|
|
57
|
+
export const getObjValueByPath = (obj, path) => {
|
|
58
|
+
if (!obj || !path) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const keyParts = path.split('.');
|
|
62
|
+
const [nextKey] = keyParts;
|
|
63
|
+
if (keyParts.length === 1) {
|
|
64
|
+
return obj[nextKey] === undefined ? null : obj[nextKey];
|
|
65
|
+
}
|
|
66
|
+
if (obj[nextKey] === undefined || obj[nextKey] === null) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return getObjValueByPath(obj[nextKey], keyParts.slice(1).join('.'));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Set object value by path.
|
|
74
|
+
* @param {object} obj - The source object to set the value in.
|
|
75
|
+
* @param {string} path - The path to the property in dot notation (e.g. 'a.b.c').
|
|
76
|
+
* @param {object | null | undefined} value - The value to set at the specified path.
|
|
77
|
+
* @throws {SyntaxError} Error when illegal path value has been provided.
|
|
78
|
+
*/
|
|
79
|
+
export const setObjValueByPath = (obj, path, value) => {
|
|
80
|
+
if (!obj || !path) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const keyParts = path.split('.');
|
|
84
|
+
const [nextKey] = keyParts;
|
|
85
|
+
if (nextKey === '__proto__') {
|
|
86
|
+
throw new SyntaxError('Security violation error. Cannot use "__proto__" as parameter.');
|
|
87
|
+
}
|
|
88
|
+
if (keyParts.length === 1) {
|
|
89
|
+
obj[nextKey] = value;
|
|
90
|
+
} else {
|
|
91
|
+
// Create the nested object if it doesn't exist
|
|
92
|
+
if (obj[nextKey] === undefined || obj[nextKey] === null) {
|
|
93
|
+
obj[nextKey] = {};
|
|
94
|
+
}
|
|
95
|
+
setObjValueByPath(obj[nextKey], keyParts.slice(1).join('.'), value);
|
|
96
|
+
}
|
|
97
|
+
};
|