@vpmedia/simplify 1.60.0 โ†’ 1.62.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,23 @@
1
+ ## [1.62.0] - 2026-01-14
2
+
3
+ ### ๐Ÿงช Testing
4
+
5
+ - Improve test coverage, added getTypedError
6
+
7
+ ### โš™๏ธ Miscellaneous Tasks
8
+
9
+ - Release
10
+ - *(release)* V1.62.0
11
+ ## [1.61.0] - 2026-01-14
12
+
13
+ ### ๐Ÿšœ Refactor
14
+
15
+ - Improve validation error message
16
+
17
+ ### โš™๏ธ Miscellaneous Tasks
18
+
19
+ - Release
20
+ - *(release)* V1.61.0
1
21
  ## [1.60.0] - 2026-01-14
2
22
 
3
23
  ### ๐Ÿ’ผ Other
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vpmedia/simplify",
3
- "version": "1.60.0",
3
+ "version": "1.62.0",
4
4
  "description": "@vpmedia/simplify",
5
5
  "author": "Andras Csizmadia <andras@vpmedia.hu> (www.vpmedia.hu)",
6
6
  "license": "MIT",
package/src/index.js CHANGED
@@ -20,7 +20,7 @@ export { typeChecker } from './typecheck/TypeChecker.js';
20
20
  export { TypeCheckError } from './typecheck/TypeCheckError.js';
21
21
  export { typeCheck, typeCheckArray, typeCheckEnum } from './typecheck/util.js';
22
22
  export { delayPromise, loadJSON } from './util/async.js';
23
- export { getErrorDetails } from './util/error.js';
23
+ export { getErrorDetails, getTypedError } from './util/error.js';
24
24
  export { FetchError, fetchRetry, HTTP_0_ANY } from './util/fetch.js';
