typesea 0.1.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 (301) hide show
  1. package/CHANGELOG.md +85 -6
  2. package/README.md +143 -28
  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 +19 -3
  7. package/dist/aot/index.d.ts.map +1 -1
  8. package/dist/aot/index.js +115 -17
  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 +43 -9
  13. package/dist/builders/composite.d.ts.map +1 -1
  14. package/dist/builders/composite.js +100 -17
  15. package/dist/builders/index.d.ts +8 -5
  16. package/dist/builders/index.d.ts.map +1 -1
  17. package/dist/builders/index.js +7 -4
  18. package/dist/builders/modifier.d.ts +36 -5
  19. package/dist/builders/modifier.d.ts.map +1 -1
  20. package/dist/builders/modifier.js +52 -5
  21. package/dist/builders/object/guard.d.ts +72 -24
  22. package/dist/builders/object/guard.d.ts.map +1 -1
  23. package/dist/builders/object/guard.js +139 -29
  24. package/dist/builders/object/index.d.ts +4 -2
  25. package/dist/builders/object/index.d.ts.map +1 -1
  26. package/dist/builders/object/index.js +3 -1
  27. package/dist/builders/object/schema.d.ts +88 -11
  28. package/dist/builders/object/schema.d.ts.map +1 -1
  29. package/dist/builders/object/schema.js +290 -23
  30. package/dist/builders/object/types.d.ts +20 -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/runtime.d.ts +40 -0
  34. package/dist/builders/runtime.d.ts.map +1 -0
  35. package/dist/builders/runtime.js +150 -0
  36. package/dist/builders/scalar.d.ts +49 -9
  37. package/dist/builders/scalar.d.ts.map +1 -1
  38. package/dist/builders/scalar.js +87 -9
  39. package/dist/builders/table.d.ts +35 -5
  40. package/dist/builders/table.d.ts.map +1 -1
  41. package/dist/builders/table.js +35 -5
  42. package/dist/builders/types.d.ts +20 -4
  43. package/dist/builders/types.d.ts.map +1 -1
  44. package/dist/builders/types.js +2 -0
  45. package/dist/compile/check-composite.d.ts +25 -2
  46. package/dist/compile/check-composite.d.ts.map +1 -1
  47. package/dist/compile/check-composite.js +699 -27
  48. package/dist/compile/check-scalar.d.ts +88 -0
  49. package/dist/compile/check-scalar.d.ts.map +1 -1
  50. package/dist/compile/check-scalar.js +570 -3
  51. package/dist/compile/check.d.ts +12 -0
  52. package/dist/compile/check.d.ts.map +1 -1
  53. package/dist/compile/check.js +62 -3
  54. package/dist/compile/context.d.ts +47 -9
  55. package/dist/compile/context.d.ts.map +1 -1
  56. package/dist/compile/context.js +53 -8
  57. package/dist/compile/first.d.ts +26 -0
  58. package/dist/compile/first.d.ts.map +1 -0
  59. package/dist/compile/first.js +850 -0
  60. package/dist/compile/graph-predicate.d.ts +4 -2
  61. package/dist/compile/graph-predicate.d.ts.map +1 -1
  62. package/dist/compile/graph-predicate.js +2272 -165
  63. package/dist/compile/guard.d.ts +16 -24
  64. package/dist/compile/guard.d.ts.map +1 -1
  65. package/dist/compile/guard.js +202 -72
  66. package/dist/compile/index.d.ts +3 -1
  67. package/dist/compile/index.d.ts.map +1 -1
  68. package/dist/compile/index.js +2 -0
  69. package/dist/compile/issue.d.ts +110 -0
  70. package/dist/compile/issue.d.ts.map +1 -1
  71. package/dist/compile/issue.js +184 -1
  72. package/dist/compile/names.d.ts +12 -2
  73. package/dist/compile/names.d.ts.map +1 -1
  74. package/dist/compile/names.js +19 -3
  75. package/dist/compile/predicate.d.ts +24 -0
  76. package/dist/compile/predicate.d.ts.map +1 -1
  77. package/dist/compile/predicate.js +287 -10
  78. package/dist/compile/runtime.d.ts +100 -13
  79. package/dist/compile/runtime.d.ts.map +1 -1
  80. package/dist/compile/runtime.js +56 -6
  81. package/dist/compile/source.d.ts +10 -2
  82. package/dist/compile/source.d.ts.map +1 -1
  83. package/dist/compile/source.js +385 -26
  84. package/dist/compile/types.d.ts +22 -0
  85. package/dist/compile/types.d.ts.map +1 -1
  86. package/dist/compile/types.js +2 -0
  87. package/dist/decoder/index.d.ts +92 -46
  88. package/dist/decoder/index.d.ts.map +1 -1
  89. package/dist/decoder/index.js +266 -39
  90. package/dist/evaluate/check-composite.d.ts +111 -2
  91. package/dist/evaluate/check-composite.d.ts.map +1 -1
  92. package/dist/evaluate/check-composite.js +343 -8
  93. package/dist/evaluate/check-scalar.d.ts +25 -0
  94. package/dist/evaluate/check-scalar.d.ts.map +1 -1
  95. package/dist/evaluate/check-scalar.js +124 -3
  96. package/dist/evaluate/check.d.ts +7 -0
  97. package/dist/evaluate/check.d.ts.map +1 -1
  98. package/dist/evaluate/check.js +62 -4
  99. package/dist/evaluate/index.d.ts +2 -0
  100. package/dist/evaluate/index.d.ts.map +1 -1
  101. package/dist/evaluate/index.js +2 -0
  102. package/dist/evaluate/issue.d.ts +11 -1
  103. package/dist/evaluate/issue.d.ts.map +1 -1
  104. package/dist/evaluate/issue.js +15 -1
  105. package/dist/evaluate/predicate.d.ts +16 -5
  106. package/dist/evaluate/predicate.d.ts.map +1 -1
  107. package/dist/evaluate/predicate.js +20 -5
  108. package/dist/evaluate/shared.d.ts +78 -13
  109. package/dist/evaluate/shared.d.ts.map +1 -1
  110. package/dist/evaluate/shared.js +101 -8
  111. package/dist/evaluate/state.d.ts +35 -13
  112. package/dist/evaluate/state.d.ts.map +1 -1
  113. package/dist/evaluate/state.js +35 -2
  114. package/dist/guard/array.d.ts +48 -0
  115. package/dist/guard/array.d.ts.map +1 -0
  116. package/dist/guard/array.js +84 -0
  117. package/dist/guard/base.d.ts +111 -31
  118. package/dist/guard/base.d.ts.map +1 -1
  119. package/dist/guard/base.js +165 -32
  120. package/dist/guard/date.d.ts +34 -0
  121. package/dist/guard/date.d.ts.map +1 -0
  122. package/dist/guard/date.js +60 -0
  123. package/dist/guard/error.d.ts +10 -5
  124. package/dist/guard/error.d.ts.map +1 -1
  125. package/dist/guard/error.js +10 -5
  126. package/dist/guard/index.d.ts +4 -0
  127. package/dist/guard/index.d.ts.map +1 -1
  128. package/dist/guard/index.js +4 -0
  129. package/dist/guard/number.d.ts +86 -11
  130. package/dist/guard/number.d.ts.map +1 -1
  131. package/dist/guard/number.js +159 -11
  132. package/dist/guard/props.d.ts +27 -3
  133. package/dist/guard/props.d.ts.map +1 -1
  134. package/dist/guard/props.js +27 -3
  135. package/dist/guard/read.d.ts +115 -10
  136. package/dist/guard/read.d.ts.map +1 -1
  137. package/dist/guard/read.js +185 -10
  138. package/dist/guard/registry.d.ts +12 -2
  139. package/dist/guard/registry.d.ts.map +1 -1
  140. package/dist/guard/registry.js +15 -3
  141. package/dist/guard/string.d.ts +115 -13
  142. package/dist/guard/string.d.ts.map +1 -1
  143. package/dist/guard/string.js +250 -13
  144. package/dist/guard/types.d.ts +110 -40
  145. package/dist/guard/types.d.ts.map +1 -1
  146. package/dist/guard/types.js +2 -0
  147. package/dist/index.d.ts +5 -5
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +4 -4
  150. package/dist/internal/index.d.ts +42 -6
  151. package/dist/internal/index.d.ts.map +1 -1
  152. package/dist/internal/index.js +51 -8
  153. package/dist/ir/builder.d.ts +17 -127
  154. package/dist/ir/builder.d.ts.map +1 -1
  155. package/dist/ir/builder.js +80 -137
  156. package/dist/ir/freeze.d.ts +4 -0
  157. package/dist/ir/freeze.d.ts.map +1 -1
  158. package/dist/ir/freeze.js +66 -0
  159. package/dist/ir/index.d.ts +3 -1
  160. package/dist/ir/index.d.ts.map +1 -1
  161. package/dist/ir/index.js +2 -0
  162. package/dist/ir/regexp.d.ts +2 -0
  163. package/dist/ir/regexp.d.ts.map +1 -1
  164. package/dist/ir/regexp.js +2 -0
  165. package/dist/ir/types.d.ts +94 -56
  166. package/dist/ir/types.d.ts.map +1 -1
  167. package/dist/ir/types.js +2 -0
  168. package/dist/ir/validate.d.ts +8 -1
  169. package/dist/ir/validate.d.ts.map +1 -1
  170. package/dist/ir/validate.js +511 -61
  171. package/dist/issue/index.d.ts +42 -10
  172. package/dist/issue/index.d.ts.map +1 -1
  173. package/dist/issue/index.js +65 -11
  174. package/dist/json-schema/emit-combinator.d.ts +44 -4
  175. package/dist/json-schema/emit-combinator.d.ts.map +1 -1
  176. package/dist/json-schema/emit-combinator.js +44 -4
  177. package/dist/json-schema/emit-composite.d.ts +16 -2
  178. package/dist/json-schema/emit-composite.d.ts.map +1 -1
  179. package/dist/json-schema/emit-composite.js +81 -13
  180. package/dist/json-schema/emit-scalar.d.ts +26 -3
  181. package/dist/json-schema/emit-scalar.d.ts.map +1 -1
  182. package/dist/json-schema/emit-scalar.js +124 -10
  183. package/dist/json-schema/emit-types.d.ts +11 -1
  184. package/dist/json-schema/emit-types.d.ts.map +1 -1
  185. package/dist/json-schema/emit-types.js +2 -0
  186. package/dist/json-schema/emit.d.ts +12 -1
  187. package/dist/json-schema/emit.d.ts.map +1 -1
  188. package/dist/json-schema/emit.js +23 -3
  189. package/dist/json-schema/freeze.d.ts +13 -2
  190. package/dist/json-schema/freeze.d.ts.map +1 -1
  191. package/dist/json-schema/freeze.js +41 -8
  192. package/dist/json-schema/index.d.ts +16 -2
  193. package/dist/json-schema/index.d.ts.map +1 -1
  194. package/dist/json-schema/index.js +23 -3
  195. package/dist/json-schema/issue.d.ts +4 -1
  196. package/dist/json-schema/issue.d.ts.map +1 -1
  197. package/dist/json-schema/issue.js +4 -1
  198. package/dist/json-schema/read.d.ts +24 -3
  199. package/dist/json-schema/read.d.ts.map +1 -1
  200. package/dist/json-schema/read.js +59 -12
  201. package/dist/json-schema/types.d.ts +45 -16
  202. package/dist/json-schema/types.d.ts.map +1 -1
  203. package/dist/json-schema/types.js +2 -0
  204. package/dist/kind/index.d.ts +40 -28
  205. package/dist/kind/index.d.ts.map +1 -1
  206. package/dist/kind/index.js +41 -13
  207. package/dist/lower/index.d.ts +6 -1
  208. package/dist/lower/index.d.ts.map +1 -1
  209. package/dist/lower/index.js +462 -46
  210. package/dist/message/index.d.ts +64 -10
  211. package/dist/message/index.d.ts.map +1 -1
  212. package/dist/message/index.js +155 -17
  213. package/dist/optimize/algebraic.d.ts +54 -0
  214. package/dist/optimize/algebraic.d.ts.map +1 -0
  215. package/dist/optimize/algebraic.js +314 -0
  216. package/dist/optimize/compact.d.ts +8 -1
  217. package/dist/optimize/compact.d.ts.map +1 -1
  218. package/dist/optimize/compact.js +13 -2
  219. package/dist/optimize/domain.d.ts +16 -0
  220. package/dist/optimize/domain.d.ts.map +1 -0
  221. package/dist/optimize/domain.js +619 -0
  222. package/dist/optimize/fold-boolean.d.ts +17 -2
  223. package/dist/optimize/fold-boolean.d.ts.map +1 -1
  224. package/dist/optimize/fold-boolean.js +59 -14
  225. package/dist/optimize/fold-common.d.ts +43 -8
  226. package/dist/optimize/fold-common.d.ts.map +1 -1
  227. package/dist/optimize/fold-common.js +37 -6
  228. package/dist/optimize/fold-constraints.d.ts +33 -0
  229. package/dist/optimize/fold-constraints.d.ts.map +1 -0
  230. package/dist/optimize/fold-constraints.js +484 -0
  231. package/dist/optimize/fold-scalar.d.ts +98 -13
  232. package/dist/optimize/fold-scalar.d.ts.map +1 -1
  233. package/dist/optimize/fold-scalar.js +98 -13
  234. package/dist/optimize/fold.d.ts +8 -1
  235. package/dist/optimize/fold.d.ts.map +1 -1
  236. package/dist/optimize/fold.js +22 -2
  237. package/dist/optimize/index.d.ts +9 -1
  238. package/dist/optimize/index.d.ts.map +1 -1
  239. package/dist/optimize/index.js +18 -3
  240. package/dist/optimize/map-node.d.ts +3 -1
  241. package/dist/optimize/map-node.d.ts.map +1 -1
  242. package/dist/optimize/map-node.js +48 -3
  243. package/dist/optimize/peephole.d.ts +16 -0
  244. package/dist/optimize/peephole.d.ts.map +1 -0
  245. package/dist/optimize/peephole.js +254 -0
  246. package/dist/optimize/remap.d.ts +2 -0
  247. package/dist/optimize/remap.d.ts.map +1 -1
  248. package/dist/optimize/remap.js +2 -0
  249. package/dist/optimize/rewrite.d.ts +13 -8
  250. package/dist/optimize/rewrite.d.ts.map +1 -1
  251. package/dist/optimize/rewrite.js +13 -8
  252. package/dist/plan/cache.d.ts +9 -3
  253. package/dist/plan/cache.d.ts.map +1 -1
  254. package/dist/plan/cache.js +34 -6
  255. package/dist/plan/index.d.ts +2 -0
  256. package/dist/plan/index.d.ts.map +1 -1
  257. package/dist/plan/index.js +2 -0
  258. package/dist/plan/predicate.d.ts +2 -0
  259. package/dist/plan/predicate.d.ts.map +1 -1
  260. package/dist/plan/predicate.js +298 -29
  261. package/dist/plan/schema-predicate.d.ts +6 -0
  262. package/dist/plan/schema-predicate.d.ts.map +1 -1
  263. package/dist/plan/schema-predicate.js +382 -19
  264. package/dist/plan/types.d.ts +2 -0
  265. package/dist/plan/types.d.ts.map +1 -1
  266. package/dist/plan/types.js +2 -0
  267. package/dist/result/index.d.ts +19 -5
  268. package/dist/result/index.d.ts.map +1 -1
  269. package/dist/result/index.js +10 -2
  270. package/dist/schema/common.d.ts +69 -6
  271. package/dist/schema/common.d.ts.map +1 -1
  272. package/dist/schema/common.js +104 -10
  273. package/dist/schema/freeze.d.ts +4 -0
  274. package/dist/schema/freeze.d.ts.map +1 -1
  275. package/dist/schema/freeze.js +40 -0
  276. package/dist/schema/index.d.ts +5 -2
  277. package/dist/schema/index.d.ts.map +1 -1
  278. package/dist/schema/index.js +4 -1
  279. package/dist/schema/lazy.d.ts +4 -0
  280. package/dist/schema/lazy.d.ts.map +1 -1
  281. package/dist/schema/lazy.js +4 -0
  282. package/dist/schema/literal.d.ts +7 -1
  283. package/dist/schema/literal.d.ts.map +1 -1
  284. package/dist/schema/literal.js +7 -1
  285. package/dist/schema/types.d.ts +109 -100
  286. package/dist/schema/types.d.ts.map +1 -1
  287. package/dist/schema/types.js +13 -2
  288. package/dist/schema/undefined.d.ts +17 -0
  289. package/dist/schema/undefined.d.ts.map +1 -0
  290. package/dist/schema/undefined.js +77 -0
  291. package/dist/schema/validate.d.ts +8 -1
  292. package/dist/schema/validate.d.ts.map +1 -1
  293. package/dist/schema/validate.js +255 -57
  294. package/docs/api.md +128 -8
  295. package/docs/assets/benchmark-headline.svg +163 -0
  296. package/docs/engine-notes.md +62 -15
  297. package/docs/index.html +1340 -702
  298. package/docs/ko/api.md +375 -0
  299. package/docs/ko/engine-notes.md +156 -0
  300. package/docs/ko/readme.md +378 -0
  301. package/package.json +66 -65
