ts-fortress 2.6.2 → 2.7.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 (36) hide show
  1. package/dist/utils/create-cast-fn.d.mts.map +1 -1
  2. package/dist/utils/create-cast-fn.mjs +1 -2
  3. package/dist/utils/create-cast-fn.mjs.map +1 -1
  4. package/dist/utils/validation-error.d.mts +2 -2
  5. package/dist/utils/validation-error.d.mts.map +1 -1
  6. package/dist/utils/validation-error.mjs +10 -3
  7. package/dist/utils/validation-error.mjs.map +1 -1
  8. package/package.json +2 -2
  9. package/src/array/array-of-length.test.mts +2 -2
  10. package/src/array/array.test.mts +2 -4
  11. package/src/array/non-empty-array.test.mts +2 -2
  12. package/src/array/tuple.test.mts +2 -2
  13. package/src/branded/number/finite-number.test.mts +1 -1
  14. package/src/branded/number/int.test.mts +1 -1
  15. package/src/branded/number/positive-int.test.mts +1 -1
  16. package/src/branded/number/uint.test.mts +1 -1
  17. package/src/branded/simple-branded-number.test.mts +2 -2
  18. package/src/branded/simple-branded-string.test.mts +2 -2
  19. package/src/compose/intersection.test.mts +2 -2
  20. package/src/enum/enum.test.mts +0 -2
  21. package/src/enum/uint-range.test.mts +0 -2
  22. package/src/primitives/bigint.test.mts +3 -3
  23. package/src/primitives/boolean.test.mts +4 -4
  24. package/src/primitives/null.test.mts +3 -3
  25. package/src/primitives/number.test.mts +5 -3
  26. package/src/primitives/string.test.mts +6 -4
  27. package/src/primitives/symbol.test.mts +1 -1
  28. package/src/primitives/undefined.test.mts +3 -3
  29. package/src/record/key-value-record.test.mts +2 -2
  30. package/src/record/omit.test.mts +1 -1
  31. package/src/record/partial.test.mts +5 -5
  32. package/src/record/pick.test.mts +1 -1
  33. package/src/record/record.test.mts +4 -4
  34. package/src/utils/create-cast-fn.mts +2 -8
  35. package/src/utils/validation-error.mts +18 -3
  36. package/src/utils/validation-error.test.mts +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"create-cast-fn.d.mts","sourceRoot":"","sources":["../../src/utils/create-cast-fn.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AASxC,eAAO,MAAM,YAAY,GACtB,CAAC,EAAG,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MACjC,GAAG,OAAO,KAAG,CAMb,CAAC"}
