ts-fortress 2.2.1 → 2.3.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 (139) hide show
  1. package/README.md +135 -0
  2. package/dist/array/array-of-length.d.mts.map +1 -1
  3. package/dist/array/array-of-length.mjs +8 -6
  4. package/dist/array/array-of-length.mjs.map +1 -1
  5. package/dist/array/array.d.mts.map +1 -1
  6. package/dist/array/array.mjs +8 -6
  7. package/dist/array/array.mjs.map +1 -1
  8. package/dist/array/non-empty-array.d.mts.map +1 -1
  9. package/dist/array/non-empty-array.mjs +8 -6
  10. package/dist/array/non-empty-array.mjs.map +1 -1
  11. package/dist/array/tuple.d.mts.map +1 -1
  12. package/dist/array/tuple.mjs +8 -6
  13. package/dist/array/tuple.mjs.map +1 -1
  14. package/dist/compose/index.d.mts +1 -0
  15. package/dist/compose/index.d.mts.map +1 -1
  16. package/dist/compose/index.mjs +1 -0
  17. package/dist/compose/index.mjs.map +1 -1
  18. package/dist/compose/intersection.d.mts.map +1 -1
  19. package/dist/compose/intersection.mjs +10 -10
  20. package/dist/compose/intersection.mjs.map +1 -1
  21. package/dist/compose/merge-records.d.mts.map +1 -1
  22. package/dist/compose/merge-records.mjs +10 -10
  23. package/dist/compose/merge-records.mjs.map +1 -1
  24. package/dist/compose/recursion.d.mts +5 -0
  25. package/dist/compose/recursion.d.mts.map +1 -0
  26. package/dist/compose/recursion.mjs +38 -0
  27. package/dist/compose/recursion.mjs.map +1 -0
  28. package/dist/compose/union.d.mts.map +1 -1
  29. package/dist/compose/union.mjs.map +1 -1
  30. package/dist/enum/enum.d.mts.map +1 -1
  31. package/dist/enum/enum.mjs.map +1 -1
  32. package/dist/enum/uint-range.d.mts.map +1 -1
  33. package/dist/enum/uint-range.mjs.map +1 -1
  34. package/dist/index.d.mts +0 -1
  35. package/dist/index.d.mts.map +1 -1
  36. package/dist/index.mjs +2 -1
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/record/key-value-record.d.mts.map +1 -1
  39. package/dist/record/key-value-record.mjs +21 -24
  40. package/dist/record/key-value-record.mjs.map +1 -1
  41. package/dist/record/omit.d.mts.map +1 -1
  42. package/dist/record/omit.mjs +3 -2
  43. package/dist/record/omit.mjs.map +1 -1
  44. package/dist/record/partial.d.mts.map +1 -1
  45. package/dist/record/partial.mjs +2 -2
  46. package/dist/record/partial.mjs.map +1 -1
  47. package/dist/record/pick.d.mts.map +1 -1
  48. package/dist/record/pick.mjs +2 -2
  49. package/dist/record/pick.mjs.map +1 -1
  50. package/dist/record/record.d.mts.map +1 -1
  51. package/dist/record/record.mjs +23 -21
  52. package/dist/record/record.mjs.map +1 -1
  53. package/dist/type.d.mts +1 -1
  54. package/dist/type.d.mts.map +1 -1
  55. package/dist/utils/create-assert-fn.mjs +1 -1
  56. package/dist/utils/create-cast-fn.mjs +1 -1
  57. package/dist/utils/create-primitive-type.mjs +1 -1
  58. package/dist/utils/index.d.mts +1 -0
  59. package/dist/utils/index.d.mts.map +1 -1
  60. package/dist/utils/index.mjs +1 -0
  61. package/dist/utils/index.mjs.map +1 -1
  62. package/dist/utils/validation-error.d.mts.map +1 -0
  63. package/dist/utils/validation-error.mjs.map +1 -0
  64. package/package.json +4 -2
  65. package/src/array/array-of-length.mts +12 -7
  66. package/src/array/array-of-length.test.mts +10 -1
  67. package/src/array/array.mts +12 -7
  68. package/src/array/array.test.mts +10 -1
  69. package/src/array/non-empty-array.mts +11 -7
  70. package/src/array/non-empty-array.test.mts +10 -1
  71. package/src/array/tuple.mts +11 -9
  72. package/src/array/tuple.test.mts +14 -1
  73. package/src/branded/brand.mts +2 -2
  74. package/src/branded/number/finite-number.test.mts +10 -1
  75. package/src/branded/number/int.test.mts +10 -1
  76. package/src/branded/number/int16.test.mts +10 -1
  77. package/src/branded/number/int32.test.mts +10 -1
  78. package/src/branded/number/non-negative-finite-number.test.mts +10 -1
  79. package/src/branded/number/non-zero-finite-number.test.mts +10 -1
  80. package/src/branded/number/non-zero-int.test.mts +10 -1
  81. package/src/branded/number/non-zero-safe-int.test.mts +10 -1
  82. package/src/branded/number/positive-finite-number.test.mts +10 -1
  83. package/src/branded/number/positive-int.test.mts +10 -1
  84. package/src/branded/number/positive-safe-int.test.mts +10 -1
  85. package/src/branded/number/safe-int.test.mts +10 -1
  86. package/src/branded/number/safe-uint.test.mts +10 -1
  87. package/src/branded/number/uint.test.mts +10 -1
  88. package/src/branded/number/uint16.test.mts +10 -1
  89. package/src/branded/number/uint32.test.mts +10 -1
  90. package/src/branded/simple-branded-number.test.mts +19 -1
  91. package/src/branded/simple-branded-string.test.mts +19 -1
  92. package/src/compose/index.mts +1 -0
  93. package/src/compose/intersection.mts +23 -19
  94. package/src/compose/intersection.test.mts +28 -1
  95. package/src/compose/merge-records.mts +22 -19
  96. package/src/compose/merge-records.test.mts +10 -1
  97. package/src/compose/recursion.mts +49 -0
  98. package/src/compose/recursion.test.mts +234 -0
  99. package/src/compose/union.mts +6 -2
  100. package/src/compose/union.test.mts +10 -1
  101. package/src/enum/enum.mts +6 -2
  102. package/src/enum/enum.test.mts +10 -1
  103. package/src/enum/uint-range.mts +6 -2
  104. package/src/enum/uint-range.test.mts +10 -1
  105. package/src/index.mts +0 -1
  106. package/src/primitives/bigint.test.mts +19 -1
  107. package/src/primitives/boolean.test.mts +28 -1
  108. package/src/primitives/null.test.mts +9 -0
  109. package/src/primitives/number.test.mts +18 -0
  110. package/src/primitives/string.test.mts +18 -0
  111. package/src/primitives/symbol.test.mts +10 -1
  112. package/src/primitives/undefined.test.mts +9 -0
  113. package/src/record/key-value-record.mts +34 -36
  114. package/src/record/key-value-record.test.mts +14 -1
  115. package/src/record/keyof.test.mts +10 -1
  116. package/src/record/omit.mts +8 -5
  117. package/src/record/omit.test.mts +46 -1
  118. package/src/record/partial.mts +7 -5
  119. package/src/record/partial.test.mts +71 -1
  120. package/src/record/pick.mts +7 -5
  121. package/src/record/pick.test.mts +46 -1
  122. package/src/record/record-allow-excess-properties.test.mts +28 -1
  123. package/src/record/record-strict-composition-simple.test.mts +38 -0
  124. package/src/record/record-strict-composition.test.mts +71 -0
  125. package/src/record/record.mts +30 -29
  126. package/src/record/record.test.mts +26 -1
  127. package/src/record/strict-record.test.mts +10 -1
  128. package/src/type.mts +1 -1
  129. package/src/utils/create-assert-fn.mts +1 -1
  130. package/src/utils/create-cast-fn.mts +1 -1
  131. package/src/utils/create-primitive-type.mts +1 -1
  132. package/src/utils/index.mts +1 -0
  133. package/dist/validation-error.d.mts.map +0 -1
  134. package/dist/validation-error.mjs.map +0 -1
  135. package/src/combination.test.mts +0 -544
  136. /package/dist/{validation-error.d.mts → utils/validation-error.d.mts} +0 -0
  137. /package/dist/{validation-error.mjs → utils/validation-error.mjs} +0 -0
  138. /package/src/{validation-error.mts → utils/validation-error.mts} +0 -0
  139. /package/src/{validation-error.test.mts → utils/validation-error.test.mts} +0 -0
