typesea 0.1.0 → 0.2.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 (285) hide show
  1. package/CHANGELOG.md +67 -6
  2. package/README.md +98 -17
  3. package/dist/adapters/index.d.ts +50 -8
  4. package/dist/adapters/index.d.ts.map +1 -1
  5. package/dist/adapters/index.js +169 -48
  6. package/dist/aot/index.d.ts +18 -2
  7. package/dist/aot/index.d.ts.map +1 -1
  8. package/dist/aot/index.js +93 -14
  9. package/dist/async/index.d.ts +28 -56
  10. package/dist/async/index.d.ts.map +1 -1
  11. package/dist/async/index.js +94 -37
  12. package/dist/builders/composite.d.ts +37 -6
  13. package/dist/builders/composite.d.ts.map +1 -1
  14. package/dist/builders/composite.js +84 -10
  15. package/dist/builders/index.d.ts +2 -0
  16. package/dist/builders/index.d.ts.map +1 -1
  17. package/dist/builders/index.js +2 -0
  18. package/dist/builders/modifier.d.ts +30 -5
  19. package/dist/builders/modifier.d.ts.map +1 -1
  20. package/dist/builders/modifier.js +38 -5
  21. package/dist/builders/object/guard.d.ts +18 -22
  22. package/dist/builders/object/guard.d.ts.map +1 -1
  23. package/dist/builders/object/guard.js +26 -26
  24. package/dist/builders/object/index.d.ts +2 -0
  25. package/dist/builders/object/index.d.ts.map +1 -1
  26. package/dist/builders/object/index.js +2 -0
  27. package/dist/builders/object/schema.d.ts +55 -9
  28. package/dist/builders/object/schema.d.ts.map +1 -1
  29. package/dist/builders/object/schema.js +92 -15
  30. package/dist/builders/object/types.d.ts +5 -31
  31. package/dist/builders/object/types.d.ts.map +1 -1
  32. package/dist/builders/object/types.js +2 -0
  33. package/dist/builders/scalar.d.ts +29 -8
  34. package/dist/builders/scalar.d.ts.map +1 -1
  35. package/dist/builders/scalar.js +33 -8
  36. package/dist/builders/table.d.ts +4 -0
  37. package/dist/builders/table.d.ts.map +1 -1
  38. package/dist/builders/table.js +4 -0
  39. package/dist/builders/types.d.ts +14 -4
  40. package/dist/builders/types.d.ts.map +1 -1
  41. package/dist/builders/types.js +2 -0
  42. package/dist/compile/check-composite.d.ts +22 -1
  43. package/dist/compile/check-composite.d.ts.map +1 -1
  44. package/dist/compile/check-composite.js +564 -24
  45. package/dist/compile/check-scalar.d.ts +78 -0
  46. package/dist/compile/check-scalar.d.ts.map +1 -1
  47. package/dist/compile/check-scalar.js +432 -1
  48. package/dist/compile/check.d.ts +12 -0
  49. package/dist/compile/check.d.ts.map +1 -1
  50. package/dist/compile/check.js +37 -0
  51. package/dist/compile/context.d.ts +47 -9
  52. package/dist/compile/context.d.ts.map +1 -1
  53. package/dist/compile/context.js +51 -8
  54. package/dist/compile/graph-predicate.d.ts +4 -2
  55. package/dist/compile/graph-predicate.d.ts.map +1 -1
  56. package/dist/compile/graph-predicate.js +1907 -171
  57. package/dist/compile/guard.d.ts +15 -24
  58. package/dist/compile/guard.d.ts.map +1 -1
  59. package/dist/compile/guard.js +158 -74
  60. package/dist/compile/index.d.ts +3 -1
  61. package/dist/compile/index.d.ts.map +1 -1
  62. package/dist/compile/index.js +2 -0
  63. package/dist/compile/issue.d.ts +110 -0
  64. package/dist/compile/issue.d.ts.map +1 -1
  65. package/dist/compile/issue.js +184 -1
  66. package/dist/compile/names.d.ts +12 -2
  67. package/dist/compile/names.d.ts.map +1 -1
  68. package/dist/compile/names.js +19 -3
  69. package/dist/compile/predicate.d.ts +24 -0
  70. package/dist/compile/predicate.d.ts.map +1 -1
  71. package/dist/compile/predicate.js +131 -5
  72. package/dist/compile/runtime.d.ts +80 -12
  73. package/dist/compile/runtime.d.ts.map +1 -1
  74. package/dist/compile/runtime.js +25 -6
  75. package/dist/compile/source.d.ts +10 -2
  76. package/dist/compile/source.d.ts.map +1 -1
  77. package/dist/compile/source.js +361 -26
  78. package/dist/compile/types.d.ts +20 -0
  79. package/dist/compile/types.d.ts.map +1 -1
  80. package/dist/compile/types.js +2 -0
  81. package/dist/decoder/index.d.ts +32 -46
  82. package/dist/decoder/index.d.ts.map +1 -1
  83. package/dist/decoder/index.js +102 -38
  84. package/dist/evaluate/check-composite.d.ts +59 -0
  85. package/dist/evaluate/check-composite.d.ts.map +1 -1
  86. package/dist/evaluate/check-composite.js +151 -3
  87. package/dist/evaluate/check-scalar.d.ts +16 -0
  88. package/dist/evaluate/check-scalar.d.ts.map +1 -1
  89. package/dist/evaluate/check-scalar.js +32 -0
  90. package/dist/evaluate/check.d.ts +7 -0
  91. package/dist/evaluate/check.d.ts.map +1 -1
  92. package/dist/evaluate/check.js +43 -0
  93. package/dist/evaluate/index.d.ts +2 -0
  94. package/dist/evaluate/index.d.ts.map +1 -1
  95. package/dist/evaluate/index.js +2 -0
  96. package/dist/evaluate/issue.d.ts +11 -1
  97. package/dist/evaluate/issue.d.ts.map +1 -1
  98. package/dist/evaluate/issue.js +15 -1
  99. package/dist/evaluate/predicate.d.ts +16 -5
  100. package/dist/evaluate/predicate.d.ts.map +1 -1
  101. package/dist/evaluate/predicate.js +20 -5
  102. package/dist/evaluate/shared.d.ts +59 -13
  103. package/dist/evaluate/shared.d.ts.map +1 -1
  104. package/dist/evaluate/shared.js +66 -8
  105. package/dist/evaluate/state.d.ts +35 -13
  106. package/dist/evaluate/state.d.ts.map +1 -1
  107. package/dist/evaluate/state.js +35 -2
  108. package/dist/guard/base.d.ts +79 -29
  109. package/dist/guard/base.d.ts.map +1 -1
  110. package/dist/guard/base.js +91 -29
  111. package/dist/guard/error.d.ts +10 -5
  112. package/dist/guard/error.d.ts.map +1 -1
  113. package/dist/guard/error.js +10 -5
  114. package/dist/guard/index.d.ts +2 -0
  115. package/dist/guard/index.d.ts.map +1 -1
  116. package/dist/guard/index.js +2 -0
  117. package/dist/guard/number.d.ts +26 -11
  118. package/dist/guard/number.d.ts.map +1 -1
  119. package/dist/guard/number.js +30 -11
  120. package/dist/guard/props.d.ts +27 -3
  121. package/dist/guard/props.d.ts.map +1 -1
  122. package/dist/guard/props.js +27 -3
  123. package/dist/guard/read.d.ts +62 -9
  124. package/dist/guard/read.d.ts.map +1 -1
  125. package/dist/guard/read.js +83 -10
  126. package/dist/guard/registry.d.ts +12 -2
  127. package/dist/guard/registry.d.ts.map +1 -1
  128. package/dist/guard/registry.js +15 -3
  129. package/dist/guard/string.d.ts +33 -13
  130. package/dist/guard/string.d.ts.map +1 -1
  131. package/dist/guard/string.js +37 -13
  132. package/dist/guard/types.d.ts +92 -40
  133. package/dist/guard/types.d.ts.map +1 -1
  134. package/dist/guard/types.js +2 -0
  135. package/dist/index.d.ts +1 -1
  136. package/dist/index.d.ts.map +1 -1
  137. package/dist/internal/index.d.ts +42 -6
  138. package/dist/internal/index.d.ts.map +1 -1
  139. package/dist/internal/index.js +51 -8
  140. package/dist/ir/builder.d.ts +16 -126
  141. package/dist/ir/builder.d.ts.map +1 -1
  142. package/dist/ir/builder.js +77 -137
  143. package/dist/ir/freeze.d.ts +4 -0
  144. package/dist/ir/freeze.d.ts.map +1 -1
  145. package/dist/ir/freeze.js +59 -0
  146. package/dist/ir/index.d.ts +3 -1
  147. package/dist/ir/index.d.ts.map +1 -1
  148. package/dist/ir/index.js +2 -0
  149. package/dist/ir/regexp.d.ts +2 -0
  150. package/dist/ir/regexp.d.ts.map +1 -1
  151. package/dist/ir/regexp.js +2 -0
  152. package/dist/ir/types.d.ts +90 -55
  153. package/dist/ir/types.d.ts.map +1 -1
  154. package/dist/ir/types.js +2 -0
  155. package/dist/ir/validate.d.ts +8 -1
  156. package/dist/ir/validate.d.ts.map +1 -1
  157. package/dist/ir/validate.js +477 -61
  158. package/dist/issue/index.d.ts +41 -9
  159. package/dist/issue/index.d.ts.map +1 -1
  160. package/dist/issue/index.js +61 -11
  161. package/dist/json-schema/emit-combinator.d.ts +44 -4
  162. package/dist/json-schema/emit-combinator.d.ts.map +1 -1
  163. package/dist/json-schema/emit-combinator.js +44 -4
  164. package/dist/json-schema/emit-composite.d.ts +10 -0
  165. package/dist/json-schema/emit-composite.d.ts.map +1 -1
  166. package/dist/json-schema/emit-composite.js +15 -1
  167. package/dist/json-schema/emit-scalar.d.ts +26 -3
  168. package/dist/json-schema/emit-scalar.d.ts.map +1 -1
  169. package/dist/json-schema/emit-scalar.js +70 -9
  170. package/dist/json-schema/emit-types.d.ts +11 -1
  171. package/dist/json-schema/emit-types.d.ts.map +1 -1
  172. package/dist/json-schema/emit-types.js +2 -0
  173. package/dist/json-schema/emit.d.ts +12 -1
  174. package/dist/json-schema/emit.d.ts.map +1 -1
  175. package/dist/json-schema/emit.js +12 -1
  176. package/dist/json-schema/freeze.d.ts +13 -2
  177. package/dist/json-schema/freeze.d.ts.map +1 -1
  178. package/dist/json-schema/freeze.js +41 -8
  179. package/dist/json-schema/index.d.ts +16 -2
  180. package/dist/json-schema/index.d.ts.map +1 -1
  181. package/dist/json-schema/index.js +23 -3
  182. package/dist/json-schema/issue.d.ts +4 -1
  183. package/dist/json-schema/issue.d.ts.map +1 -1
  184. package/dist/json-schema/issue.js +4 -1
  185. package/dist/json-schema/read.d.ts +24 -3
  186. package/dist/json-schema/read.d.ts.map +1 -1
  187. package/dist/json-schema/read.js +59 -12
  188. package/dist/json-schema/types.d.ts +38 -15
  189. package/dist/json-schema/types.d.ts.map +1 -1
  190. package/dist/json-schema/types.js +2 -0
  191. package/dist/kind/index.d.ts +15 -28
  192. package/dist/kind/index.d.ts.map +1 -1
  193. package/dist/kind/index.js +15 -10
  194. package/dist/lower/index.d.ts +6 -1
  195. package/dist/lower/index.d.ts.map +1 -1
  196. package/dist/lower/index.js +411 -44
  197. package/dist/message/index.d.ts +46 -10
  198. package/dist/message/index.d.ts.map +1 -1
  199. package/dist/message/index.js +88 -17
  200. package/dist/optimize/algebraic.d.ts +54 -0
  201. package/dist/optimize/algebraic.d.ts.map +1 -0
  202. package/dist/optimize/algebraic.js +314 -0
  203. package/dist/optimize/compact.d.ts +8 -1
  204. package/dist/optimize/compact.d.ts.map +1 -1
  205. package/dist/optimize/compact.js +13 -2
  206. package/dist/optimize/domain.d.ts +16 -0
  207. package/dist/optimize/domain.d.ts.map +1 -0
  208. package/dist/optimize/domain.js +615 -0
  209. package/dist/optimize/fold-boolean.d.ts +17 -2
  210. package/dist/optimize/fold-boolean.d.ts.map +1 -1
  211. package/dist/optimize/fold-boolean.js +59 -14
  212. package/dist/optimize/fold-common.d.ts +43 -8
  213. package/dist/optimize/fold-common.d.ts.map +1 -1
  214. package/dist/optimize/fold-common.js +37 -6
  215. package/dist/optimize/fold-constraints.d.ts +33 -0
  216. package/dist/optimize/fold-constraints.d.ts.map +1 -0
  217. package/dist/optimize/fold-constraints.js +484 -0
  218. package/dist/optimize/fold-scalar.d.ts +98 -13
  219. package/dist/optimize/fold-scalar.d.ts.map +1 -1
  220. package/dist/optimize/fold-scalar.js +98 -13
  221. package/dist/optimize/fold.d.ts +8 -1
  222. package/dist/optimize/fold.d.ts.map +1 -1
  223. package/dist/optimize/fold.js +22 -2
  224. package/dist/optimize/index.d.ts +9 -1
  225. package/dist/optimize/index.d.ts.map +1 -1
  226. package/dist/optimize/index.js +18 -3
  227. package/dist/optimize/map-node.d.ts +3 -1
  228. package/dist/optimize/map-node.d.ts.map +1 -1
  229. package/dist/optimize/map-node.js +45 -3
  230. package/dist/optimize/peephole.d.ts +16 -0
  231. package/dist/optimize/peephole.d.ts.map +1 -0
  232. package/dist/optimize/peephole.js +254 -0
  233. package/dist/optimize/remap.d.ts +2 -0
  234. package/dist/optimize/remap.d.ts.map +1 -1
  235. package/dist/optimize/remap.js +2 -0
  236. package/dist/optimize/rewrite.d.ts +13 -8
  237. package/dist/optimize/rewrite.d.ts.map +1 -1
  238. package/dist/optimize/rewrite.js +13 -8
  239. package/dist/plan/cache.d.ts +9 -3
  240. package/dist/plan/cache.d.ts.map +1 -1
  241. package/dist/plan/cache.js +21 -5
  242. package/dist/plan/index.d.ts +2 -0
  243. package/dist/plan/index.d.ts.map +1 -1
  244. package/dist/plan/index.js +2 -0
  245. package/dist/plan/predicate.d.ts +2 -0
  246. package/dist/plan/predicate.d.ts.map +1 -1
  247. package/dist/plan/predicate.js +268 -29
  248. package/dist/plan/schema-predicate.d.ts +6 -0
  249. package/dist/plan/schema-predicate.d.ts.map +1 -1
  250. package/dist/plan/schema-predicate.js +117 -13
  251. package/dist/plan/types.d.ts +2 -0
  252. package/dist/plan/types.d.ts.map +1 -1
  253. package/dist/plan/types.js +2 -0
  254. package/dist/result/index.d.ts +19 -5
  255. package/dist/result/index.d.ts.map +1 -1
  256. package/dist/result/index.js +10 -2
  257. package/dist/schema/common.d.ts +69 -6
  258. package/dist/schema/common.d.ts.map +1 -1
  259. package/dist/schema/common.js +104 -10
  260. package/dist/schema/freeze.d.ts +4 -0
  261. package/dist/schema/freeze.d.ts.map +1 -1
  262. package/dist/schema/freeze.js +18 -0
  263. package/dist/schema/index.d.ts +3 -0
  264. package/dist/schema/index.d.ts.map +1 -1
  265. package/dist/schema/index.js +3 -0
  266. package/dist/schema/lazy.d.ts +4 -0
  267. package/dist/schema/lazy.d.ts.map +1 -1
  268. package/dist/schema/lazy.js +4 -0
  269. package/dist/schema/literal.d.ts +7 -1
  270. package/dist/schema/literal.d.ts.map +1 -1
  271. package/dist/schema/literal.js +7 -1
  272. package/dist/schema/types.d.ts +20 -96
  273. package/dist/schema/types.d.ts.map +1 -1
  274. package/dist/schema/types.js +5 -1
  275. package/dist/schema/undefined.d.ts +17 -0
  276. package/dist/schema/undefined.d.ts.map +1 -0
  277. package/dist/schema/undefined.js +72 -0
  278. package/dist/schema/validate.d.ts +8 -1
  279. package/dist/schema/validate.d.ts.map +1 -1
  280. package/dist/schema/validate.js +146 -55
  281. package/docs/api.md +57 -0
  282. package/docs/assets/benchmark-headline.svg +163 -0
  283. package/docs/engine-notes.md +58 -15
  284. package/docs/index.html +130 -110
  285. package/package.json +65 -65
