@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.
Files changed (112) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/package.json +16 -16
  3. package/src/index.js +9 -19
  4. package/src/logging/Logger.js +1 -1
  5. package/src/logging/OpenTelemetryLogHandler.js +1 -1
  6. package/src/logging/util.js +2 -0
  7. package/src/logging/util.test.js +4 -1
  8. package/src/pagelifecycle/util.js +17 -21
  9. package/src/util/async.js +20 -0
  10. package/src/util/{delayPromise.test.js → async.test.js} +1 -1
  11. package/src/util/{getErrorDetails.test.js → error.test.js} +1 -1
  12. package/src/util/{fetchRetry.js → fetch.js} +6 -4
  13. package/src/util/number.js +116 -0
  14. package/src/util/number.test.js +115 -0
  15. package/src/util/object.js +97 -0
  16. package/src/util/object.test.js +205 -0
  17. package/src/util/{getURLParam.js → query.js} +12 -2
  18. package/src/util/{sanitizeURLParam.test.js → query.test.js} +18 -1
  19. package/src/util/{serverDataToState.js → state.js} +1 -1
  20. package/src/util/{serverDataToState.test.js → state.test.js} +1 -1
  21. package/src/util/string.js +54 -0
  22. package/src/util/{capitalize.test.js → string.test.js} +19 -1
  23. package/src/util/validate.js +272 -0
  24. package/src/util/validate.test.js +325 -0
  25. package/tsconfig.build.json +24 -0
  26. package/types/index.d.ts +9 -19
  27. package/types/logging/util.d.ts.map +1 -1
  28. package/types/pagelifecycle/util.d.ts +1 -1
  29. package/types/pagelifecycle/util.d.ts.map +1 -1
  30. package/types/util/async.d.ts +3 -0
  31. package/types/util/async.d.ts.map +1 -0
  32. package/types/util/{getErrorDetails.d.ts → error.d.ts} +1 -1
  33. package/types/util/error.d.ts.map +1 -0
  34. package/types/util/{fetchRetry.d.ts → fetch.d.ts} +1 -1
  35. package/types/util/fetch.d.ts.map +1 -0
  36. package/types/util/number.d.ts +13 -0
  37. package/types/util/number.d.ts.map +1 -0
  38. package/types/util/object.d.ts +6 -0
  39. package/types/util/object.d.ts.map +1 -0
  40. package/types/util/{getURLParam.d.ts → query.d.ts} +2 -1
  41. package/types/util/query.d.ts.map +1 -0
  42. package/types/util/{serverDataToState.d.ts → state.d.ts} +1 -1
  43. package/types/util/state.d.ts.map +1 -0
  44. package/types/util/string.d.ts +5 -0
  45. package/types/util/string.d.ts.map +1 -0
  46. package/types/util/validate.d.ts +54 -0
  47. package/types/util/validate.d.ts.map +1 -0
  48. package/src/util/addLeadingZero.js +0 -16
  49. package/src/util/addLeadingZero.test.js +0 -11
  50. package/src/util/capitalize.js +0 -15
  51. package/src/util/deepMerge.js +0 -24
  52. package/src/util/deepMerge.test.js +0 -103
  53. package/src/util/deg2rad.js +0 -8
  54. package/src/util/deg2rad.test.js +0 -5
  55. package/src/util/delayPromise.js +0 -6
  56. package/src/util/fixFloatPrecision.js +0 -16
  57. package/src/util/fixFloatPrecision.test.js +0 -27
  58. package/src/util/getObjArrayPropSum.js +0 -11
  59. package/src/util/getObjValueByPath.js +0 -20
  60. package/src/util/getObjValueByPath.test.js +0 -51
  61. package/src/util/getRandomInt.js +0 -9
  62. package/src/util/getRandomInt.test.js +0 -24
  63. package/src/util/getURLParam.test.js +0 -18
  64. package/src/util/loadJSON.js +0 -10
  65. package/src/util/purgeObject.js +0 -13
  66. package/src/util/purgeObject.test.js +0 -8
  67. package/src/util/safeFloat.js +0 -31
  68. package/src/util/safeFloat.test.js +0 -13
  69. package/src/util/sanitizeURLParam.js +0 -11
  70. package/src/util/saveAsFile.js +0 -14
  71. package/src/util/setObjValueByPath.js +0 -26
  72. package/src/util/setObjValueByPath.test.js +0 -47
  73. package/src/util/underscoreToCamelCase.js +0 -10
  74. package/src/util/underscoreToCamelCase.test.js +0 -7
  75. package/typedefs/global.d.ts +0 -5
  76. package/types/util/addLeadingZero.d.ts +0 -2
  77. package/types/util/addLeadingZero.d.ts.map +0 -1
  78. package/types/util/capitalize.d.ts +0 -2
  79. package/types/util/capitalize.d.ts.map +0 -1
  80. package/types/util/deepMerge.d.ts +0 -2
  81. package/types/util/deepMerge.d.ts.map +0 -1
  82. package/types/util/deg2rad.d.ts +0 -2
  83. package/types/util/deg2rad.d.ts.map +0 -1
  84. package/types/util/delayPromise.d.ts +0 -2
  85. package/types/util/delayPromise.d.ts.map +0 -1
  86. package/types/util/fetchRetry.d.ts.map +0 -1
  87. package/types/util/fixFloatPrecision.d.ts +0 -7
  88. package/types/util/fixFloatPrecision.d.ts.map +0 -1
  89. package/types/util/getErrorDetails.d.ts.map +0 -1
  90. package/types/util/getObjArrayPropSum.d.ts +0 -2
  91. package/types/util/getObjArrayPropSum.d.ts.map +0 -1
  92. package/types/util/getObjValueByPath.d.ts +0 -2
  93. package/types/util/getObjValueByPath.d.ts.map +0 -1
  94. package/types/util/getRandomInt.d.ts +0 -2
  95. package/types/util/getRandomInt.d.ts.map +0 -1
  96. package/types/util/getURLParam.d.ts.map +0 -1
  97. package/types/util/loadJSON.d.ts +0 -7
  98. package/types/util/loadJSON.d.ts.map +0 -1
  99. package/types/util/purgeObject.d.ts +0 -2
  100. package/types/util/purgeObject.d.ts.map +0 -1
  101. package/types/util/safeFloat.d.ts +0 -22
  102. package/types/util/safeFloat.d.ts.map +0 -1
  103. package/types/util/sanitizeURLParam.d.ts +0 -2
  104. package/types/util/sanitizeURLParam.d.ts.map +0 -1
  105. package/types/util/saveAsFile.d.ts +0 -2
  106. package/types/util/saveAsFile.d.ts.map +0 -1
  107. package/types/util/serverDataToState.d.ts.map +0 -1
  108. package/types/util/setObjValueByPath.d.ts +0 -2
  109. package/types/util/setObjValueByPath.d.ts.map +0 -1
  110. package/types/util/underscoreToCamelCase.d.ts +0 -2
  111. package/types/util/underscoreToCamelCase.d.ts.map +0 -1
  112. /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.53.0",
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.31.0"
25
+ "@sentry/browser": "^10.32.1"
26
26
  },
