@vpmedia/simplify 1.63.0 โ†’ 1.65.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 CHANGED
@@ -1,3 +1,37 @@
1
+ ## [1.65.0] - 2026-01-30
2
+
3
+ ### ๐Ÿ’ผ Other
4
+
5
+ - *(deps)* Bump dependency versions
6
+
7
+ ### ๐Ÿšœ Refactor
8
+
9
+ - Relax fixFloatPrecision error handling
10
+
11
+ ### โš™๏ธ Miscellaneous Tasks
12
+
13
+ - Release
14
+ - *(release)* V1.65.0
15
+ ## [1.64.0] - 2026-01-30
16
+
17
+ ### ๐Ÿ’ผ Other
18
+
19
+ - *(deps)* Bump dependency versions
20
+ - *(deps)* Bump dependency versions
21
+
22
+ ### ๐Ÿšœ Refactor
23
+
24
+ - Improve error handling
25
+ - Improve type guards and performance optimizations
26
+
27
+ ### ๐Ÿงช Testing
28
+
29
+ - Improve test coverage
30
+
31
+ ### โš™๏ธ Miscellaneous Tasks
32
+
33
+ - Release
34
+ - *(release)* V1.64.0
1
35
  ## [1.63.0] - 2026-01-28
2
36
 
3
37
  ### ๐Ÿ’ผ Other
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vpmedia/simplify",
3
- "version": "1.63.0",
3
+ "version": "1.65.0",
4
4
  "description": "@vpmedia/simplify",
5
5
  "author": "Andras Csizmadia <andras@vpmedia.hu> (www.vpmedia.hu)",
6
6
  "license": "MIT",
@@ -22,23 +22,23 @@
22
22
  "eventemitter3": "^5.0.4"
23
23
  },