@@ -1,8 +1,14 @@
1
- import { NumberCheckTag, ObjectModeTag, PresenceTag, SchemaTag, StringCheckTag } from "../kind/index.js";
1
+ import { NumberCheckTag, PresenceTag, SchemaTag, StringCheckTag } from "../kind/index.js";
2
2
  import { GraphBuilder } from "../ir/index.js";
3
- import { UUID_PATTERN } from "../schema/index.js";
3
+ import { optimizeGraph } from "../optimize/index.js";
4
+ import { EMAIL_PATTERN, IPV4_PATTERN, IPV6_PATTERN, ISO_DATETIME_PATTERN, ISO_DATE_PATTERN, ULID_PATTERN, URL_PATTERN, UUID_PATTERN } from "../schema/index.js";
4
5
  /**
5
- * @brief lower schema.
6
+ * @brief Convert one schema root into a Sea-of-Nodes predicate graph.
7
+ * @details The graph always starts with a single input parameter and ends in a
8
+ * boolean return node. Keeping that calling convention uniform lets optimizer,
9
+ * interpreter, compiler, and AOT code consume the same IR shape.
10
+ * @param schema Root schema to lower into predicate IR.
11
+ * @returns Graph with one parameter and one return node.
6
12
  */
7
13
  export function lowerSchema(schema) {
8
14
  const builder = new GraphBuilder();
@@ -13,7 +19,14 @@ export function lowerSchema(schema) {
13
19
  return builder.finish(entry, ret);
14
20
  }
15
21
  /**
16
- * @brief lower predicate.
22
+ * @brief Lower one schema node into a boolean-producing IR node.
23
+ * @details Static schemas become explicit graph operations so later passes can
24
+ * fold, reorder, or specialize them. Dynamic schemas stay behind SchemaCheck
25
+ * nodes because their semantics require runtime callbacks or lazy resolution.
26
+ * @param builder Graph builder owning the current graph.
27
+ * @param schema Schema node to lower.
28
+ * @param value Node id that produces the candidate value.
29
+ * @returns Node id for the boolean predicate result.
17
30
  */
18
31
  function lowerPredicate(builder, schema, value) {
19
32
  switch (schema.tag) {
@@ -25,6 +38,8 @@ function lowerPredicate(builder, schema, value) {
25
38
  return lowerString(builder, schema, value);
26
39
  case SchemaTag.Number:
27
40
  return lowerNumber(builder, schema, value);
41
+ case SchemaTag.Date:
42
+ return builder.schemaCheck(value, schema);
28
43
  case SchemaTag.BigInt:
29
44
  return builder.isBigInt(value);
30
45
  case SchemaTag.Symbol:
@@ -34,20 +49,33 @@ function lowerPredicate(builder, schema, value) {
34
49
  case SchemaTag.Literal:
35
50
  return builder.equals(value, builder.constant(schema.value));
36
51
  case SchemaTag.Array:
52
+ /*
53
+ * Container checks stay as explicit siblings of iteration nodes. The
54
+ * domain pass can later remove redundant guards when iteration already
55
+ * proves the same container type.
56
+ */
37
57
  return builder.and([
38
58
  builder.isArray(value),
39
- builder.arrayEvery(value, schema.item)
59
+ builder.arrayEvery(value, schema.item, schema.checks, lowerChildGraph(schema.item))
40
60
  ]);
41
61
  case SchemaTag.Tuple:
62
+ if (schema.rest !== undefined) {
63
+ return builder.schemaCheck(value, schema);
64
+ }
42
65
  return builder.and([
43
66
  builder.isArray(value),
44
- builder.tupleItems(value, schema.items)
67
+ builder.tupleItems(value, schema.items, lowerChildGraphs(schema.items))
45
68
  ]);
46
69
  case SchemaTag.Record:
47
70
  return builder.and([
48
71
  builder.isObject(value),
49
- builder.recordEvery(value, schema.value)
72
+ builder.recordEvery(value, schema.value, lowerChildGraph(schema.value))
50
73
  ]);
74
+ case SchemaTag.Map:
75
+ case SchemaTag.Set:
76
+ case SchemaTag.InstanceOf:
77
+ case SchemaTag.Property:
78
+ return builder.schemaCheck(value, schema);
51
79
  case SchemaTag.Object:
52
80
  return lowerObject(builder, schema, value);
53
81
  case SchemaTag.Union:
@@ -74,15 +102,31 @@ function lowerPredicate(builder, schema, value) {
74
102
  return lowerPredicate(builder, schema.inner, value);
75
103
  case SchemaTag.Lazy:
76
104
  case SchemaTag.Refine:
105
+ /*
106
+ * Lazy resolution and user predicates are represented as opaque schema
107
+ * checks. Lowering keeps the graph pure and lets runtime evaluation
108
+ * decide those dynamic cases.
109
+ */
77
110
  return builder.schemaCheck(value, schema);
78
111
  }
79
112
  }
80
113
  /**
81
- * @brief lower string.
114
+ * @brief Lower a string schema into independent scalar predicate nodes.
115
+ * @details The base string check and each constraint remain separate in the IR
116
+ * so algebraic and constraint passes can remove redundant bounds without
117
+ * re-parsing the source schema.
118
+ * @param builder Graph builder owning the current graph.
119
+ * @param schema String schema with scalar checks.
120
+ * @param value Node id that produces the candidate value.
121
+ * @returns Node id for the combined string predicate.
82
122
  */
83
123
  function lowerString(builder, schema, value) {
84
124
  const tests = [builder.isString(value)];
85
125
  const checks = schema.checks;
126
+ /*
127
+ * String constraints are represented as independent nodes. Later algebraic
128
+ * and domain passes can fold impossible or redundant checks.
129
+ */
86
130
  for (let index = 0; index < checks.length; index += 1) {
87
131
  const check = checks[index];
88
132
  if (check === undefined) {
@@ -101,12 +145,40 @@ function lowerString(builder, schema, value) {
101
145
  case StringCheckTag.Uuid:
102
146
  tests.push(builder.regex(value, UUID_PATTERN, "uuid"));
103
147
  break;
148
+ case StringCheckTag.Email:
149
+ tests.push(builder.regex(value, EMAIL_PATTERN, "email"));
150
+ break;
151
+ case StringCheckTag.Url:
152
+ tests.push(builder.regex(value, URL_PATTERN, "url"));
153
+ break;
154
+ case StringCheckTag.IsoDate:
155
+ tests.push(builder.regex(value, ISO_DATE_PATTERN, "iso_date"));
156
+ break;
157
+ case StringCheckTag.IsoDateTime:
158
+ tests.push(builder.regex(value, ISO_DATETIME_PATTERN, "iso_datetime"));
159
+ break;
160
+ case StringCheckTag.Ulid:
161
+ tests.push(builder.regex(value, ULID_PATTERN, "ulid"));
162
+ break;
163
+ case StringCheckTag.Ipv4:
164
+ tests.push(builder.regex(value, IPV4_PATTERN, "ipv4"));
165
+ break;
166
+ case StringCheckTag.Ipv6:
167
+ tests.push(builder.regex(value, IPV6_PATTERN, "ipv6"));
168
+ break;
104
169
  }
105
170
  }
106
171
  return builder.and(tests);
107
172
  }
108
173
  /**
109
- * @brief lower number.
174
+ * @brief Lower a number schema into numeric predicate nodes.
175
+ * @details Bounds are represented as comparisons against Const nodes. That
176
+ * gives constant folding one canonical place to intern literal limits and gives
177
+ * constraint folding clean min/max facts.
178
+ * @param builder Graph builder owning the current graph.
179
+ * @param schema Number schema with scalar checks.
180
+ * @param value Node id that produces the candidate value.
181
+ * @returns Node id for the combined number predicate.
110
182
  */
111
183
  function lowerNumber(builder, schema, value) {
112
184
  const tests = [builder.isNumber(value)];
@@ -126,64 +198,303 @@ function lowerNumber(builder, schema, value) {
126
198
  case NumberCheckTag.Lte:
127
199
  tests.push(builder.lte(value, builder.constant(check.value)));
128
200
  break;
201
+ case NumberCheckTag.Gt:
202
+ tests.push(builder.not(builder.lte(value, builder.constant(check.value))));
203
+ break;
204
+ case NumberCheckTag.Lt:
205
+ tests.push(builder.not(builder.gte(value, builder.constant(check.value))));
206
+ break;
207
+ case NumberCheckTag.MultipleOf:
208
+ tests.push(builder.schemaCheck(value, {
209
+ tag: SchemaTag.Number,
210
+ checks: [check]
211
+ }));
212
+ break;
129
213
  }
130
214
  }
131
215
  return builder.and(tests);
132
216
  }
133
217
  /**
134
- * @brief lower object.
218
+ * @brief Lower an object schema into one structured shape node.
219
+ * @details Object validation keeps key order, strict-mode metadata, and child
220
+ * graphs together. Codegen can then emit one object-shaped fast path instead of
221
+ * rediscovering shape facts from generic boolean nodes.
222
+ * @param builder Graph builder owning the current graph.
223
+ * @param schema Object schema with shape metadata.
224
+ * @param value Node id that produces the candidate value.
225
+ * @returns Node id for the object-shape predicate.
135
226
  */
136
227
  function lowerObject(builder, schema, value) {
137
- const entries = schema.entries;
138
- const tests = [builder.isObject(value)];
228
+ return builder.objectShape(value, lowerObjectShapeEntries(schema.entries), schema.keys, schema.mode, schema.catchall, schema.catchall === undefined ? undefined : lowerChildGraph(schema.catchall));
229
+ }
230
+ /**
231
+ * @brief Lower and optimize a child schema before embedding it in a parent node.
232
+ * @details Composite nodes store child graphs by value. Optimizing them at
233
+ * construction time keeps array, tuple, record, object, and union codegen from
234
+ * carrying avoidable dead nodes in every embedded child.
235
+ * @param schema Child schema to lower.
236
+ * @returns Optimized child graph suitable for dispatch or iteration nodes.
237
+ */
238
+ function lowerChildGraph(schema) {
239
+ /*
240
+ * Child graphs are optimized immediately because they are embedded into
241
+ * dispatch and iteration nodes. Smaller child graphs make later codegen
242
+ * and graph introspection cheaper.
243
+ */
244
+ return optimizeGraph(lowerSchema(schema));
245
+ }
246
+ /**
247
+ * @brief Lower a schema vector into index-aligned child graphs.
248
+ * @details Undefined holes are preserved so union and tuple branch indexes
249
+ * remain stable for diagnostics and generated dispatch tables.
250
+ * @param schemas Closed schema list.
251
+ * @returns Child graph list preserving source indexes.
252
+ */
253
+ function lowerChildGraphs(schemas) {
254
+ const graphs = new Array(schemas.length);
255
+ for (let index = 0; index < schemas.length; index += 1) {
256
+ const schema = schemas[index];
257
+ if (schema !== undefined) {
258
+ graphs[index] = lowerChildGraph(schema);
259
+ }
260
+ }
261
+ return graphs;
262
+ }
263
+ /**
264
+ * @brief Lower object entries into IR entries with optimized child graphs.
265
+ * @details The IR keeps the original schema beside each child graph so
266
+ * diagnostics and dynamic fallback paths can still recover schema-level detail.
267
+ * @param entries Object schema entries.
268
+ * @returns IR object-shape entries with optimized child graphs.
269
+ */
270
+ function lowerObjectShapeEntries(entries) {
271
+ const lowered = new Array(entries.length);
139
272
  for (let index = 0; index < entries.length; index += 1) {
140
273
  const entry = entries[index];
141
- if (entry === undefined) {
142
- continue;
143
- }
144
- const prop = builder.getProp(value, entry.key);
145
- const propTest = lowerPredicate(builder, entry.schema, prop);
146
- const hasData = builder.hasOwnData(value, entry.key);
147
- if (entry.presence === PresenceTag.Optional) {
148
- const hasKey = builder.hasOwn(value, entry.key);
149
- tests.push(builder.or([
150
- builder.not(hasKey),
151
- builder.and([hasData, propTest])
152
- ]));
153
- }
154
- else {
155
- tests.push(hasData);
156
- tests.push(propTest);
157
- }
158
- }
159
- if (schema.mode === ObjectModeTag.Strict) {
160
- const keys = new Array(entries.length);
161
- for (let index = 0; index < entries.length; index += 1) {
162
- const entry = entries[index];
163
- if (entry !== undefined) {
164
- keys[index] = entry.key;
165
- }
274
+ if (entry !== undefined) {
275
+ lowered[index] = {
276
+ key: entry.key,
277
+ schema: entry.schema,
278
+ graph: lowerChildGraph(entry.schema),
279
+ presence: entry.presence
280
+ };
166
281
  }
167
- tests.push(builder.strictKeys(value, keys));
168
282
  }
169
- return builder.and(tests);
283
+ return lowered;
170
284
  }
171
285
  /**
172
- * @brief lower union.
286
+ * @brief Select the most specific IR shape for a union schema.
287
+ * @details The lowering order is intentional: literal object discriminants are
288
+ * most precise, primitive-only masks are cheapest for scalar unions, and the
289
+ * general dispatch node preserves behavior for mixed or opaque branches.
290
+ * @param builder Graph builder owning the current graph.
291
+ * @param options Union option schemas.
292
+ * @param value Node id that produces the candidate value.
293
+ * @returns Node id for the selected union lowering strategy.
173
294
  */
174
295
  function lowerUnion(builder, options, value) {
175
- const tests = [];
296
+ const discriminant = inferObjectUnionDiscriminant(options);
297
+ if (discriminant !== undefined) {
298
+ /*
299
+ * Object unions with a common required string literal key can dispatch by
300
+ * one property read instead of probing every branch.
301
+ */
302
+ return builder.discriminantDispatch(value, discriminant.key, discriminant.literals, options, lowerChildGraphs(options));
303
+ }
304
+ if (isPrimitiveUnionOptions(options)) {
305
+ /*
306
+ * Primitive-only unions lower to a compact mask dispatch so codegen can
307
+ * group arms by typeof result.
308
+ */
309
+ return builder.primitiveUnion(value, lowerChildGraphs(options), lowerUnionMasks(options));
310
+ }
311
+ return builder.unionDispatch(value, options, lowerChildGraphs(options), lowerUnionMasks(options));
312
+ }
313
+ /**
314
+ * @brief Bit positions used to summarize possible union root domains.
315
+ * @details Masks let lowering and codegen skip branches whose root JavaScript
316
+ * kind is impossible for the candidate value.
317
+ */
318
+ const UnionMask = {
319
+ None: 0,
320
+ String: 1 << 0,
321
+ Number: 1 << 1,
322
+ Boolean: 1 << 2,
323
+ BigInt: 1 << 3,
324
+ Symbol: 1 << 4,
325
+ Undefined: 1 << 5,
326
+ Null: 1 << 6,
327
+ Array: 1 << 7,
328
+ Object: 1 << 8,
329
+ Function: 1 << 9,
330
+ Any: (1 << 10) - 1
331
+ };
332
+ /**
333
+ * @brief Mask containing only domains accepted by primitive-union dispatch.
334
+ * @details Object and array domains are excluded because they need descriptor,
335
+ * key, or child graph handling that primitive dispatch deliberately avoids.
336
+ */
337
+ const PrimitiveUnionMask = UnionMask.String |
338
+ UnionMask.Number |
339
+ UnionMask.Boolean |
340
+ UnionMask.BigInt |
341
+ UnionMask.Symbol |
342
+ UnionMask.Undefined |
343
+ UnionMask.Null;
344
+ /**
345
+ * @brief Decide whether a union can use primitive-domain dispatch.
346
+ * @details The generated graph can replace ordered branch probing with a small
347
+ * domain mask only when every reachable option is primitive-shaped. Empty
348
+ * unions are rejected here so the caller does not emit a vacuous dispatcher.
349
+ * @param options Union option schemas in public declaration order.
350
+ * @returns True when every reachable option has a primitive-only mask.
351
+ */
352
+ function isPrimitiveUnionOptions(options) {
353
+ let sawReachable = false;
176
354
  for (let index = 0; index < options.length; index += 1) {
177
355
  const option = options[index];
178
- if (option === undefined) {
356
+ const mask = option === undefined ? UnionMask.None : schemaUnionMask(option);
357
+ if (mask === UnionMask.None) {
179
358
  continue;
180
359
  }
181
- tests.push(lowerPredicate(builder, option, value));
360
+ if ((mask & ~PrimitiveUnionMask) !== 0) {
361
+ return false;
362
+ }
363
+ sawReachable = true;
364
+ }
365
+ return sawReachable;
366
+ }
367
+ /**
368
+ * @brief Compute root-domain masks for every union option.
369
+ * @details The returned array is index-aligned with the source options so
370
+ * generated dispatch can keep branch order while cheaply rejecting impossible
371
+ * arms by runtime kind.
372
+ * @param options Union option schemas.
373
+ * @returns Primitive-domain mask per union option.
374
+ */
375
+ function lowerUnionMasks(options) {
376
+ const masks = new Array(options.length);
377
+ for (let index = 0; index < options.length; index += 1) {
378
+ const option = options[index];
379
+ masks[index] = option === undefined ? UnionMask.None : schemaUnionMask(option);
380
+ }
381
+ return masks;
382
+ }
383
+ /**
384
+ * @brief Approximate the possible root runtime domains for a schema.
385
+ * @details The mask is intentionally conservative. Static scalar and container
386
+ * schemas narrow to precise domains, while lazy and refinement schemas widen to
387
+ * the full mask because their behavior is not known during lowering.
388
+ * @param schema Schema whose possible runtime domain is needed.
389
+ * @returns Bit mask describing the schema's possible runtime domains.
390
+ */
391
+ function schemaUnionMask(schema) {
392
+ switch (schema.tag) {
393
+ case SchemaTag.Unknown:
394
+ return UnionMask.Any;
395
+ case SchemaTag.Never:
396
+ return UnionMask.None;
397
+ case SchemaTag.String:
398
+ return UnionMask.String;
399
+ case SchemaTag.Number:
400
+ return UnionMask.Number;
401
+ case SchemaTag.Date:
402
+ return UnionMask.Object;
403
+ case SchemaTag.BigInt:
404
+ return UnionMask.BigInt;
405
+ case SchemaTag.Symbol:
406
+ return UnionMask.Symbol;
407
+ case SchemaTag.Boolean:
408
+ return UnionMask.Boolean;
409
+ case SchemaTag.Literal:
410
+ return literalUnionMask(schema.value);
411
+ case SchemaTag.Array:
412
+ case SchemaTag.Tuple:
413
+ return UnionMask.Array;
414
+ case SchemaTag.Object:
415
+ case SchemaTag.Record:
416
+ case SchemaTag.Map:
417
+ case SchemaTag.Set:
418
+ case SchemaTag.InstanceOf:
419
+ case SchemaTag.Property:
420
+ case SchemaTag.DiscriminatedUnion:
421
+ return UnionMask.Object;
422
+ case SchemaTag.Optional:
423
+ case SchemaTag.Undefinedable:
424
+ return UnionMask.Undefined | schemaUnionMask(schema.inner);
425
+ case SchemaTag.Nullable:
426
+ return UnionMask.Null | schemaUnionMask(schema.inner);
427
+ case SchemaTag.Brand:
428
+ return schemaUnionMask(schema.inner);
429
+ case SchemaTag.Intersection:
430
+ /*
431
+ * Intersections accept values accepted by both sides, so their
432
+ * possible runtime domain is the bitwise intersection of both masks.
433
+ */
434
+ return schemaUnionMask(schema.left) & schemaUnionMask(schema.right);
435
+ case SchemaTag.Union:
436
+ return unionOptionsMask(schema.options);
437
+ case SchemaTag.Lazy:
438
+ case SchemaTag.Refine:
439
+ return UnionMask.Any;
440
+ }
441
+ }
442
+ /**
443
+ * @brief Merge root-domain masks for a union option vector.
444
+ * @details This summary feeds recursive mask analysis for nested unions without
445
+ * expanding branch graphs during a simple domain query.
446
+ * @param options Union option schemas.
447
+ * @returns Bitwise union of all option masks.
448
+ */
449
+ function unionOptionsMask(options) {
450
+ let mask = UnionMask.None;
451
+ for (let index = 0; index < options.length; index += 1) {
452
+ const option = options[index];
453
+ if (option !== undefined) {
454
+ mask |= schemaUnionMask(option);
455
+ }
456
+ }
457
+ return mask;
458
+ }
459
+ /**
460
+ * @brief Map a literal value to the matching root-domain bit.
461
+ * @details Literal schemas participate in union dispatch by their JavaScript
462
+ * runtime kind. Object literals are not supported by TypeSea literal schemas and
463
+ * therefore fall back to the empty mask.
464
+ * @param value Literal value from a literal schema.
465
+ * @returns Domain bit for the literal's runtime type.
466
+ */
467
+ function literalUnionMask(value) {
468
+ if (value === null) {
469
+ return UnionMask.Null;
470
+ }
471
+ switch (typeof value) {
472
+ case "string":
473
+ return UnionMask.String;
474
+ case "number":
475
+ return UnionMask.Number;
476
+ case "boolean":
477
+ return UnionMask.Boolean;
478
+ case "bigint":
479
+ return UnionMask.BigInt;
480
+ case "symbol":
481
+ return UnionMask.Symbol;
482
+ case "undefined":
483
+ return UnionMask.Undefined;
484
+ default:
485
+ return UnionMask.None;
182
486
  }
183
- return builder.or(tests);
184
487
  }
185
488
  /**
186
- * @brief lower discriminated union.
489
+ * @brief Lower an explicit discriminated union into table dispatch IR.
490
+ * @details Builder validation already proved that every branch requires its
491
+ * literal tag. Lowering can therefore store the literal table directly and let
492
+ * codegen emit a single property read plus branch selection.
493
+ * @param builder Graph builder owning the current graph.
494
+ * @param key Discriminant property name.
495
+ * @param cases Closed discriminated union cases.
496
+ * @param value Node id that produces the candidate value.
497
+ * @returns Node id for discriminant dispatch.
187
498
  */
188
499
  function lowerDiscriminatedUnion(builder, key, cases, value) {
189
500
  const literals = new Array(cases.length);
@@ -195,5 +506,110 @@ function lowerDiscriminatedUnion(builder, key, cases, value) {
195
506
  schemas[index] = unionCase.schema;
196
507
  }
197
508
  }
198
- return builder.discriminantDispatch(value, key, literals, schemas);
509
+ return builder.discriminantDispatch(value, key, literals, schemas, lowerChildGraphs(schemas));
510
+ }
511
+ /**
512
+ * @brief Search a plain object union for an implicit discriminant key.
513
+ * @details Only required string literal fields from the first branch are
514
+ * candidates. Every other branch must prove the same key with a unique literal
515
+ * before the union can use table dispatch.
516
+ * @param options Union option schemas.
517
+ * @returns Shared discriminant key and literals, or undefined when not provable.
518
+ */
519
+ function inferObjectUnionDiscriminant(options) {
520
+ if (options.length < 2) {
521
+ return undefined;
522
+ }
523
+ const first = options[0];
524
+ if (first?.tag !== SchemaTag.Object) {
525
+ return undefined;
526
+ }
527
+ const entries = first.entries;
528
+ for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 1) {
529
+ const entry = entries[entryIndex];
530
+ if (entry === undefined) {
531
+ continue;
532
+ }
533
+ const firstLiteral = readRequiredStringLiteral(entry);
534
+ if (firstLiteral === undefined) {
535
+ continue;
536
+ }
537
+ /*
538
+ * Only keys already required by the first option are candidates. Every
539
+ * other option must carry a unique required string literal for that key.
540
+ */
541
+ const literals = readObjectUnionDiscriminantLiterals(options, entry.key);
542
+ if (literals !== undefined) {
543
+ return {
544
+ key: entry.key,
545
+ literals
546
+ };
547
+ }
548
+ }
549
+ return undefined;
550
+ }
551
+ /**
552
+ * @brief Build the literal dispatch table for an inferred object union.
553
+ * @details The table is usable only when each branch owns the candidate key,
554
+ * requires it, and binds it to a unique string literal. Repeated or missing
555
+ * literals make branch selection ambiguous, so this helper returns undefined
556
+ * and the lowerer keeps the general union path.
557
+ * @param options Union option schemas in branch order.
558
+ * @param key Candidate discriminant key shared by the branches.
559
+ * @returns Literal table when every option has a unique required string literal.
560
+ */
561
+ function readObjectUnionDiscriminantLiterals(options, key) {
562
+ const literals = new Array(options.length);
563
+ const seen = new Set();
564
+ for (let index = 0; index < options.length; index += 1) {
565
+ const option = options[index];
566
+ if (option?.tag !== SchemaTag.Object) {
567
+ return undefined;
568
+ }
569
+ const literal = readObjectDiscriminantLiteral(option, key);
570
+ if (literal === undefined || seen.has(literal)) {
571
+ /*
572
+ * Missing or repeated literals make direct dispatch ambiguous, so the
573
+ * union must fall back to general branch probing.
574
+ */
575
+ return undefined;
576
+ }
577
+ seen.add(literal);
578
+ literals[index] = literal;
579
+ }
580
+ return literals;
581
+ }
582
+ /**
583
+ * @brief Locate the required string literal carried by one object branch.
584
+ * @details Optional and wrapped non-literal fields are rejected because direct
585
+ * dispatch must prove that accepting the branch also proves the tag value.
586
+ * @param schema Object schema to inspect.
587
+ * @param key Candidate discriminant key.
588
+ * @returns Required string literal value for the key, or undefined.
589
+ */
590
+ function readObjectDiscriminantLiteral(schema, key) {
591
+ const entries = schema.entries;
592
+ for (let index = 0; index < entries.length; index += 1) {
593
+ const entry = entries[index];
594
+ if (entry?.key === key) {
595
+ return readRequiredStringLiteral(entry);
596
+ }
597
+ }
598
+ return undefined;
599
+ }
600
+ /**
601
+ * @brief Prove that an object entry is a required string literal tag.
602
+ * @details The discriminant optimizer deliberately refuses optional,
603
+ * undefinedable, or refined tags. Those forms may still validate correctly, but
604
+ * they do not provide a branch-selection proof before child validation runs.
605
+ * @param entry Object entry to inspect.
606
+ * @returns Literal string when the entry is required and exactly string-literal.
607
+ */
608
+ function readRequiredStringLiteral(entry) {
609
+ if (entry.presence !== PresenceTag.Required ||
610
+ entry.schema.tag !== SchemaTag.Literal ||
611
+ typeof entry.schema.value !== "string") {
612
+ return undefined;
613
+ }
614
+ return entry.schema.value;
199
615
  }