@@ -1,10 +1,12 @@
1
- import { expectType, isRecord, Result } from 'ts-data-forge';
1
+ import { Arr, expectType, isRecord, Result } from 'ts-data-forge';
2
2
  import { type Type, type TypeOf } from '../type.mjs';
3
- import { createAssertFn, createCastFn, createIsFn } from '../utils/index.mjs';
4
3
  import {
4
+ createAssertFn,
5
+ createCastFn,
6
+ createIsFn,
5
7
  type ValidationError,
6
8
  type ValidationErrorWithMessage,
7
- } from '../validation-error.mjs';
9
+ } from '../utils/index.mjs';
8
10
 
9
11
  export const mergeRecords = <
10
12
  const Types extends NonEmptyArray<Type<UnknownRecord>>,
@@ -24,22 +26,23 @@ export const mergeRecords = <
24
26
  `Intersection<${types.map((a) => a.typeName).join(', ')}>`;
25
27
 
26
28
  const validate: Type<T>['validate'] = (a) => {
27
- const errors: readonly ValidationError[] = types.flatMap((type) => {
28
- const r = type.validate(a);
29
- return Result.isErr(r)
30
- ? [
31
- {
32
- path: [],
33
- actualValue: a,
34
- expectedType: typeNameFilled,
35
- message: `The type of value is expected to match all types of { ${types
36
- .map((t) => t.typeName)
37
- .join(', ')} }`,
38
- typeName: typeNameFilled,
39
- } satisfies ValidationErrorWithMessage,
40
- ...r.value,
41
- ]
42
- : [];
29
+ const errors: readonly ValidationError[] = Arr.generate(function* () {
30
+ for (const type of types) {
31
+ const res = type.validate(a);
32
+ if (Result.isErr(res)) {
33
+ yield {
34
+ path: [],
35
+ actualValue: a,
36
+ expectedType: typeNameFilled,
37
+ message: `The type of value is expected to match all types of { ${types
38
+ .map((t) => t.typeName)
39
+ .join(', ')} }`,
40
+ typeName: typeNameFilled,
41
+ } satisfies ValidationErrorWithMessage;
42
+
43
+ yield* res.value;
44
+ }
45
+ }
43
46
  });
44
47
 
45
48
  if (errors.length > 0) {
@@ -5,7 +5,7 @@ import { type TypeOf } from '../type.mjs';
5
5
  import {
6
6
  type ValidationError,
7
7
  validationErrorsToMessages,
8
- } from '../validation-error.mjs';
8
+ } from '../utils/index.mjs';
9
9
  import { mergeRecords } from './merge-records.mjs';
10
10
 
11
11
  describe('mergeRecords', () => {
@@ -82,6 +82,15 @@ describe('mergeRecords', () => {
82
82
  }
83
83
  });
84
84
 
85
+ test('validate returns input as-is for OK cases', () => {
86
+ const input = { x: 0, y: 1, z: 2, w: 3 };
87
+ const result = targetType.validate(input);
88
+ expect(Result.isOk(result)).toBe(true);
89
+ if (Result.isOk(result)) {
90
+ expect(result.value).toBe(input); // ✅ same reference
91
+ }
92
+ });
93
+
85
94
  test('falsy case', () => {
86
95
  const result = targetType.validate({ x: 0, y: 1 });
87
96
  expect(Result.isErr(result)).toBe(true);
@@ -0,0 +1,49 @@
1
+ import { type Type } from '../type.mjs';
2
+ import { createAssertFn, createCastFn, createIsFn } from '../utils/index.mjs';
3
+
4
+ export const recursion = <A,>(
5
+ typeName: string,
6
+ definition: () => Type<A>,
7
+ options?: Partial<
8
+ Readonly<{
9
+ defaultValue: A;
10
+ }>
11
+ >,
12
+ ): Type<A> => {
13
+ const cache: {
14
+ innerType: Type<A> | undefined;
15
+ computedDefaultValue: A | undefined;
16
+ } = {
17
+ innerType: undefined as Type<A> | undefined,
18
+ computedDefaultValue: undefined as A | undefined,
19
+ };
20
+
21
+ const getInnerType = (): Type<A> => {
22
+ // eslint-disable-next-line functional/immutable-data
23
+ cache.innerType ??= definition();
24
+ return cache.innerType;
25
+ };
26
+
27
+ const validate: Type<A>['validate'] = (a) => getInnerType().validate(a);
28
+
29
+ const is = createIsFn<A>(validate);
30
+
31
+ const fill: Type<A>['fill'] = (a) => getInnerType().fill(a);
32
+
33
+ return {
34
+ typeName,
35
+ get defaultValue(): A {
36
+ if (options?.defaultValue !== undefined) {
37
+ return options.defaultValue;
38
+ }
39
+ // eslint-disable-next-line functional/immutable-data
40
+ cache.computedDefaultValue ??= getInnerType().defaultValue;
41
+ return cache.computedDefaultValue;
42
+ },
43
+ fill,
44
+ validate,
45
+ is,
46
+ assertIs: createAssertFn(validate),
47
+ cast: createCastFn(validate),
48
+ };
49
+ };
@@ -0,0 +1,234 @@
1
+ import { expectType } from 'ts-data-forge';
2
+ import { array } from '../array/index.mjs';
3
+ import { boolean, nullType, number, string } from '../primitives/index.mjs';
4
+ import { keyValueRecord, record } from '../record/index.mjs';
5
+ import { type Type, type TypeOf } from '../type.mjs';
6
+ import { recursion } from './recursion.mjs';
7
+ import { union } from './union.mjs';
8
+
9
+ describe('recursive', () => {
10
+ // Define JsonPrimitive type
11
+ const JsonPrimitive = union([nullType, number(), string(), boolean()]);
12
+
13
+ type JsonPrimitive = TypeOf<typeof JsonPrimitive>;
14
+
15
+ test('JsonPrimitive.defaultValue', () => {
16
+ expectType<JsonPrimitive, null | boolean | number | string>('=');
17
+
18
+ expect(JsonPrimitive.defaultValue).toBeNull();
19
+ });
20
+
21
+ // Define recursive JsonValue type
22
+ type JsonValue =
23
+ | JsonPrimitive
24
+ | Readonly<{
25
+ [k: string]: JsonValue;
26
+ }>
27
+ | readonly JsonValue[];
28
+
29
+ const JsonValue: Type<JsonValue> = recursion('JsonValue', () =>
30
+ union([
31
+ JsonPrimitive,
32
+ keyValueRecord(string(), JsonValue),
33
+ array(JsonValue),
34
+ ]),
35
+ );
36
+
37
+ test('JsonValue.defaultValue', () => {
38
+ expectType<TypeOf<typeof JsonValue>, JsonValue>('=');
39
+
40
+ expect(JsonValue.defaultValue).toBeNull();
41
+ });
42
+
43
+ test('JsonValue - primitive values', () => {
44
+ expect(JsonValue.is(true)).toBe(true);
45
+ expect(JsonValue.is(false)).toBe(true);
46
+ expect(JsonValue.is(42)).toBe(true);
47
+ expect(JsonValue.is(3.14)).toBe(true);
48
+ expect(JsonValue.is('hello')).toBe(true);
49
+ expect(JsonValue.is(null)).toBe(true);
50
+ expect(JsonValue.is(undefined)).toBe(false);
51
+ });
52
+
53
+ test('JsonValue - arrays', () => {
54
+ expect(JsonValue.is([])).toBe(true);
55
+ expect(JsonValue.is([1, 2, 3])).toBe(true);
56
+ expect(JsonValue.is(['a', 'b', 'c'])).toBe(true);
57
+ expect(JsonValue.is([true, false, null])).toBe(true);
58
+ expect(JsonValue.is([1, 'a', true, null])).toBe(true);
59
+ });
60
+
61
+ test('JsonValue - objects', () => {
62
+ expect(JsonValue.is({})).toBe(true);
63
+ expect(JsonValue.is({ a: 1 })).toBe(true);
64
+ expect(JsonValue.is({ a: 1, b: 'hello' })).toBe(true);
65
+ expect(JsonValue.is({ a: 1, b: [1, 2, 3] })).toBe(true);
66
+ });
67
+
68
+ test('JsonValue - nested structures', () => {
69
+ const nestedData = {
70
+ name: 'John',
71
+ age: 30,
72
+ active: true,
73
+ address: {
74
+ street: '123 Main St',
75
+ city: 'New York',
76
+ coordinates: {
77
+ lat: 40.7128,
78
+ lon: -74.006,
79
+ },
80
+ },
81
+ hobbies: ['reading', 'coding', 'gaming'],
82
+ scores: [95, 87, 92],
83
+ metadata: null,
84
+ tags: [],
85
+ settings: {
86
+ theme: 'dark',
87
+ notifications: {
88
+ email: true,
89
+ push: false,
90
+ },
91
+ },
92
+ };
93
+
94
+ expect(JsonValue.is(nestedData)).toBe(true);
95
+ });
96
+
97
+ test('JsonValue - deeply nested arrays', () => {
98
+ const deepArray = [
99
+ [
100
+ [1, 2, 3],
101
+ [4, 5, 6],
102
+ ],
103
+ [
104
+ [7, 8, 9],
105
+ [10, 11, 12],
106
+ ],
107
+ ];
108
+
109
+ expect(JsonValue.is(deepArray)).toBe(true);
110
+ });
111
+
112
+ test('JsonValue - mixed nested structure', () => {
113
+ const mixedData = {
114
+ users: [
115
+ {
116
+ id: 1,
117
+ name: 'Alice',
118
+ tasks: [
119
+ { id: 'task1', completed: true },
120
+ { id: 'task2', completed: false },
121
+ ],
122
+ },
123
+ {
124
+ id: 2,
125
+ name: 'Bob',
126
+ tasks: [],
127
+ },
128
+ ],
129
+ config: {
130
+ version: '1.0.0',
131
+ features: ['feature1', 'feature2'],
132
+ settings: {
133
+ debug: false,
134
+ timeout: 5000,
135
+ },
136
+ },
137
+ };
138
+
139
+ expect(JsonValue.is(mixedData)).toBe(true);
140
+ });
141
+
142
+ test('JsonValue - invalid values', () => {
143
+ expect(JsonValue.is(undefined)).toBe(false);
144
+ expect(JsonValue.is(Symbol('test'))).toBe(false);
145
+ expect(JsonValue.is(() => 'function')).toBe(false);
146
+
147
+ // Note: keyValueRecord accepts any object with string keys, including Date/Set/Map
148
+ // This is expected behavior for keyValueRecord, which doesn't distinguish
149
+ // between plain objects and other object instances
150
+ });
151
+
152
+ test('JsonValue - cast function', () => {
153
+ const result = JsonValue.cast({ a: 1, b: [2, 3], c: { d: 'hello' } });
154
+ expect(result).toStrictEqual({ a: 1, b: [2, 3], c: { d: 'hello' } });
155
+ });
156
+
157
+ test('JsonValue - fill function', () => {
158
+ const result = JsonValue.fill({ a: 1, b: [2, 3] });
159
+ expect(result).toStrictEqual({ a: 1, b: [2, 3] });
160
+
161
+ // Fill with default value for invalid input
162
+ const defaultResult = JsonValue.fill(undefined);
163
+ expect(JsonValue.is(defaultResult)).toBe(true);
164
+ });
165
+
166
+ // Test other recursive structures
167
+ test('Linked list structure', () => {
168
+ type LinkedList<T> = { value: T; next: LinkedList<T> | null };
169
+
170
+ const LinkedListNumber: Type<LinkedList<number>> = recursion(
171
+ 'LinkedList<number>',
172
+ () =>
173
+ record({
174
+ value: number(0),
175
+ next: union([nullType, LinkedListNumber]),
176
+ }),
177
+ { defaultValue: { value: 0, next: null } },
178
+ );
179
+
180
+ const list: LinkedList<number> = {
181
+ value: 1,
182
+ next: {
183
+ value: 2,
184
+ next: {
185
+ value: 3,
186
+ next: null,
187
+ },
188
+ },
189
+ };
190
+
191
+ expect(LinkedListNumber.is(list)).toBe(true);
192
+ expect(LinkedListNumber.is({ value: 1, next: null })).toBe(true);
193
+ expect(LinkedListNumber.is({ value: 'not a number', next: null })).toBe(
194
+ false,
195
+ );
196
+ });
197
+
198
+ test('Tree structure', () => {
199
+ type TreeNode<T> = Readonly<{
200
+ value: T;
201
+ children: readonly TreeNode<T>[];
202
+ }>;
203
+
204
+ const TreeNodeString: Type<TreeNode<string>> = recursion(
205
+ 'TreeNode<string>',
206
+ () =>
207
+ record({
208
+ value: string(),
209
+ children: array(TreeNodeString),
210
+ }),
211
+ );
212
+
213
+ const tree: TreeNode<string> = {
214
+ value: 'root',
215
+ children: [
216
+ {
217
+ value: 'branch1',
218
+ children: [
219
+ { value: 'leaf1', children: [] },
220
+ { value: 'leaf2', children: [] },
221
+ ],
222
+ },
223
+ {
224
+ value: 'branch2',
225
+ children: [{ value: 'leaf3', children: [] }],
226
+ },
227
+ ],
228
+ };
229
+
230
+ expect(TreeNodeString.is(tree)).toBe(true);
231
+ expect(TreeNodeString.is({ value: 'node', children: [] })).toBe(true);
232
+ expect(TreeNodeString.is({ value: 123, children: [] })).toBe(false);
233
+ });
234
+ });
@@ -1,7 +1,11 @@
1
1
  import { Result, expectType } from 'ts-data-forge';
2
2
  import { type Type, type TypeOf } from '../type.mjs';
3
- import { createAssertFn, createCastFn, createIsFn } from '../utils/index.mjs';
4
- import { type ValidationErrorWithMessage } from '../validation-error.mjs';
3
+ import {
4
+ createAssertFn,
5
+ createCastFn,
6
+ createIsFn,
7
+ type ValidationErrorWithMessage,
8
+ } from '../utils/index.mjs';
5
9
 
6
10
  export const union = <const Types extends NonEmptyArray<Type<unknown>>>(
7
11
  types: Types,
@@ -5,7 +5,7 @@ import { type TypeOf } from '../type.mjs';
5
5
  import {
6
6
  type ValidationError,
7
7
  validationErrorsToMessages,
8
- } from '../validation-error.mjs';
8
+ } from '../utils/index.mjs';
9
9
  import { union } from './union.mjs';
10
10
 
11
11
  describe('union', () => {
@@ -105,6 +105,15 @@ describe('union', () => {
105
105
  ]);
106
106
  }
107
107
  });
108
+
109
+ test('validate returns input as-is for OK cases', () => {
110
+ const input = { x: 10, y: 20 };
111
+ const result = targetType.validate(input);
112
+ expect(Result.isOk(result)).toBe(true);
113
+ if (Result.isOk(result)) {
114
+ expect(result.value).toBe(input); // ✅ same reference
115
+ }
116
+ });
108
117
  });
109
118
 
110
119
  describe('fill', () => {
package/src/enum/enum.mts CHANGED
@@ -1,7 +1,11 @@
1
1
  import { ISet, Result } from 'ts-data-forge';
2
2
  import { type Type } from '../type.mjs';
3
- import { createAssertFn, createCastFn, createIsFn } from '../utils/index.mjs';
4
- import { type ValidationErrorWithMessage } from '../validation-error.mjs';
3
+ import {
4
+ createAssertFn,
5
+ createCastFn,
6
+ createIsFn,
7
+ type ValidationErrorWithMessage,
8
+ } from '../utils/index.mjs';
5
9
 
6
10
  export const enumType = <const Values extends NonEmptyArray<Primitive>>(
7
11
  values: Values,
@@ -3,7 +3,7 @@ import { type TypeOf } from '../type.mjs';
3
3
  import {
4
4
  type ValidationError,
5
5
  validationErrorsToMessages,
6
- } from '../validation-error.mjs';
6
+ } from '../utils/index.mjs';
7
7
  import { enumType } from './enum.mjs';
8
8
 
9
9
  describe('enumType', () => {
@@ -76,6 +76,15 @@ describe('enumType', () => {
76
76
  throw new Error('Expected validation to fail');
77
77
  }
78
78
  });
79
+
80
+ test('validate returns input as-is for OK cases', () => {
81
+ const input = 'a';
82
+ const result = targetType.validate(input);
83
+ expect(Result.isOk(result)).toBe(true);
84
+ if (Result.isOk(result)) {
85
+ expect(result.value).toBe(input); // ✅ same reference
86
+ }
87
+ });
79
88
  });
80
89
 
81
90
  describe('fill', () => {
@@ -1,7 +1,11 @@
1
1
  import { isNumber, Num, Result } from 'ts-data-forge';
2
2
  import { type Type } from '../type.mjs';
3
- import { createAssertFn, createCastFn, createIsFn } from '../utils/index.mjs';
4
- import { type ValidationErrorWithMessage } from '../validation-error.mjs';
3
+ import {
4
+ createAssertFn,
5
+ createCastFn,
6
+ createIsFn,
7
+ type ValidationErrorWithMessage,
8
+ } from '../utils/index.mjs';
5
9
 
6
10
  export const uintRange = <
7
11
  Start extends Uint8,
@@ -3,7 +3,7 @@ import { type TypeOf } from '../type.mjs';
3
3
  import {
4
4
  type ValidationError,
5
5
  validationErrorsToMessages,
6
- } from '../validation-error.mjs';
6
+ } from '../utils/index.mjs';
7
7
  import { uintRange } from './uint-range.mjs';
8
8
 
9
9
  describe('uintRange', () => {
@@ -57,6 +57,15 @@ describe('uintRange', () => {
57
57
  }
58
58
  });
59
59
 
60
+ test('validate returns input as-is for OK cases', () => {
61
+ const input = 5;
62
+ const result = month.validate(input);
63
+ expect(Result.isOk(result)).toBe(true);
64
+ if (Result.isOk(result)) {
65
+ expect(result.value).toBe(input); // ✅ same reference
66
+ }
67
+ });
68
+
60
69
  test('falsy case', () => {
61
70
  const result = month.validate(13);
62
71
  expect(Result.isErr(result)).toBe(true);
package/src/index.mts CHANGED
@@ -7,4 +7,3 @@ export * from './record/index.mjs';
7
7
  export * from './type.mjs';
8
8
  export * from './unknown.mjs';
9
9
  export * from './utils/index.mjs';
10
- export * from './validation-error.mjs';
@@ -1,6 +1,6 @@
1
1
  import { expectType, Result } from 'ts-data-forge';
2
2
  import { type TypeOf } from '../type.mjs';
3
- import { validationErrorsToMessages } from '../validation-error.mjs';
3
+ import { validationErrorsToMessages } from '../utils/index.mjs';
4
4
  import { bigint, bigintLiteral } from './bigint.mjs';
5
5
 
6
6
  describe('bigint', () => {
@@ -68,6 +68,15 @@ describe('bigint', () => {
68
68
  ]);
69
69
  }
70
70
  });
71
+
72
+ test('validate returns input as-is for OK cases', () => {
73
+ const input = 999n;
74
+ const result = targetType.validate(input);
75
+ expect(Result.isOk(result)).toBe(true);
76
+ if (Result.isOk(result)) {
77
+ expect(result.value).toBe(input); // ✅ same reference
78
+ }
79
+ });
71
80
  });
72
81
 
73
82
  describe('assertIs', () => {
@@ -215,6 +224,15 @@ describe('bigintLiteral', () => {
215
224
  ]);
216
225
  }
217
226
  });
227
+
228
+ test('validate returns input as-is for OK cases', () => {
229
+ const input = 42n;
230
+ const result = targetType.validate(input);
231
+ expect(Result.isOk(result)).toBe(true);
232
+ if (Result.isOk(result)) {
233
+ expect(result.value).toBe(input); // ✅ same reference
234
+ }
235
+ });
218
236
  });
219
237
 
220
238
  describe('cast', () => {
@@ -1,6 +1,6 @@
1
1
  import { expectType, Result } from 'ts-data-forge';
2
2
  import { type TypeOf } from '../type.mjs';
3
- import { validationErrorsToMessages } from '../validation-error.mjs';
3
+ import { validationErrorsToMessages } from '../utils/index.mjs';
4
4
  import { boolean, booleanLiteral } from './boolean.mjs';
5
5
 
6
6
  describe('boolean', () => {
@@ -68,6 +68,15 @@ describe('boolean', () => {
68
68
  ]);
69
69
  }
70
70
  });
71
+
72
+ test('validate returns input as-is for OK cases', () => {
73
+ const input = false;
74
+ const result = targetType.validate(input);
75
+ expect(Result.isOk(result)).toBe(true);
76
+ if (Result.isOk(result)) {
77
+ expect(result.value).toBe(input); // ✅ same reference
78
+ }
79
+ });
71
80
  });
72
81
 
73
82
  describe('assertIs', () => {
@@ -218,6 +227,15 @@ describe('booleanLiteral', () => {
218
227
  ]);
219
228
  }
220
229
  });