1
+ {"version":3,"file":"create-cast-fn.d.mts","sourceRoot":"","sources":["../../src/utils/create-cast-fn.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAGxC,eAAO,MAAM,YAAY,GACtB,CAAC,EAAG,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MACjC,GAAG,OAAO,KAAG,CAMb,CAAC"}
@@ -1,11 +1,10 @@
1
1
  import { Result } from 'ts-data-forge';
2
2
  import { validationErrorsToMessages } from './validation-error.mjs';
3
3
 
4
- const validationResultToString = (result) => `\n${validationErrorsToMessages(result).join(',\n')}`;
5
4
  const createCastFn = (validate) => (a) => {
6
5
  const res = validate(a);
7
6
  if (Result.isErr(res)) {
8
- throw new Error(validationResultToString(res.value));
7
+ throw new Error(validationErrorsToMessages(res.value).join('\n'));
9
8
  }
10
9
  return res.value;
11
10
  };
@@ -1 +1 @@
1
- {"version":3,"file":"create-cast-fn.mjs","sources":["../../src/utils/create-cast-fn.mts"],"sourcesContent":[null],"names":[],"mappings":";;;AAOA,MAAM,wBAAwB,GAAG,CAAC,MAAkC,KAClE,CAAA,EAAA,EAAK,0BAA0B,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;AAEhD,MAAM,YAAY,GACvB,CAAK,QAA6B,KAClC,CAAC,CAAU,KAAO;AAChB,IAAA,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC;AACvB,IAAA,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;QACrB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACtD;IACA,OAAO,GAAG,CAAC,KAAK;AAClB;;;;"}
1
+ {"version":3,"file":"create-cast-fn.mjs","sources":["../../src/utils/create-cast-fn.mts"],"sourcesContent":[null],"names":[],"mappings":";;;AAIO,MAAM,YAAY,GACvB,CAAK,QAA6B,KAClC,CAAC,CAAU,KAAO;AAChB,IAAA,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC;AACvB,IAAA,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;AACrB,QAAA,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnE;IACA,OAAO,GAAG,CAAC,KAAK;AAClB;;;;"}
@@ -19,12 +19,12 @@ export type ValidationErrorWithMessage = MergeIntersection<StrictOmit<Validation
19
19
  /**
20
20
  * Converts a validation error to a human-readable string message
21
21
  */
22
- export declare const validationErrorToMessage: (error: ValidationError) => string;
22
+ export declare const validationErrorToMessage: (error: ValidationError, maxLengthToPrintActualValue?: number) => string;
23
23
  /**
24
24
  * Converts an array of validation errors to an array of string messages
25
25
  * (for backward compatibility)
26
26
  */
27
- export declare const validationErrorsToMessages: (errors: readonly ValidationError[]) => readonly string[];
27
+ export declare const validationErrorsToMessages: (errors: readonly ValidationError[], maxLengthToPrintActualValue?: number) => readonly string[];
28
28
  /**
29
29
  * Prepends a path segment to all validation errors
30
30
  */
@@ -1 +1 @@
1
- {"version":3,"file":"validation-error.d.mts","sourceRoot":"","sources":["../../src/utils/validation-error.mts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,iFAAiF;IACjF,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,8CAA8C;IAC9C,WAAW,EAAE,OAAO,CAAC;IACrB,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,MAAM,0BAA0B,GAAG,iBAAiB,CACxD,UAAU,CAAC,eAAe,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CACvE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wBAAwB,GAAI,OAAO,eAAe,KAAG,MASjE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GACrC,QAAQ,SAAS,eAAe,EAAE,KACjC,SAAS,MAAM,EAA0C,CAAC;AAE7D;;GAEG;AACH,eAAO,MAAM,6BAA6B,GACxC,QAAQ,SAAS,eAAe,EAAE,EAClC,aAAa,MAAM,KAClB,SAAS,eAAe,EAItB,CAAC;AAEN;;GAEG;AACH,eAAO,MAAM,8BAA8B,GACzC,QAAQ,SAAS,eAAe,EAAE,EAClC,OAAO,MAAM,KACZ,SAAS,eAAe,EAC8B,CAAC;AAE1D;;GAEG;AACH,eAAO,MAAM,8BAA8B,GAAI,0CAI5C,QAAQ,CAAC;IACV,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,KAAG,eAMH,CAAC"}
1
+ {"version":3,"file":"validation-error.d.mts","sourceRoot":"","sources":["../../src/utils/validation-error.mts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,iFAAiF;IACjF,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,8CAA8C;IAC9C,WAAW,EAAE,OAAO,CAAC;IACrB,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,MAAM,0BAA0B,GAAG,iBAAiB,CACxD,UAAU,CAAC,eAAe,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CACvE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wBAAwB,GACnC,OAAO,eAAe,EACtB,8BAA6B,MAAW,KACvC,MAiBF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GACrC,QAAQ,SAAS,eAAe,EAAE,EAClC,8BAA6B,MAAW,KACvC,SAAS,MAAM,EAC2D,CAAC;AAE9E;;GAEG;AACH,eAAO,MAAM,6BAA6B,GACxC,QAAQ,SAAS,eAAe,EAAE,EAClC,aAAa,MAAM,KAClB,SAAS,eAAe,EAItB,CAAC;AAEN;;GAEG;AACH,eAAO,MAAM,8BAA8B,GACzC,QAAQ,SAAS,eAAe,EAAE,EAClC,OAAO,MAAM,KACZ,SAAS,eAAe,EAC8B,CAAC;AAE1D;;GAEG;AACH,eAAO,MAAM,8BAA8B,GAAI,0CAI5C,QAAQ,CAAC;IACV,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,KAAG,eAMH,CAAC"}
@@ -1,19 +1,26 @@
1
+ import { isString, pipe, unknownToString } from 'ts-data-forge';
2
+
1
3
  /**
2
4
  * Converts a validation error to a human-readable string message
3
5
  */
4
- const validationErrorToMessage = (error) => {
6
+ const validationErrorToMessage = (error, maxLengthToPrintActualValue = 20) => {
5
7
  const pathStr = error.path.length > 0 ? ` at ${error.path.join('.')}` : '';
6
8
  const actualTypeStr = typeof error.actualValue;
7
9
  if (error.message !== undefined) {
8
10
  return `${error.message}${pathStr}`;
9
11
  }
10
- return `Expected ${error.expectedType}${pathStr}, got ${actualTypeStr}`;
12
+ const actualValueStr = isString(error.actualValue)
13
+ ? error.actualValue.length <= maxLengthToPrintActualValue
14
+ ? ` "${error.actualValue}"`
15
+ : ''
16
+ : pipe(unknownToString(error.actualValue)).map((s) => s.length <= maxLengthToPrintActualValue ? ` \`${s}\`` : '').value;
17
+ return `Expected <${error.expectedType}>${pathStr}, got <${actualTypeStr}> type value${actualValueStr}.`;
11
18
  };
12
19
  /**
13
20
  * Converts an array of validation errors to an array of string messages
14
21
  * (for backward compatibility)
15
22
  */
16
- const validationErrorsToMessages = (errors) => errors.map(validationErrorToMessage);
23
+ const validationErrorsToMessages = (errors, maxLengthToPrintActualValue = 20) => errors.map((e) => validationErrorToMessage(e, maxLengthToPrintActualValue));
17
24
  /**
18
25
  * Prepends a path segment to all validation errors
19
26
  */
@@ -1 +1 @@
1
- {"version":3,"file":"validation-error.mjs","sources":["../../src/utils/validation-error.mts"],"sourcesContent":[null],"names":[],"mappings":"AAoBA;;AAEG;AACI,MAAM,wBAAwB,GAAG,CAAC,KAAsB,KAAY;IACzE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,CAAA,IAAA,EAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA,CAAE,GAAG,EAAE;AAC1E,IAAA,MAAM,aAAa,GAAG,OAAO,KAAK,CAAC,WAAW;AAE9C,IAAA,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE;AAC/B,QAAA,OAAO,GAAG,KAAK,CAAC,OAAO,CAAA,EAAG,OAAO,EAAE;IACrC;IAEA,OAAO,CAAA,SAAA,EAAY,KAAK,CAAC,YAAY,GAAG,OAAO,CAAA,MAAA,EAAS,aAAa,CAAA,CAAE;AACzE;AAEA;;;AAGG;AACI,MAAM,0BAA0B,GAAG,CACxC,MAAkC,KACZ,MAAM,CAAC,GAAG,CAAC,wBAAwB;AAE3D;;AAEG;MACU,6BAA6B,GAAG,CAC3C,MAAkC,EAClC,WAAmB,KAEnB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,MAAM;AACrB,IAAA,GAAG,KAAK;IACR,IAAI,EAAE,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC;AACnC,CAAA,CAAC;AAEJ;;AAEG;MACU,8BAA8B,GAAG,CAC5C,MAAkC,EAClC,KAAa,KAEb,6BAA6B,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,EAAE;AAExD;;AAEG;AACI,MAAM,8BAA8B,GAAG,CAAC,EAC7C,WAAW,EACX,YAAY,EACZ,QAAQ,GAKR,MAAuB;AACvB,IAAA,IAAI,EAAE,EAAE;IACR,WAAW;IACX,YAAY;IACZ,QAAQ;AACR,IAAA,OAAO,EAAE,SAAS;AACnB,CAAA;;;;"}
1
+ {"version":3,"file":"validation-error.mjs","sources":["../../src/utils/validation-error.mts"],"sourcesContent":[null],"names":[],"mappings":";;AAsBA;;AAEG;AACI,MAAM,wBAAwB,GAAG,CACtC,KAAsB,EACtB,2BAAA,GAAsC,EAAE,KAC9B;IACV,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,CAAA,IAAA,EAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA,CAAE,GAAG,EAAE;AAC1E,IAAA,MAAM,aAAa,GAAG,OAAO,KAAK,CAAC,WAAW;AAE9C,IAAA,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE;AAC/B,QAAA,OAAO,GAAG,KAAK,CAAC,OAAO,CAAA,EAAG,OAAO,EAAE;IACrC;AAEA,IAAA,MAAM,cAAc,GAAW,QAAQ,CAAC,KAAK,CAAC,WAAW;AACvD,UAAE,KAAK,CAAC,WAAW,CAAC,MAAM,IAAI;AAC5B,cAAE,CAAA,EAAA,EAAK,KAAK,CAAC,WAAW,CAAA,CAAA;AACxB,cAAE;AACJ,UAAE,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAC7C,CAAC,CAAC,MAAM,IAAI,2BAA2B,GAAG,CAAA,GAAA,EAAM,CAAC,CAAA,EAAA,CAAI,GAAG,EAAE,CAC3D,CAAC,KAAK;IAEX,OAAO,CAAA,UAAA,EAAa,KAAK,CAAC,YAAY,CAAA,CAAA,EAAI,OAAO,CAAA,OAAA,EAAU,aAAa,CAAA,YAAA,EAAe,cAAc,CAAA,CAAA,CAAG;AAC1G;AAEA;;;AAGG;AACI,MAAM,0BAA0B,GAAG,CACxC,MAAkC,EAClC,2BAAA,GAAsC,EAAE,KAExC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,wBAAwB,CAAC,CAAC,EAAE,2BAA2B,CAAC;AAE5E;;AAEG;MACU,6BAA6B,GAAG,CAC3C,MAAkC,EAClC,WAAmB,KAEnB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,MAAM;AACrB,IAAA,GAAG,KAAK;IACR,IAAI,EAAE,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC;AACnC,CAAA,CAAC;AAEJ;;AAEG;MACU,8BAA8B,GAAG,CAC5C,MAAkC,EAClC,KAAa,KAEb,6BAA6B,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,EAAE;AAExD;;AAEG;AACI,MAAM,8BAA8B,GAAG,CAAC,EAC7C,WAAW,EACX,YAAY,EACZ,QAAQ,GAKR,MAAuB;AACvB,IAAA,IAAI,EAAE,EAAE;IACR,WAAW;IACX,YAAY;IACZ,QAAQ;AACR,IAAA,OAAO,EAAE,SAAS;AACnB,CAAA;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-fortress",
3
- "version": "2.6.2",
3
+ "version": "2.7.0",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "typescript"
@@ -94,7 +94,7 @@
94
94
  "ts-repo-utils": "^7.0.4",
95
95
  "tslib": "^2.8.1",
96
96
  "tsx": "^4.20.4",
97
- "typedoc": "^0.28.10",
97
+ "typedoc": "^0.28.11",
98
98
  "typedoc-plugin-markdown": "^4.8.1",
99
99
  "typescript": "^5.9.2",
100
100
  "typescript-eslint": "^8.41.0",
@@ -146,8 +146,8 @@ describe('arrayOfLength', () => {
146
146
  ]);
147
147
 
148
148
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
149
- 'Expected number at 1, got string',
150
- 'Expected number at 2, got string',
149
+ 'Expected <number> at 1, got <string> type value "1".',
150
+ 'Expected <number> at 2, got <string> type value "".',
151
151
  ]);
152
152
  }
153
153
  });
