typesea 0.2.0 → 0.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 (157) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +45 -11
  3. package/dist/aot/index.d.ts +1 -1
  4. package/dist/aot/index.d.ts.map +1 -1
  5. package/dist/aot/index.js +22 -3
  6. package/dist/builders/composite.d.ts +6 -3
  7. package/dist/builders/composite.d.ts.map +1 -1
  8. package/dist/builders/composite.js +22 -13
  9. package/dist/builders/index.d.ts +6 -5
  10. package/dist/builders/index.d.ts.map +1 -1
  11. package/dist/builders/index.js +5 -4
  12. package/dist/builders/modifier.d.ts +6 -0
  13. package/dist/builders/modifier.d.ts.map +1 -1
  14. package/dist/builders/modifier.js +14 -0
  15. package/dist/builders/object/guard.d.ts +54 -2
  16. package/dist/builders/object/guard.d.ts.map +1 -1
  17. package/dist/builders/object/guard.js +117 -7
  18. package/dist/builders/object/index.d.ts +2 -2
  19. package/dist/builders/object/index.d.ts.map +1 -1
  20. package/dist/builders/object/index.js +1 -1
  21. package/dist/builders/object/schema.d.ts +33 -2
  22. package/dist/builders/object/schema.d.ts.map +1 -1
  23. package/dist/builders/object/schema.js +198 -8
  24. package/dist/builders/object/types.d.ts +15 -0
  25. package/dist/builders/object/types.d.ts.map +1 -1
  26. package/dist/builders/runtime.d.ts +40 -0
  27. package/dist/builders/runtime.d.ts.map +1 -0
  28. package/dist/builders/runtime.js +150 -0
  29. package/dist/builders/scalar.d.ts +20 -1
  30. package/dist/builders/scalar.d.ts.map +1 -1
  31. package/dist/builders/scalar.js +54 -1
  32. package/dist/builders/table.d.ts +31 -5
  33. package/dist/builders/table.d.ts.map +1 -1
  34. package/dist/builders/table.js +31 -5
  35. package/dist/builders/types.d.ts +6 -0
  36. package/dist/builders/types.d.ts.map +1 -1
  37. package/dist/compile/check-composite.d.ts +3 -1
  38. package/dist/compile/check-composite.d.ts.map +1 -1
  39. package/dist/compile/check-composite.js +143 -11
  40. package/dist/compile/check-scalar.d.ts +10 -0
  41. package/dist/compile/check-scalar.d.ts.map +1 -1
  42. package/dist/compile/check-scalar.js +138 -2
  43. package/dist/compile/check.d.ts.map +1 -1
  44. package/dist/compile/check.js +25 -3
  45. package/dist/compile/context.d.ts.map +1 -1
  46. package/dist/compile/context.js +2 -0
  47. package/dist/compile/first.d.ts +26 -0
  48. package/dist/compile/first.d.ts.map +1 -0
  49. package/dist/compile/first.js +850 -0
  50. package/dist/compile/graph-predicate.d.ts.map +1 -1
  51. package/dist/compile/graph-predicate.js +389 -18
  52. package/dist/compile/guard.d.ts +2 -1
  53. package/dist/compile/guard.d.ts.map +1 -1
  54. package/dist/compile/guard.js +54 -8
  55. package/dist/compile/predicate.d.ts.map +1 -1
  56. package/dist/compile/predicate.js +156 -5
  57. package/dist/compile/runtime.d.ts +20 -1
  58. package/dist/compile/runtime.d.ts.map +1 -1
  59. package/dist/compile/runtime.js +31 -0
  60. package/dist/compile/source.d.ts.map +1 -1
  61. package/dist/compile/source.js +27 -3
  62. package/dist/compile/types.d.ts +2 -0
  63. package/dist/compile/types.d.ts.map +1 -1
  64. package/dist/decoder/index.d.ts +60 -0
  65. package/dist/decoder/index.d.ts.map +1 -1
  66. package/dist/decoder/index.js +164 -1
  67. package/dist/evaluate/check-composite.d.ts +52 -2
  68. package/dist/evaluate/check-composite.d.ts.map +1 -1
  69. package/dist/evaluate/check-composite.js +193 -6
  70. package/dist/evaluate/check-scalar.d.ts +9 -0
  71. package/dist/evaluate/check-scalar.d.ts.map +1 -1
  72. package/dist/evaluate/check-scalar.js +92 -3
  73. package/dist/evaluate/check.d.ts.map +1 -1
  74. package/dist/evaluate/check.js +19 -4
  75. package/dist/evaluate/shared.d.ts +19 -0
  76. package/dist/evaluate/shared.d.ts.map +1 -1
  77. package/dist/evaluate/shared.js +35 -0
  78. package/dist/guard/array.d.ts +48 -0
  79. package/dist/guard/array.d.ts.map +1 -0
  80. package/dist/guard/array.js +84 -0
  81. package/dist/guard/base.d.ts +32 -2
  82. package/dist/guard/base.d.ts.map +1 -1
  83. package/dist/guard/base.js +74 -3
  84. package/dist/guard/date.d.ts +34 -0
  85. package/dist/guard/date.d.ts.map +1 -0
  86. package/dist/guard/date.js +60 -0
  87. package/dist/guard/index.d.ts +2 -0
  88. package/dist/guard/index.d.ts.map +1 -1
  89. package/dist/guard/index.js +2 -0
  90. package/dist/guard/number.d.ts +60 -0
  91. package/dist/guard/number.d.ts.map +1 -1
  92. package/dist/guard/number.js +129 -0
  93. package/dist/guard/read.d.ts +53 -1
  94. package/dist/guard/read.d.ts.map +1 -1
  95. package/dist/guard/read.js +102 -0
  96. package/dist/guard/string.d.ts +82 -0
  97. package/dist/guard/string.d.ts.map +1 -1
  98. package/dist/guard/string.js +213 -0
  99. package/dist/guard/types.d.ts +18 -0
  100. package/dist/guard/types.d.ts.map +1 -1
  101. package/dist/index.d.ts +4 -4
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +4 -4
  104. package/dist/ir/builder.d.ts +3 -3
  105. package/dist/ir/builder.d.ts.map +1 -1
  106. package/dist/ir/builder.js +5 -2
  107. package/dist/ir/freeze.js +7 -0
  108. package/dist/ir/types.d.ts +4 -1
  109. package/dist/ir/types.d.ts.map +1 -1
  110. package/dist/ir/validate.d.ts.map +1 -1
  111. package/dist/ir/validate.js +35 -1
  112. package/dist/issue/index.d.ts +1 -1
  113. package/dist/issue/index.d.ts.map +1 -1
  114. package/dist/issue/index.js +4 -0
  115. package/dist/json-schema/emit-composite.d.ts +6 -2
  116. package/dist/json-schema/emit-composite.d.ts.map +1 -1
  117. package/dist/json-schema/emit-composite.js +66 -12
  118. package/dist/json-schema/emit-scalar.d.ts.map +1 -1
  119. package/dist/json-schema/emit-scalar.js +54 -1
  120. package/dist/json-schema/emit.d.ts.map +1 -1
  121. package/dist/json-schema/emit.js +11 -2
  122. package/dist/json-schema/types.d.ts +7 -1
  123. package/dist/json-schema/types.d.ts.map +1 -1
  124. package/dist/kind/index.d.ts +25 -0
  125. package/dist/kind/index.d.ts.map +1 -1
  126. package/dist/kind/index.js +26 -3
  127. package/dist/lower/index.d.ts.map +1 -1
  128. package/dist/lower/index.js +52 -3
  129. package/dist/message/index.d.ts +18 -0
  130. package/dist/message/index.d.ts.map +1 -1
  131. package/dist/message/index.js +67 -0
  132. package/dist/optimize/domain.js +6 -2
  133. package/dist/optimize/map-node.d.ts.map +1 -1
  134. package/dist/optimize/map-node.js +3 -0
  135. package/dist/plan/cache.js +13 -1
  136. package/dist/plan/predicate.d.ts.map +1 -1
  137. package/dist/plan/predicate.js +33 -3
  138. package/dist/plan/schema-predicate.d.ts.map +1 -1
  139. package/dist/plan/schema-predicate.js +267 -8
  140. package/dist/schema/freeze.js +22 -0
  141. package/dist/schema/index.d.ts +2 -2
  142. package/dist/schema/index.d.ts.map +1 -1
  143. package/dist/schema/index.js +1 -1
  144. package/dist/schema/types.d.ts +89 -4
  145. package/dist/schema/types.d.ts.map +1 -1
  146. package/dist/schema/types.js +8 -1
  147. package/dist/schema/undefined.d.ts.map +1 -1
  148. package/dist/schema/undefined.js +5 -0
  149. package/dist/schema/validate.d.ts.map +1 -1
  150. package/dist/schema/validate.js +111 -4
  151. package/docs/api.md +71 -8
  152. package/docs/engine-notes.md +4 -0
  153. package/docs/index.html +1340 -722
  154. package/docs/ko/api.md +375 -0
  155. package/docs/ko/engine-notes.md +156 -0
  156. package/docs/ko/readme.md +378 -0
  157. package/package.json +3 -2
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/schema/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoBH,OAAO,KAAK,EAER,MAAM,EAET,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAK7D"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/schema/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsBH,OAAO,KAAK,EAIR,MAAM,EAET,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAK7D"}
@@ -4,7 +4,7 @@
4
4
  * @details Schema helpers enforce construction-time invariants before values reach