24
24
  "optionalDependencies": {
25
- "@sentry/browser": "^10.37.0"
25
+ "@sentry/browser": "^10.38.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@commitlint/cli": "^20.3.1",
29
29
  "@commitlint/config-conventional": "^20.3.1",
30
30
  "@eslint/js": "^9.39.2",
31
- "@types/node": "^25.0.10",
31
+ "@types/node": "^25.1.0",
32
32
  "@vitest/coverage-v8": "^4.0.18",
33
33
  "eslint": "^9.39.2",
34
- "eslint-plugin-jsdoc": "^62.4.1",
34
+ "eslint-plugin-jsdoc": "^62.5.0",
35
35
  "eslint-plugin-oxlint": "^1.42.0",
36
36
  "eslint-plugin-unicorn": "^62.0.0",
37
37
  "globals": "^17.2.0",
38
38
  "jsdom": "^27.4.0",
39
39
  "msw": "^2.12.7",
40
40
  "oxlint": "^1.42.0",
41
- "oxlint-tsgolint": "^0.11.2",
41
+ "oxlint-tsgolint": "^0.11.3",
42
42
  "prettier": "^3.8.1",
43
43
  "typescript": "^5.9.3",
44
44
  "typescript-eslint": "^8.54.0",
package/src/index.js CHANGED
@@ -23,9 +23,7 @@ export { delayPromise, loadJSON } from './util/async.js';
23
23
  export { getErrorDetails, getTypedError } from './util/error.js';
24
24
  export { FetchError, fetchRetry, HTTP_0_ANY } from './util/fetch.js';
25
25
  export {
26
- addFloat,
27
26
  deg2rad,
28
- fixFloat,
29
27
  fixFloatPrecision,
30
28
  getRandomInt,
31
29
  isEqual,
@@ -34,9 +32,9 @@ export {
34
32
  isInRange,
35
33
  isLess,
36
34
  isLessOrEqual,
37
- subFloat,
35
+ rad2deg,
38
36
  } from './util/number.js';
39
- export { purgeObject, deepMerge, getObjArrayPropSum, getObjValueByPath, setObjValueByPath } from './util/object.js';
37
+ export { deepMerge, getObjArrayPropSum, getObjValueByPath, purgeObject, setObjValueByPath } from './util/object.js';
40
38
  export { getURLParam, sanitizeURLParam } from './util/query.js';
41
39
  export { serverDataToState } from './util/state.js';
42
40
  export { addLeadingZero, capitalize, getTypeFromValue, saveAsFile, underscoreToCamelCase } from './util/string.js';
@@ -52,10 +52,13 @@ test('Tests Logger custom handler', () => {
52
52
  logger.info('info');
53
53
  expect(testLogHandler.emitLogLevel).toBe(LOG_LEVEL_INFO);
54
54
  expect(testLogHandler.emitLogMessage).toBe('info');
55
- // info
55
+ // warning
56
56
  logger.warn('warning');
57
57
  expect(testLogHandler.emitLogLevel).toBe(LOG_LEVEL_WARNING);
58
58
  expect(testLogHandler.emitLogMessage).toBe('warning');
59
+ logger.warning('warning');
60
+ expect(testLogHandler.emitLogLevel).toBe(LOG_LEVEL_WARNING);
61
+ expect(testLogHandler.emitLogMessage).toBe('warning');
59
62
  // error
60
63
  logger.error('error');
61
64
  expect(testLogHandler.emitLogLevel).toBe(LOG_LEVEL_ERROR);
@@ -4,11 +4,27 @@ export const PAGE_LIFECYCLE_STATE_PASSIVE = 'passive';
4
4
  export const PAGE_LIFECYCLE_STATE_FROZEN = 'frozen';
5
5
  export const PAGE_LIFECYCLE_STATE_TERMINATED = 'terminated';
6
6
 
7
+ export const PAGE_LIFECYCLE_STATES = new Set([
8
+ PAGE_LIFECYCLE_STATE_ACTIVE,
9
+ PAGE_LIFECYCLE_STATE_FROZEN,
10
+ PAGE_LIFECYCLE_STATE_HIDDEN,
11
+ PAGE_LIFECYCLE_STATE_PASSIVE,
12
+ PAGE_LIFECYCLE_STATE_TERMINATED,
13
+ ]);
14
+
7
15
  export const DOCUMENT_STATE_DOM_LOADED = 'domLoaded';
8
16
  export const DOCUMENT_STATE_FULLY_LOADED = 'fullyLoaded';
9
17
  export const DOCUMENT_STATE_COMPLETE = 'complete';
10
18
  export const DOCUMENT_STATE_INTERACTIVE = 'interactive';
11
19
  export const DOCUMENT_STATE_LOADING = 'loading';
12
20
 
21
+ export const DOCUMENT_STATES = new Set([
22
+ DOCUMENT_STATE_COMPLETE,
23
+ DOCUMENT_STATE_DOM_LOADED,
24
+ DOCUMENT_STATE_FULLY_LOADED,
25
+ DOCUMENT_STATE_INTERACTIVE,
26
+ DOCUMENT_STATE_LOADING,
27
+ ]);
28
+
13
29
  export const PAGE_LIFECYCLE_STATE_CHANGE_EVENT = 'pageLifecycleStateChange';
14
30
  export const DOCUMENT_STATE_CHANGE_EVENT = 'documentStateChange';
@@ -0,0 +1,102 @@
1
+ import { beforeAll, describe, expect, it } from 'vitest';
2
+ import {
3
+ DOCUMENT_STATE_CHANGE_EVENT,
4
+ DOCUMENT_STATES,
5
+ PAGE_LIFECYCLE_STATE_CHANGE_EVENT,
6
+ PAGE_LIFECYCLE_STATE_HIDDEN,
7
+ PAGE_LIFECYCLE_STATES,
8
+ } from './const.js';
9
+ import {
10
+ addPageLifecycleCallback,
11
+ getDocumentState,
12
+ getPageLifecycleEventEmitter,
13
+ getPageLifecycleState,
14
+ initPageLifecycle,
15
+ isPageLifecycleInitialized,
16
+ } from './util.js';
17
+ import { delayPromise } from '../util/async.js';
18
+
19
+ describe('Page Lifecycle', () => {
20
+ beforeAll(() => {
21
+ initPageLifecycle();
22
+ });
23
+
24
+ it('should initialize page lifecycle', () => {
25
+ expect(isPageLifecycleInitialized()).toBe(true);
26
+ });
27
+
28
+ it('should return current page lifecycle state', () => {
29
+ const state = getPageLifecycleState();
30
+ expect(state).toBeOneOf(PAGE_LIFECYCLE_STATES);
31
+ });
32
+
33
+ it('should return current document state', () => {
34
+ const state = getDocumentState();
35
+ expect(state).toBeOneOf(DOCUMENT_STATES);
36
+ });
37
+
38
+ it('should return event emitter instance', () => {
39
+ const emitter = getPageLifecycleEventEmitter();
40
+ expect(emitter).toBeDefined();
41
+ });
42
+
43
+ it('should handle page lifecycle state changes', () => {
44
+ const emitter = getPageLifecycleEventEmitter();
45
+ let stateChanged = false;
46
+
47
+ emitter.on(PAGE_LIFECYCLE_STATE_CHANGE_EVENT, (data) => {
48
+ stateChanged = true;
49
+ expect(data).toHaveProperty('previousState');
50
+ expect(data).toHaveProperty('nextState');
51
+ });
52
+
53
+ // Trigger visibility change
54
+ const originalVisibilityState = document.visibilityState;
55
+ Object.defineProperty(document, 'visibilityState', {
56
+ value: originalVisibilityState === 'visible' ? 'hidden' : 'visible',
57
+ writable: true,
58
+ configurable: true,
59
+ });
60
+
61
+ document.dispatchEvent(new Event('visibilitychange'));
62
+
63
+ // Restore original state
64
+ Object.defineProperty(document, 'visibilityState', {
65
+ value: originalVisibilityState,
66
+ writable: true,
67
+ configurable: true,
68
+ });
69
+
70
+ expect(stateChanged).toBe(true);
71
+ });
72
+
73
+ it('should handle document state changes', () => {
74
+ const emitter = getPageLifecycleEventEmitter();
75
+ let stateChanged = false;
76
+
77
+ emitter.on(DOCUMENT_STATE_CHANGE_EVENT, (data) => {
78
+ stateChanged = true;
79
+ expect(data).toHaveProperty('previousState');
80
+ expect(data).toHaveProperty('nextState');
81
+ });
82
+
83
+ // Trigger ready state change
84
+ const originalReadyState = document.readyState;
85
+ Object.defineProperty(document, 'readyState', {
86
+ value: originalReadyState === 'complete' ? 'interactive' : 'complete',
87
+ writable: true,
88
+ configurable: true,
89
+ });
90
+
91
+ document.dispatchEvent(new Event('readystatechange'));
92
+
93
+ // Restore original state
94
+ Object.defineProperty(document, 'readyState', {
95
+ value: originalReadyState,
96
+ writable: true,
97
+ configurable: true,
98
+ });
99
+
100
+ expect(stateChanged).toBe(true);
101
+ });
102
+ });
@@ -2,9 +2,11 @@ export class TypeCheckError extends TypeError {
2
2
  /**
3
3
  * Creates a new `TypeCheckError` instance.
4
4
  * @param {string} message - Error message.
5
+ * @param {{ cause?: unknown, value?: unknown }} [options] - Error options.
5
6
  */
6
- constructor(message) {
7
- super(message);
7
+ constructor(message, options) {
8
+ super(message, options);
8
9
  this.name = 'TypeCheckError';
10
+ this.value = options?.value;
9
11
  }
10
12
  }
@@ -0,0 +1,70 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { typeChecker } from './TypeChecker.js';
3
+ import { TypeCheckError } from './TypeCheckError.js';
4
+
5
+ const stringValidator = (value) => typeof value === 'string';
6
+
7
+ describe('TypeChecker', () => {
8
+ it('should check value type correctly', () => {
9
+ const result = typeChecker.check('test', stringValidator);
10
+ expect(result).toBe('test');
11
+ });
12
+
13
+ it('should throw TypeCheckError for invalid type', () => {
14
+ expect(() => typeChecker.check(123, stringValidator)).toThrow(TypeCheckError);
15
+ });
16
+
17
+ it('should swallow errors when enabled', () => {
18
+ typeChecker.setSwallowErrors(true);
19
+ const result = typeChecker.check(123, stringValidator);
20
+ expect(result).toBe(123);
21
+ typeChecker.setSwallowErrors(false);
22
+ });
23
+
24
+ it('should check array type correctly', () => {
25
+ const result = typeChecker.checkArray(['a', 'b', 'c'], stringValidator);
26
+ expect(result).toEqual(['a', 'b', 'c']);
27
+ });
28
+
29
+ it('should throw TypeCheckError for invalid array type', () => {
30
+ expect(() => typeChecker.checkArray([1, 2, 3], stringValidator)).toThrow(TypeCheckError);
31
+ });
32
+
33
+ it('should swallow array errors when enabled', () => {
34
+ typeChecker.setSwallowErrors(true);
35
+ const result = typeChecker.checkArray([1, 2, 3], stringValidator);
36
+ expect(result).toEqual([1, 2, 3]);
37
+ typeChecker.setSwallowErrors(false);
38
+ });
39
+
40
+ it('should check enum value correctly', () => {
41
+ const choices = ['option1', 'option2', 'option3'];
42
+ const result = typeChecker.checkEnum('option2', choices);
43
+ expect(result).toBe('option2');
44
+ });
45
+
46
+ it('should throw TypeCheckError for invalid enum value', () => {
47
+ const choices = ['option1', 'option2', 'option3'];
48
+ expect(() => typeChecker.checkEnum('invalid', choices)).toThrow(TypeCheckError);
49
+ });
50
+
51
+ it('should swallow enum errors when enabled', () => {
52
+ typeChecker.setSwallowErrors(true);
53
+ const choices = ['option1', 'option2', 'option3'];
54
+ const result = typeChecker.checkEnum('invalid', choices);
55
+ expect(result).toBe('invalid');
56
+ typeChecker.setSwallowErrors(false);
57
+ });
58
+
59
+ it('should handle number enum values', () => {
60
+ const choices = [1, 2, 3];
61
+ const result = typeChecker.checkEnum(2, choices);
62
+ expect(result).toBe(2);
63
+ });
64
+
65
+ it('should handle mixed string and number enum values', () => {
66
+ const choices = ['option1', 2, 'option3'];
67
+ const result = typeChecker.checkEnum(2, choices);
68
+ expect(result).toBe(2);
69
+ });
70
+ });
package/src/util/async.js CHANGED
@@ -11,9 +11,12 @@ export const delayPromise = (delayMS) =>
11
11
  /**
12
12
  * Load JSON file using a fetch GET request.
13
13
  * @param {string} url - URL to load.
14
- * @returns {Promise<string>} The parsed JSON data.
14
+ * @returns {Promise<unknown>} The parsed JSON data.
15
15
  */
16
16
  export const loadJSON = async (url) => {
17
17
  const response = await fetch(url);
18
- return await response.json();
18
+ if (!response.ok) {
19
+ throw new DOMException(`Fetch error ${response.status}`, 'FetchError');
20
+ }
21
+ return response.json();
19
22
  };
@@ -1,4 +1,4 @@
1
- import { delayPromise } from './async.js';
1
+ import { delayPromise, loadJSON } from './async.js';
2
2
 
3
3
  describe('delayPromise', () => {
4
4
  test('Returns a promise that resolves after specified delay', async () => {
@@ -19,3 +19,13 @@ describe('delayPromise', () => {
19
19
  expect(end - start).toBeGreaterThanOrEqual(0);
20
20
  });
21
21
  });
22
+
23
+ describe('loadJSON', () => {
24
+ test('Load JSON data', async () => {
25
+ const data = await loadJSON('/test.json');
26
+ expect(data).toMatchObject({
27
+ method: 'GET',
28
+ success: true,
29
+ });
30
+ });
31
+ });
package/src/util/error.js CHANGED
@@ -1,10 +1,12 @@
1
+ const DEFAULT_EXCLUDES = new Set(['stack']);
2
+
1
3
  /**
2
4
  * Retrieves detailed information from an error object.
3
5
  * @param {Error} error - The error to extract details from.
4
- * @param {string[]} [excludes] - An array of property names to exclude from the result.
6
+ * @param {null | undefined | string[]} [excludes] - An array of property names to exclude from the result.
5
7
  * @returns {object} - An object containing the error details.
6
8
  */
7
- export const getErrorDetails = (error, excludes = ['stack']) => {
9
+ export const getErrorDetails = (error, excludes) => {
8
10
  const errorDetails = {
9
11
  name: error.name,
10
12
  type: error.constructor?.name ?? typeof error,
@@ -16,7 +18,7 @@ export const getErrorDetails = (error, excludes = ['stack']) => {
16
18
  errorDetails.cause = error.cause;
17
19
  }
18
20
  for (const key of Object.getOwnPropertyNames(error)) {
19
- if (!excludes.includes(key)) {
21
+ if (!excludes?.includes(key) && !DEFAULT_EXCLUDES.has(key)) {
20
22
  errorDetails[key] = error[key];
21
23
  }
22
24
  }
@@ -9,6 +9,7 @@ describe('error', () => {
9
9
  expect(errorDetails.type).toBe('Error');
10
10
  expect(errorDetails.message).toBe('Test error');
11
11
  expect(errorDetails.cause).toBe('Test cause');
12
+ expect(errorDetails.stack).toBe(undefined);
12
13
  });
13
14
 
14
15
  test('getErrorDetails with Error cause', () => {
package/src/util/fetch.js CHANGED
@@ -1,4 +1,3 @@
1
- /* eslint-disable jsdoc/no-undefined-types */
2
1
  /* oxlint-disable no-await-in-loop */
3
2
 
4
3
  import {
@@ -1,9 +1,19 @@
1
1
  import { HTTP_404_NOT_FOUND } from '../const/http_status.js';
2
2
  import { fetchRetry, FetchError } from './fetch.js';
3
3
 
4
+ describe('FetchError', () => {
5
+ test('constructor', () => {
6
+ const error = new FetchError('message', 'url', { method: 'GET' }, null);
7
+ expect(error.message).toEqual('message');
8
+ expect(error.resource).toEqual('url');
9
+ expect(error.fetchOptions).toMatchObject({ method: 'GET' });
10
+ expect(error.response).toBe(null);
11
+ });
12
+ });
13
+
4
14
  describe('fetchRetry', () => {
5
15
  test('fetch OK', async () => {
6
- const response = await fetchRetry('https://jsonplaceholder.typicode.com/todos/1', {
16
+ const response = await fetchRetry('/test.json', {
7
17
  cache: 'no-cache',
8
18
  keepalive: false,
9
19
  method: 'GET',
@@ -11,16 +21,14 @@ describe('fetchRetry', () => {
11
21
  });
12
22
  const json = await response.json();
13
23
  const expectedJSON = {
14
- completed: false,
15
- id: 1,
16
- title: 'delectus aut autem',
17
- userId: 1,
24
+ success: true,
25
+ method: 'GET',
18
26
  };
19
27
  expect(json).toEqual(expectedJSON);
20
28
  });
21
29
  test('fetch unknown scheme', async () => {
22
30
  try {
23
- await fetchRetry('htps://jsonplaceholder', {});
31
+ await fetchRetry('htps://', {});
24
32
  } catch (error) {
25
33
  const typedError = error instanceof Error ? error : new Error(String(error));
26
34
  expect(typedError.message).toEqual('fetch failed');
@@ -32,7 +40,7 @@ describe('fetchRetry', () => {
32
40
  test('fetch 404 error with retry', async () => {
33
41
  try {
34
42
  await fetchRetry(
35
- 'https://jsonplaceholder.typicode.com/todos/1',
43
+ '/test_error.json',
36
44
  {
37
45
  cache: 'no-cache',
38
46
  keepalive: false,
@@ -1,63 +1,65 @@
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);
1
+ import { TypeCheckError } from '../typecheck/TypeCheckError.js';
7
2
 
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;
3
+ const DEG_TO_RAD = Math.PI / 180;
4
+ const RAD_TO_DEG = 180 / Math.PI;
5
+
6
+ const PRECISION = 12;
7
+ const EPSILON = 1e-11;
15
8
 
16
9
  /**
17
- * Fixes floating point number (0.20000000000000004 -> 0.2).
18
- * @param {number | string} value - Number to fix.
19
- * @returns {number} The fixed number.
10
+ * Converts degrees to radians.
11
+ * @param {number} degrees - Angle in degrees.
12
+ * @returns {number} Angle in radians.
13
+ * @throws {TypeCheckError}
20
14
  */
21
- export const fixFloatPrecision = (value) => {
22
- // Handle string inputs by converting to number first
23
- if (typeof value === 'string') {
24
- value = Number(value);
15
+ export const deg2rad = (degrees) => {
16
+ if (!Number.isFinite(degrees)) {
17
+ throw new TypeCheckError('Argument degrees must be a finite number', { value: degrees });
25
18
  }
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));
19
+ return degrees * DEG_TO_RAD;
31
20
  };
32
21
 
33
22
  /**
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.
23
+ * Converts radians to degrees.
24
+ * @param {number} radians - Angle in radians.
25
+ * @returns {number} Angle in degrees.
26
+ * @throws {TypeCheckError}
38
27
  */
39
- export const fixFloat = (value, p = 2) => Number.parseFloat(value.toFixed(p));
28
+ export const rad2deg = (radians) => {
29
+ if (!Number.isFinite(radians)) {
30
+ throw new TypeCheckError('Argument radians must be a finite number', { value: radians });
31
+ }
32
+ return radians * RAD_TO_DEG;
33
+ };
40
34
 
41
35
  /**
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.
36
+ * Returns random integer in range.
37
+ * @param {number} min - Min value.
38
+ * @param {number} max - Max value.
39
+ * @returns {number} Random integer in given range.
40
+ * @throws {TypeError}
46
41
  */
47
- export const addFloat = (a, b) => {
48
- const p = 100;
49
- return fixFloat((a * p + b * p) / p);
42
+ export const getRandomInt = (min, max) => {
43
+ if (!Number.isFinite(min)) {
44
+ throw new TypeCheckError('Argument min must be finite number', { value: min });
45
+ }
46
+ if (!Number.isFinite(max)) {
47
+ throw new TypeCheckError('Argument max must be finite number', { value: max });
48
+ }
49
+ return Math.floor(Math.random() * (max - min + 1)) + min;
50
50
  };
51
51
 
52
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.
53
+ * Normalizes floating point precision (e.g. 0.20000000000000004 โ†’ 0.2).
54
+ * @param {number | string} value - Input value.
55
+ * @returns {number} Fixed float precision value.
57
56
  */
58
- export const subFloat = (a, b) => {
59
- const p = 100;
60
- return fixFloat((a * p - b * p) / p);
57
+ export const fixFloatPrecision = (value) => {
58
+ const parsedValue = typeof value === 'string' ? Number(value) : value;
59
+ if (!Number.isFinite(parsedValue)) {
60
+ return Number.NaN;
61
+ }
62
+ return Math.abs(parsedValue) < EPSILON ? 0 : Number(parsedValue.toPrecision(PRECISION));
61
63
  };
62
64
 
63
65
  /**
@@ -1,6 +1,7 @@
1
+ /* eslint-disable unicorn/no-useless-undefined */
2
+
3
+ import { TypeCheckError } from '../typecheck/TypeCheckError.js';
1
4
  import {
2
- addFloat,
3
- fixFloat,
4
5
  fixFloatPrecision,
5
6
  getRandomInt,
6
7
  isEqual,
@@ -9,12 +10,20 @@ import {
9
10
  isInRange,
10
11
  isLess,
11
12
  isLessOrEqual,
12
- subFloat,
13
13
  deg2rad,
14
+ rad2deg,
14
15
  } from './number.js';
15
16
 
16
- test('Converts degrees to radians', () => {
17
+ test('Converts angle in degrees to radians', () => {
17
18
  expect(deg2rad(90)).toBe(1.5707963267948966);
19
+ // @ts-expect-error
20
+ expect(() => deg2rad('')).toThrowError(TypeCheckError);
21
+ });
22
+
23
+ test('Converts angle in radians to degrees', () => {
24
+ expect(rad2deg(1.5707963267948966)).toBe(90);
25
+ // @ts-expect-error
26
+ expect(() => rad2deg('')).toThrowError(TypeCheckError);
18
27
  });
19
28
 
20
29
  describe('fixFloatPrecision', () => {
@@ -32,18 +41,32 @@ describe('fixFloatPrecision', () => {
32
41
 
33
42
  test('Handles very small numbers', () => {
34
43
  expect(fixFloatPrecision(0.0000000000001)).toBe(0);
44
+ expect(fixFloatPrecision(-0.0000000000001)).toBe(0);
35
45
  });
36
46
 
37
47
  test('Handles integer numbers', () => {
38
48
  expect(fixFloatPrecision(5)).toBe(5);
39
49
  });
40
50
 
41
- test('Handles string input', () => {
51
+ test('Handles string number input', () => {
42
52
  expect(fixFloatPrecision('5.123456789')).toBe(5.123456789);
43
53
  });
54
+
55
+ test('Throws error for invalid input', () => {
56
+ expect(fixFloatPrecision('abc')).toBe(Number.NaN);
57
+ expect(fixFloatPrecision(null)).toBe(Number.NaN);
58
+ expect(fixFloatPrecision(undefined)).toBe(Number.NaN);
59
+ });
44
60
  });
45
61
 
46
62
  describe('getRandomInt', () => {
63
+ test('Throws error if min or max is not finite number', () => {
64
+ // @ts-expect-error
65
+ expect(() => getRandomInt('', 1)).toThrowError(TypeCheckError);
66
+ // @ts-expect-error
67
+ expect(() => getRandomInt(1, '')).toThrowError(TypeCheckError);
68
+ });
69
+
47
70
  test('Returns random integer within range when min equals max', () => {
48
71
  expect(getRandomInt(1, 1)).toBe(1);
49
72
  });
@@ -66,18 +89,6 @@ describe('getRandomInt', () => {
66
89
  });
67
90
  });
68
91
 
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
92
  describe('number', () => {
82
93
  test('isEq', () => {
83
94
  expect(isEqual(1, 0)).toBe(false);
@@ -1,4 +1,4 @@
1
- /* eslint-disable jsdoc/reject-any-type, jsdoc/no-undefined-types */
1
+ /* eslint-disable jsdoc/reject-any-type */
2
2
 
3
3
  import { isEqual, isGreater, isGreaterOrEqual, isInRange, isLess, isLessOrEqual } from './number.js';
4
4
 
@@ -0,0 +1,34 @@
1
+ import { http, HttpResponse } from 'msw';
2
+
3
+ export const fetchHandlers = [
4
+ http.get('/test.json', () => {
5
+ const response = HttpResponse.json(
6
+ {
7
+ success: true,
8
+ method: 'GET',
9
+ },
10
+ { status: 200 }
11
+ );
12
+ return response;
13
+ }),
14
+ http.post('/test.json', () => {
15
+ const response = HttpResponse.json(
16
+ {
17
+ success: true,
18
+ method: 'POST',
19
+ },
20
+ { status: 200 }
21
+ );
22
+ return response;
23
+ }),
24
+ http.post('/test_error.json', () => {
25
+ const response = HttpResponse.json(
26
+ {
27
+ success: false,
28
+ method: 'POST',
29
+ },
30
+ { status: 404 }
31
+ );
32
+ return response;
33
+ }),
34
+ ];
@@ -0,0 +1,3 @@
1
+ import { fetchHandlers } from './handlers/fetch.js';
2
+
3
+ export const handlers = [...fetchHandlers];
package/types/index.d.ts CHANGED
@@ -17,8 +17,8 @@ export { typeCheck, typeCheckArray, typeCheckEnum } from "./typecheck/util.js";
17
17
  export { delayPromise, loadJSON } from "./util/async.js";
18
18
  export { getErrorDetails, getTypedError } from "./util/error.js";
19
19
  export { FetchError, fetchRetry, HTTP_0_ANY } from "./util/fetch.js";
20
- export { addFloat, deg2rad, fixFloat, fixFloatPrecision, getRandomInt, isEqual, isGreater, isGreaterOrEqual, isInRange, isLess, isLessOrEqual, subFloat } from "./util/number.js";
21
- export { purgeObject, deepMerge, getObjArrayPropSum, getObjValueByPath, setObjValueByPath } from "./util/object.js";
20
+ export { deg2rad, fixFloatPrecision, getRandomInt, isEqual, isGreater, isGreaterOrEqual, isInRange, isLess, isLessOrEqual, rad2deg } from "./util/number.js";
21
+ export { deepMerge, getObjArrayPropSum, getObjValueByPath, purgeObject, setObjValueByPath } from "./util/object.js";
22
22
  export { getURLParam, sanitizeURLParam } from "./util/query.js";
23
23
  export { addLeadingZero, capitalize, getTypeFromValue, saveAsFile, underscoreToCamelCase } from "./util/string.js";
24
24
  //# sourceMappingURL=index.d.ts.map
@@ -3,11 +3,13 @@ export const PAGE_LIFECYCLE_STATE_ACTIVE: "active";
3
3
  export const PAGE_LIFECYCLE_STATE_PASSIVE: "passive";
4
4
  export const PAGE_LIFECYCLE_STATE_FROZEN: "frozen";
5
5
  export const PAGE_LIFECYCLE_STATE_TERMINATED: "terminated";
6
+ export const PAGE_LIFECYCLE_STATES: Set<string>;
6
7
  export const DOCUMENT_STATE_DOM_LOADED: "domLoaded";
7
8
  export const DOCUMENT_STATE_FULLY_LOADED: "fullyLoaded";
8
9
  export const DOCUMENT_STATE_COMPLETE: "complete";
9
10
  export const DOCUMENT_STATE_INTERACTIVE: "interactive";
10
11
  export const DOCUMENT_STATE_LOADING: "loading";
12
+ export const DOCUMENT_STATES: Set<string>;
11
13
  export const PAGE_LIFECYCLE_STATE_CHANGE_EVENT: "pageLifecycleStateChange";
12
14
  export const DOCUMENT_STATE_CHANGE_EVENT: "documentStateChange";
13
15
  //# sourceMappingURL=const.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"const.d.ts","sourceRoot":"","sources":["../../src/pagelifecycle/const.js"],"names":[],"mappings":"AAAA,0CAA2C,QAAQ,CAAC;AACpD,0CAA2C,QAAQ,CAAC;AACpD,2CAA4C,SAAS,CAAC;AACtD,0CAA2C,QAAQ,CAAC;AACpD,8CAA+C,YAAY,CAAC;AAE5D,wCAAyC,WAAW,CAAC;AACrD,0CAA2C,aAAa,CAAC;AACzD,sCAAuC,UAAU,CAAC;AAClD,yCAA0C,aAAa,CAAC;AACxD,qCAAsC,SAAS,CAAC;AAEhD,gDAAiD,0BAA0B,CAAC;AAC5E,0CAA2C,qBAAqB,CAAC"}
1
+ {"version":3,"file":"const.d.ts","sourceRoot":"","sources":["../../src/pagelifecycle/const.js"],"names":[],"mappings":"AAAA,0CAA2C,QAAQ,CAAC;AACpD,0CAA2C,QAAQ,CAAC;AACpD,2CAA4C,SAAS,CAAC;AACtD,0CAA2C,QAAQ,CAAC;AACpD,8CAA+C,YAAY,CAAC;AAE5D,gDAMG;AAEH,wCAAyC,WAAW,CAAC;AACrD,0CAA2C,aAAa,CAAC;AACzD,sCAAuC,UAAU,CAAC;AAClD,yCAA0C,aAAa,CAAC;AACxD,qCAAsC,SAAS,CAAC;AAEhD,0CAMG;AAEH,gDAAiD,0BAA0B,CAAC;AAC5E,0CAA2C,qBAAqB,CAAC"}
@@ -2,7 +2,12 @@ export class TypeCheckError extends TypeError {
2
2
  /**
3
3
  * Creates a new `TypeCheckError` instance.
4
4
  * @param {string} message - Error message.
5
+ * @param {{ cause?: unknown, value?: unknown }} [options] - Error options.
5
6
  */
6
- constructor(message: string);
7
+ constructor(message: string, options?: {
8
+ cause?: unknown;
9
+ value?: unknown;
10
+ });
11
+ value: unknown;
7
12
  }
8
13
  //# sourceMappingURL=TypeCheckError.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TypeCheckError.d.ts","sourceRoot":"","sources":["../../src/typecheck/TypeCheckError.js"],"names":[],"mappings":"AAAA;IACE;;;OAGG;IACH,qBAFW,MAAM,EAKhB;CACF"}
1
+ {"version":3,"file":"TypeCheckError.d.ts","sourceRoot":"","sources":["../../src/typecheck/TypeCheckError.js"],"names":[],"mappings":"AAAA;IACE;;;;OAIG;IACH,qBAHW,MAAM,YACN;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAM9C;IADC,eAA2B;CAE9B"}
@@ -1,3 +1,3 @@
1
1
  export function delayPromise(delayMS: number): Promise<void>;
2
- export function loadJSON(url: string): Promise<string>;
2
+ export function loadJSON(url: string): Promise<unknown>;
3
3
  //# sourceMappingURL=async.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../../src/util/async.js"],"names":[],"mappings":"AAKO,sCAHI,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAKtB;AAOG,8BAHI,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,CAK3B"}
1
+ {"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../../src/util/async.js"],"names":[],"mappings":"AAKO,sCAHI,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAKtB;AAOG,8BAHI,MAAM,GACJ,OAAO,CAAC,OAAO,CAAC,CAQ5B"}
@@ -1,3 +1,3 @@
1
- export function getErrorDetails(error: Error, excludes?: string[]): object;
1
+ export function getErrorDetails(error: Error, excludes?: null | undefined | string[]): object;
2
2
  export function getTypedError(error: unknown): Error;
3
3
  //# sourceMappingURL=error.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/util/error.js"],"names":[],"mappings":"AAMO,uCAJI,KAAK,aACL,MAAM,EAAE,GACN,MAAM,CAmBlB;AAOM,qCAHI,OAAO,GACL,KAAK,CAEiF"}
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/util/error.js"],"names":[],"mappings":"AAQO,uCAJI,KAAK,aACL,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,GACzB,MAAM,CAmBlB;AAOM,qCAHI,OAAO,GACL,KAAK,CAEiF"}
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/util/fetch.js"],"names":[],"mappings":"AAeA,yBAA0B,CAAC,CAAC;AAE5B;IACE;;;;;;OAMG;IACH,qBALW,MAAM,YACN,MAAM,GAAG,GAAG,GAAG,OAAO,gBACtB,WAAW,YACX,QAAQ,EASlB;IAJC,iCAAwB;IACxB,0BAAgC;IAChC,mBAAwB;IACxB,cAAqC;CAExC;AASM,qCALI,MAAM,GAAG,GAAG,GAAG,OAAO,gBACtB,WAAW,iBACX;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAC,GAC9E,OAAO,CAAC,QAAQ,CAAC,CAkD7B"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/util/fetch.js"],"names":[],"mappings":"AAcA,yBAA0B,CAAC,CAAC;AAE5B;IACE;;;;;;OAMG;IACH,qBALW,MAAM,YACN,MAAM,GAAG,GAAG,GAAG,OAAO,gBACtB,WAAW,YACX,QAAQ,EASlB;IAJC,iCAAwB;IACxB,0BAAgC;IAChC,mBAAwB;IACxB,cAAqC;CAExC;AASM,qCALI,MAAM,GAAG,GAAG,GAAG,OAAO,gBACtB,WAAW,iBACX;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAC,GAC9E,OAAO,CAAC,QAAQ,CAAC,CAkD7B"}
@@ -1,9 +1,7 @@
1
- export function deg2rad(deg: number): number;
1
+ export function deg2rad(degrees: number): number;
2
+ export function rad2deg(radians: number): number;
2
3
  export function getRandomInt(min: number, max: number): number;
3
4
  export function fixFloatPrecision(value: number | string): number;
4
- export function fixFloat(value: number, p?: number): number;
5
- export function addFloat(a: number, b: number): number;
6
- export function subFloat(a: number, b: number): number;
7
5
  export function isGreater(value: number, min: number): boolean;
8
6
  export function isGreaterOrEqual(value: number, min: number): boolean;
9
7
  export function isLess(value: number, min: number): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"number.d.ts","sourceRoot":"","sources":["../../src/util/number.js"],"names":[],"mappings":"AAKO,6BAHI,MAAM,GACJ,MAAM,CAEkC;AAQ9C,kCAJI,MAAM,OACN,MAAM,GACJ,MAAM,CAEwE;AAOpF,yCAHI,MAAM,GAAG,MAAM,GACb,MAAM,CAYlB;AAQM,gCAJI,MAAM,MACN,MAAM,GACJ,MAAM,CAE0D;AAQtE,4BAJI,MAAM,KACN,MAAM,GACJ,MAAM,CAKlB;AAQM,4BAJI,MAAM,KACN,MAAM,GACJ,MAAM,CAKlB;AASM,iCALI,MAAM,OACN,MAAM,GACJ,OAAO,CAGgC;AAS7C,wCALI,MAAM,OACN,MAAM,GACJ,OAAO,CAGwC;AASrD,8BALI,MAAM,OACN,MAAM,GACJ,OAAO,CAG6B;AAS1C,qCALI,MAAM,OACN,MAAM,GACJ,OAAO,CAGqC;AAUlD,iCANI,MAAM,OACN,MAAM,OACN,MAAM,GACJ,OAAO,CAGsD;AASnE,+BALI,MAAM,YACN,MAAM,GACJ,OAAO,CAG0C"}
1
+ {"version":3,"file":"number.d.ts","sourceRoot":"","sources":["../../src/util/number.js"],"names":[],"mappings":"AAcO,iCAJI,MAAM,GACJ,MAAM,CAQlB;AAQM,iCAJI,MAAM,GACJ,MAAM,CAQlB;AASM,kCALI,MAAM,OACN,MAAM,GACJ,MAAM,CAWlB;AAOM,yCAHI,MAAM,GAAG,MAAM,GACb,MAAM,CAQlB;AASM,iCALI,MAAM,OACN,MAAM,GACJ,OAAO,CAGgC;AAS7C,wCALI,MAAM,OACN,MAAM,GACJ,OAAO,CAGwC;AASrD,8BALI,MAAM,OACN,MAAM,GACJ,OAAO,CAG6B;AAS1C,qCALI,MAAM,OACN,MAAM,GACJ,OAAO,CAGqC;AAUlD,iCANI,MAAM,OACN,MAAM,OACN,MAAM,GACJ,OAAO,CAGsD;AASnE,+BALI,MAAM,YACN,MAAM,GACJ,OAAO,CAG0C"}
@@ -0,0 +1,17 @@
1
+ import { setupServer } from 'msw/node';
2
+ import { beforeAll } from 'vitest';
3
+ import { handlers } from './tests/mocks/handlers.js';
4
+
5
+ const server = setupServer(...handlers);
6
+
7
+ beforeAll(() => {
8
+ server.listen();
9
+ });
10
+
11
+ afterEach(() => {
12
+ server.resetHandlers();
13
+ });
14
+
15
+ afterAll(() => {
16
+ server.close();
17
+ });