@@ -101,11 +101,9 @@ describe('array', () => {
101
101
 
102
102
  // Test that we can convert to legacy string format for backward compatibility
103
103
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
104
- 'Expected number at 0, got string',
105
- 'Expected number at 1, got string',
104
+ 'Expected <number> at 0, got <string> type value "1".',
105
+ 'Expected <number> at 1, got <string> type value "".',
106
106
  ]);
107
- } else {
108
- throw new Error('Expected validation to fail');
109
107
  }
110
108
  });
111
109
 
@@ -146,8 +146,8 @@ describe('nonEmptyArray', () => {
146
146
  ]);
147
147
 
148
148
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
149
- 'Expected number at 0, got string',
150
- 'Expected number at 1, got string',
149
+ 'Expected <number> at 0, got <string> type value "1".',
150
+ 'Expected <number> at 1, got <string> type value "".',
151
151
  ]);
152
152
  }
153
153
  });
@@ -153,8 +153,8 @@ describe('tuple', () => {
153
153
  ]);
154
154
 
155
155
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
156
- 'Expected number at 0.x, got string',
157
- 'Expected number at 0.y, got string',
156
+ 'Expected <number> at 0.x, got <string> type value "str".',
157
+ 'Expected <number> at 0.y, got <string> type value "str".',
158
158
  ]);