5
5
  * validation, compilation, or export.
6
6
  */
7
- import { NumberCheckTag, ObjectModeTag, PresenceTag, SchemaTag, StringCheckTag } from "../kind/index.js";
7
+ import { ArrayCheckTag, DateCheckTag, NumberCheckTag, ObjectModeTag, PresenceTag, SchemaTag, StringCheckTag } from "../kind/index.js";
8
8
  import { isLiteralValue } from "./literal.js";
9
9
  import { includesString, isMissingDataProperty, isObjectKeyLookup, isPlainRegExp, isRecord, isStringArray, isUnknownArray, readOwnDataProperty } from "./common.js";
10
10
  /**
@@ -69,6 +69,8 @@ function isSchemaRecord(value, state) {
69
69
  case SchemaTag.Symbol:
70
70
  case SchemaTag.Boolean:
71
71
  return true;
72
+ case SchemaTag.Date:
73
+ return isDateChecks(readOwnDataProperty(value, "checks"));
72
74
  case SchemaTag.String:
73
75
  return isStringChecks(readOwnDataProperty(value, "checks"));
74
76
  case SchemaTag.Number:
@@ -82,11 +84,25 @@ function isSchemaRecord(value, state) {
82
84
  return !isMissingDataProperty(literal) && isLiteralValue(literal);
83
85
  }
84
86
  case SchemaTag.Array:
85
- return isSchemaValueInner(readOwnDataProperty(value, "item"), state);
87
+ return isSchemaValueInner(readOwnDataProperty(value, "item"), state) &&
88
+ isArrayChecks(readOwnDataProperty(value, "checks"));
86
89
  case SchemaTag.Tuple:
87
- return isSchemaArray(readOwnDataProperty(value, "items"), state);
90
+ return isSchemaArray(readOwnDataProperty(value, "items"), state) &&
91
+ isOptionalSchemaValue(readOwnDataProperty(value, "rest"), state);
88
92
  case SchemaTag.Record:
89
93
  return isSchemaValueInner(readOwnDataProperty(value, "value"), state);
94
+ case SchemaTag.Map:
95
+ return isSchemaValueInner(readOwnDataProperty(value, "key"), state) &&
96
+ isSchemaValueInner(readOwnDataProperty(value, "value"), state);
97
+ case SchemaTag.Set:
98
+ return isSchemaValueInner(readOwnDataProperty(value, "item"), state);
99
+ case SchemaTag.InstanceOf:
100
+ return typeof readOwnDataProperty(value, "constructor") === "function" &&
101
+ typeof readOwnDataProperty(value, "name") === "string";
102
+ case SchemaTag.Property:
103
+ return typeof readOwnDataProperty(value, "key") === "string" &&
104
+ isSchemaValueInner(readOwnDataProperty(value, "base"), state) &&
105
+ isSchemaValueInner(readOwnDataProperty(value, "value"), state);
90
106
  case SchemaTag.Object:
91
107
  return isObjectSchemaValue(value, state);
92
108
  case SchemaTag.Union:
@@ -113,6 +129,15 @@ function isSchemaRecord(value, state) {
113
129
  return false;
114
130
  }
115
131
  }
132
+ /**
133
+ * @brief Validate an optional schema payload.
134
+ * @param value Candidate child schema or undefined.
135
+ * @param state Recursion state for child schemas.
136
+ * @returns True when the value is absent or a valid schema.
137
+ */
138
+ function isOptionalSchemaValue(value, state) {
139
+ return value === undefined || isSchemaValueInner(value, state);
140
+ }
116
141
  /**
117
142
  * @brief Validate the check vector attached to a string schema.
118
143
  * @param value Candidate check vector.
@@ -149,6 +174,13 @@ function isStringChecks(value) {
149
174
  }
150
175
  break;
151
176
  case StringCheckTag.Uuid:
177
+ case StringCheckTag.Email:
178
+ case StringCheckTag.Url:
179
+ case StringCheckTag.IsoDate:
180
+ case StringCheckTag.IsoDateTime:
181
+ case StringCheckTag.Ulid:
182
+ case StringCheckTag.Ipv4:
183
+ case StringCheckTag.Ipv6:
152
184
  break;
153
185
  default:
154
186
  return false;
@@ -176,7 +208,47 @@ function isNumberChecks(value) {
176
208
  case NumberCheckTag.Integer:
177
209
  break;
178
210
  case NumberCheckTag.Gte:
179
- case NumberCheckTag.Lte: {
211
+ case NumberCheckTag.Lte:
212
+ case NumberCheckTag.Gt:
213
+ case NumberCheckTag.Lt: {
214
+ const bound = readOwnDataProperty(check, "value");
215
+ if (typeof bound !== "number" || !Number.isFinite(bound)) {
216
+ return false;
217
+ }
218
+ break;
219
+ }
220
+ case NumberCheckTag.MultipleOf: {
221
+ const divisor = readOwnDataProperty(check, "value");
222
+ if (typeof divisor !== "number" ||
223
+ !Number.isFinite(divisor) ||
224
+ divisor <= 0) {
225
+ return false;
226
+ }
227
+ break;
228
+ }
229
+ default:
230
+ return false;
231
+ }
232
+ }
233
+ return true;
234
+ }
235
+ /**
236
+ * @brief Validate the check vector attached to a Date schema.
237
+ * @param value Candidate check vector.
238
+ * @returns True when every Date bound is finite epoch milliseconds.
239
+ */
240
+ function isDateChecks(value) {
241
+ if (!isUnknownArray(value)) {
242
+ return false;
243
+ }
244
+ for (let index = 0; index < value.length; index += 1) {
245
+ const check = value[index];
246
+ if (!isRecord(check)) {
247
+ return false;
248
+ }
249
+ switch (readOwnDataProperty(check, "tag")) {
250
+ case DateCheckTag.Min:
251
+ case DateCheckTag.Max: {
180
252
  const bound = readOwnDataProperty(check, "value");
181
253
  if (typeof bound !== "number" || !Number.isFinite(bound)) {
182
254
  return false;
@@ -189,6 +261,37 @@ function isNumberChecks(value) {
189
261
  }
190
262
  return true;
191
263
  }
264
+ /**
265
+ * @brief Validate the check vector attached to an array schema.
266
+ * @param value Candidate check vector.
267
+ * @returns True when every array length bound is a non-negative integer.
268
+ * @details Array length checks are admitted once at schema construction so
269
+ * interpreters and code generators can emit direct `length` comparisons later.
270
+ */
271
+ function isArrayChecks(value) {
272
+ if (!isUnknownArray(value)) {
273
+ return false;
274
+ }
275
+ for (let index = 0; index < value.length; index += 1) {
276
+ const check = value[index];
277
+ if (!isRecord(check)) {
278
+ return false;
279
+ }
280
+ switch (readOwnDataProperty(check, "tag")) {
281
+ case ArrayCheckTag.Min:
282
+ case ArrayCheckTag.Max: {
283
+ const bound = readOwnDataProperty(check, "value");
284
+ if (typeof bound !== "number" || !Number.isInteger(bound) || bound < 0) {
285
+ return false;
286
+ }
287
+ break;
288
+ }
289
+ default:
290
+ return false;
291
+ }
292
+ }
293
+ return true;
294
+ }
192
295
  /**
193
296
  * @brief Validate a dense vector of child schemas.
194
297
  * @details Schema helpers enforce construction-time invariants before values reach
@@ -226,10 +329,14 @@ function isObjectSchemaValue(value, state) {
226
329
  const entries = readOwnDataProperty(value, "entries");
227
330
  const keys = readOwnDataProperty(value, "keys");
228
331
  const keyLookup = readOwnDataProperty(value, "keyLookup");
332
+ const catchall = readOwnDataProperty(value, "catchall");
229
333
  if (!isUnknownArray(entries) || !isStringArray(keys) ||
230
334
  !isObjectKeyLookup(keyLookup, keys) || entries.length !== keys.length) {
231
335
  return false;
232
336
  }
337
+ if (catchall !== undefined && !isSchemaValueInner(catchall, state)) {
338
+ return false;
339
+ }
233
340
  const seen = [];
234
341
  for (let index = 0; index < entries.length; index += 1) {
235
342
  const entry = entries[index];
package/docs/api.md CHANGED
@@ -27,6 +27,7 @@ condition.
27
27
  interface Guard<T> {
28
28
  is(value: unknown): value is T;
29
29
  check(value: unknown): CheckResult<T>;
30
+ checkFirst(value: unknown): CheckResult<T>;
30
31
  assert(value: unknown): asserts value is T;
31
32
  graph(): Graph;
32
33
  }
@@ -36,6 +37,7 @@ interface Guard<T> {
36
37
  | --- | --- | --- |
37
38
  | `is` | Hot boolean narrowing | Avoids diagnostic allocation on the success path. |
38
39
  | `check` | Validation with issues | Returns frozen `Result<T, Issue[]>` containers. |
40
+ | `checkFirst` | Hot rejection diagnostics | Returns the same frozen `Result` shape, but failure contains at most one issue. Compiled and AOT guards use a dedicated first-fault collector. |
39
41
  | `assert` | Throwing integration boundaries | Throws `TypeSeaAssertionError` with copied, frozen issues. |
40
42
  | `graph` | Runtime plan introspection | Returns the validated, optimized, frozen Sea-of-Nodes graph held by the validation plan. |
41
43
 
@@ -47,14 +49,19 @@ cross the API boundary.
47
49
 
48
50
  | Family | Builders |
49
51
  | --- | --- |
50
- | Scalars | `t.unknown`, `t.never`, `t.string`, `t.number`, `t.bigint`, `t.symbol`, `t.boolean` |
51
- | Literals and containers | `t.literal(value)`, `t.array(item)`, `t.tuple([a, b])`, `t.record(value)` |
52
+ | Scalars | `t.unknown`, `t.never`, `t.string`, `t.number`, `t.date`, `t.bigint`, `t.symbol`, `t.boolean`, `t.null`, `t.undefined`, `t.void` |
53
+ | String checks | `.min`, `.max`, `.length`, `.nonempty`, `.regex`, `.startsWith`, `.endsWith`, `.includes`, `.uuid`, `.email`, `.url`, `.isoDate`, `.isoDateTime`, `.ulid`, `.ipv4`, `.ipv6` |
54
+ | Number checks | `.int`, `.finite`, `.safe`, `.gte`, `.lte`, `.min`, `.max`, `.gt`, `.lt`, `.multipleOf`, `.positive`, `.nonnegative`, `.negative`, `.nonpositive` |
55
+ | Date checks | `.min`, `.max` |
56
+ | Literals and containers | `t.literal(value)`, `t.enum(values)`, `t.array(item)`, `t.tuple([a, b])`, `t.tuple([head], rest)`, `t.record(value)`, `t.map(key, value)`, `t.set(item)`, `t.json()` |
57
+ | Array checks | `.min`, `.max`, `.length`, `.nonempty` |
52
58
  | Objects | `t.object(shape)`, `t.strictObject(shape)` |
53
- | Object transforms | `t.extend`, `t.pick`, `t.omit`, `t.partial`, and matching object guard methods |
59
+ | Object transforms | `t.extend`, `t.safeExtend`, `t.merge`, `t.pick`, `t.omit`, `t.partial`, `t.deepPartial`, `t.required`, `t.strict`, `t.passthrough`, `t.strip`, `t.catchall`, and matching object guard methods |
60
+ | Runtime object contracts | `t.instanceOf(Ctor)`, `t.property(base, key, value)`, `guard.property(key, value)` |
54
61
  | Composition | `t.union`, `t.discriminatedUnion`, `t.intersect`, `guard.intersect` |
55
- | Presence | `t.optional`, `t.undefinedable`, `t.nullable` |
62
+ | Presence | `t.optional`, `t.undefinedable`, `t.nullable`, `t.nullish` |
56
63
  | Dynamic guards | `t.lazy`, `t.refine` |
57
- | Decoders | `t.decoder`, `t.transform`, `t.pipe`, `t.coerce` |
64
+ | Decoders | `t.decoder`, `t.transform`, `t.pipe`, `t.default`, `t.defaultValue`, `t.prefault`, `t.catch`, `t.codec`, `t.coerce`, `t.string.trim()`, `t.string.toLowerCase()`, `t.string.toUpperCase()` |
58
65
  | Async decoders | `t.asyncDecoder`, `t.asyncRefine`, `t.asyncTransform`, `t.asyncPipe` |
59
66
 
60
67
  Builder functions validate inputs before a schema can enter the validation plan,
@@ -80,6 +87,8 @@ const Shape = t.object({
80
87
  - `name` may be absent. If `name` exists, its value must be a string.
81
88
  - `nickname` must be present. Its value may be a string or `undefined`.
82
89
  - `t.nullable(inner)` adds `null` to the value domain.
90
+ - `t.nullish(inner)` combines nullable value semantics with optional object-key
91
+ presence.
83
92
  - Presence-preserving wrappers keep optional-key semantics through `nullable`,
84
93
  `undefinedable`, `brand`, and `refine`.
85
94
 
@@ -87,6 +96,18 @@ Object combinators preserve object mode. Strict object guards remain strict
87
96
  after `extend`, `pick`, `omit`, or `partial`; passthrough object guards keep
88
97
  allowing unknown keys.
89
98
 
99
+ `catchall(schema)` validates every undeclared own key with `schema`.
100
+ `strip()` is validation-only in TypeSea: guards return the original value, so it
101
+ has the same validation behavior as `passthrough()`.
102
+ `pick` and `omit` accept either key arrays or Zod-style `{ key: true }` masks.
103
+ `deepPartial()` recursively partializes pure object, array, tuple, tuple rest,
104
+ record, map, set, property, union, intersection, nullable, undefinedable,
105
+ optional, and brand schemas. Lazy and refinement schemas are semantic barriers.
106
+
107
+ `property` validates only own data descriptors. It is useful for class instances
108
+ with stable fields; prototype getters and accessor-backed properties are rejected
109
+ instead of executed.
110
+
90
111
  ## Composition
91
112
 
92
113
  `t.union(a, b)` accepts a value that satisfies at least one branch.
@@ -125,6 +146,16 @@ lazy chain must eventually resolve to a concrete non-lazy schema.
125
146
  ```ts
126
147
  const Count = t.pipe(t.coerce.number(), t.number.int().gte(0));
127
148
  const result = Count.decode("42");
149
+
150
+ const Name = t.default(t.string.min(1), "anonymous");
151
+ const NumberText = t.codec(
152
+ t.string.regex(/^\d+$/u, "digits"),
153
+ t.number.int().nonnegative(),
154
+ {
155
+ decode: (value) => Number(value),
156
+ encode: (value) => String(value)
157
+ }
158
+ );
128
159
  ```
129
160
 
130
161
  Decoders are for output-producing operations. They return `Result` from
@@ -134,8 +165,15 @@ not be the same runtime value as the input.
134
165
  - `t.transform(source, mapper)` decodes `source`, then maps the decoded value.
135
166
  - `t.pipe(source, next)` feeds a successful decoded value into the next guard or
136
167
  decoder.
168
+ - `t.default(source, value)` returns a fallback output for `undefined` input.
169
+ - `t.prefault(source, value)` feeds a fallback input through the source.
170
+ - `t.codec(input, output, mapping)` validates both sides of a bidirectional
171
+ decode/encode pair.
137
172
  - `t.coerce.string`, `t.coerce.number`, and `t.coerce.boolean` provide explicit
138
173
  primitive coercion.
174
+ - `t.string.trim()`, `t.string.toLowerCase()`, and `t.string.toUpperCase()`
175
+ are decoder helpers. They validate the string first, then return transformed
176
+ output from `decode()`.
139
177
  - `t.asyncRefine`, `t.asyncTransform`, and `t.asyncPipe` return
140
178
  `Promise<Result<T, Issue[]>>` from `decodeAsync()`.
141
179
 
@@ -152,14 +190,16 @@ const checked = withMessages(User.check(input), {
152
190
  });
153
191
  ```
154
192
 
155
- `formatIssue`, `formatIssues`, and `withMessages` render diagnostics after
156
- validation has finished. This keeps `is()` and ordinary `check()` paths free from
157
- message allocation.
193
+ `formatIssue`, `formatIssues`, `flattenIssues`, and `withMessages` render
194
+ diagnostics after validation has finished. This keeps `is()` and ordinary
195
+ `check()` paths free from message allocation.
158
196
 
159
197
  Built-in locales are `en` and `ko`. Custom catalogs can use string templates
160
198
  with `{path}`, `{code}`, `{expected}`, and `{actual}`, or formatter callbacks.
161
199
  `withMessages(result, options)` preserves successful results and returns a new
162
200
  failed `Result` with copied, frozen issues whose `message` fields are populated.
201
+ `flattenIssues(issues, options)` groups rendered messages into `formErrors` and
202
+ top-level `fieldErrors` buckets.
163
203
 
164
204
  ## Runtime Compile
165
205
 
@@ -270,6 +310,28 @@ const resolver = toReactHookFormResolver(User);
270
310
  Adapters are structural and zero-dependency. TypeSea does not import tRPC,
271
311
  Fastify, or React Hook Form.
272
312
 
313
+ Compiled guards can be passed to the same adapters. This is the preferred shape
314
+ for hot request paths: compile once during startup, then let the adapter reuse
315
+ the generated predicate.
316
+
317
+ ```ts
318
+ const FastUser = compile(User);
319
+ const fastParser = toTrpcParser(FastUser);
320
+ const fastValidatorCompiler = toFastifyValidatorCompiler(FastUser);
321
+ ```
322
+
323
+ Use the default compiled mode at public input boundaries. For trusted,
324
+ already-normalized internal data, the faster modes can be wired through adapters
325
+ the same way.
326
+
327
+ ```ts
328
+ const UnsafeUser = compile(User, { mode: "unsafe" });
329
+ const internalParser = toTrpcParser(UnsafeUser);
330
+
331
+ const TrustedShapeUser = compile(User, { mode: "unchecked" });
332
+ const internalValidatorCompiler = toFastifyValidatorCompiler(TrustedShapeUser);
333
+ ```
334
+
273
335
  | Adapter | Export | Behavior |
274
336
  | --- | --- | --- |
275
337
  | tRPC | `toTrpcParser`, `toAsyncTrpcParser` | Return parser objects that emit decoded values or throw `TypeSeaAssertionError`. |
@@ -314,6 +376,7 @@ Runtime-only concepts return explicit export issues:
314
376
  - `undefined`
315
377
  - `bigint`
316
378
  - `symbol`
379
+ - JavaScript `Date`, `Map`, `Set`, `instanceOf`, and `property` contracts
317
380
  - `lazy`
318
381
  - `refine`
319
382
  - decoder transforms
@@ -154,6 +154,10 @@ still checking the original object fields on the outer frame.
154
154
  Compiled `lazy` and `refine` fallbacks use the same IR-backed runtime path, so
155
155
  recursive behavior stays consistent across execution engines.
156
156
 
157
+ `checkFirst()` has a separate generated collector. It returns one frozen issue
158
+ as soon as the first diagnostic is known, instead of running the full `check()`
159
+ collector and truncating its issue array.
160
+
157
161
  ## JSON Schema Export
158
162
 
159
163
  JSON Schema export succeeds only when the TypeSea schema can be represented over