230
+
231
+ test('validate returns input as-is for OK cases', () => {
232
+ const input = true;
233
+ const result = targetType.validate(input);
234
+ expect(Result.isOk(result)).toBe(true);
235
+ if (Result.isOk(result)) {
236
+ expect(result.value).toBe(input); // ✅ same reference
237
+ }
238
+ });
221
239
  });
222
240
 
223
241
  describe('cast', () => {
@@ -304,6 +322,15 @@ describe('booleanLiteral', () => {
304
322
  ]);
305
323
  }
306
324
  });
325
+
326
+ test('validate returns input as-is for OK cases', () => {
327
+ const input = false;
328
+ const result = targetType.validate(input);
329
+ expect(Result.isOk(result)).toBe(true);
330
+ if (Result.isOk(result)) {
331
+ expect(result.value).toBe(input); // ✅ same reference
332
+ }
333
+ });
307
334
  });
308
335
 
309
336
  describe('fill', () => {
@@ -166,5 +166,14 @@ describe('nullType', () => {
166
166
  ]);
167
167
  }
168
168
  });
169
+
170
+ test('validate returns input as-is for OK cases', () => {
171
+ const input = null;
172
+ const result = nullType.validate(input);
173
+ expect(Result.isOk(result)).toBe(true);
174
+ if (Result.isOk(result)) {
175
+ expect(result.value).toBe(input); // ✅ same reference
176
+ }
177
+ });
169
178
  });