159
159
  }
160
160
  });
@@ -115,7 +115,7 @@ describe('finiteNumber', () => {
115
115
  ]);
116
116
 
117
117
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
118
- 'Expected number, got string',
118
+ 'Expected <number>, got <string> type value "not a number".',
119
119
  ]);
120
120
  }
121
121
  });
@@ -98,7 +98,7 @@ describe('int', () => {
98
98
  ]);
99
99
 
100
100
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
101
- 'Expected number, got string',
101
+ 'Expected <number>, got <string> type value "not a number".',
102
102
  ]);
103
103
  }
104
104
  });
@@ -125,7 +125,7 @@ describe('positiveInt', () => {
125
125
  },
126
126
  ]);
127
127
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
128
- 'Expected number, got string',
128
+ 'Expected <number>, got <string> type value "not a number".',
129
129
  ]);
130
130
  }
131
131
  });
@@ -120,7 +120,7 @@ describe('uint', () => {
120
120
  },
121
121
  ]);
122
122
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
123
- 'Expected number, got string',
123
+ 'Expected <number>, got <string> type value "not a number".',
124
124
  ]);
125
125
  }
126
126
  });
@@ -69,7 +69,7 @@ describe('simpleBrandedNumber', () => {
69
69
  },
70
70
  ]);