27
27
  "devDependencies": {
28
- "@commitlint/cli": "^20.2.0",
29
- "@commitlint/config-conventional": "^20.2.0",
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.3",
31
+ "@types/node": "^25.0.5",
32
32
  "@vitest/coverage-v8": "^4.0.16",
33
33
  "eslint": "^9.39.2",
34
- "eslint-plugin-jsdoc": "^61.5.0",
35
- "eslint-plugin-oxlint": "^1.33.0",
34
+ "eslint-plugin-jsdoc": "^62.0.0",
35
+ "eslint-plugin-oxlint": "^1.38.0",
36
36
  "eslint-plugin-unicorn": "^62.0.0",
37
- "globals": "^16.5.0",
38
- "jsdom": "^27.3.0",
39
- "oxlint": "^1.33.0",
40
- "oxlint-tsgolint": "^0.9.1",
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.50.0",
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": "exit 0",
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 \"**/*.{js,jsx}\"",
56
+ "lint": "oxlint src && eslint",
57
57
  "test": "vitest --coverage",
58
- "typecheck": "rm -rf types && tsc"
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 { addLeadingZero } from './util/addLeadingZero.js';
15
- export { capitalize } from './util/capitalize.js';
16
- export { deepMerge } from './util/deepMerge.js';
17
- export { deg2rad } from './util/deg2rad.js';
18
- export { delayPromise } from './util/delayPromise.js';
19
- export { FetchError, fetchRetry, HTTP_0_ANY } from './util/fetchRetry.js';
20
- export { fixFloatPrecision } from './util/fixFloatPrecision.js';
21
- export { getErrorDetails } from './util/getErrorDetails.js';
22
- export { getObjValueByPath } from './util/getObjValueByPath.js';
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';
@@ -1,4 +1,4 @@
1
- import { getURLParam } from '../util/getURLParam.js';
1
+ import { getURLParam } from '../util/query.js';
2
2
  import {
3
3
  LOG_LEVEL_DEBUG,
4
4
  LOG_LEVEL_ERROR,
@@ -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 = LOG_LEVEL_DEBUG, emitter) {
10
+ constructor(level, emitter) {
11
11
  super(level);
12
12
  this.emitter = emitter;
13
13
  }
@@ -1,3 +1,5 @@
1
+ /* eslint-disable dot-notation */
2
+
1
3
  import { LOG_LEVEL_NAMES } from './const.js';
2
4
 
3
5
  /**
@@ -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({ name: 'loggerName' }, Date.now, LOG_LEVEL_INFO, 'logMessage').endsWith('[loggerName] logMessage')
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 {{[key: string]: (() => void)[]}} */
32
- let callbacks = {};
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
- return isInitialized;
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,4 +1,4 @@
1
- import { delayPromise } from './delayPromise.js';
1
+ import { delayPromise } from './async.js';
2
2
 
3
3
  describe('delayPromise', () => {
4
4
  test('Returns a promise that resolves after specified delay', async () => {
@@ -1,4 +1,4 @@
1
- import { getErrorDetails } from './getErrorDetails.js';
1
+ import { getErrorDetails } from './error.js';
2
2
 
3
3
  test('Tests getErrorDetails', () => {
4
4
  const error = new Error('Test error', { cause: 'Test cause' });
@@ -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 './delayPromise.js';
9
- import { getErrorDetails } from './getErrorDetails.js';
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 = 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 = 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
+ };