ts-fortress 1.0.0 → 2.0.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 (138) hide show
  1. package/README.md +430 -34
  2. package/dist/array/array-of-length.d.mts.map +1 -1
  3. package/dist/array/array-of-length.mjs +20 -8
  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 +13 -7
  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 +20 -8
  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 +20 -8
  13. package/dist/array/tuple.mjs.map +1 -1
  14. package/dist/branded/brand.d.mts.map +1 -1
  15. package/dist/branded/brand.mjs +7 -2
  16. package/dist/branded/brand.mjs.map +1 -1
  17. package/dist/compose/intersection.d.mts.map +1 -1
  18. package/dist/compose/intersection.mjs +17 -8
  19. package/dist/compose/intersection.mjs.map +1 -1
  20. package/dist/compose/merge-records.d.mts.map +1 -1
  21. package/dist/compose/merge-records.mjs +17 -8
  22. package/dist/compose/merge-records.mjs.map +1 -1
  23. package/dist/compose/union.d.mts.map +1 -1
  24. package/dist/compose/union.mjs +9 -4
  25. package/dist/compose/union.mjs.map +1 -1
  26. package/dist/enum/enum.d.mts.map +1 -1
  27. package/dist/enum/enum.mjs +17 -9
  28. package/dist/enum/enum.mjs.map +1 -1
  29. package/dist/enum/uint-range.d.mts.map +1 -1
  30. package/dist/enum/uint-range.mjs +10 -10
  31. package/dist/enum/uint-range.mjs.map +1 -1
  32. package/dist/index.d.mts +1 -0
  33. package/dist/index.d.mts.map +1 -1
  34. package/dist/index.mjs +1 -1
  35. package/dist/record/key-value-record.d.mts.map +1 -1
  36. package/dist/record/key-value-record.mjs +39 -15
  37. package/dist/record/key-value-record.mjs.map +1 -1
  38. package/dist/record/keyof.d.mts +1 -1
  39. package/dist/record/keyof.d.mts.map +1 -1
  40. package/dist/record/omit.d.mts.map +1 -1
  41. package/dist/record/omit.mjs +6 -2
  42. package/dist/record/omit.mjs.map +1 -1
  43. package/dist/record/partial.d.mts.map +1 -1
  44. package/dist/record/partial.mjs +6 -2
  45. package/dist/record/partial.mjs.map +1 -1
  46. package/dist/record/pick.d.mts.map +1 -1
  47. package/dist/record/pick.mjs +6 -2
  48. package/dist/record/pick.mjs.map +1 -1
  49. package/dist/record/record.d.mts.map +1 -1
  50. package/dist/record/record.mjs +22 -7
  51. package/dist/record/record.mjs.map +1 -1
  52. package/dist/type.d.mts +3 -2
  53. package/dist/type.d.mts.map +1 -1
  54. package/dist/utils/create-assert-fn.d.mts.map +1 -1
  55. package/dist/utils/create-assert-fn.mjs +2 -1
  56. package/dist/utils/create-assert-fn.mjs.map +1 -1
  57. package/dist/utils/create-cast-fn.d.mts.map +1 -1
  58. package/dist/utils/create-cast-fn.mjs +2 -1
  59. package/dist/utils/create-cast-fn.mjs.map +1 -1
  60. package/dist/utils/create-primitive-type.d.mts.map +1 -1
  61. package/dist/utils/create-primitive-type.mjs +6 -2
  62. package/dist/utils/create-primitive-type.mjs.map +1 -1
  63. package/dist/utils/index.d.mts +0 -1
  64. package/dist/utils/index.d.mts.map +1 -1
  65. package/dist/utils/index.mjs +0 -1
  66. package/dist/utils/index.mjs.map +1 -1
  67. package/dist/validation-error.d.mts +44 -0
  68. package/dist/validation-error.d.mts.map +1 -0
  69. package/dist/validation-error.mjs +40 -0
  70. package/dist/validation-error.mjs.map +1 -0
  71. package/package.json +1 -1
  72. package/src/array/array-of-length.mts +24 -17
  73. package/src/array/array-of-length.test.mts +46 -8
  74. package/src/array/array.mts +16 -16
  75. package/src/array/array.test.mts +32 -5
  76. package/src/array/non-empty-array.mts +25 -17
  77. package/src/array/non-empty-array.test.mts +46 -8
  78. package/src/array/tuple.mts +28 -17
  79. package/src/array/tuple.test.mts +68 -7
  80. package/src/branded/brand.mts +13 -6
  81. package/src/branded/number/finite-number.test.mts +164 -0
  82. package/src/branded/number/int.test.mts +156 -0
  83. package/src/branded/number/int16.test.mts +115 -0
  84. package/src/branded/number/int32.test.mts +109 -0
  85. package/src/branded/number/non-negative-finite-number.test.mts +126 -0
  86. package/src/branded/number/non-zero-finite-number.test.mts +120 -0
  87. package/src/branded/number/non-zero-int.test.mts +113 -0
  88. package/src/branded/number/non-zero-safe-int.test.mts +120 -0
  89. package/src/branded/number/positive-finite-number.test.mts +122 -0
  90. package/src/branded/number/positive-int.test.mts +174 -0
  91. package/src/branded/number/positive-safe-int.test.mts +126 -0
  92. package/src/branded/number/safe-int.test.mts +113 -0
  93. package/src/branded/number/safe-uint.test.mts +121 -0
  94. package/src/branded/number/uint.test.mts +169 -0
  95. package/src/branded/number/uint16.test.mts +121 -0
  96. package/src/branded/number/uint32.test.mts +121 -0
  97. package/src/branded/simple-branded-number.test.mts +160 -0
  98. package/src/branded/simple-branded-string.test.mts +178 -0
  99. package/src/combination.test.mts +268 -11
  100. package/src/compose/intersection.mts +23 -17
  101. package/src/compose/intersection.test.mts +125 -13
  102. package/src/compose/merge-records.mts +23 -17
  103. package/src/compose/merge-records.test.mts +38 -5
  104. package/src/compose/union.mts +9 -10
  105. package/src/compose/union.test.mts +20 -4
  106. package/src/enum/enum.mts +22 -19
  107. package/src/enum/enum.test.mts +20 -4
  108. package/src/enum/uint-range.mts +11 -16
  109. package/src/enum/uint-range.test.mts +19 -4
  110. package/src/index.mts +1 -0
  111. package/src/primitives/bigint.test.mts +247 -0
  112. package/src/primitives/boolean.test.mts +323 -0
  113. package/src/primitives/symbol.test.mts +124 -0
  114. package/src/record/key-value-record.mts +48 -28
  115. package/src/record/key-value-record.test.mts +43 -5
  116. package/src/record/keyof.mts +6 -1
  117. package/src/record/keyof.test.mts +20 -4
  118. package/src/record/omit.mts +7 -7
  119. package/src/record/omit.test.mts +19 -5
  120. package/src/record/optional.test.mts +46 -0
  121. package/src/record/partial.mts +7 -7
  122. package/src/record/partial.test.mts +69 -13
  123. package/src/record/pick.mts +7 -7
  124. package/src/record/pick.test.mts +19 -5
  125. package/src/record/record.mts +40 -24
  126. package/src/record/record.test.mts +52 -9
  127. package/src/type.mts +3 -2
  128. package/src/utils/create-assert-fn.mts +6 -2
  129. package/src/utils/create-cast-fn.mts +6 -2
  130. package/src/utils/create-primitive-type.mts +6 -5
  131. package/src/utils/index.mts +0 -1
  132. package/src/validation-error.mts +81 -0
  133. package/src/validation-error.test.mts +173 -0
  134. package/dist/utils/validation-error-message.d.mts +0 -2
  135. package/dist/utils/validation-error-message.d.mts.map +0 -1
  136. package/dist/utils/validation-error-message.mjs +0 -13
  137. package/dist/utils/validation-error-message.mjs.map +0 -1
  138. package/src/utils/validation-error-message.mts +0 -17