170
179
  });
@@ -174,6 +174,15 @@ describe('number', () => {
174
174
  ]);
175
175
  }
176
176
  });
177
+
178
+ test('validate returns input as-is for OK cases', () => {
179
+ const input = 123.456;
180
+ const result = num.validate(input);
181
+ expect(Result.isOk(result)).toBe(true);
182
+ if (Result.isOk(result)) {
183
+ expect(result.value).toBe(input); // ✅ same reference
184
+ }
185
+ });
177
186
  });
178
187
  });
179
188
 
@@ -280,5 +289,14 @@ describe('numberLiteral', () => {
280
289
  ]);
281
290
  }
282
291
  });
292
+
293
+ test('validate returns input as-is for OK cases', () => {
294
+ const input = 42;
295
+ const result = literal42.validate(input);
296
+ expect(Result.isOk(result)).toBe(true);
297
+ if (Result.isOk(result)) {
298
+ expect(result.value).toBe(input); // ✅ same reference
299
+ }
300
+ });
283
301
  });
284
302
  });
@@ -187,6 +187,15 @@ describe('string', () => {
187
187
  ]);
188
188
  }
189
189
  });
190
+
191
+ test('validate returns input as-is for OK cases', () => {
192
+ const input = 'hello world';
193
+ const result = str.validate(input);
194
+ expect(Result.isOk(result)).toBe(true);
195
+ if (Result.isOk(result)) {
196
+ expect(result.value).toBe(input); // ✅ same reference
197
+ }
198
+ });
190
199
  });