71
71
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
72
- 'Expected number, got string',
72
+ 'Expected <number>, got <string> type value "not a number".',
73
73
  ]);
74
74
  }
75
75
  });
@@ -142,7 +142,7 @@ describe('simpleBrandedNumber', () => {
142
142
  },
143
143
  ]);
144
144
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
145
- 'Expected number, got string',
145
+ 'Expected <number>, got <string> type value "invalid".',
146
146
  ]);
147
147
  }
148
148
  });
@@ -70,7 +70,7 @@ describe('simpleBrandedString', () => {
70
70
  },
71
71
  ]);
72
72
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
73
- 'Expected string, got number',
73
+ 'Expected <string>, got <number> type value `42`.',
74
74
  ]);
75
75
  }
76
76
  });
@@ -143,7 +143,7 @@ describe('simpleBrandedString', () => {
143
143
  },
144
144
  ]);
145
145
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
146
- 'Expected string, got object',
146
+ 'Expected <string>, got <object> type value `null`.',
147
147
  ]);
148
148
  }
149
149
  });
@@ -251,9 +251,9 @@ describe('intersection', () => {
251
251
  ]);
252
252
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
253
253
  'The type of value is expected to match all types of { number, number }',
254
- 'Expected number, got string',
254
+ 'Expected <number>, got <string> type value "aaa".',
255
255
  'The type of value is expected to match all types of { number, number }',
256
- 'Expected number, got string',
256
+ 'Expected <number>, got <string> type value "aaa".',
257
257
  ]);
258
258
  }
259
259
  });
@@ -72,8 +72,6 @@ describe('enumType', () => {
72
72
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
73
73
  'The value is expected to be one of the elements contained in { 3, 2, a }',
74
74
  ]);
75
- } else {
76
- throw new Error('Expected validation to fail');
77
75
  }
78
76
  });
79
77
 
@@ -81,8 +81,6 @@ describe('uintRange', () => {
81
81
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
82
82
  'The value is expected to be an integer between 1 and 12',
83
83
  ]);
84
- } else {
85
- throw new Error('Expected validation to fail');
86
84
  }
87
85
  });
88
86
  });
@@ -64,7 +64,7 @@ describe('bigint', () => {
64
64
  ]);
65
65
 
66
66
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
67
- 'Expected bigint, got number',
67
+ 'Expected <bigint>, got <number> type value `123`.',
68
68
  ]);
69
69
  }
70
70
  });
@@ -199,7 +199,7 @@ describe('bigintLiteral', () => {
199
199
  ]);
200
200
 
201
201
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
202
- 'Expected bigintLiteral(42), got bigint',
202
+ 'Expected <bigintLiteral(42)>, got <bigint> type value `99`.',
203
203
  ]);
204
204
  }
205
205
  });
@@ -220,7 +220,7 @@ describe('bigintLiteral', () => {
220
220
  ]);
221
221
 
222
222
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
223
- 'Expected bigintLiteral(42), got string',
223
+ 'Expected <bigintLiteral(42)>, got <string> type value "not a bigint".',
224
224
  ]);
225
225
  }
226
226
  });
@@ -64,7 +64,7 @@ describe('boolean', () => {
64
64
  ]);
65
65
 
66
66
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
67
- 'Expected boolean, got string',
67
+ 'Expected <boolean>, got <string> type value "not a boolean".',
68
68
  ]);