25
25
  export {
26
26
  addFloat,
@@ -1,9 +1,17 @@
1
- import { getTypeFromValue } from '../util/string.js';
1
+ import { getDisplayValue, getTypeFromValue } from '../util/string.js';
2
2
  import { isArrayOf, isEnum } from '../util/validate.js';
3
3
  import { TypeCheckError } from './TypeCheckError.js';
4
4
 
5
- const VALIDATOR_FALLBACK_NAME = '<anonymous>';
6
-
5
+ /**
6
+ * Get error message for validator exceptions.
7
+ * @param {string} validatorName - Validator name.
8
+ * @param {unknown} value - Input value.
9
+ */
10
+ const getErrorMessage = (validatorName, value) => {
11
+ const displayValue = getDisplayValue(value);
12
+ const displayType = getTypeFromValue(value);
13
+ throw new TypeCheckError(`Validation failed: ${validatorName || '<anonymous>'} - ${displayValue} (${displayType})`);
14
+ };
7
15
  /**
8
16
  * Type check a value using a validator.
9
17
  * @template T
@@ -14,9 +22,8 @@ const VALIDATOR_FALLBACK_NAME = '<anonymous>';
14
22
  */
15
23
  export const typeCheck = (value, validator) => {
16
24
  if (!validator(value)) {
17
- const validatorName = validator.name || VALIDATOR_FALLBACK_NAME;
18
- const displayValue = getTypeFromValue(value);
19
- throw new TypeCheckError(`Validation failed: ${validatorName} (${displayValue})`);
25
+ const errorMessage = getErrorMessage(validator.name, value);
26
+ throw new TypeCheckError(errorMessage);
20
27
  }
21
28
  return value;
22
29
  };
@@ -31,9 +38,8 @@ export const typeCheck = (value, validator) => {
31
38
  */
32
39
  export const typeCheckArray = (value, validator) => {
33
40
  if (!isArrayOf(value, validator)) {
34
- const validatorName = validator.name || VALIDATOR_FALLBACK_NAME;
35
- const displayValue = getTypeFromValue(value);
36
- throw new TypeCheckError(`Validation failed: ${validatorName} (${displayValue})`);
41
+ const errorMessage = getErrorMessage(validator.name, value);
42
+ throw new TypeCheckError(errorMessage);
37
43
  }
38
44
  return value;
39
45
  };
@@ -47,9 +53,8 @@ export const typeCheckArray = (value, validator) => {
47
53
  */
48
54
  export const typeCheckEnum = (value, choices) => {
49
55
  if (!isEnum(value, choices)) {
50
- const validatorName = 'isEnum';
51
- const displayValue = getTypeFromValue(value);
52
- throw new TypeCheckError(`Validation failed: ${validatorName} (${displayValue})`);
56
+ const errorMessage = getErrorMessage('isEnum', value);
57
+ throw new TypeCheckError(errorMessage);
53
58
  }
54
59
  return value;
55
60
  };
@@ -24,5 +24,12 @@ describe('typecheck', () => {
24
24
  expect(() => typeCheckEnum(null, ['BB'])).toThrowError(TypeCheckError);
25
25
  // @ts-expect-error
26
26
  expect(() => typeCheckEnum(['AA'], null)).toThrowError(TypeCheckError);
27
+ try {
28
+ typeCheckEnum('AA', ['BB']);
29
+ } catch (error) {
30
+ if (error instanceof Error) {
31
+ expect(error.message).toBe('Validation failed: isEnum - "AA" (string)');
32
+ }
33
+ }
27
34
  });
28
35
  });
@@ -10,7 +10,7 @@ describe('delayPromise', () => {
10
10
  expect(end - start).toBeGreaterThanOrEqual(9);
11
11
  });
12
12
 
13
- test('Handles zero delay correctly', async () => {
13
+ test('delayPromise with zero delay', async () => {
14
14
  const start = Date.now();
15
15
  await delayPromise(0);
16
16
  const end = Date.now();
package/src/util/error.js CHANGED
@@ -22,3 +22,10 @@ export const getErrorDetails = (error, excludes = ['stack']) => {
22
22
  }
23
23
  return errorDetails;
24
24
  };
25
+
26
+ /**
27
+ * Get typed error from an unknown type.
28
+ * @param {unknown} error - The error to cast.
29
+ * @returns {Error} The typed error object.
30
+ */
31
+ export const getTypedError = (error) => (error instanceof Error ? error : new Error(String(error)));
@@ -1,20 +1,33 @@
1
- import { getErrorDetails } from './error.js';
1
+ /* eslint-disable unicorn/no-useless-undefined */
2
2
 
3
- test('Tests getErrorDetails', () => {
4
- const error = new Error('Test error', { cause: 'Test cause' });
5
- const errorDetails = getErrorDetails(error);
6
- expect(errorDetails.type).toBe('Error');
7
- expect(errorDetails.message).toBe('Test error');
8
- expect(errorDetails.cause).toBe('Test cause');
9
- });
3
+ import { getErrorDetails, getTypedError } from './error.js';
4
+
5
+ describe('error', () => {
6
+ test('getErrorDetails', () => {
7
+ const error = new Error('Test error', { cause: 'Test cause' });
8
+ const errorDetails = getErrorDetails(error);
9
+ expect(errorDetails.type).toBe('Error');
10
+ expect(errorDetails.message).toBe('Test error');
11
+ expect(errorDetails.cause).toBe('Test cause');
12
+ });
13
+
14
+ test('getErrorDetails with Error cause', () => {
15
+ const error = new SyntaxError('Test error', { cause: new TypeError('Cause error') });
16
+ const errorDetails = getErrorDetails(error);
17
+ expect(errorDetails.type).toBe('SyntaxError');
18
+ expect(errorDetails.message).toBe('Test error');
19
+ expect(errorDetails.cause instanceof Error).toBe(true);
20
+ if (errorDetails.cause instanceof Error) {
21
+ expect(errorDetails.cause.message).toBe('Cause error');
22
+ }
23
+ });
10
24
 
11
- test('Tests getErrorDetails with Error cause', () => {
12
- const error = new SyntaxError('Test error', { cause: new TypeError('Cause error') });
13
- const errorDetails = getErrorDetails(error);
14
- expect(errorDetails.type).toBe('SyntaxError');
15
- expect(errorDetails.message).toBe('Test error');
16
- expect(errorDetails.cause instanceof Error).toBe(true);
17
- if (errorDetails.cause instanceof Error) {
18
- expect(errorDetails.cause.message).toBe('Cause error');
19
- }
25
+ test('getTypedError', () => {
26
+ expect(getTypedError(new Error('Error message')).message).toBe('Error message');
27
+ expect(getTypedError('Error message').message).toBe('Error message');
28
+ expect(getTypedError(1).message).toBe('1');
29
+ expect(getTypedError(true).message).toBe('true');
30
+ expect(getTypedError(null).message).toBe('null');
31
+ expect(getTypedError(undefined).message).toBe('undefined');
32
+ });
20
33
  });
@@ -1,45 +1,47 @@
1
1
  import { serverDataToState } from './state.js';
2
2
 
3
- test('serverDataToState() recursive', () => {
4
- const state = serverDataToState(
5
- {
6
- my_array: [{ key_a: 'value1' }],
7
- my_data: { key_a: 'value1' },
8
- my_list: [1, 2, 3],
9
- my_null: null,
10
- my_number: 1000,
11
- my_string: 'a',
12
- my_var: 'test',
13
- },
14
- true
15
- );
16
- expect(state.myArray[0].keyA).toBe('value1');
17
- expect(state.myData.keyA).toBe('value1');
18
- expect(state.myList[0]).toBe(1);
19
- expect(state.myNull).toBe(null);
20
- expect(state.myNumber).toBe(1000);
21
- expect(state.myString).toBe('a');
22
- expect(state.myVar).toBe('test');
23
- });
3
+ describe('state', () => {
4
+ test('serverDataToState() recursive', () => {
5
+ const state = serverDataToState(
6
+ {
7
+ my_array: [{ key_a: 'value1' }],
8
+ my_data: { key_a: 'value1' },
9
+ my_list: [1, 2, 3],
10
+ my_null: null,
11
+ my_number: 1000,
12
+ my_string: 'a',
13
+ my_var: 'test',
14
+ },
15
+ true
16
+ );
17
+ expect(state.myArray[0].keyA).toBe('value1');
18
+ expect(state.myData.keyA).toBe('value1');
19
+ expect(state.myList[0]).toBe(1);
20
+ expect(state.myNull).toBe(null);
21
+ expect(state.myNumber).toBe(1000);
22
+ expect(state.myString).toBe('a');
23
+ expect(state.myVar).toBe('test');
24
+ });
24
25
 
25
- test('serverDataToState() non-recursive', () => {
26
- const state = serverDataToState(
27
- {
28
- my_array: [{ key_a: 'value1' }],
29
- my_data: { key_a: 'value1' },
30
- my_list: [1, 2, 3],
31
- my_null: null,
32
- my_number: 1000,
33
- my_string: 'a',
34
- my_var: 'test',
35
- },
36
- false
37
- );
38
- expect(state.myArray[0].key_a).toBe('value1');
39
- expect(state.myData.key_a).toBe('value1');
40
- expect(state.myList[0]).toBe(1);
41
- expect(state.myNull).toBe(null);
42
- expect(state.myNumber).toBe(1000);
43
- expect(state.myString).toBe('a');
44
- expect(state.myVar).toBe('test');
26
+ test('serverDataToState() non-recursive', () => {
27
+ const state = serverDataToState(
28
+ {
29
+ my_array: [{ key_a: 'value1' }],
30
+ my_data: { key_a: 'value1' },
31
+ my_list: [1, 2, 3],
32
+ my_null: null,
33
+ my_number: 1000,
34
+ my_string: 'a',
35
+ my_var: 'test',
36
+ },
37
+ false
38
+ );
39
+ expect(state.myArray[0].key_a).toBe('value1');
40
+ expect(state.myData.key_a).toBe('value1');
41
+ expect(state.myList[0]).toBe(1);
42
+ expect(state.myNull).toBe(null);
43
+ expect(state.myNumber).toBe(1000);
44
+ expect(state.myString).toBe('a');
45
+ expect(state.myVar).toBe('test');
46
+ });
45
47
  });
@@ -59,3 +59,18 @@ export const saveAsFile = (filename, text) => {
59
59
  * @returns {string} Type in human readable format.
60
60
  */
61
61
  export const getTypeFromValue = (value) => Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
62
+
63
+ /**
64
+ * Get value in human readable format.
65
+ * @param {unknown} value - The value to check.
66
+ * @returns {string} Value in human readable format.
67
+ */
68
+ export const getDisplayValue = (value) => {
69
+ if (typeof value === 'string') {
70
+ return `"${value}"`;
71
+ }
72
+ if (typeof value === 'object') {
73
+ return JSON.stringify(value);
74
+ }
75
+ return String(value);
76
+ };
@@ -1,59 +1,66 @@
1
1
  /* eslint-disable unicorn/no-useless-undefined */
2
2
 
3
- import { underscoreToCamelCase, capitalize, addLeadingZero, getTypeFromValue } from './string.js';
3
+ import {
4
+ underscoreToCamelCase,
5
+ capitalize,
6
+ addLeadingZero,
7
+ getTypeFromValue,
8
+ getDisplayValue,
9
+ saveAsFile,
10
+ } from './string.js';
4
11
 
5
- test('Tests add leading zero', () => {
6
- expect(addLeadingZero(1)).toBe('01');
7
- expect(addLeadingZero('1')).toBe('01');
8
- expect(addLeadingZero(1, 3)).toBe('001');
9
- expect(addLeadingZero('21')).toBe('21');
10
- expect(addLeadingZero(21)).toBe('21');
11
- expect(addLeadingZero(null)).toBe(null);
12
- expect(addLeadingZero(undefined)).toBe(null);
13
- });
12
+ describe('string', () => {
13
+ test('addLeadingZero', () => {
14
+ expect(addLeadingZero(1)).toBe('01');
15
+ expect(addLeadingZero('1')).toBe('01');
16
+ expect(addLeadingZero(1, 3)).toBe('001');
17
+ expect(addLeadingZero('21')).toBe('21');
18
+ expect(addLeadingZero(21)).toBe('21');
19
+ expect(addLeadingZero(null)).toBe(null);
20
+ expect(addLeadingZero(undefined)).toBe(null);
21
+ });
14
22
 
15
- describe('capitalize', () => {
16
- test('Capitalizes first letter of string', () => {
23
+ test('capitalize', () => {
17
24
  expect(capitalize('test')).toBe('Test');
18
25
  expect(capitalize('TEST')).toBe('Test');
19
26
  expect(capitalize('tEST')).toBe('Test');
20
- });
21
-
22
- test('Handles null input', () => {
23
27
  expect(capitalize(null)).toBe(null);
24
- });
25
-
26
- test('Handles empty string', () => {
27
28
  expect(capitalize('')).toBe('');
28
- });
29
-
30
- test('Handles empty string with whitespace', () => {
31
29
  expect(capitalize(' ')).toBe(' ');
32
- });
33
-
34
- test('Handles single character', () => {
35
30
  expect(capitalize('a')).toBe('A');
36
31
  expect(capitalize('A')).toBe('A');
32
+ expect(capitalize('test123')).toBe('Test123');
37
33
  });
38
34
 
39
- test('Handles strings with numbers', () => {
40
- expect(capitalize('test123')).toBe('Test123');
35
+ test('underscoreToCamelCase', () => {
36
+ expect(underscoreToCamelCase('test')).toBe('test');
37
+ expect(underscoreToCamelCase('test_variable')).toBe('testVariable');
38
+ expect(underscoreToCamelCase('test_variable_name')).toBe('testVariableName');
41
39
  });
42
- });
43
40
 
44
- test('Converts underscore to camelCase', () => {
45
- expect(underscoreToCamelCase('test')).toBe('test');
46
- expect(underscoreToCamelCase('test_variable')).toBe('testVariable');
47
- expect(underscoreToCamelCase('test_variable_name')).toBe('testVariableName');
48
- });
41
+ test('getTypeFromValue', () => {
42
+ expect(getTypeFromValue('test')).toBe('string');
43
+ expect(getTypeFromValue(() => null)).toBe('function');
44
+ expect(getTypeFromValue([])).toBe('array');
45
+ expect(getTypeFromValue({})).toBe('object');
46
+ expect(getTypeFromValue(new Date())).toBe('date');
47
+ expect(getTypeFromValue(null)).toBe('null');
48
+ expect(getTypeFromValue(true)).toBe('boolean');
49
+ expect(getTypeFromValue(undefined)).toBe('undefined');
50
+ });
51
+
52
+ test('getDisplayValue', () => {
53
+ expect(getDisplayValue('test')).toBe('"test"');
54
+ expect(getDisplayValue(() => null)).toBe('() => null');
55
+ expect(getDisplayValue([])).toBe('[]');
56
+ expect(getDisplayValue({})).toBe('{}');
57
+ expect(getDisplayValue(new Date())).not.toBe(null);
58
+ expect(getDisplayValue(null)).toBe('null');
59
+ expect(getDisplayValue(true)).toBe('true');
60
+ expect(getDisplayValue(undefined)).toBe('undefined');
61
+ });
49
62
 
50
- test('getDisplayValue', () => {
51
- expect(getTypeFromValue('test')).toBe('string');
52
- expect(getTypeFromValue(() => null)).toBe('function');
53
- expect(getTypeFromValue([])).toBe('array');
54
- expect(getTypeFromValue({})).toBe('object');
55
- expect(getTypeFromValue(new Date())).toBe('date');
56
- expect(getTypeFromValue(null)).toBe('null');
57
- expect(getTypeFromValue(true)).toBe('boolean');
58
- expect(getTypeFromValue(undefined)).toBe('undefined');
63
+ test('saveAsFile', () => {
64
+ expect(() => saveAsFile('test.txt', 'test content')).not.toThrowError(Error);
65
+ });
59
66
  });
package/types/index.d.ts CHANGED
@@ -10,12 +10,12 @@ export { OpenTelemetryLogHandler } from "./logging/OpenTelemetryLogHandler.js";
10
10
  export { SentryLogHandler } from "./logging/SentryLogHandler.js";
11
11
  export { typeChecker } from "./typecheck/TypeChecker.js";
12
12
  export { TypeCheckError } from "./typecheck/TypeCheckError.js";
13
- export { getErrorDetails } from "./util/error.js";
14
13
  export { serverDataToState } from "./util/state.js";
15
14
  export { formatLogMessage, getLogLevelName } from "./logging/util.js";
16
15
  export { addPageLifecycleCallback, getDocumentState, getPageLifecycleEventEmitter, getPageLifecycleState, initPageLifecycle, isPageLifecycleInitialized } from "./pagelifecycle/util.js";
17
16
  export { typeCheck, typeCheckArray, typeCheckEnum } from "./typecheck/util.js";
18
17
  export { delayPromise, loadJSON } from "./util/async.js";
18
+ export { getErrorDetails, getTypedError } from "./util/error.js";
19
19
  export { FetchError, fetchRetry, HTTP_0_ANY } from "./util/fetch.js";
20
20
  export { addFloat, deg2rad, fixFloat, fixFloatPrecision, getRandomInt, isEqual, isGreater, isGreaterOrEqual, isInRange, isLess, isLessOrEqual, subFloat } from "./util/number.js";
21
21
  export { purgeObject, deepMerge, getObjArrayPropSum, getObjValueByPath, setObjValueByPath } from "./util/object.js";
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/typecheck/util.js"],"names":[],"mappings":"AAcO,0BANM,CAAC,SACH,OAAO,aACP,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,GAC5B,CAAC,CAUb;AAUM,+BANM,CAAC,SACH,OAAO,EAAE,aACT,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,GAC5B,CAAC,EAAE,CAUf;AASM,qCALI,MAAM,GAAG,MAAM,WACf,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GACnF,MAAM,GAAG,MAAM,CAU3B"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/typecheck/util.js"],"names":[],"mappings":"AAsBO,0BANM,CAAC,SACH,OAAO,aACP,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,GAC5B,CAAC,CASb;AAUM,+BANM,CAAC,SACH,OAAO,EAAE,aACT,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,GAC5B,CAAC,EAAE,CASf;AASM,qCALI,MAAM,GAAG,MAAM,WACf,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GACnF,MAAM,GAAG,MAAM,CAS3B"}
@@ -1,2 +1,3 @@
1
1
  export function getErrorDetails(error: Error, excludes?: string[]): object;
2
+ export function getTypedError(error: unknown): Error;
2
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"}
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"}
@@ -3,4 +3,5 @@ export function capitalize(value: string | null | undefined): string | null;
3
3
  export function underscoreToCamelCase(value: string): string;
4
4
  export function saveAsFile(filename: string, text: string): void;
5
5
  export function getTypeFromValue(value: unknown): string;
6
+ export function getDisplayValue(value: unknown): string;
6
7
  //# sourceMappingURL=string.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../src/util/string.js"],"names":[],"mappings":"AAMO,sCAJI,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,SAClC,MAAM,GACJ,MAAM,GAAG,IAAI,CAWzB;AAOM,kCAHI,MAAM,GAAG,IAAI,GAAG,SAAS,GACvB,MAAM,GAAG,IAAI,CAWzB;AAOM,6CAHI,MAAM,GACJ,MAAM,CAEmF;AAO/F,qCAHI,MAAM,QACN,MAAM,QAUhB;AAOM,wCAHI,OAAO,GACL,MAAM,CAEwF"}
1
+ {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../src/util/string.js"],"names":[],"mappings":"AAMO,sCAJI,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,SAClC,MAAM,GACJ,MAAM,GAAG,IAAI,CAWzB;AAOM,kCAHI,MAAM,GAAG,IAAI,GAAG,SAAS,GACvB,MAAM,GAAG,IAAI,CAWzB;AAOM,6CAHI,MAAM,GACJ,MAAM,CAEmF;AAO/F,qCAHI,MAAM,QACN,MAAM,QAUhB;AAOM,wCAHI,OAAO,GACL,MAAM,CAEwF;AAOpG,uCAHI,OAAO,GACL,MAAM,CAUlB"}