191
200
  });
192
201
 
@@ -302,6 +311,15 @@ describe('stringLiteral', () => {
302
311
  ]);
303
312
  }
304
313
  });
314
+
315
+ test('validate returns input as-is for OK cases', () => {
316
+ const input = 'hello';
317
+ const result = hello.validate(input);
318
+ expect(Result.isOk(result)).toBe(true);
319
+ if (Result.isOk(result)) {
320
+ expect(result.value).toBe(input); // ✅ same reference
321
+ }
322
+ });
305
323
  });
306
324
 
307
325
  describe('cast', () => {
@@ -1,6 +1,6 @@
1
1
  import { expectType, Result } from 'ts-data-forge';
2
2
  import { type TypeOf } from '../type.mjs';
3
- import { validationErrorsToMessages } from '../validation-error.mjs';
3
+ import { validationErrorsToMessages } from '../utils/index.mjs';
4
4
  import { symbol } from './symbol.mjs';
5
5
 
6
6
  describe('symbol', () => {
@@ -70,6 +70,15 @@ describe('symbol', () => {
70
70
  ]);
71
71
  }
72
72
  });
73
+
74
+ test('validate returns input as-is for OK cases', () => {
75
+ const input = Symbol('my-symbol');
76
+ const result = targetType.validate(input);
77
+ expect(Result.isOk(result)).toBe(true);
78
+ if (Result.isOk(result)) {
79
+ expect(result.value).toBe(input); // ✅ same reference
80
+ }
81
+ });
73
82
  });
74
83
 
75
84
  describe('assertIs', () => {