69
69
  }
70
70
  });
@@ -202,7 +202,7 @@ describe('booleanLiteral', () => {
202
202
  ]);
203
203
 
204
204
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
205
- 'Expected booleanLiteral(true), got boolean',
205
+ 'Expected <booleanLiteral(true)>, got <boolean> type value `false`.',
206
206
  ]);
207
207
  }
208
208
  });
@@ -223,7 +223,7 @@ describe('booleanLiteral', () => {
223
223
  ]);
224
224
 
225
225
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
226
- 'Expected booleanLiteral(true), got string',
226
+ 'Expected <booleanLiteral(true)>, got <string> type value "not a boolean".',
227
227
  ]);
228
228
  }
229
229
  });
@@ -318,7 +318,7 @@ describe('booleanLiteral', () => {
318
318
  ]);
319
319
 
320
320
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
321
- 'Expected booleanLiteral(false), got boolean',
321
+ 'Expected <booleanLiteral(false)>, got <boolean> type value `true`.',
322
322
  ]);
323
323
  }
324
324
  });
@@ -68,7 +68,7 @@ describe('nullType', () => {
68
68
  const value: unknown = undefined;
69
69
  expect(() => {
70
70
  nullType.assertIs(value);
71
- }).toThrow('Expected null, got undefined');
71
+ }).toThrow('Expected <null>, got <undefined> type value `undefined`.');
72
72
  });
73
73
  });
74
74
 
@@ -82,7 +82,7 @@ describe('nullType', () => {
82
82
  test('non-null throws error', () => {
83
83
  const value: unknown = undefined;
84
84
  expect(() => nullType.cast(value)).toThrow(
85
- 'Expected null, got undefined',
85
+ 'Expected <null>, got <undefined> type value `undefined`.',
86
86
  );
87
87
  });
88
88
 
@@ -91,7 +91,7 @@ describe('nullType', () => {
91
91
 
92
92
  for (const value of values) {
93
93
  expect(() => nullType.cast(value)).toThrow(
94
- /Expected null, got (number|string|boolean|undefined)/u,
94
+ /Expected <null>, got <(number|string|boolean|undefined)> type value/u,
95
95
  );
96
96
  }
97
97
  });
@@ -88,7 +88,7 @@ describe('number', () => {
88
88
  const value: unknown = '42';
89
89
  expect(() => {
90
90
  assertIs(value);
91
- }).toThrow(/Expected number/u);
91
+ }).toThrow(/Expected <number>/u);
92
92
  });
93
93
  });
94
94
 
@@ -101,14 +101,16 @@ describe('number', () => {
101
101
 
102
102
  test('invalid value throws error', () => {
103
103
  const value: unknown = 'not a number';
104
- expect(() => num.cast(value)).toThrow('Expected number, got string');
104
+ expect(() => num.cast(value)).toThrow(
105
+ 'Expected <number>, got <string> type value "not a number".',
106
+ );
105
107
  });
106
108
 
107
109
  test('throws error with type mismatch', () => {
108
110
  const numWithDefault = number(100);
109
111
  const value: unknown = 'not a number';
110
112
  expect(() => numWithDefault.cast(value)).toThrow(
111
- 'Expected number, got string',
113
+ 'Expected <number>, got <string> type value "not a number".',
112
114
  );
113
115
  });
114
116
  });
@@ -91,7 +91,7 @@ describe('string', () => {
91
91
  const value: unknown = 42;
92
92
  expect(() => {
93
93
  assertIs(value);
94
- }).toThrow(/Expected string/u);
94
+ }).toThrow(/Expected <string>/u);
95
95
  });
96
96
  });
97
97
 
@@ -104,14 +104,16 @@ describe('string', () => {
104
104
 
105
105
  test('invalid value throws error', () => {
106
106
  const value: unknown = 42;
107
- expect(() => str.cast(value)).toThrow('Expected string, got number');
107
+ expect(() => str.cast(value)).toThrow(
108
+ 'Expected <string>, got <number> type value `42`.',
109
+ );
108
110
  });
109
111
 
110
112
  test('throws error with type mismatch', () => {
111
113
  const strWithDefault = string('default');
112
114
  const value: unknown = 42;
113
115
  expect(() => strWithDefault.cast(value)).toThrow(
114
- 'Expected string, got number',
116
+ 'Expected <string>, got <number> type value `42`.',
115
117
  );
116
118
  });
117
119
  });