@@ -1,12 +1,19 @@
1
1
  /**
2
2
  * @file schema/validate.ts
3
3
  * @brief Runtime validators for direct schema objects.
4
+ * @details Schema helpers enforce construction-time invariants before values reach
5
+ * validation, compilation, or export.
4
6
  */
5
7
  import { NumberCheckTag, ObjectModeTag, PresenceTag, SchemaTag, StringCheckTag } from "../kind/index.js";
6
8
  import { isLiteralValue } from "./literal.js";
7
- import { includesString, isObjectKeyLookup, isPlainRegExp, isRecord, isStringArray, isUnknownArray } from "./common.js";
9
+ import { includesString, isMissingDataProperty, isObjectKeyLookup, isPlainRegExp, isRecord, isStringArray, isUnknownArray, readOwnDataProperty } from "./common.js";
8
10
  /**
9
- * @brief is schema value.
11
+ * @brief Validate an unknown value as a TypeSea schema tree.
12
+ * @param value Candidate schema object from a public boundary.
13
+ * @returns True when the complete tree satisfies the internal schema layout.
14
+ * @details This routine is intentionally stricter than normal JavaScript object
15
+ * access: every record and vector must be data-only so later consumers can read
16
+ * fields without invoking user code.
10
17
  */
11
18
  export function isSchemaValue(value) {
12
19
  return isSchemaValueInner(value, {
@@ -15,7 +22,12 @@ export function isSchemaValue(value) {
15
22
  });
16
23
  }
17
24
  /**
18
- * @brief is schema value inner.
25
+ * @brief Validate one schema node with cycle protection.
26
+ * @details Schema helpers enforce construction-time invariants before values reach
27
+ * validation, compilation, or export.
28
+ * @param value Candidate node.
29
+ * @param state Recursion state shared by the root validation pass.
30
+ * @returns True when this node and its reachable children are well-formed.
19
31
  */
20
32
  function isSchemaValueInner(value, state) {
21
33
  if (!isRecord(value)) {
@@ -28,6 +40,10 @@ function isSchemaValueInner(value, state) {
28
40
  return false;
29
41
  }
30
42
  state.visiting.add(value);
43
+ /*
44
+ * The node is placed in `visiting` before child traversal so recursive
45
+ * object graphs fail closed instead of creating an infinite walk.
46
+ */
31
47
  const valid = isSchemaRecord(value, state);
32
48
  state.visiting.delete(value);
33
49
  if (valid) {
@@ -36,10 +52,17 @@ function isSchemaValueInner(value, state) {
36
52
  return valid;
37
53
  }
38
54
  /**
39
- * @brief is schema record.
55
+ * @brief Dispatch validation by schema tag.
56
+ * @param value Data-only record already accepted by isRecord.
57
+ * @param state Recursion state for child schemas.
58
+ * @returns True when the tag-specific payload is well-formed.
59
+ * @details Tag and payload fields are read through readOwnDataProperty. This
60
+ * preserves the rule that forged schema prototypes never participate in schema
61
+ * admission.
40
62
  */
41
63
  function isSchemaRecord(value, state) {
42
- switch (value["tag"]) {
64
+ const tag = readOwnDataProperty(value, "tag");
65
+ switch (tag) {
43
66
  case SchemaTag.Unknown:
44
67
  case SchemaTag.Never:
45
68
  case SchemaTag.BigInt:
@@ -47,45 +70,55 @@ function isSchemaRecord(value, state) {
47
70
  case SchemaTag.Boolean:
48
71
  return true;
49
72
  case SchemaTag.String:
50
- return isStringChecks(value["checks"]);
73
+ return isStringChecks(readOwnDataProperty(value, "checks"));
51
74
  case SchemaTag.Number:
52
- return isNumberChecks(value["checks"]);
53
- case SchemaTag.Literal:
54
- return isLiteralValue(value["value"]);
75
+ return isNumberChecks(readOwnDataProperty(value, "checks"));
76
+ case SchemaTag.Literal: {
77
+ const literal = readOwnDataProperty(value, "value");
78
+ /*
79
+ * `undefined` is a legal literal payload. The sentinel distinguishes
80
+ * a missing `value` field from a stored undefined literal.
81
+ */
82
+ return !isMissingDataProperty(literal) && isLiteralValue(literal);
83
+ }
55
84
  case SchemaTag.Array:
56
- return isSchemaValueInner(value["item"], state);
85
+ return isSchemaValueInner(readOwnDataProperty(value, "item"), state);
57
86
  case SchemaTag.Tuple:
58
- return isSchemaArray(value["items"], state);
87
+ return isSchemaArray(readOwnDataProperty(value, "items"), state);
59
88
  case SchemaTag.Record:
60
- return isSchemaValueInner(value["value"], state);
89
+ return isSchemaValueInner(readOwnDataProperty(value, "value"), state);
61
90
  case SchemaTag.Object:
62
91
  return isObjectSchemaValue(value, state);
63
92
  case SchemaTag.Union:
64
- return isSchemaArray(value["options"], state);
93
+ return isSchemaArray(readOwnDataProperty(value, "options"), state);
65
94
  case SchemaTag.Intersection:
66
- return isSchemaValueInner(value["left"], state) &&
67
- isSchemaValueInner(value["right"], state);
95
+ return isSchemaValueInner(readOwnDataProperty(value, "left"), state) &&
96
+ isSchemaValueInner(readOwnDataProperty(value, "right"), state);
68
97
  case SchemaTag.Optional:
69
98
  case SchemaTag.Undefinedable:
70
99
  case SchemaTag.Nullable:
71
- return isSchemaValueInner(value["inner"], state);
100
+ return isSchemaValueInner(readOwnDataProperty(value, "inner"), state);
72
101
  case SchemaTag.DiscriminatedUnion:
73
102
  return isDiscriminatedUnionSchemaValue(value, state);
74
103
  case SchemaTag.Brand:
75
- return typeof value["brand"] === "string" &&
76
- isSchemaValueInner(value["inner"], state);
104
+ return typeof readOwnDataProperty(value, "brand") === "string" &&
105
+ isSchemaValueInner(readOwnDataProperty(value, "inner"), state);
77
106
  case SchemaTag.Lazy:
78
- return typeof value["get"] === "function";
107
+ return typeof readOwnDataProperty(value, "get") === "function";
79
108
  case SchemaTag.Refine:
80
- return typeof value["name"] === "string" &&
81
- typeof value["predicate"] === "function" &&
82
- isSchemaValueInner(value["inner"], state);
109
+ return typeof readOwnDataProperty(value, "name") === "string" &&
110
+ typeof readOwnDataProperty(value, "predicate") === "function" &&
111
+ isSchemaValueInner(readOwnDataProperty(value, "inner"), state);
83
112
  default:
84
113
  return false;
85
114
  }
86
115
  }
87
116
  /**
88
- * @brief is string checks.
117
+ * @brief Validate the check vector attached to a string schema.
118
+ * @param value Candidate check vector.
119
+ * @returns True when every check is data-only and semantically usable.
120
+ * @details Bounds are constrained at admission so interpreter and codegen do not
121
+ * need to repeat defensive numeric checks in their hot paths.
89
122
  */
90
123
  function isStringChecks(value) {
91
124
  if (!isUnknownArray(value)) {
@@ -96,17 +129,22 @@ function isStringChecks(value) {
96
129
  if (!isRecord(check)) {
97
130
  return false;
98
131
  }
99
- switch (check["tag"]) {
132
+ switch (readOwnDataProperty(check, "tag")) {
100
133
  case StringCheckTag.Min:
101
134
  case StringCheckTag.Max: {
102
- const bound = check["value"];
135
+ const bound = readOwnDataProperty(check, "value");
103
136
  if (typeof bound !== "number" || !Number.isInteger(bound) || bound < 0) {
104
137
  return false;
105
138
  }
106
139
  break;
107
140
  }
108
141
  case StringCheckTag.Regex:
109
- if (!isPlainRegExp(check["regex"]) || typeof check["name"] !== "string") {
142
+ /*
143
+ * Regex checks carry executable engine state. Only a plain
144
+ * RegExp plus a stable diagnostic name can enter the schema.
145
+ */
146
+ if (!isPlainRegExp(readOwnDataProperty(check, "regex")) ||
147
+ typeof readOwnDataProperty(check, "name") !== "string") {
110
148
  return false;
111
149
  }
112
150
  break;
@@ -119,7 +157,11 @@ function isStringChecks(value) {
119
157
  return true;
120
158
  }
121
159
  /**
122
- * @brief is number checks.
160
+ * @brief Validate the check vector attached to a number schema.
161
+ * @details Schema helpers enforce construction-time invariants before values reach
162
+ * validation, compilation, or export.
163
+ * @param value Candidate check vector.
164
+ * @returns True when all numeric bounds are finite and tag-compatible.
123
165
  */
124
166
  function isNumberChecks(value) {
125
167
  if (!isUnknownArray(value)) {
@@ -130,12 +172,12 @@ function isNumberChecks(value) {
130
172
  if (!isRecord(check)) {
131
173
  return false;
132
174
  }
133
- switch (check["tag"]) {
175
+ switch (readOwnDataProperty(check, "tag")) {
134
176
  case NumberCheckTag.Integer:
135
177
  break;
136
178
  case NumberCheckTag.Gte:
137
179
  case NumberCheckTag.Lte: {
138
- const bound = check["value"];
180
+ const bound = readOwnDataProperty(check, "value");
139
181
  if (typeof bound !== "number" || !Number.isFinite(bound)) {
140
182
  return false;
141
183
  }
@@ -148,7 +190,12 @@ function isNumberChecks(value) {
148
190
  return true;
149
191
  }
150
192
  /**
151
- * @brief is schema array.
193
+ * @brief Validate a dense vector of child schemas.
194
+ * @details Schema helpers enforce construction-time invariants before values reach
195
+ * validation, compilation, or export.
196
+ * @param value Candidate schema vector.
197
+ * @param state Recursion state shared with the parent node.
198
+ * @returns True when every vector slot is a valid schema.
152
199
  */
153
200
  function isSchemaArray(value, state) {
154
201
  if (!isUnknownArray(value)) {
@@ -162,16 +209,23 @@ function isSchemaArray(value, state) {
162
209
  return true;
163
210
  }
164
211
  /**
165
- * @brief is object schema value.
212
+ * @brief Validate object-schema payload invariants.
213
+ * @param value Candidate object schema record.
214
+ * @param state Recursion state for property schemas.
215
+ * @returns True when entries, key order, lookup, and presence metadata agree.
216
+ * @details Object validation depends on three redundant views: ordered entries
217
+ * for stable codegen, ordered keys for strict key checks, and lookup for fast
218
+ * membership. Admission rejects drift between those views.
166
219
  */
167
220
  function isObjectSchemaValue(value, state) {
168
- if (value["mode"] !== ObjectModeTag.Passthrough &&
169
- value["mode"] !== ObjectModeTag.Strict) {
221
+ const mode = readOwnDataProperty(value, "mode");
222
+ if (mode !== ObjectModeTag.Passthrough &&
223
+ mode !== ObjectModeTag.Strict) {
170
224
  return false;
171
225
  }
172
- const entries = value["entries"];
173
- const keys = value["keys"];
174
- const keyLookup = value["keyLookup"];
226
+ const entries = readOwnDataProperty(value, "entries");
227
+ const keys = readOwnDataProperty(value, "keys");
228
+ const keyLookup = readOwnDataProperty(value, "keyLookup");
175
229
  if (!isUnknownArray(entries) || !isStringArray(keys) ||
176
230
  !isObjectKeyLookup(keyLookup, keys) || entries.length !== keys.length) {
177
231
  return false;
@@ -179,43 +233,70 @@ function isObjectSchemaValue(value, state) {
179
233
  const seen = [];
180
234
  for (let index = 0; index < entries.length; index += 1) {
181
235
  const entry = entries[index];
182
- if (!isRecord(entry) ||
183
- typeof entry["key"] !== "string" ||
184
- entry["key"] !== keys[index] ||
185
- includesString(seen, entry["key"]) ||
186
- (entry["presence"] !== PresenceTag.Required &&
187
- entry["presence"] !== PresenceTag.Optional) ||
188
- !isSchemaValueInner(entry["schema"], state)) {
236
+ if (!isRecord(entry)) {
237
+ return false;
238
+ }
239
+ const key = readOwnDataProperty(entry, "key");
240
+ const presence = readOwnDataProperty(entry, "presence");
241
+ const schema = readOwnDataProperty(entry, "schema");
242
+ /*
243
+ * The entry key must match the parallel `keys` slot. That keeps emitted
244
+ * object code deterministic and prevents a lookup table from describing
245
+ * a different shape than the property graph.
246
+ */
247
+ if (typeof key !== "string" ||
248
+ key !== keys[index] ||
249
+ includesString(seen, key) ||
250
+ (presence !== PresenceTag.Required &&
251
+ presence !== PresenceTag.Optional) ||
252
+ !isSchemaValueInner(schema, state)) {
189
253
  return false;
190
254
  }
191
- seen.push(entry["key"]);
255
+ seen.push(key);
192
256
  }
193
257
  return true;
194
258
  }
195
259
  /**
196
- * @brief is discriminated union schema value.
260
+ * @brief Validate discriminated-union dispatch metadata.
261
+ * @param value Candidate discriminated union record.
262
+ * @param state Recursion state for case schemas.
263
+ * @returns True when each case owns a unique literal and proves the tag field.
264
+ * @details The case schema must require the discriminant literal. Without that
265
+ * proof, dispatch could choose a branch before the branch actually validates the
266
+ * same discriminant field.
197
267
  */
198
268
  function isDiscriminatedUnionSchemaValue(value, state) {
199
- const cases = value["cases"];
200
- const key = value["key"];
269
+ const cases = readOwnDataProperty(value, "cases");
270
+ const key = readOwnDataProperty(value, "key");
201
271
  if (typeof key !== "string" || !isUnknownArray(cases) || cases.length === 0) {
202
272
  return false;
203
273
  }
204
274
  const literals = [];
205
275
  for (let index = 0; index < cases.length; index += 1) {
206
276
  const unionCase = cases[index];
207
- if (!isRecord(unionCase) || typeof unionCase["literal"] !== "string" ||
208
- includesString(literals, unionCase["literal"]) ||
209
- !isSchemaValueInner(unionCase["schema"], state) ||
210
- !caseRequiresDiscriminant(unionCase["schema"], key, unionCase["literal"])) {
277
+ if (!isRecord(unionCase)) {
278
+ return false;
279
+ }
280
+ const literal = readOwnDataProperty(unionCase, "literal");
281
+ const schema = readOwnDataProperty(unionCase, "schema");
282
+ if (typeof literal !== "string" ||
283
+ includesString(literals, literal) ||
284
+ !isSchemaValueInner(schema, state) ||
285
+ !caseRequiresDiscriminant(schema, key, literal)) {
211
286
  return false;
212
287
  }
213
- literals.push(unionCase["literal"]);
288
+ literals.push(literal);
214
289
  }
215
290
  return true;
216
291
  }
217
292
  /**
218
- * @brief case requires discriminant.
293
+ * @brief Prove that one case schema requires the requested discriminant value.
294
+ * @details Schema helpers enforce construction-time invariants before values reach
295
+ * validation, compilation, or export.
296
+ * @param schema Case schema after structural validation.
297
+ * @param key Discriminant property name.
298
+ * @param literal Required literal value for this case.
299
+ * @returns True when an object case contains a required matching literal field.
219
300
  */
220
301
  function caseRequiresDiscriminant(schema, key, literal) {
221
302
  const objectSchema = unwrapCaseObjectSchema(schema);
@@ -234,7 +315,12 @@ function caseRequiresDiscriminant(schema, key, literal) {
234
315
  return false;
235
316
  }
236
317
  /**
237
- * @brief unwrap case object schema.
318
+ * @brief Peel transparent wrappers until an object case schema is reached.
319
+ * @param schema Case schema.
320
+ * @returns The object schema used for discriminant proof, or undefined.
321
+ * @details Intersections and transparent wrappers can carry the object branch
322
+ * while still adding extra validation. Lazy and union nodes are not unwrapped
323
+ * here because they do not give a local, branch-stable object proof.
238
324
  */
239
325
  function unwrapCaseObjectSchema(schema) {
240
326
  switch (schema.tag) {
@@ -250,7 +336,12 @@ function unwrapCaseObjectSchema(schema) {
250
336
  }
251
337
  }
252
338
  /**
253
- * @brief schema requires literal.
339
+ * @brief Check whether a schema forces one exact string literal.
340
+ * @details Schema helpers enforce construction-time invariants before values reach
341
+ * validation, compilation, or export.
342
+ * @param schema Property schema.
343
+ * @param literal Required literal value.
344
+ * @returns True when the schema cannot accept a different discriminant value.
254
345
  */
255
346
  function schemaRequiresLiteral(schema, literal) {
256
347
  switch (schema.tag) {
package/docs/api.md CHANGED
@@ -187,10 +187,67 @@ Generated source never interpolates user-controlled values directly. Literals,
187
187
  regexps, property keys, keysets, and dynamic schema fallbacks are captured in
188
188
  side tables and referenced by numeric index.
189
189
 
190
+ ### Unsafe Compile Mode
191
+
192
+ ```ts
193
+ const FastButLooseUser = compile(User, {
194
+ name: "isUserFast",
195
+ mode: "unsafe"
196
+ });
197
+ ```
198
+
199
+ `CompileOptions["mode"]` and `AotCompileOptions["mode"]` are
200
+ `"safe" | "unsafe" | "unchecked" | undefined`; omitted options default to
201
+ `"safe"`. Safe mode keeps TypeSea's hostile-input contract: descriptor-based
202
+ property reads, no getter execution, and strict-object rejection for symbol and
203
+ non-enumerable extras.
204
+
205
+ Unsafe mode is an explicit performance escape hatch for trusted, normalized
206
+ plain data:
207
+
208
+ - Required object fields read with `value[key]` when the field schema rejects
209
+ `undefined`.
210
+ - Discriminant dispatch reads the tag with direct bracket access.
211
+ - Arrays and tuples read items with direct indexed loads.
212
+ - Strict-object extra-key rejection uses an allocation-free own-enumerable
213
+ `for...in` loop.
214
+
215
+ This may execute getters, may accept prototype-backed values, and does not
216
+ reject symbol or non-enumerable extras on strict objects. Because compiled
217
+ `check()` first trusts the generated predicate verdict, an unsafe predicate
218
+ that returns `true` also returns a successful `check()` result. Use unsafe mode
219
+ only after the input has crossed a trusted normalization boundary.
220
+
221
+ Unsafe mode may embed escaped static property keys directly into generated
222
+ predicate source so V8 can attach ordinary property-load inline caches. Safe
223
+ mode keeps property keys in side tables.
224
+
225
+ Unchecked mode uses the unsafe direct-read shape and also skips strict-object
226
+ extra-key loops. It is only for input whose object shape has already been
227
+ trusted or normalized; strict objects no longer reject extra keys in this mode.
228
+ Unsafe and unchecked compiled `check()` calls also return raw successful Result
229
+ objects without `Object.freeze()`. Failure diagnostics remain frozen. Safe mode
230
+ keeps frozen success and failure Result objects.
231
+ FastMode diagnostic collectors use direct field reads and FastMode strict-key
232
+ rules for object diagnostics where possible, so missing/accessor issue codes
233
+ are not guaranteed to match safe mode. Array and tuple diagnostics also use
234
+ direct indexed reads in fast modes, so sparse slots are diagnosed from the
235
+ loaded `undefined` value. Record diagnostics use direct `record[key]` reads;
236
+ unchecked mode also visits inherited enumerable keys. Discriminant diagnostics
237
+ read the tag directly and compare string cases with `===`.
238
+
190
239
  ## AOT Emit
191
240
 
192
241
  ```ts
193
242
  const emitted = emitAotModule(User, { name: "aotUser" });
243
+ const unsafeEmitted = emitAotModule(User, {
244
+ name: "aotUserFast",
245
+ mode: "unsafe"
246
+ });
247
+ const uncheckedEmitted = emitAotModule(User, {
248
+ name: "aotUserTrustedShape",
249
+ mode: "unchecked"
250
+ });
194
251
  ```
195
252
 
196
253
  `emitAotModule` returns `Result<AotModule, AotIssue[]>`. A successful result
@@ -0,0 +1,163 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1120" height="760" viewBox="0 0 1120 760" role="img" aria-labelledby="title desc">
2
+ <title id="title">TypeSea benchmark comparison</title>
3
+ <desc id="desc">Local strict object benchmark comparing TypeSea safe, unsafe, unchecked, Zod, Valibot, and Ajv throughput.</desc>
4
+ <defs>
5
+ <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
6
+ <stop offset="0" stop-color="#071014"/>
7
+ <stop offset="0.58" stop-color="#0f172a"/>
8
+ <stop offset="1" stop-color="#13251f"/>
9
+ </linearGradient>
10
+ <linearGradient id="panel" x1="0" y1="0" x2="1" y2="1">
11
+ <stop offset="0" stop-color="#111827"/>
12
+ <stop offset="1" stop-color="#0b1220"/>
13
+ </linearGradient>
14
+ <linearGradient id="safe" x1="0" y1="0" x2="1" y2="0">
15
+ <stop offset="0" stop-color="#14b8a6"/>
16
+ <stop offset="1" stop-color="#2dd4bf"/>
17
+ </linearGradient>
18
+ <linearGradient id="unsafe" x1="0" y1="0" x2="1" y2="0">
19
+ <stop offset="0" stop-color="#f97316"/>
20
+ <stop offset="1" stop-color="#fbbf24"/>
21
+ </linearGradient>
22
+ <linearGradient id="unchecked" x1="0" y1="0" x2="1" y2="0">
23
+ <stop offset="0" stop-color="#22c55e"/>
24
+ <stop offset="1" stop-color="#a3e635"/>
25
+ </linearGradient>
26
+ <linearGradient id="ajv" x1="0" y1="0" x2="1" y2="0">
27
+ <stop offset="0" stop-color="#3b82f6"/>
28
+ <stop offset="1" stop-color="#93c5fd"/>
29
+ </linearGradient>
30
+ <linearGradient id="zod" x1="0" y1="0" x2="1" y2="0">
31
+ <stop offset="0" stop-color="#8b5cf6"/>
32
+ <stop offset="1" stop-color="#c4b5fd"/>
33
+ </linearGradient>
34
+ <linearGradient id="valibot" x1="0" y1="0" x2="1" y2="0">
35
+ <stop offset="0" stop-color="#ec4899"/>
36
+ <stop offset="1" stop-color="#f9a8d4"/>
37
+ </linearGradient>
38
+ <filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
39
+ <feDropShadow dx="0" dy="14" stdDeviation="18" flood-color="#000000" flood-opacity="0.35"/>
40
+ </filter>
41
+ <style>
42
+ text { font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
43
+ .title { fill: #f8fafc; font-size: 42px; font-weight: 800; letter-spacing: 0; }
44
+ .subtitle { fill: #94a3b8; font-size: 16px; font-weight: 500; }
45
+ .card-title { fill: #e5e7eb; font-size: 18px; font-weight: 800; }
46
+ .card-note { fill: #94a3b8; font-size: 12px; font-weight: 600; }
47
+ .metric-label { fill: #94a3b8; font-size: 12px; font-weight: 700; text-transform: uppercase; }
48
+ .metric-value { fill: #f8fafc; font-size: 30px; font-weight: 900; }
49
+ .metric-sub { fill: #cbd5e1; font-size: 13px; font-weight: 600; }
50
+ .bar-label { fill: #cbd5e1; font-size: 13px; font-weight: 700; }
51
+ .bar-value { fill: #f8fafc; font-size: 13px; font-weight: 800; }
52
+ .axis { stroke: #334155; stroke-width: 1; }
53
+ </style>
54
+ </defs>
55
+
56
+ <rect width="1120" height="760" rx="28" fill="url(#bg)"/>
57
+ <circle cx="1028" cy="82" r="180" fill="#22c55e" opacity="0.08"/>
58
+ <circle cx="84" cy="690" r="220" fill="#38bdf8" opacity="0.07"/>
59
+
60
+ <text id="headline" x="44" y="58" class="title">TypeSea benchmark comparison</text>
61
+ <text x="46" y="88" class="subtitle">Local run on 2026-07-04 KST, strict-object contract, ops/sec. Higher is better.</text>
62
+
63
+ <g filter="url(#shadow)">
64
+ <rect x="44" y="118" width="322" height="108" rx="18" fill="url(#panel)" stroke="#1f2937"/>
65
+ <text x="66" y="148" class="metric-label">Unchecked valid hot path</text>
66
+ <text x="66" y="188" class="metric-value">42.58M</text>
67
+ <text x="66" y="210" class="metric-sub">31.7x Zod, 10.0x Ajv</text>
68
+
69
+ <rect x="399" y="118" width="322" height="108" rx="18" fill="url(#panel)" stroke="#1f2937"/>
70
+ <text x="421" y="148" class="metric-label">Safe invalid fast-fail</text>
71
+ <text x="421" y="188" class="metric-value">42.08M</text>
72
+ <text x="421" y="210" class="metric-sub">1.51x Ajv, 499x Zod</text>
73
+
74
+ <rect x="754" y="118" width="322" height="108" rx="18" fill="url(#panel)" stroke="#1f2937"/>
75
+ <text x="776" y="148" class="metric-label">Safe valid path</text>
76
+ <text x="776" y="188" class="metric-value">4.30M</text>
77
+ <text x="776" y="210" class="metric-sub">Ajv-class while staying safe</text>
78
+ </g>
79
+
80
+ <g filter="url(#shadow)">
81
+ <rect x="44" y="260" width="1032" height="136" rx="18" fill="url(#panel)" stroke="#1f2937"/>
82
+ <text x="66" y="291" class="card-title">Valid object path</text>
83
+ <text x="230" y="291" class="card-note">linear scale to 42.58M</text>
84
+ <line x1="270" y1="318" x2="1010" y2="318" class="axis"/>
85
+
86
+ <text x="66" y="326" class="bar-label">TypeSea unchecked</text>
87
+ <rect x="270" y="312" width="740" height="15" rx="7.5" fill="url(#unchecked)"/>
88
+ <text x="1024" y="326" class="bar-value">42.58M</text>
89
+
90
+ <text x="66" y="350" class="bar-label">TypeSea unsafe</text>
91
+ <rect x="270" y="336" width="631" height="15" rx="7.5" fill="url(#unsafe)"/>
92
+ <text x="915" y="350" class="bar-value">36.30M</text>
93
+
94
+ <text x="66" y="374" class="bar-label">TypeSea safe / Ajv / Zod</text>
95
+ <rect x="270" y="360" width="75" height="15" rx="7.5" fill="url(#safe)"/>
96
+ <rect x="354" y="360" width="74" height="15" rx="7.5" fill="url(#ajv)"/>
97
+ <rect x="437" y="360" width="24" height="15" rx="7.5" fill="url(#valibot)"/>
98
+ <rect x="470" y="360" width="23" height="15" rx="7.5" fill="url(#zod)"/>
99
+ <text x="506" y="374" class="bar-value">safe 4.30M, Ajv 4.28M, Valibot 1.41M, Zod 1.34M</text>
100
+ </g>
101
+
102
+ <g filter="url(#shadow)">
103
+ <rect x="44" y="418" width="1032" height="150" rx="18" fill="url(#panel)" stroke="#1f2937"/>
104
+ <text x="66" y="449" class="card-title">Invalid object fast-fail</text>
105
+ <text x="268" y="449" class="card-note">linear scale to 50.48M</text>
106
+ <line x1="270" y1="476" x2="1010" y2="476" class="axis"/>
107
+
108
+ <text x="66" y="482" class="bar-label">TypeSea unchecked</text>
109
+ <rect x="270" y="468" width="740" height="15" rx="7.5" fill="url(#unchecked)"/>
110
+ <text x="1024" y="482" class="bar-value">50.48M</text>
111
+
112
+ <text x="66" y="506" class="bar-label">TypeSea unsafe</text>
113
+ <rect x="270" y="492" width="728" height="15" rx="7.5" fill="url(#unsafe)"/>
114
+ <text x="1024" y="506" class="bar-value">49.65M</text>
115
+
116
+ <text x="66" y="530" class="bar-label">TypeSea safe</text>
117
+ <rect x="270" y="516" width="617" height="15" rx="7.5" fill="url(#safe)"/>
118
+ <text x="902" y="530" class="bar-value">42.08M</text>
119
+
120
+ <text x="66" y="554" class="bar-label">Ajv / Valibot / Zod</text>
121
+ <rect x="270" y="540" width="408" height="15" rx="7.5" fill="url(#ajv)"/>
122
+ <rect x="690" y="540" width="13" height="15" rx="7.5" fill="url(#valibot)"/>
123
+ <rect x="714" y="540" width="3" height="15" rx="1.5" fill="url(#zod)"/>
124
+ <text x="734" y="554" class="bar-value">Ajv 27.82M, Valibot 0.88M, Zod 0.08M</text>
125
+ </g>
126
+
127
+ <g filter="url(#shadow)">
128
+ <rect x="44" y="576" width="1032" height="136" rx="18" fill="url(#panel)" stroke="#1f2937"/>
129
+ <text x="66" y="607" class="card-title">Invalid diagnostic path</text>
130
+ <text x="270" y="607" class="card-note">linear scale to 28.71M; Ajv is a boolean baseline</text>
131
+ <line x1="270" y1="634" x2="1010" y2="634" class="axis"/>
132
+
133
+ <text x="66" y="642" class="bar-label">Ajv compiled</text>
134
+ <rect x="270" y="628" width="740" height="15" rx="7.5" fill="url(#ajv)"/>
135
+ <text x="1024" y="642" class="bar-value">28.71M</text>
136
+
137
+ <text x="66" y="666" class="bar-label">TypeSea modes</text>
138
+ <rect x="270" y="652" width="95" height="15" rx="7.5" fill="url(#unchecked)"/>
139
+ <rect x="374" y="652" width="79" height="15" rx="7.5" fill="url(#unsafe)"/>
140
+ <rect x="462" y="652" width="54" height="15" rx="7.5" fill="url(#safe)"/>
141
+ <text x="532" y="666" class="bar-value">unchecked 3.67M, unsafe 3.08M, safe 2.09M</text>
142
+
143
+ <text x="66" y="690" class="bar-label">Valibot / Zod</text>
144
+ <rect x="270" y="676" width="23" height="15" rx="7.5" fill="url(#valibot)"/>
145
+ <rect x="302" y="676" width="3" height="15" rx="1.5" fill="url(#zod)"/>
146
+ <text x="322" y="690" class="bar-value">Valibot 0.89M, Zod 0.08M</text>
147
+ </g>
148
+
149
+ <g transform="translate(44 730)">
150
+ <rect x="0" y="-11" width="13" height="13" rx="3" fill="url(#safe)"/>
151
+ <text x="20" y="0" class="card-note">TypeSea safe</text>
152
+ <rect x="128" y="-11" width="13" height="13" rx="3" fill="url(#unsafe)"/>
153
+ <text x="148" y="0" class="card-note">unsafe</text>
154
+ <rect x="228" y="-11" width="13" height="13" rx="3" fill="url(#unchecked)"/>
155
+ <text x="248" y="0" class="card-note">unchecked</text>
156
+ <rect x="356" y="-11" width="13" height="13" rx="3" fill="url(#ajv)"/>
157
+ <text x="376" y="0" class="card-note">Ajv</text>
158
+ <rect x="426" y="-11" width="13" height="13" rx="3" fill="url(#valibot)"/>
159
+ <text x="446" y="0" class="card-note">Valibot</text>
160
+ <rect x="536" y="-11" width="13" height="13" rx="3" fill="url(#zod)"/>
161
+ <text x="556" y="0" class="card-note">Zod</text>
162
+ </g>
163
+ </svg>