package/README.md CHANGED
@@ -2,17 +2,23 @@
2
2
 
3
3
  TypeScript-first schema validation library with static type inference.
4
4
 
5
- ts-fortress is a runtime validation library similar to [io-ts](https://github.com/gcanti/io-ts) and [zod](https://github.com/colinhacks/zod), designed to provide type-safe schema validation with excellent TypeScript integration and static type inference.
5
+ [![npm version](https://img.shields.io/npm/v/ts-fortress.svg)](https://www.npmjs.com/package/ts-fortress)
6
+ [![npm downloads](https://img.shields.io/npm/dm/ts-fortress.svg)](https://www.npmjs.com/package/ts-fortress)
7
+ [![License](https://img.shields.io/npm/l/ts-fortress.svg)](./LICENSE)
8
+ [![codecov](https://codecov.io/gh/noshiro-pf/ts-fortress/graph/badge.svg?token=Y522SWQTPB)](https://codecov.io/gh/noshiro-pf/ts-fortress)
9
+
10
+ **ts-fortress** is a runtime validation library similar to [io-ts](https://github.com/gcanti/io-ts) and [zod](https://github.com/colinhacks/zod), designed to provide type-safe schema validation with excellent TypeScript integration and static type inference.
6
11
 
7
12
  ## Features
8
13
 
9
14
  - 🔒 **Type-safe validation** - Full TypeScript support with static type inference
10
15
  - 🏗️ **Composable schemas** - Build complex validation schemas from simple building blocks
11
16
  - 🎯 **Zero-runtime overhead** - Type information is inferred at compile time
12
- - 🔄 **Result-based error handling** - Structured error reporting with `Result<T, Error[]>`
17
+ - 🔄 **Result-based error handling** - Structured error reporting with `Result<T, readonly ValidationError[]>`
13
18
  - 🏷️ **Branded types** - Rich collection of branded number types (Int, SafeInt, PositiveInt, etc.)
14
19
  - ⚡ **Performance focused** - Minimal runtime overhead and optimized validation
15
20
  - 🧩 **Functional composition** - Union, intersection, and merge operations for schema composition
21
+ - 🛠️ **Required default values** - All schemas require explicit default values, enabling automatic data filling via `fill()` function
16
22
 
17
23
  ## Installation
18
24
 
@@ -69,6 +75,387 @@ if (result.isOk()) {
69
75
  }
70
76
  ```
71
77
 
78
+ ## Default Values and Data Filling
79
+
80
+ One of ts-fortress's key design decisions is **requiring explicit default values** for all schema types. This enables powerful data filling capabilities:
81
+
82
+ ```typescript
83
+ import * as t from 'ts-fortress';
84
+
85
+ // Every type requires a default value
86
+ const UserProfile = t.record({
87
+ name: t.string('Anonymous'), // Default: 'Anonymous'
88
+ age: t.number(0), // Default: 0
89
+ email: t.optional(t.string('')), // Optional field with default ''
90
+ preferences: t.record({
91
+ theme: t.string('light'), // Default: 'light'
92
+ notifications: t.boolean(true), // Default: true
93
+ }),
94
+ tags: t.array(t.string('')), // Default: empty array []
95
+ });
96
+
97
+ // The fill() function automatically provides missing values
98
+ const partialData = {
99
+ name: 'John Doe',
100
+ preferences: {
101
+ theme: 'dark',
102
+ // notifications missing - will be filled with default
103
+ },
104
+ // age, email, tags missing - will be filled with defaults
105
+ };
106
+
107
+ const filledData = UserProfile.fill(partialData);
108
+ console.log(filledData);
109
+ // Output: {
110
+ // name: 'John Doe',
111
+ // age: 0, // ← Filled with default
112
+ // email: '', // ← Filled with default
113
+ // preferences: {
114
+ // theme: 'dark',
115
+ // notifications: true, // ← Filled with default
116
+ // },
117
+ // tags: [], // ← Filled with default
118
+ // }
119
+
120
+ // fill() is type-safe and always returns a complete object
121
+ type UserProfile = t.TypeOf<typeof UserProfile>;
122
+ const completeUser: UserProfile = UserProfile.fill(anyPartialData);
123
+ ```
124
+
125
+ ### Benefits of Required Default Values
126
+
127
+ - **Consolidated definitions**: Type definitions and default values are defined in one place, eliminating the need to maintain separate default objects
128
+ - **Data integrity**: Never worry about missing required fields
129
+ - **API resilience**: Handle incomplete data gracefully from external APIs
130
+ - **Form handling**: Easily initialize forms with default values
131
+ - **Configuration**: Provide sensible defaults for optional configuration
132
+ - **Testing**: Generate complete test data from partial fixtures
133
+
134
+ ```typescript
135
+ // Real-world example: API response handling
136
+ const ApiResponse = t.record({
137
+ data: t.array(t.string('')),
138
+ pagination: t.record({
139
+ page: t.number(1),
140
+ limit: t.number(10),
141
+ total: t.number(0),
142
+ }),
143
+ meta: t.record({
144
+ timestamp: t.number(Date.now()),
145
+ version: t.string('1.0.0'),
146
+ }),
147
+ });
148
+
149
+ // Handle incomplete API responses gracefully
150
+ const incompleteResponse = { data: ['item1', 'item2'] };
151
+ const completeResponse = ApiResponse.fill(incompleteResponse);
152
+ // All missing fields are automatically filled with their defaults
153
+ ```
154
+
155
+ ## Why ts-fortress over Zod and io-ts?
156
+
157
+ While ts-fortress, [Zod](https://github.com/colinhacks/zod), and [io-ts](https://github.com/gcanti/io-ts) are all excellent TypeScript validation libraries, ts-fortress provides enhanced type safety during validator construction and addresses critical runtime consistency issues found in io-ts.
158
+
159
+ ### Type Safety in Schema Definition
160
+
161
+ **Problem with Zod**: The following code compiles without errors but creates an invalid schema:
162
+
163
+ ```typescript
164
+ import * as z from 'zod';
165
+
166
+ // ❌ This compiles but is incorrect!
167
+ export const SomeObject = z.object({
168
+ key1: 1, // Should be z.literal(1)
169
+ key2: 'string', // Should be z.string()
170
+ });
171
+
172
+ export type SomeObject = z.infer<typeof SomeObject>;
173
+ ```
174
+
175
+ The above Zod schema will fail at runtime because raw values (`1`, `'string'`) are not valid Zod validators.
176
+
177
+ **Correct Zod usage** requires remembering to wrap all values:
178
+
179
+ ```typescript
180
+ // ✅ Correct Zod usage
181
+ export const SomeObject = z.object({
182
+ key1: z.literal(1),
183
+ key2: z.string(),
184
+ });
185
+ ```
186
+
187
+ **ts-fortress prevents this error at compile time**:
188
+
189
+ ```typescript
190
+ import * as t from 'ts-fortress';
191
+
192
+ // ❌ TypeScript error - this won't compile!
193
+ export const SomeObject = t.record({
194
+ key1: 1, // Error: number is not assignable to Type<unknown>
195
+ key2: 'string', // Error: string is not assignable to Type<unknown>
196
+ });
197
+
198
+ // ✅ Correct ts-fortress usage - enforced by TypeScript
199
+ export const SomeObject = t.record({
200
+ key1: t.numberLiteral(1), // or t.number(1) with default
201
+ key2: t.string(''),
202
+ });
203
+ ```
204
+
205
+ ### Benefits
206
+
207
+ - **Compile-time safety**: TypeScript catches invalid schema definitions immediately
208
+ - **IDE support**: Better autocomplete and error highlighting during development
209
+ - **Reduced runtime errors**: Impossible to create invalid schemas that fail at runtime
210
+ - **Self-documenting**: The type system guides you toward correct usage
211
+
212
+ ### Deep Readonly Types by Default
213
+
214
+ **ts-fortress generates deeply readonly types**, promoting immutability and preventing accidental mutations:
215
+
216
+ ```typescript
217
+ import * as t from 'ts-fortress';
218
+
219
+ const UserType = t.record({
220
+ name: t.string(''),
221
+ address: t.record({
222
+ street: t.string(''),
223
+ city: t.string(''),
224
+ }),
225
+ tags: t.array(t.string('')),
226
+ });
227
+
228
+ type User = t.TypeOf<typeof UserType>;
229
+ // ↑ Readonly<{
230
+ // name: string;
231
+ // address: Readonly<{
232
+ // street: string;
233
+ // city: string;
234
+ // }>;
235
+ // tags: readonly string[];
236
+ // }>
237
+
238
+ const user: User = UserType.cast(someData);
239
+
240
+ // ❌ All of these produce TypeScript errors:
241
+ user.name = 'new name'; // Cannot assign to 'name' because it is read-only
242
+ user.address.street = 'new street'; // Cannot assign to 'street' because it is read-only
243
+ user.tags.push('new tag'); // Property 'push' does not exist on readonly array
244
+ user.tags[0] = 'modified'; // Index signature in type 'readonly string[]' only permits reading
245
+ ```
246
+
247
+ **In contrast, Zod types are mutable by default**:
248
+
249
+ ```typescript
250
+ import * as z from 'zod';
251
+
252
+ const UserSchema = z.object({
253
+ name: z.string(),
254
+ address: z.object({
255
+ street: z.string(),
256
+ city: z.string(),
257
+ }),
258
+ tags: z.array(z.string()),
259
+ });
260
+
261
+ type User = z.infer<typeof UserSchema>;
262
+ // ↑ {
263
+ // name: string;
264
+ // address: {
265
+ // street: string;
266
+ // city: string;
267
+ // };
268
+ // tags: string[];
269
+ // }
270
+
271
+ const user: User = UserSchema.parse(someData);
272
+
273
+ // ✅ These all work in Zod (but may not be desirable):
274
+ user.name = 'new name'; // No error
275
+ user.address.street = 'new street'; // No error
276
+ user.tags.push('new tag'); // No error
277
+ user.tags[0] = 'modified'; // No error
278
+ ```
279
+
280
+ ### Benefits of Deep Readonly
281
+
282
+ - **Immutability by default**: Prevents accidental mutations that can lead to bugs
283
+ - **Functional programming support**: Encourages functional programming patterns
284
+ - **Predictable data flow**: Ensures data integrity throughout your application
285
+ - **Better optimization**: Enables better compiler optimizations and reasoning about code
286
+ - **Thread safety**: Immutable data is inherently safe to share between contexts
287
+
288
+ ### Runtime-Type Consistency Issues in io-ts
289
+
290
+ **io-ts has several long-standing bugs** where runtime behavior doesn't match TypeScript types, which have remained unfixed for years:
291
+
292
+ #### 1. Keyof Type Mismatch ([Issue #697](https://github.com/gcanti/io-ts/issues/697))
293
+
294
+ ```typescript
295
+ import * as t from 'io-ts';
296
+
297
+ const T = t.keyof({
298
+ 0: undefined,
299
+ 1: undefined,
300
+ 2: undefined,
301
+ 3: undefined,
302
+ 4: undefined,
303
+ });
304
+
305
+ // ❌ Runtime behavior is inconsistent with TypeScript types!
306
+ console.log(T.decode(0)); // Left (fails) - number 0 is rejected
307
+ console.log(T.decode('0')); // Right (succeeds) - string "0" is accepted
308
+
309
+ type T = t.TypeOf<typeof T>;
310
+ // ↑ TypeScript infers: 0 | 1 | 2 | 3 | 4 (number literals)
311
+ // But should be: "0" | "1" | "2" | "3" | "4" (string literals)
312
+
313
+ // The runtime validator only accepts strings, but TypeScript thinks it accepts numbers!
314
+ ```
315
+
316
+ #### 2. Union + Undefined Decode Issues ([Issue #677](https://github.com/gcanti/io-ts/issues/677))
317
+
318
+ ```typescript
319
+ import * as t from 'io-ts';
320
+ import { isRight } from 'fp-ts/Either';
321
+
322
+ const A = t.type({
323
+ A: t.union([t.number, t.undefined, t.null]),
324
+ });
325
+
326
+ const B = t.type({
327
+ B: t.union([t.number, t.undefined, t.null]),
328
+ });
329
+
330
+ const C = t.partial({
331
+ C: t.union([t.number, t.null]),
332
+ });
333
+
334
+ // ❌ Case 1: Union decode adds unexpected fields
335
+ {
336
+ const UnionBA = t.union([B, A]);
337
+ const res = UnionBA.decode({ A: 1 });
338
+
339
+ if (isRight(res)) {
340
+ console.log(res.right); // { A: 1, B: undefined } <- NG (expected: { A: 1 })
341
+ console.log(A.is(res.right)); // true <- ok
342
+ console.log(B.is(res.right)); // true <- NG (expected: false)
343
+ }
344
+ }
345
+
346
+ // ❌ Case 2: Union decode produces inconsistent results
347
+ {
348
+ const UnionCA = t.union([C, A]);
349
+ const res = UnionCA.decode({ A: 1 });
350
+
351
+ if (isRight(res)) {
352
+ console.log(res.right); // { A: 1 } <- NG (expected: {})
353
+ console.log(A.is(res.right)); // true <- ok
354
+ console.log(C.is(res.right)); // true <- ok
355
+ }
356
+ }
357
+ ```
358
+
359
+ **ts-fortress eliminates these problems** by ensuring strict runtime-type consistency:
360
+
361
+ ```typescript
362
+ import * as t from 'ts-fortress';
363
+
364
+ // ✅ ts-fortress: Runtime and types always match
365
+ const T = t.keyof(
366
+ t.record({
367
+ 0: t.undefinedType,
368
+ 1: t.undefinedType,
369
+ 2: t.undefinedType,
370
+ 3: t.undefinedType,
371
+ 4: t.undefinedType,
372
+ }),
373
+ );
374
+
375
+ type T = t.TypeOf<typeof T>;
376
+ // ↑ TypeScript correctly infers: "0" | "1" | "2" | "3" | "4" (string literals)
377
+
378
+ // ✅ Runtime behavior matches TypeScript types exactly
379
+ console.log(T.validate(0)); // ❌ Fails correctly - number 0 is rejected
380
+ console.log(T.validate('0')); // ✅ Success - string "0" is accepted
381
+
382
+ // ✅ Complex union types work reliably without unexpected behavior
383
+ const A = t.record({
384
+ A: t.union([t.number(0), t.undefinedType, t.nullType]),
385
+ });
386
+
387
+ const B = t.record({
388
+ B: t.union([t.number(0), t.undefinedType, t.nullType]),
389
+ });
390
+
391
+ const C = t.partial(
392
+ t.record({
393
+ C: t.union([t.number(0), t.nullType]),
394
+ }),
395
+ );
396
+
397
+ // ✅ Case 1: Union validation is predictable and correct
398
+ {
399
+ const UnionBA = t.union([B, A]);
400
+ const result = UnionBA.validate({ A: 1 });
401
+
402
+ if (result.isOk()) {
403
+ console.log(result.value); // { A: 1 } <- ✅ Correct! No unexpected fields
404
+ console.log(A.is(result.value)); // true <- ✅ Correct
405
+ console.log(B.is(result.value)); // false <- ✅ Correct! B requires field B
406
+ }
407
+ }
408
+
409
+ // ✅ Case 2: Consistent validation behavior
410
+ {
411
+ const UnionCA = t.union([C, A]);
412
+ const result = UnionCA.validate({ A: 1 });
413
+
414
+ if (result.isOk()) {
415
+ console.log(result.value); // { A: 1 } <- ✅ Correct and consistent
416
+ console.log(A.is(result.value)); // true <- ✅ Correct
417
+ console.log(C.is(result.value)); // true <- ✅ Consistent! ts-fortress partial types allow extra fields
418
+ }
419
+ }
420
+ ```
421
+
422
+ This makes ts-fortress especially valuable in large codebases where schema correctness is critical and runtime failures need to be minimized.
423
+
424
+ ## Migration from io-ts
425
+
426
+ If you're coming from io-ts, here's how common patterns translate:
427
+
428
+ ```typescript
429
+ // io-ts style
430
+ import * as t from 'io-ts';
431
+
432
+ const UserCodec = t.type({
433
+ id: t.string,
434
+ name: t.string,
435
+ age: t.number,
436
+ });
437
+
438
+ type User = t.TypeOf<typeof UserCodec>;
439
+
440
+ // ts-fortress style
441
+ import * as t from 'ts-fortress';
442
+
443
+ const UserType = t.record({
444
+ id: t.string(''),
445
+ name: t.string(''),
446
+ age: t.number(0),
447
+ });
448
+
449
+ type User = t.TypeOf<typeof UserType>;
450
+ ```
451
+
452
+ Key differences:
453
+
454
+ - **Default values**: ts-fortress requires explicit default values for better type safety
455
+ - **Naming**: `record` instead of `type`, more explicit function names
456
+ - **Error handling**: Built-in `Result` type instead of `Either`
457
+ - **Branded types**: Rich collection of pre-built branded number types
458
+
72
459
  ## Core Concepts
73
460
 
74
461
  ### Type Interface
@@ -76,15 +463,15 @@ if (result.isOk()) {
76
463
  Every validator in ts-fortress implements the `Type<A>` interface:
77
464
 
78
465
  ```typescript
79
- interface Type<A> {
466
+ type Type<A> = Readonly<{
80
467
  typeName: string; // Human-readable type name
81
468
  defaultValue: A; // Default value for this type
82
469
  is: (a: unknown) => a is A; // Type guard function
83
470
  assertIs: (a: unknown) => asserts a is A; // Type assertion
84
471
  cast: (a: unknown) => A; // Cast with fallback to default
85
472
  fill: (a: unknown) => A; // Fill missing values with defaults
86
- validate: (a: unknown) => Result<A, string[]>; // Detailed validation
87
- }
473
+ validate: (a: unknown) => Result<A, readonly ValidationError[]>; // Detailed validation
474
+ }>;
88
475
  ```
89
476
 
90
477
  ### Primitive Types
@@ -212,7 +599,7 @@ type DiceRoll = t.TypeOf<typeof DiceRoll>; // 1 | 2 | 3 | 4 | 5 | 6
212
599
 
213
600
  ## Error Handling
214
601
 
215
- ts-fortress uses `Result<T, string[]>` for structured error handling:
602
+ ts-fortress uses `Result<T, readonly ValidationError[]>` for structured error handling with detailed error information:
216
603
 
217
604
  ```typescript
218
605
  const UserType = t.record({
@@ -223,9 +610,18 @@ const UserType = t.record({
223
610
  const invalidData = { name: 123, age: 'not a number' };
224
611
 
225
612
  const result = UserType.validate(invalidData);
226
- if (result.isErr()) {
227
- console.log(result.value);
228
- // Array of error messages:
613
+ if (Result.isErr(result)) {
614
+ // result.value is an array of ValidationError objects
615
+ result.value.forEach((error) => {
616
+ console.log('Path:', error.path); // ['name'] or ['age']
617
+ console.log('Expected:', error.expectedType); // 'string' or 'number'
618
+ console.log('Actual:', error.actualValue); // 123 or 'not a number'
619
+ console.log('Type:', error.typeName); // type being validated
620
+ });
621
+
622
+ // Convert to legacy string messages for backward compatibility
623
+ const messages = t.validationErrorsToMessages(result.value);
624
+ console.log(messages);
229
625
  // ['Expected string at name, got number', 'Expected number at age, got string']
230
626
  }
231
627
 
@@ -237,40 +633,40 @@ try {
237
633
  }
238
634
  ```
239
635
 
240
- ## Migration from io-ts
636
+ ### ValidationError Structure
241
637
 
242
- If you're coming from io-ts, here's how common patterns translate:
638
+ Each validation error provides detailed information:
243
639
 
244
640
  ```typescript
245
- // io-ts style
246
- import * as t from 'io-ts';
247
-
248
- const UserCodec = t.type({
249
- id: t.string,
250
- name: t.string,
251
- age: t.number,
252
- });
641
+ interface ValidationError {
642
+ path: readonly string[]; // Path to the field that failed
643
+ actualValue: unknown; // The actual value that failed validation
644
+ expectedType: string; // The expected type or constraint
645
+ message?: string; // Optional custom error message
646
+ typeName: string; // Name of the type being validated
647
+ }
648
+ ```
253
649
 
254
- type User = t.TypeOf<typeof UserCodec>;
650
+ ### Error Utilities
255
651
 
256
- // ts-fortress style
652
+ ```typescript
257
653
  import * as t from 'ts-fortress';
258
654
 
259
- const UserType = t.record({
260
- id: t.string(''),
261
- name: t.string(''),
262
- age: t.number(0),
263
- });
264
-
265
- type User = t.TypeOf<typeof UserType>;
266
- ```
655
+ // Convert ValidationError objects to string messages
656
+ const messages = t.validationErrorsToMessages(errors);
267
657
 
268
- Key differences:
658
+ // Convert single ValidationError to message
659
+ const message = t.validationErrorToMessage(error);
269
660
 
270
- - **Default values**: ts-fortress requires explicit default values for better type safety
271
- - **Naming**: `record` instead of `type`, more explicit function names
272
- - **Error handling**: Built-in `Result` type instead of `Either`
273
- - **Branded types**: Rich collection of pre-built branded number types
661
+ // Create custom validation errors
662
+ const customError = t.createValidationError({
663
+ path: ['user', 'email'],
664
+ actualValue: 'invalid-email',
665
+ expectedType: 'valid email address',
666
+ typeName: 'EmailAddress',
667
+ message: 'Please provide a valid email address',
668
+ });
669
+ ```
274
670
 
275
671
  ## API Reference
276
672
 
@@ -1 +1 @@
1
- {"version":3,"file":"array-of-length.d.mts","sourceRoot":"","sources":["../../src/array/array-of-length.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAQxC,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,CAAC,SAAS,SAAS,EAClD,MAAM,CAAC,EACP,aAAa,IAAI,CAAC,CAAC,CAAC,EACpB,UAAU,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CACpC,CAAC,KACD,IAAI,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CA2D1B,CAAC"}
1
+ {"version":3,"file":"array-of-length.d.mts","sourceRoot":"","sources":["../../src/array/array-of-length.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAQxC,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,CAAC,SAAS,SAAS,EAClD,MAAM,CAAC,EACP,aAAa,IAAI,CAAC,CAAC,CAAC,EACpB,UAAU,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CACpC,CAAC,KACD,IAAI,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAkE1B,CAAC"}
@@ -2,7 +2,7 @@ import { Arr, Result } from 'ts-data-forge';
2
2
  import { createAssertFn } from '../utils/create-assert-fn.mjs';
3
3
  import { createCastFn } from '../utils/create-cast-fn.mjs';
4
4
  import { createIsFn } from '../utils/create-is-fn.mjs';
5
- import { validationErrorMessage } from '../utils/validation-error-message.mjs';
5
+ import { createPrimitiveValidationError, prependIndexToValidationErrors } from '../validation-error.mjs';
6
6
 
7
7
  const arrayOfLength = (size, elementType, options) => {
8
8
  const { typeName,
@@ -12,20 +12,32 @@ const arrayOfLength = (size, elementType, options) => {
12
12
  const validate = (a) => {
13
13
  if (!Arr.isArray(a)) {
14
14
  return Result.err([
15
- validationErrorMessage(a, 'The value is expected to be an array'),
15
+ createPrimitiveValidationError({
16
+ actualValue: a,
17
+ expectedType: 'array',
18
+ typeName: typeNameFilled,
19
+ }),
16
20
  ]);
17
21
  }
18
22
  if (a.length !== size) {
19
23
  return Result.err([
20
- `The value is expected to be an array of length ${size}, but it is ${a.length}.`,
24
+ {
25
+ path: [],
26
+ actualValue: a,
27
+ expectedType: typeNameFilled,
28
+ message: `Expected array of length ${size}, got length ${a.length}`,
29
+ typeName: typeNameFilled,
30
+ },
21
31
  ]);
22
32
  }
23
- for (const [index, el] of a.entries()) {
33
+ const errors = a.flatMap((el, index) => {
24
34
  const res = elementType.validate(el);
25
- if (Result.isErr(res)) {
26
- const message = validationErrorMessage(el, `The array element is expected to be <${elementType.typeName}>`, (str) => `but the actual value at index ${index} is '${str}'`);
27
- return Result.err([message, ...res.value]);
28
- }
35
+ return Result.isErr(res)
36
+ ? prependIndexToValidationErrors(res.value, index)
37
+ : [];
38
+ });
39
+ if (errors.length > 0) {
40
+ return Result.err(errors);
29
41
  }
30
42
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
31
43
  return Result.ok(a);
@@ -1 +1 @@
1
- {"version":3,"file":"array-of-length.mjs","sources":["../../src/array/array-of-length.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;AASO,MAAM,aAAa,GAAG,CAC3B,IAAO,EACP,WAAoB,EACpB,OAGE,KAC2B;AAG7B,IAAA,MAAM,EACJ,QAAQ;;AAER,IAAA,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,YAAY,CAAM,GAC/D,GAAG,OAAO,IAAI,EAAE;IAEjB,MAAM,cAAc,GAClB,QAAQ,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAA,CAAG;AAEtE,IAAA,MAAM,QAAQ,GAAwB,CAAC,CAAC,KAAI;QAC1C,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;YACnB,OAAO,MAAM,CAAC,GAAG,CAAC;AAChB,gBAAA,sBAAsB,CAAC,CAAC,EAAE,sCAAsC,CAAC;AAClE,aAAA,CAAC;QACJ;AAEA,QAAA,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,EAAE;YACrB,OAAO,MAAM,CAAC,GAAG,CAAC;AAChB,gBAAA,CAAA,+CAAA,EAAkD,IAAI,CAAA,YAAA,EAAe,CAAC,CAAC,MAAM,CAAA,CAAA,CAAG;AACjF,aAAA,CAAC;QACJ;AAEA,QAAA,KAAK,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YACrC,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;AAEpC,YAAA,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;gBACrB,MAAM,OAAO,GAAG,sBAAsB,CACpC,EAAE,EACF,CAAA,qCAAA,EAAwC,WAAW,CAAC,QAAQ,CAAA,CAAA,CAAG,EAC/D,CAAC,GAAG,KAAK,CAAA,8BAAA,EAAiC,KAAK,CAAA,KAAA,EAAQ,GAAG,CAAA,CAAA,CAAG,CAC9D;AAED,gBAAA,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;YAC5C;QACF;;AAGA,QAAA,OAAO,MAAM,CAAC,EAAE,CAAC,CAAiB,CAAC;AACrC,IAAA,CAAC;AAED,IAAA,MAAM,IAAI,GAAoB,CAAC,CAAC,KAC9B,GAAG,CAAC,OAAO,CAAC,CAAC;AACX;;YAEG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAa;UACjE,YAAY;IAElB,OAAO;AACL,QAAA,QAAQ,EAAE,cAAc;QACxB,YAAY;QACZ,IAAI;QACJ,QAAQ;AACR,QAAA,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC;AACxB,QAAA,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC;AAC5B,QAAA,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC;KACnC;AACH;;;;"}
1
+ {"version":3,"file":"array-of-length.mjs","sources":["../../src/array/array-of-length.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;AASO,MAAM,aAAa,GAAG,CAC3B,IAAO,EACP,WAAoB,EACpB,OAGE,KAC2B;AAG7B,IAAA,MAAM,EACJ,QAAQ;;AAER,IAAA,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,YAAY,CAAM,GAC/D,GAAG,OAAO,IAAI,EAAE;IAEjB,MAAM,cAAc,GAClB,QAAQ,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAA,CAAG;AAEtE,IAAA,MAAM,QAAQ,GAAwB,CAAC,CAAC,KAAI;QAC1C,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;YACnB,OAAO,MAAM,CAAC,GAAG,CAAC;AAChB,gBAAA,8BAA8B,CAAC;AAC7B,oBAAA,WAAW,EAAE,CAAC;AACd,oBAAA,YAAY,EAAE,OAAO;AACrB,oBAAA,QAAQ,EAAE,cAAc;iBACzB,CAAC;AACH,aAAA,CAAC;QACJ;AAEA,QAAA,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,EAAE;YACrB,OAAO,MAAM,CAAC,GAAG,CAAC;AAChB,gBAAA;AACE,oBAAA,IAAI,EAAE,EAAE;AACR,oBAAA,WAAW,EAAE,CAAC;AACd,oBAAA,YAAY,EAAE,cAAc;AAC5B,oBAAA,OAAO,EAAE,CAAA,yBAAA,EAA4B,IAAI,gBAAgB,CAAC,CAAC,MAAM,CAAA,CAAE;AACnE,oBAAA,QAAQ,EAAE,cAAc;AACY,iBAAA;AACvC,aAAA,CAAC;QACJ;QAEA,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,KAAK,KAAI;YACrC,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;AACpC,YAAA,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG;kBACnB,8BAA8B,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK;kBAC/C,EAAE;AACR,QAAA,CAAC,CAAC;AAEF,QAAA,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;AACrB,YAAA,OAAO,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;QAC3B;;AAGA,QAAA,OAAO,MAAM,CAAC,EAAE,CAAC,CAAiB,CAAC;AACrC,IAAA,CAAC;AAED,IAAA,MAAM,IAAI,GAAoB,CAAC,CAAC,KAC9B,GAAG,CAAC,OAAO,CAAC,CAAC;AACX;;YAEG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAa;UACjE,YAAY;IAElB,OAAO;AACL,QAAA,QAAQ,EAAE,cAAc;QACxB,YAAY;QACZ,IAAI;QACJ,QAAQ;AACR,QAAA,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC;AACxB,QAAA,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC;AAC5B,QAAA,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC;KACnC;AACH;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"array.d.mts","sourceRoot":"","sources":["../../src/array/array.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAQxC,eAAO,MAAM,KAAK,GAAI,CAAC,EACrB,aAAa,IAAI,CAAC,CAAC,CAAC,EACpB,UAAU,OAAO,CACf,QAAQ,CAAC;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,SAAS,CAAC,EAAE,CAAC;CAC5B,CAAC,CACH,KACA,IAAI,CAAC,SAAS,CAAC,EAAE,CA4CnB,CAAC"}
1
+ {"version":3,"file":"array.d.mts","sourceRoot":"","sources":["../../src/array/array.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAOxC,eAAO,MAAM,KAAK,GAAI,CAAC,EACrB,aAAa,IAAI,CAAC,CAAC,CAAC,EACpB,UAAU,OAAO,CACf,QAAQ,CAAC;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,SAAS,CAAC,EAAE,CAAC;CAC5B,CAAC,CACH,KACA,IAAI,CAAC,SAAS,CAAC,EAAE,CA6CnB,CAAC"}
@@ -2,7 +2,7 @@ import { Arr, Result } from 'ts-data-forge';
2
2
  import { createAssertFn } from '../utils/create-assert-fn.mjs';
3
3
  import { createCastFn } from '../utils/create-cast-fn.mjs';
4
4
  import { createIsFn } from '../utils/create-is-fn.mjs';
5
- import { validationErrorMessage } from '../utils/validation-error-message.mjs';
5
+ import { createPrimitiveValidationError, prependIndexToValidationErrors } from '../validation-error.mjs';
6
6
 
7
7
  const array = (elementType, options) => {
8
8
  const { typeName, defaultValue = [] } = options ?? {};
@@ -10,15 +10,21 @@ const array = (elementType, options) => {
10
10
  const validate = (a) => {
11
11
  if (!Arr.isArray(a)) {
12
12
  return Result.err([
13
- validationErrorMessage(a, 'The value is expected to be an array'),
13
+ createPrimitiveValidationError({
14
+ actualValue: a,
15
+ expectedType: 'array',
16
+ typeName: typeNameFilled,
17
+ }),
14
18
  ]);
15
19
  }
16
- for (const [index, el] of a.entries()) {
20
+ const errors = a.flatMap((el, index) => {
17
21
  const res = elementType.validate(el);
18
- if (Result.isErr(res)) {
19
- const message = validationErrorMessage(el, `The array element is expected to be <${elementType.typeName}>`, (str) => `but the actual value at index ${index} is '${str}'`);
20
- return Result.err([message, ...res.value]);
21
- }
22
+ return Result.isErr(res)
23
+ ? prependIndexToValidationErrors(res.value, index)
24
+ : [];
25
+ });
26
+ if (errors.length > 0) {
27
+ return Result.err(errors);
22
28
  }
23
29
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
24
30
  return Result.ok(a);
@@ -1 +1 @@
1
- {"version":3,"file":"array.mjs","sources":["../../src/array/array.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;MASa,KAAK,GAAG,CACnB,WAAoB,EACpB,OAKC,KACqB;IAGtB,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,EAAE,EAAE,GAAG,OAAO,IAAI,EAAE;IAErD,MAAM,cAAc,GAAW,QAAQ,IAAI,IAAI,WAAW,CAAC,QAAQ,CAAA,GAAA,CAAK;AAExE,IAAA,MAAM,QAAQ,GAAwB,CAAC,CAAC,KAAI;QAC1C,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;YACnB,OAAO,MAAM,CAAC,GAAG,CAAC;AAChB,gBAAA,sBAAsB,CAAC,CAAC,EAAE,sCAAsC,CAAC;AAClE,aAAA,CAAC;QACJ;AAEA,QAAA,KAAK,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YACrC,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;AAEpC,YAAA,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;gBACrB,MAAM,OAAO,GAAG,sBAAsB,CACpC,EAAE,EACF,CAAA,qCAAA,EAAwC,WAAW,CAAC,QAAQ,CAAA,CAAA,CAAG,EAC/D,CAAC,GAAG,KAAK,CAAA,8BAAA,EAAiC,KAAK,CAAA,KAAA,EAAQ,GAAG,CAAA,CAAA,CAAG,CAC9D;AAED,gBAAA,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;YAC5C;QACF;;AAGA,QAAA,OAAO,MAAM,CAAC,EAAE,CAAC,CAAM,CAAC;AAC1B,IAAA,CAAC;AAED,IAAA,MAAM,IAAI,GAAoB,CAAC,CAAC,KAC9B,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY;IAEnE,OAAO;AACL,QAAA,QAAQ,EAAE,cAAc;QACxB,YAAY;QACZ,IAAI;QACJ,QAAQ;AACR,QAAA,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC;AACxB,QAAA,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC;AAClC,QAAA,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC;KAC7B;AACH;;;;"}
1
+ {"version":3,"file":"array.mjs","sources":["../../src/array/array.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;MAQa,KAAK,GAAG,CACnB,WAAoB,EACpB,OAKC,KACqB;IAGtB,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,EAAE,EAAE,GAAG,OAAO,IAAI,EAAE;IAErD,MAAM,cAAc,GAAW,QAAQ,IAAI,IAAI,WAAW,CAAC,QAAQ,CAAA,GAAA,CAAK;AAExE,IAAA,MAAM,QAAQ,GAAwB,CAAC,CAAC,KAAI;QAC1C,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;YACnB,OAAO,MAAM,CAAC,GAAG,CAAC;AAChB,gBAAA,8BAA8B,CAAC;AAC7B,oBAAA,WAAW,EAAE,CAAC;AACd,oBAAA,YAAY,EAAE,OAAO;AACrB,oBAAA,QAAQ,EAAE,cAAc;iBACzB,CAAC;AACH,aAAA,CAAC;QACJ;QAEA,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,KAAK,KAAI;YACrC,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;AACpC,YAAA,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG;kBACnB,8BAA8B,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK;kBAC/C,EAAE;AACR,QAAA,CAAC,CAAC;AAEF,QAAA,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;AACrB,YAAA,OAAO,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;QAC3B;;AAGA,QAAA,OAAO,MAAM,CAAC,EAAE,CAAC,CAAM,CAAC;AAC1B,IAAA,CAAC;AAED,IAAA,MAAM,IAAI,GAAoB,CAAC,CAAC,KAC9B,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY;IAEnE,OAAO;AACL,QAAA,QAAQ,EAAE,cAAc;QACxB,YAAY;QACZ,IAAI;QACJ,QAAQ;AACR,QAAA,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC;AACxB,QAAA,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC;AAClC,QAAA,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC;KAC7B;AACH;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"non-empty-array.d.mts","sourceRoot":"","sources":["../../src/array/non-empty-array.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAQxC,eAAO,MAAM,aAAa,GAAI,CAAC,EAC7B,aAAa,IAAI,CAAC,CAAC,CAAC,EACpB,UAAU,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;CACjC,CAAC,KACD,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAsDvB,CAAC"}
1
+ {"version":3,"file":"non-empty-array.d.mts","sourceRoot":"","sources":["../../src/array/non-empty-array.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AASxC,eAAO,MAAM,aAAa,GAAI,CAAC,EAC7B,aAAa,IAAI,CAAC,CAAC,CAAC,EACpB,UAAU,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;CACjC,CAAC,KACD,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CA6DvB,CAAC"}