@@ -332,7 +334,7 @@ describe('stringLiteral', () => {
332
334
  test('invalid value throws error', () => {
333
335
  const value: unknown = 'world';
334
336
  expect(() => hello.cast(value)).toThrow(
335
- 'Expected stringLiteral("hello"), got string',
337
+ 'Expected <stringLiteral("hello")>, got <string> type value "world".',
336
338
  );
337
339
  });
338
340
  });
@@ -66,7 +66,7 @@ describe('symbol', () => {
66
66
  ]);
67
67
 
68
68
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
69
- 'Expected symbol, got string',
69
+ 'Expected <symbol>, got <string> type value "not a symbol".',
70
70
  ]);
71
71
  }
72
72
  });
@@ -77,7 +77,7 @@ describe('undefinedType', () => {
77
77
  const value: unknown = null;
78
78
  expect(() => {
79
79
  undefinedType.assertIs(value);
80
- }).toThrow(`Expected undefined, got ${typeof value}`);
80
+ }).toThrow('Expected <undefined>, got <object> type value `null`.');
81
81
  });
82
82
  });
83
83
 
@@ -92,7 +92,7 @@ describe('undefinedType', () => {
92
92
  const value: unknown = null;
93
93
  expect(() => {
94
94
  undefinedType.cast(value);
95
- }).toThrow('Expected undefined, got object');
95
+ }).toThrow('Expected <undefined>, got <object> type value `null`.');
96
96
  });
97
97
 
98
98
  test('any non-undefined value throws error', () => {
@@ -101,7 +101,7 @@ describe('undefinedType', () => {
101
101
  for (const value of values) {
102
102
  expect(() => {
103
103
  undefinedType.cast(value);
104
- }).toThrow(`Expected undefined, got ${typeof value}`);
104
+ }).toThrow(/^Expected <undefined>, got <.*> type value .+\.$/u);
105
105
  }
106
106
  });
107
107
  });
@@ -130,9 +130,9 @@ describe('keyValueRecord', () => {
130
130
  ]);
131
131
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
132
132
  'The value of the record is expected to be <number>',
133
- 'Expected number at month, got string',
133
+ 'Expected <number> at month, got <string> type value "ab".',
134
134
  'The value of the record is expected to be <number>',
135
- 'Expected number at date, got string',
135
+ 'Expected <number> at date, got <string> type value "cd".',
136
136
  ]);
137
137
  }
138
138
  });
@@ -138,7 +138,7 @@ describe('omit', () => {
138
138
  },
139
139
  ]);
140
140
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
141
- 'Expected number at month, got string',
141
+ 'Expected <number> at month, got <string> type value "ab".',
142
142
  ]);
143
143
  }
144
144
  });
@@ -186,8 +186,8 @@ describe('partial', () => {
186
186
  },
187
187
  ]);
188
188
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
189
- 'Expected number at month, got string',
190
- 'Expected number at date, got string',
189
+ 'Expected <number> at month, got <string> type value "ab".',
190
+ 'Expected <number> at date, got <string> type value "cd".',
191
191
  ]);
192
192
  }
193
193
  });
@@ -212,7 +212,7 @@ describe('partial', () => {
212
212
  },
213
213
  ]);
214
214
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
215
- 'Expected number at month, got string',
215
+ 'Expected <number> at month, got <string> type value "ab".',
216
216
  ]);
217
217
  }
218
218
  });
@@ -385,8 +385,8 @@ describe('partial', () => {
385
385
  },
386
386
  ]);
387
387
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
388
- 'Expected number at month, got string',
389
- 'Expected number at date, got string',
388
+ 'Expected <number> at month, got <string> type value "ab".',
389
+ 'Expected <number> at date, got <string> type value "cd".',
390
390
  ]);
391
391
  }
392
392
  });
@@ -138,7 +138,7 @@ describe('pick', () => {
138
138
  },
139
139
  ]);
140
140
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
141
- 'Expected number at month, got string',
141
+ 'Expected <number> at month, got <string> type value "ab".',
142
142
  ]);
143
143
  }
144
144
  });
@@ -106,8 +106,8 @@ describe('record', () => {
106
106
  },
107
107
  ]);
108
108
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
109
- 'Expected number at month, got string',
110
- 'Expected number at date, got string',
109
+ 'Expected <number> at month, got <string> type value "ab".',
110
+ 'Expected <number> at date, got <string> type value "cd".',
111
111
  ]);
112
112
  }
113
113
  });
@@ -319,8 +319,8 @@ describe('partial record', () => {
319
319
  },
320
320
  ]);
321
321
  expect(validationErrorsToMessages(result.value)).toStrictEqual([
322
- 'Expected number at month, got string',
323
- 'Expected number at date, got string',
322
+ 'Expected <number> at month, got <string> type value "ab".',
323
+ 'Expected <number> at date, got <string> type value "cd".',
324
324
  ]);
325
325
  }
326
326
  });
@@ -1,19 +1,13 @@
1
1
  import { Result } from 'ts-data-forge';
2
2
  import { type Type } from '../type.mjs';
3
- import {
4
- validationErrorsToMessages,
5
- type ValidationError,
6
- } from './validation-error.mjs';
7
-
8
- const validationResultToString = (result: readonly ValidationError[]): string =>
9
- `\n${validationErrorsToMessages(result).join(',\n')}`;
3
+ import { validationErrorsToMessages } from './validation-error.mjs';
10
4
 
11
5
  export const createCastFn =
12
6
  <T,>(validate: Type<T>['validate']) =>
13
7
  (a: unknown): T => {
14
8
  const res = validate(a);
15
9
  if (Result.isErr(res)) {
16
- throw new Error(validationResultToString(res.value));
10
+ throw new Error(validationErrorsToMessages(res.value).join('\n'));
17
11
  }
18
12
  return res.value;
19
13
  };
@@ -1,3 +1,5 @@
1
+ import { isString, pipe, unknownToString } from 'ts-data-forge';
2
+
1
3
  /**
2
4
  * Represents a validation error with structured information
3
5
  */
@@ -21,7 +23,10 @@ export type ValidationErrorWithMessage = MergeIntersection<
21
23
  /**
22
24
  * Converts a validation error to a human-readable string message
23
25
  */
24
- export const validationErrorToMessage = (error: ValidationError): string => {
26
+ export const validationErrorToMessage = (
27
+ error: ValidationError,
28
+ maxLengthToPrintActualValue: number = 20,
29
+ ): string => {
25
30
  const pathStr = error.path.length > 0 ? ` at ${error.path.join('.')}` : '';
26
31
  const actualTypeStr = typeof error.actualValue;
27
32
 
@@ -29,7 +34,15 @@ export const validationErrorToMessage = (error: ValidationError): string => {
29
34
  return `${error.message}${pathStr}`;
30
35
  }
31
36
 
32
- return `Expected ${error.expectedType}${pathStr}, got ${actualTypeStr}`;
37
+ const actualValueStr: string = isString(error.actualValue)
38
+ ? error.actualValue.length <= maxLengthToPrintActualValue
39
+ ? ` "${error.actualValue}"`
40
+ : ''
41
+ : pipe(unknownToString(error.actualValue)).map((s) =>
42
+ s.length <= maxLengthToPrintActualValue ? ` \`${s}\`` : '',
43
+ ).value;
44
+
45
+ return `Expected <${error.expectedType}>${pathStr}, got <${actualTypeStr}> type value${actualValueStr}.`;
33
46
  };
34
47
 
35
48
  /**
@@ -38,7 +51,9 @@ export const validationErrorToMessage = (error: ValidationError): string => {
38
51
  */
39
52
  export const validationErrorsToMessages = (
40
53
  errors: readonly ValidationError[],
41
- ): readonly string[] => errors.map(validationErrorToMessage);
54
+ maxLengthToPrintActualValue: number = 20,
55
+ ): readonly string[] =>
56
+ errors.map((e) => validationErrorToMessage(e, maxLengthToPrintActualValue));
42
57
 
43
58
  /**
44
59
  * Prepends a path segment to all validation errors
@@ -144,9 +144,9 @@ describe('validation-error', () => {
144
144
  ];
145
145
 
146
146
  expect(validationErrorsToMessages(errors)).toStrictEqual([
147
- 'Expected string at user.name, got number',
148
- 'Expected number at items.0, got string',
149
- 'Expected boolean, got object',
147
+ 'Expected <string> at user.name, got <number> type value `123`.',
148
+ 'Expected <number> at items.0, got <string> type value "invalid".',
149
+ 'Expected <boolean>, got <object> type value `null`.',
150
150
  ]);
151
151
  });
152
152