@elaraai/east 0.0.1-beta.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 (251) hide show
  1. package/LICENSE.md +682 -0
  2. package/README.md +276 -0
  3. package/dist/src/analyze.d.ts +95 -0
  4. package/dist/src/analyze.d.ts.map +1 -0
  5. package/dist/src/analyze.js +1110 -0
  6. package/dist/src/analyze.js.map +1 -0
  7. package/dist/src/ast.d.ts +263 -0
  8. package/dist/src/ast.d.ts.map +1 -0
  9. package/dist/src/ast.js +151 -0
  10. package/dist/src/ast.js.map +1 -0
  11. package/dist/src/ast_to_ir.d.ts +24 -0
  12. package/dist/src/ast_to_ir.d.ts.map +1 -0
  13. package/dist/src/ast_to_ir.js +834 -0
  14. package/dist/src/ast_to_ir.js.map +1 -0
  15. package/dist/src/builtins.d.ts +18 -0
  16. package/dist/src/builtins.d.ts.map +1 -0
  17. package/dist/src/builtins.js +1105 -0
  18. package/dist/src/builtins.js.map +1 -0
  19. package/dist/src/comparison.d.ts +28 -0
  20. package/dist/src/comparison.d.ts.map +1 -0
  21. package/dist/src/comparison.js +1017 -0
  22. package/dist/src/comparison.js.map +1 -0
  23. package/dist/src/compile.d.ts +22 -0
  24. package/dist/src/compile.d.ts.map +1 -0
  25. package/dist/src/compile.js +3260 -0
  26. package/dist/src/compile.js.map +1 -0
  27. package/dist/src/containers/ref.d.ts +106 -0
  28. package/dist/src/containers/ref.d.ts.map +1 -0
  29. package/dist/src/containers/ref.js +100 -0
  30. package/dist/src/containers/ref.js.map +1 -0
  31. package/dist/src/containers/sortedmap.d.ts +165 -0
  32. package/dist/src/containers/sortedmap.d.ts.map +1 -0
  33. package/dist/src/containers/sortedmap.js +237 -0
  34. package/dist/src/containers/sortedmap.js.map +1 -0
  35. package/dist/src/containers/sortedset.d.ts +185 -0
  36. package/dist/src/containers/sortedset.d.ts.map +1 -0
  37. package/dist/src/containers/sortedset.js +312 -0
  38. package/dist/src/containers/sortedset.js.map +1 -0
  39. package/dist/src/containers/variant.d.ts +131 -0
  40. package/dist/src/containers/variant.d.ts.map +1 -0
  41. package/dist/src/containers/variant.js +68 -0
  42. package/dist/src/containers/variant.js.map +1 -0
  43. package/dist/src/datetime_format/parse.d.ts +50 -0
  44. package/dist/src/datetime_format/parse.d.ts.map +1 -0
  45. package/dist/src/datetime_format/parse.js +908 -0
  46. package/dist/src/datetime_format/parse.js.map +1 -0
  47. package/dist/src/datetime_format/print.d.ts +35 -0
  48. package/dist/src/datetime_format/print.d.ts.map +1 -0
  49. package/dist/src/datetime_format/print.js +157 -0
  50. package/dist/src/datetime_format/print.js.map +1 -0
  51. package/dist/src/datetime_format/tokenize.d.ts +76 -0
  52. package/dist/src/datetime_format/tokenize.d.ts.map +1 -0
  53. package/dist/src/datetime_format/tokenize.js +271 -0
  54. package/dist/src/datetime_format/tokenize.js.map +1 -0
  55. package/dist/src/datetime_format/types.d.ts +99 -0
  56. package/dist/src/datetime_format/types.d.ts.map +1 -0
  57. package/dist/src/datetime_format/types.js +103 -0
  58. package/dist/src/datetime_format/types.js.map +1 -0
  59. package/dist/src/datetime_format/validate.d.ts +51 -0
  60. package/dist/src/datetime_format/validate.d.ts.map +1 -0
  61. package/dist/src/datetime_format/validate.js +208 -0
  62. package/dist/src/datetime_format/validate.js.map +1 -0
  63. package/dist/src/default.d.ts +21 -0
  64. package/dist/src/default.d.ts.map +1 -0
  65. package/dist/src/default.js +82 -0
  66. package/dist/src/default.js.map +1 -0
  67. package/dist/src/eastir.d.ts +33 -0
  68. package/dist/src/eastir.d.ts.map +1 -0
  69. package/dist/src/eastir.js +92 -0
  70. package/dist/src/eastir.js.map +1 -0
  71. package/dist/src/error.d.ts +13 -0
  72. package/dist/src/error.d.ts.map +1 -0
  73. package/dist/src/error.js +8 -0
  74. package/dist/src/error.js.map +1 -0
  75. package/dist/src/expr/array.d.ts +1711 -0
  76. package/dist/src/expr/array.d.ts.map +1 -0
  77. package/dist/src/expr/array.js +1805 -0
  78. package/dist/src/expr/array.js.map +1 -0
  79. package/dist/src/expr/ast.d.ts +17 -0
  80. package/dist/src/expr/ast.d.ts.map +1 -0
  81. package/dist/src/expr/ast.js +302 -0
  82. package/dist/src/expr/ast.js.map +1 -0
  83. package/dist/src/expr/blob.d.ts +141 -0
  84. package/dist/src/expr/blob.d.ts.map +1 -0
  85. package/dist/src/expr/blob.js +198 -0
  86. package/dist/src/expr/blob.js.map +1 -0
  87. package/dist/src/expr/block.d.ts +201 -0
  88. package/dist/src/expr/block.d.ts.map +1 -0
  89. package/dist/src/expr/block.js +1505 -0
  90. package/dist/src/expr/block.js.map +1 -0
  91. package/dist/src/expr/boolean.d.ts +207 -0
  92. package/dist/src/expr/boolean.d.ts.map +1 -0
  93. package/dist/src/expr/boolean.js +261 -0
  94. package/dist/src/expr/boolean.js.map +1 -0
  95. package/dist/src/expr/datetime.d.ts +544 -0
  96. package/dist/src/expr/datetime.d.ts.map +1 -0
  97. package/dist/src/expr/datetime.js +980 -0
  98. package/dist/src/expr/datetime.js.map +1 -0
  99. package/dist/src/expr/dict.d.ts +1242 -0
  100. package/dist/src/expr/dict.d.ts.map +1 -0
  101. package/dist/src/expr/dict.js +1492 -0
  102. package/dist/src/expr/dict.js.map +1 -0
  103. package/dist/src/expr/expr.d.ts +95 -0
  104. package/dist/src/expr/expr.d.ts.map +1 -0
  105. package/dist/src/expr/expr.js +171 -0
  106. package/dist/src/expr/expr.js.map +1 -0
  107. package/dist/src/expr/float.d.ts +357 -0
  108. package/dist/src/expr/float.d.ts.map +1 -0
  109. package/dist/src/expr/float.js +637 -0
  110. package/dist/src/expr/float.js.map +1 -0
  111. package/dist/src/expr/function.d.ts +46 -0
  112. package/dist/src/expr/function.d.ts.map +1 -0
  113. package/dist/src/expr/function.js +58 -0
  114. package/dist/src/expr/function.js.map +1 -0
  115. package/dist/src/expr/index.d.ts +450 -0
  116. package/dist/src/expr/index.d.ts.map +1 -0
  117. package/dist/src/expr/index.js +423 -0
  118. package/dist/src/expr/index.js.map +1 -0
  119. package/dist/src/expr/integer.d.ts +256 -0
  120. package/dist/src/expr/integer.d.ts.map +1 -0
  121. package/dist/src/expr/integer.js +311 -0
  122. package/dist/src/expr/integer.js.map +1 -0
  123. package/dist/src/expr/libs/array.d.ts +106 -0
  124. package/dist/src/expr/libs/array.d.ts.map +1 -0
  125. package/dist/src/expr/libs/array.js +140 -0
  126. package/dist/src/expr/libs/array.js.map +1 -0
  127. package/dist/src/expr/libs/blob.d.ts +42 -0
  128. package/dist/src/expr/libs/blob.d.ts.map +1 -0
  129. package/dist/src/expr/libs/blob.js +70 -0
  130. package/dist/src/expr/libs/blob.js.map +1 -0
  131. package/dist/src/expr/libs/datetime.d.ts +479 -0
  132. package/dist/src/expr/libs/datetime.d.ts.map +1 -0
  133. package/dist/src/expr/libs/datetime.js +624 -0
  134. package/dist/src/expr/libs/datetime.js.map +1 -0
  135. package/dist/src/expr/libs/dict.d.ts +66 -0
  136. package/dist/src/expr/libs/dict.d.ts.map +1 -0
  137. package/dist/src/expr/libs/dict.js +77 -0
  138. package/dist/src/expr/libs/dict.js.map +1 -0
  139. package/dist/src/expr/libs/float.d.ts +299 -0
  140. package/dist/src/expr/libs/float.d.ts.map +1 -0
  141. package/dist/src/expr/libs/float.js +564 -0
  142. package/dist/src/expr/libs/float.js.map +1 -0
  143. package/dist/src/expr/libs/integer.d.ts +228 -0
  144. package/dist/src/expr/libs/integer.d.ts.map +1 -0
  145. package/dist/src/expr/libs/integer.js +398 -0
  146. package/dist/src/expr/libs/integer.js.map +1 -0
  147. package/dist/src/expr/libs/set.d.ts +59 -0
  148. package/dist/src/expr/libs/set.d.ts.map +1 -0
  149. package/dist/src/expr/libs/set.js +69 -0
  150. package/dist/src/expr/libs/set.js.map +1 -0
  151. package/dist/src/expr/libs/string.d.ts +71 -0
  152. package/dist/src/expr/libs/string.d.ts.map +1 -0
  153. package/dist/src/expr/libs/string.js +75 -0
  154. package/dist/src/expr/libs/string.js.map +1 -0
  155. package/dist/src/expr/never.d.ts +15 -0
  156. package/dist/src/expr/never.d.ts.map +1 -0
  157. package/dist/src/expr/never.js +12 -0
  158. package/dist/src/expr/never.js.map +1 -0
  159. package/dist/src/expr/null.d.ts +15 -0
  160. package/dist/src/expr/null.d.ts.map +1 -0
  161. package/dist/src/expr/null.js +12 -0
  162. package/dist/src/expr/null.js.map +1 -0
  163. package/dist/src/expr/ref.d.ts +103 -0
  164. package/dist/src/expr/ref.d.ts.map +1 -0
  165. package/dist/src/expr/ref.js +131 -0
  166. package/dist/src/expr/ref.js.map +1 -0
  167. package/dist/src/expr/regex_validation.d.ts +25 -0
  168. package/dist/src/expr/regex_validation.d.ts.map +1 -0
  169. package/dist/src/expr/regex_validation.js +130 -0
  170. package/dist/src/expr/regex_validation.js.map +1 -0
  171. package/dist/src/expr/set.d.ts +1071 -0
  172. package/dist/src/expr/set.d.ts.map +1 -0
  173. package/dist/src/expr/set.js +1137 -0
  174. package/dist/src/expr/set.js.map +1 -0
  175. package/dist/src/expr/string.d.ts +414 -0
  176. package/dist/src/expr/string.d.ts.map +1 -0
  177. package/dist/src/expr/string.js +683 -0
  178. package/dist/src/expr/string.js.map +1 -0
  179. package/dist/src/expr/struct.d.ts +48 -0
  180. package/dist/src/expr/struct.d.ts.map +1 -0
  181. package/dist/src/expr/struct.js +65 -0
  182. package/dist/src/expr/struct.js.map +1 -0
  183. package/dist/src/expr/types.d.ts +68 -0
  184. package/dist/src/expr/types.d.ts.map +1 -0
  185. package/dist/src/expr/types.js +6 -0
  186. package/dist/src/expr/types.js.map +1 -0
  187. package/dist/src/expr/variant.d.ts +137 -0
  188. package/dist/src/expr/variant.d.ts.map +1 -0
  189. package/dist/src/expr/variant.js +105 -0
  190. package/dist/src/expr/variant.js.map +1 -0
  191. package/dist/src/fuzz.d.ts +80 -0
  192. package/dist/src/fuzz.d.ts.map +1 -0
  193. package/dist/src/fuzz.js +300 -0
  194. package/dist/src/fuzz.js.map +1 -0
  195. package/dist/src/index.d.ts +21 -0
  196. package/dist/src/index.d.ts.map +1 -0
  197. package/dist/src/index.js +21 -0
  198. package/dist/src/index.js.map +1 -0
  199. package/dist/src/internal.d.ts +36 -0
  200. package/dist/src/internal.d.ts.map +1 -0
  201. package/dist/src/internal.js +11 -0
  202. package/dist/src/internal.js.map +1 -0
  203. package/dist/src/ir.d.ts +1571 -0
  204. package/dist/src/ir.d.ts.map +1 -0
  205. package/dist/src/ir.js +56 -0
  206. package/dist/src/ir.js.map +1 -0
  207. package/dist/src/location.d.ts +48 -0
  208. package/dist/src/location.d.ts.map +1 -0
  209. package/dist/src/location.js +62 -0
  210. package/dist/src/location.js.map +1 -0
  211. package/dist/src/platform.d.ts +21 -0
  212. package/dist/src/platform.d.ts.map +1 -0
  213. package/dist/src/platform.js +8 -0
  214. package/dist/src/platform.js.map +1 -0
  215. package/dist/src/serialization/beast.d.ts +39 -0
  216. package/dist/src/serialization/beast.d.ts.map +1 -0
  217. package/dist/src/serialization/beast.js +555 -0
  218. package/dist/src/serialization/beast.js.map +1 -0
  219. package/dist/src/serialization/beast2-stream.d.ts +38 -0
  220. package/dist/src/serialization/beast2-stream.d.ts.map +1 -0
  221. package/dist/src/serialization/beast2-stream.js +665 -0
  222. package/dist/src/serialization/beast2-stream.js.map +1 -0
  223. package/dist/src/serialization/beast2.d.ts +41 -0
  224. package/dist/src/serialization/beast2.d.ts.map +1 -0
  225. package/dist/src/serialization/beast2.js +489 -0
  226. package/dist/src/serialization/beast2.js.map +1 -0
  227. package/dist/src/serialization/binary-utils.d.ts +151 -0
  228. package/dist/src/serialization/binary-utils.d.ts.map +1 -0
  229. package/dist/src/serialization/binary-utils.js +929 -0
  230. package/dist/src/serialization/binary-utils.js.map +1 -0
  231. package/dist/src/serialization/east.d.ts +84 -0
  232. package/dist/src/serialization/east.d.ts.map +1 -0
  233. package/dist/src/serialization/east.js +1802 -0
  234. package/dist/src/serialization/east.js.map +1 -0
  235. package/dist/src/serialization/index.d.ts +11 -0
  236. package/dist/src/serialization/index.d.ts.map +1 -0
  237. package/dist/src/serialization/index.js +12 -0
  238. package/dist/src/serialization/index.js.map +1 -0
  239. package/dist/src/serialization/json.d.ts +36 -0
  240. package/dist/src/serialization/json.d.ts.map +1 -0
  241. package/dist/src/serialization/json.js +849 -0
  242. package/dist/src/serialization/json.js.map +1 -0
  243. package/dist/src/type_of_type.d.ts +115 -0
  244. package/dist/src/type_of_type.d.ts.map +1 -0
  245. package/dist/src/type_of_type.js +362 -0
  246. package/dist/src/type_of_type.js.map +1 -0
  247. package/dist/src/types.d.ts +648 -0
  248. package/dist/src/types.d.ts.map +1 -0
  249. package/dist/src/types.js +1631 -0
  250. package/dist/src/types.js.map +1 -0
  251. package/package.json +87 -0
@@ -0,0 +1,1631 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ import { isRef } from "./containers/ref.js";
6
+ import { SortedMap } from "./containers/sortedmap.js";
7
+ import { SortedSet } from "./containers/sortedset.js";
8
+ import { variant_symbol } from "./containers/variant.js";
9
+ /**
10
+ * Error thrown when type operations encounter incompatible types.
11
+ *
12
+ * @remarks
13
+ * Used by type union, intersection, and equality operations when types
14
+ * cannot be combined or compared as requested.
15
+ */
16
+ class TypeMismatchError extends Error {
17
+ constructor(message, options) {
18
+ super(message, options);
19
+ this.name = "TypeMismatchError";
20
+ }
21
+ }
22
+ /** Singleton instance of the Never type. */
23
+ export const NeverType = { type: "Never" };
24
+ /** Singleton instance of the Null type. */
25
+ export const NullType = { type: "Null" };
26
+ /** Singleton instance of the Boolean type. */
27
+ export const BooleanType = { type: "Boolean" };
28
+ /** Singleton instance of the Integer type. */
29
+ export const IntegerType = { type: "Integer" };
30
+ /** Singleton instance of the Float type. */
31
+ export const FloatType = { type: "Float" };
32
+ /** Singleton instance of the String type. */
33
+ export const StringType = { type: "String" };
34
+ /** Singleton instance of the DateTime type. */
35
+ export const DateTimeType = { type: "DateTime" };
36
+ /** Singleton instance of the Blob type. */
37
+ export const BlobType = { type: "Blob" };
38
+ /**
39
+ * Constructs a reference (or cell) type with the specified element type.
40
+ *
41
+ * Conceptually, a reference is a box or cell that can hold a value of a specified type.
42
+ * The reference can be mutated by updating the contents of the reference.
43
+ * Multiple references can point to the same underlying value.
44
+ *
45
+ * @typeParam T - The type of value held by the reference
46
+ * @param type - The element type
47
+ * @returns A reference type
48
+ * @throws When the element type contains functions
49
+ */
50
+ export function RefType(type) {
51
+ if (typeof type !== "string" && !isDataType(type)) {
52
+ throw new Error(`Ref value type must be a (non-function) data type, got ${printType(type)}`);
53
+ }
54
+ return { type: "Ref", value: type };
55
+ }
56
+ ;
57
+ /**
58
+ * Constructs an Array type with the specified element type.
59
+ *
60
+ * @typeParam T - The type of elements in the array
61
+ * @param type - The element type
62
+ * @returns An Array type
63
+ * @throws When the element type contains functions
64
+ */
65
+ export function ArrayType(type) {
66
+ if (typeof type !== "string" && !isDataType(type)) {
67
+ throw new Error(`Array value type must be a (non-function) data type, got ${printType(type)}`);
68
+ }
69
+ return { type: "Array", value: type };
70
+ }
71
+ ;
72
+ /**
73
+ * Constructs a Set type with the specified key type.
74
+ *
75
+ * @typeParam T - The type of keys in the set
76
+ * @param type - The key type
77
+ * @returns A Set type
78
+ * @throws When the key type is not immutable
79
+ */
80
+ export function SetType(type) {
81
+ if (typeof type !== "string" && !isImmutableType(type)) {
82
+ throw new Error(`Set key type must be an immutable type, got ${printType(type)}`);
83
+ }
84
+ return { type: "Set", key: type };
85
+ }
86
+ ;
87
+ /**
88
+ * Constructs a Dict type with the specified key and value types.
89
+ *
90
+ * @typeParam K - The type of keys in the dictionary
91
+ * @typeParam T - The type of values in the dictionary
92
+ * @param key - The key type
93
+ * @param value - The value type
94
+ * @returns A Dict type
95
+ * @throws When the key type is not immutable or value type contains functions
96
+ */
97
+ export function DictType(key, value) {
98
+ if (typeof key !== "string" && !isImmutableType(key)) {
99
+ throw new Error(`Dict key type must be an immutable type, got ${printType(key)}`);
100
+ }
101
+ if (typeof value !== "string" && !isDataType(value)) {
102
+ throw new Error(`Dict value type must be a (non-function) data type, got ${printType(value)}`);
103
+ }
104
+ return { type: "Dict", key, value };
105
+ }
106
+ ;
107
+ /**
108
+ * Constructs a Struct type with the specified field types.
109
+ *
110
+ * @typeParam Fields - Object type mapping field names to their types
111
+ * @param field_types - Object mapping field names to {@link EastType} instances
112
+ * @returns A Struct type
113
+ */
114
+ export function StructType(field_types) {
115
+ // Validate all field types are data types (no functions)
116
+ for (const [field_name, field_type] of Object.entries(field_types)) {
117
+ if (typeof field_type !== "string" && !isDataType(field_type)) {
118
+ throw new Error(`Struct field ${field_name} must be a (non-function) data type, got ${printType(field_type)}`);
119
+ }
120
+ }
121
+ return { type: "Struct", fields: field_types };
122
+ }
123
+ ;
124
+ /**
125
+ * Constructs a Variant type with the specified cases.
126
+ *
127
+ * @typeParam Cases - Object type mapping case names to their value types
128
+ * @param case_types - Object mapping case names to {@link EastType} instances
129
+ * @returns A Variant type with cases sorted alphabetically
130
+ */
131
+ export function VariantType(case_types) {
132
+ // Validate all case types are data types (no functions)
133
+ for (const [case_name, case_type] of Object.entries(case_types)) {
134
+ if (typeof case_type !== "string" && !isDataType(case_type)) {
135
+ throw new Error(`Variant case ${case_name} must be a (non-function) data type, got ${printType(case_type)}`);
136
+ }
137
+ }
138
+ // Cases are sorted alphabetically by their name
139
+ const cases_sorted = Object.fromEntries(Object.entries(case_types).sort((x, y) => x[0] < y[0] ? -1 : x[0] === y[0] ? 0 : 1));
140
+ return { type: "Variant", cases: cases_sorted };
141
+ }
142
+ ;
143
+ /**
144
+ * Validates that a recursive type has SCC (Strongly Connected Component) size 1.
145
+ *
146
+ * @param type - The type to validate
147
+ * @param allowedMarker - The recursive marker that is allowed (the one being defined)
148
+ * @throws When nested RecursiveTypes with cross-references are detected
149
+ *
150
+ * @remarks
151
+ * This ensures that recursive types don't have mutual recursion or nested
152
+ * RecursiveTypes that reference outer scopes. Only simple recursion (SCC size 1)
153
+ * is supported for predictable behavior and implementation simplicity.
154
+ */
155
+ function validateNotMutuallyRecursive(type, allowedMarker) {
156
+ const visited = new Set();
157
+ function check(t) {
158
+ // Skip string placeholders (used for generic builtins)
159
+ if (typeof t === "string")
160
+ return;
161
+ if (typeof t !== "object" || t === null) {
162
+ let allowedMarkerString = null;
163
+ try {
164
+ allowedMarkerString = printType(allowedMarker);
165
+ }
166
+ catch { }
167
+ throw new Error(`Invalid type encountered during recursion validation: ${t}${allowedMarkerString === null ? "" : ` (allowed marker: ${allowedMarkerString})`}`);
168
+ }
169
+ // Skip allowedMarker
170
+ if (t === allowedMarker)
171
+ return;
172
+ // Avoid infinite loops
173
+ if (visited.has(t))
174
+ return;
175
+ visited.add(t);
176
+ if (t.type === "Recursive") {
177
+ if (t.node === undefined) {
178
+ throw new Error("RecursiveType must have SCC size 1: nested RecursiveTypes with cross-references are not supported. " +
179
+ "Each RecursiveType can only reference itself, not other recursive scopes.");
180
+ }
181
+ else {
182
+ // Recurse into the recursive type's body, with t as the new root
183
+ validateNotMutuallyRecursive(t.node, t);
184
+ }
185
+ }
186
+ else if (t.type === "Struct") {
187
+ for (const [_, field_type] of Object.entries(t.fields)) {
188
+ check(field_type);
189
+ }
190
+ }
191
+ else if (t.type === "Variant") {
192
+ for (const [_, case_type] of Object.entries(t.cases)) {
193
+ check(case_type);
194
+ }
195
+ }
196
+ else if (t.type === "Array") {
197
+ check(t.value);
198
+ }
199
+ else if (t.type === "Set") {
200
+ check(t.key);
201
+ }
202
+ else if (t.type === "Dict") {
203
+ check(t.key);
204
+ check(t.value);
205
+ }
206
+ else if (t.type === "Function") {
207
+ t.inputs.forEach(check);
208
+ check(t.output);
209
+ }
210
+ // Primitive types don't need recursion
211
+ }
212
+ check(type);
213
+ }
214
+ /**
215
+ * Constructs a recursive type with the specified node structure.
216
+ *
217
+ * @typeParam F - Function type that defines the recursive structure
218
+ * @param f - Function that receives a self-reference marker and returns the node type
219
+ * @returns A RecursiveType representing the recursive structure
220
+ * @throws When the type contains functions or has SCC size > 1 (mutual recursion)
221
+ *
222
+ * @remarks
223
+ * The function `f` receives a `RecursiveTypeMarker` representing the recursive
224
+ * reference point. This marker should be used wherever the type recursively refers to itself.
225
+ *
226
+ * Only simple recursion is supported - the type can reference itself multiple times, but
227
+ * nested RecursiveTypes with cross-references (mutual recursion) are not allowed.
228
+ *
229
+ * @example
230
+ * ```ts
231
+ * // Linked list
232
+ * const ListType = RecursiveType(self =>
233
+ * VariantType({
234
+ * nil: NullType,
235
+ * cons: StructType({ head: IntegerType, tail: self })
236
+ * })
237
+ * );
238
+ *
239
+ * // Binary tree
240
+ * const TreeType = RecursiveType(self =>
241
+ * StructType({
242
+ * value: IntegerType,
243
+ * left: OptionType(self),
244
+ * right: OptionType(self)
245
+ * })
246
+ * );
247
+ * ```
248
+ */
249
+ export function RecursiveType(f) {
250
+ const ret = { type: "Recursive", node: undefined };
251
+ const type = f(ret);
252
+ ret.node = type;
253
+ // Validate SCC size 1 (no nested RecursiveTypes with cross-references)
254
+ validateNotMutuallyRecursive(type, ret);
255
+ if (typeof type !== "string" && !isDataType(type)) {
256
+ throw new Error(`Recursive node type must be a (non-function) data type, got ${printType(type)}`);
257
+ }
258
+ return ret;
259
+ }
260
+ ;
261
+ /**
262
+ * Constructs a Function type with the specified input and output types.
263
+ *
264
+ * @typeParam I - Tuple type of input parameter types
265
+ * @typeParam O - The output/return type
266
+ * @param inputs - Array of {@link EastType} instances for each parameter
267
+ * @param output - The {@link EastType} of the return value
268
+ * @returns A Function type
269
+ */
270
+ export function FunctionType(inputs, output, platforms) {
271
+ // TODO - for the moment we default function types to being pure, meaning users may need to annotate their higher-order functions.
272
+ // In future we may want to track side-effects more precisely.
273
+ return { type: "Function", inputs, output, platforms };
274
+ }
275
+ ;
276
+ /**
277
+ * Checks if a type is a pure data type (excludes functions).
278
+ *
279
+ * @param type - The {@link EastType} to check
280
+ * @param recursive_type - Internal parameter for tracking the current recursive type being checked
281
+ * @returns `true` if the type is a pure data type, `false` if it contains functions
282
+ *
283
+ * @remarks
284
+ * Data types can be fully serialized and transmitted between runtimes.
285
+ * This recursively checks {@link StructType}, {@link VariantType}, and {@link RecursiveType} fields.
286
+ */
287
+ export function isDataType(type, recursive_type) {
288
+ // Avoid infinite loops
289
+ if (type === recursive_type) {
290
+ return true;
291
+ }
292
+ if (type.type === "Ref") {
293
+ // Ref constructors check their value type are data types
294
+ return true;
295
+ }
296
+ else if (type.type === "Array") {
297
+ // Array constructors check their value type are data types
298
+ return true;
299
+ }
300
+ else if (type.type === "Set") {
301
+ // Set constructors check their value type are (immutable) data types
302
+ return true;
303
+ }
304
+ else if (type.type === "Dict") {
305
+ // Dict constructors check their key and value types are data types
306
+ return true;
307
+ }
308
+ else if (type.type === "Struct") {
309
+ for (const field_type of Object.values(type.fields)) {
310
+ if (!isDataType(field_type, recursive_type)) {
311
+ return false;
312
+ }
313
+ }
314
+ return true;
315
+ }
316
+ else if (type.type === "Variant") {
317
+ for (const variant of Object.values(type.cases)) {
318
+ if (!isDataType(variant, recursive_type)) {
319
+ return false;
320
+ }
321
+ }
322
+ return true;
323
+ }
324
+ else if (type.type === "Recursive") {
325
+ return type.node === recursive_type ? true : isDataType(type.node, type);
326
+ }
327
+ else if (type.type === "Function") {
328
+ return false;
329
+ }
330
+ else {
331
+ // Primitive types are data types
332
+ return true;
333
+ }
334
+ }
335
+ /**
336
+ * Checks if a type is immutable (can be used as dict keys or set elements).
337
+ *
338
+ * @param type - The {@link EastType} to check
339
+ * @param recursive_type - Internal parameter for tracking the current recursive type being checked
340
+ * @returns `true` if the type is immutable, `false` if it contains mutable collections or functions
341
+ *
342
+ * @remarks
343
+ * Immutable types exclude {@link ArrayType}, {@link SetType}, {@link DictType}, and {@link FunctionType}.
344
+ * This recursively checks {@link StructType}, {@link VariantType}, and {@link RecursiveType} fields.
345
+ */
346
+ export function isImmutableType(type, recursive_type) {
347
+ // Avoid infinite loops
348
+ if (type === recursive_type) {
349
+ return true;
350
+ }
351
+ if (type.type === "Ref") {
352
+ // Refs are mutable
353
+ return false;
354
+ }
355
+ else if (type.type === "Array") {
356
+ // Arrays are mutable
357
+ return false;
358
+ }
359
+ else if (type.type === "Set") {
360
+ // Set are mutable
361
+ return false;
362
+ }
363
+ else if (type.type === "Dict") {
364
+ // Dict are mutable
365
+ return false;
366
+ }
367
+ else if (type.type === "Struct") {
368
+ for (const field_type of Object.values(type.fields)) {
369
+ if (!isImmutableType(field_type, recursive_type)) {
370
+ return false;
371
+ }
372
+ }
373
+ return true;
374
+ }
375
+ else if (type.type === "Variant") {
376
+ for (const variant of Object.values(type.cases)) {
377
+ if (!isImmutableType(variant, recursive_type)) {
378
+ return false;
379
+ }
380
+ }
381
+ return true;
382
+ }
383
+ else if (type.type === "Recursive") {
384
+ return type.node === recursive_type ? true : isImmutableType(type.node, type);
385
+ }
386
+ else if (type.type === "Function") {
387
+ // Functions are not data types, and "immutable data" is a subset of "data"
388
+ return false;
389
+ }
390
+ else {
391
+ // Primitive types are immutable
392
+ return true;
393
+ }
394
+ }
395
+ /**
396
+ * Infers the East type from a runtime JavaScript value.
397
+ *
398
+ * @typeParam V - The TypeScript type of the value
399
+ * @param value - The JavaScript value to analyze
400
+ * @returns The corresponding {@link EastType}
401
+ * @throws When the value is a JavaScript function or cannot be typed
402
+ *
403
+ * @remarks
404
+ * This is the runtime version of the {@link EastTypeOf} type.
405
+ * For arrays, it infers the element type from the first element.
406
+ */
407
+ export function EastTypeOf(value) {
408
+ if (value === null) {
409
+ return NullType;
410
+ }
411
+ else if (typeof value === "boolean") {
412
+ return BooleanType;
413
+ }
414
+ else if (typeof value === "bigint") {
415
+ return IntegerType;
416
+ }
417
+ else if (typeof value === "number") {
418
+ return FloatType;
419
+ }
420
+ else if (typeof value === "string") {
421
+ return StringType;
422
+ }
423
+ if (value instanceof Date) {
424
+ return DateTimeType;
425
+ }
426
+ else if (value instanceof Uint8Array) {
427
+ return BlobType;
428
+ }
429
+ else if (Array.isArray(value)) {
430
+ if (value.length === 0) {
431
+ throw new Error(`Cannot infer East type of empty array`);
432
+ }
433
+ return ArrayType(EastTypeOf(value[0]));
434
+ }
435
+ else if (value instanceof Set || value instanceof SortedSet) {
436
+ if (value.size === 0) {
437
+ throw new Error(`Cannot infer East type of empty set`);
438
+ }
439
+ const first = value.values().next().value;
440
+ return SetType(EastTypeOf(first));
441
+ }
442
+ else if (value instanceof Map || value instanceof SortedMap) {
443
+ if (value.size === 0) {
444
+ throw new Error(`Cannot infer East type of empty map`);
445
+ }
446
+ const first = value.entries().next().value;
447
+ return DictType(EastTypeOf(first[0]), EastTypeOf(first[1]));
448
+ }
449
+ else if (typeof value === "function") {
450
+ throw new Error(`Javascript functions cannot be converted to East functions`);
451
+ }
452
+ else if (typeof value === "object") {
453
+ return StructType(Object.fromEntries(Object.entries(value).map(([k, v]) => [k, EastTypeOf(v)])));
454
+ }
455
+ else {
456
+ throw new Error(`Cannot determine East type for value ${value}`);
457
+ }
458
+ }
459
+ /**
460
+ * Checks if two East types are structurally equal.
461
+ *
462
+ * @param t1 - First {@link EastType} to compare
463
+ * @param t2 - Second {@link EastType} to compare
464
+ * @param r1 - Internal parameter for tracking the first recursive type being compared
465
+ * @param r2 - Internal parameter for tracking the second recursive type being compared
466
+ * @returns `true` if the types are equal, `false` otherwise
467
+ *
468
+ * @remarks
469
+ * Performs structural equality checking, recursively comparing compound types.
470
+ * For {@link StructType} and {@link VariantType}, field/case order matters.
471
+ * For {@link RecursiveType}, uses cycle tracking to handle recursive references.
472
+ */
473
+ export function isTypeEqual(t1, t2, r1 = t1, r2 = t2) {
474
+ if (t1.type === "Recursive") {
475
+ if (t2.type === "Recursive") {
476
+ if (t1.node === r1) {
477
+ return t2.node === r2;
478
+ }
479
+ else if (t2.node === r2) {
480
+ return false;
481
+ }
482
+ else {
483
+ return isTypeEqual(t1.node, t2.node, t1.node, t2.node);
484
+ }
485
+ }
486
+ else {
487
+ // The RecursiveType wrapper is transparent
488
+ return isTypeEqual(t1.node, t2, t1.node, r2);
489
+ }
490
+ }
491
+ else if (t2.type === "Recursive") {
492
+ // The RecursiveType wrapper is transparent
493
+ return isTypeEqual(t1, t2.node, r1, t2.node);
494
+ }
495
+ else if (t1.type === "Never") {
496
+ return t2.type === "Never";
497
+ }
498
+ else if (t1.type === "Null") {
499
+ return t2.type === "Null";
500
+ }
501
+ else if (t1.type === "Boolean") {
502
+ return t2.type === "Boolean";
503
+ }
504
+ else if (t1.type === "Integer") {
505
+ return t2.type === "Integer";
506
+ }
507
+ else if (t1.type === "Float") {
508
+ return t2.type === "Float";
509
+ }
510
+ else if (t1.type === "String") {
511
+ return t2.type === "String";
512
+ }
513
+ else if (t1.type === "DateTime") {
514
+ return t2.type === "DateTime";
515
+ }
516
+ else if (t1.type === "Blob") {
517
+ return t2.type === "Blob";
518
+ }
519
+ else if (t1.type === "Ref") {
520
+ if (t2.type === "Ref") {
521
+ return isTypeEqual(t1.value, t2.value, r1, r2);
522
+ }
523
+ else {
524
+ return false;
525
+ }
526
+ }
527
+ else if (t1.type === "Array") {
528
+ if (t2.type === "Array") {
529
+ return isTypeEqual(t1.value, t2.value, r1, r2);
530
+ }
531
+ else {
532
+ return false;
533
+ }
534
+ }
535
+ else if (t1.type === "Set") {
536
+ if (t2.type === "Set") {
537
+ return isTypeEqual(t1.key, t2.key, r1, r2);
538
+ }
539
+ else {
540
+ return false;
541
+ }
542
+ }
543
+ else if (t1.type === "Dict") {
544
+ if (t2.type === "Dict") {
545
+ return isTypeEqual(t1.key, t2.key, r1, r2) && isTypeEqual(t1.value, t2.value, r1, r2);
546
+ }
547
+ else {
548
+ return false;
549
+ }
550
+ }
551
+ else if (t1.type === "Struct") {
552
+ if (t2.type === "Struct") {
553
+ const e1 = Object.entries(t1.fields);
554
+ const e2 = Object.entries(t2.fields);
555
+ if (e1.length !== e2.length)
556
+ return false;
557
+ let i = 0;
558
+ for (const [k1, f1] of e1) {
559
+ const [k2, f2] = e2[i];
560
+ if (k1 !== k2)
561
+ return false;
562
+ if (!isTypeEqual(f1, f2, r1, r2))
563
+ return false;
564
+ i += 1;
565
+ }
566
+ return true;
567
+ }
568
+ else {
569
+ return false;
570
+ }
571
+ }
572
+ else if (t1.type === "Variant") {
573
+ if (t2.type === "Variant") {
574
+ const e1 = Object.entries(t1.cases);
575
+ const e2 = Object.entries(t2.cases);
576
+ if (e1.length !== e2.length)
577
+ return false;
578
+ let i = 0;
579
+ for (const [k1, f1] of e1) {
580
+ const [k2, f2] = e2[i];
581
+ if (k1 !== k2)
582
+ return false;
583
+ if (!isTypeEqual(f1, f2, r1, r2))
584
+ return false;
585
+ i += 1;
586
+ }
587
+ return true;
588
+ }
589
+ else {
590
+ return false;
591
+ }
592
+ }
593
+ else if (t1.type === "Function") {
594
+ if (t2.type !== "Function") {
595
+ return false;
596
+ }
597
+ // Check input types match
598
+ if (t1.inputs.length !== t2.inputs.length) {
599
+ return false;
600
+ }
601
+ for (let i = 0; i < t1.inputs.length; i++) {
602
+ if (!isTypeEqual(t1.inputs[i], t2.inputs[i], r1, r2)) {
603
+ return false;
604
+ }
605
+ }
606
+ // Check output type matches
607
+ if (!isTypeEqual(t1.output, t2.output, r1, r2)) {
608
+ return false;
609
+ }
610
+ // Check platform requirements match
611
+ if (t1.platforms === null && t2.platforms === null) {
612
+ return true;
613
+ }
614
+ if (t1.platforms === null || t2.platforms === null) {
615
+ return false;
616
+ }
617
+ if (t1.platforms.length !== t2.platforms.length) {
618
+ return false;
619
+ }
620
+ for (let i = 0; i < t1.platforms.length; i++) {
621
+ if (t1.platforms[i] !== t2.platforms[i]) {
622
+ return false;
623
+ }
624
+ }
625
+ return true;
626
+ }
627
+ else {
628
+ throw new Error(`Unknown type encountered during type equality check: ${t1.type}`);
629
+ }
630
+ }
631
+ /**
632
+ * Checks if a JavaScript value conforms to an East type.
633
+ *
634
+ * @param value - The JavaScript value to check
635
+ * @param type - The {@link EastType} to validate against
636
+ * @param node_type - Internal parameter for tracking the current recursive type node
637
+ * @param nodes_visited - Internal parameter for tracking visited nodes to detect cycles
638
+ * @returns `true` if the value matches the type, `false` otherwise
639
+ *
640
+ * @remarks
641
+ * Performs runtime type checking, recursively validating compound types.
642
+ * Accepts both native JavaScript `Set`/`Map` and {@link SortedSet}/{@link SortedMap}.
643
+ * For {@link RecursiveType}, uses cycle detection to handle recursive values.
644
+ */
645
+ export function isValueOf(value, type, node_type, nodes_visited) {
646
+ if (type.type === "Never") {
647
+ return false;
648
+ }
649
+ else if (type.type === "Null") {
650
+ return value === null;
651
+ }
652
+ else if (type.type === "Boolean") {
653
+ return typeof value === "boolean";
654
+ }
655
+ else if (type.type === "Integer") {
656
+ return typeof value === "bigint";
657
+ }
658
+ else if (type.type === "Float") {
659
+ return typeof value === "number";
660
+ }
661
+ else if (type.type === "String") {
662
+ return typeof value === "string";
663
+ }
664
+ else if (type.type === "DateTime") {
665
+ return value instanceof Date;
666
+ }
667
+ else if (type.type === "Blob") {
668
+ return value instanceof Uint8Array;
669
+ }
670
+ else if (type.type === "Ref") {
671
+ if (!isRef(value)) {
672
+ return false;
673
+ }
674
+ return isValueOf(value.value, type.value, node_type, nodes_visited);
675
+ }
676
+ else if (type.type === "Array") {
677
+ if (!Array.isArray(value)) {
678
+ return false;
679
+ }
680
+ for (const x of value) {
681
+ if (!isValueOf(x, type.value, node_type, nodes_visited)) {
682
+ return false;
683
+ }
684
+ }
685
+ return true;
686
+ }
687
+ else if (type.type === "Set") {
688
+ if (!(value instanceof Set || value instanceof SortedSet)) {
689
+ return false;
690
+ }
691
+ for (const x of value) {
692
+ if (!isValueOf(x, type.key, node_type, nodes_visited)) {
693
+ return false;
694
+ }
695
+ }
696
+ return true;
697
+ }
698
+ else if (type.type === "Dict") {
699
+ if (!(value instanceof Map || value instanceof SortedMap)) {
700
+ return false;
701
+ }
702
+ for (const [k, v] of value) {
703
+ if (!isValueOf(k, type.key, node_type, nodes_visited) || !isValueOf(v, type.value, node_type, nodes_visited)) {
704
+ return false;
705
+ }
706
+ }
707
+ return true;
708
+ }
709
+ else if (type.type === "Struct") {
710
+ if (typeof value !== "object" || value === null || Object.getPrototypeOf(value) !== Object.getPrototypeOf({}))
711
+ return false;
712
+ const vs = Object.entries(value);
713
+ const ts = Object.entries(type.fields);
714
+ if (vs.length !== ts.length)
715
+ return false;
716
+ let i = 0;
717
+ for (const [k1, ft] of ts) {
718
+ const [k2, fv] = vs[i];
719
+ if (k1 !== k2)
720
+ return false;
721
+ if (!isValueOf(fv, ft, node_type, nodes_visited))
722
+ return false;
723
+ i += 1;
724
+ }
725
+ return true;
726
+ }
727
+ else if (type.type === "Variant") {
728
+ if (typeof value !== "object" || value === null || value[variant_symbol] !== null)
729
+ return false;
730
+ const t = type.cases[value.type];
731
+ if (t === undefined) {
732
+ return false;
733
+ }
734
+ ;
735
+ return isValueOf(value.value, t, node_type, nodes_visited);
736
+ }
737
+ else if (type.type === "Recursive") {
738
+ if (node_type === type.node) {
739
+ if (nodes_visited.has(value)) {
740
+ return true; // already seen this object
741
+ }
742
+ nodes_visited.add(value);
743
+ return isValueOf(value, type.node, type.node, nodes_visited);
744
+ }
745
+ else {
746
+ // new recursive type, reset stack
747
+ return isValueOf(value, type.node, type.node, new Set([value]));
748
+ }
749
+ }
750
+ else if (type.type === "Function") {
751
+ throw new Error('Javascript functions cannot be converted to East functions');
752
+ }
753
+ else {
754
+ throw new Error(`Unknown type encountered during value type check: ${type.type}`);
755
+ }
756
+ }
757
+ /**
758
+ * Converts an East type to its string representation.
759
+ *
760
+ * @param type - The {@link EastType} to print
761
+ * @param stack - Internal parameter for tracking recursive types
762
+ * @returns A human-readable string representation of the type
763
+ *
764
+ * @remarks
765
+ * Uses East's value syntax with leading dots (e.g., `.Integer`, `.Array .String`).
766
+ */
767
+ export function printType(type, stack = []) {
768
+ // Note this is essentially a bootstrap function.
769
+ // It should print the same output as printing an `EastType` as an `EastTypeType`, but
770
+ // printing values hasn't been defined by this stage and printType is crucial for
771
+ // internal debugging and `Expr` error messages.
772
+ // We do skip mutable aliasing of struct field or variant case lists for clarity.
773
+ // Types are intended to be immutable in any case.
774
+ if (type.type === "Never") {
775
+ return ".Never";
776
+ }
777
+ else if (type.type === "Null") {
778
+ return ".Null";
779
+ }
780
+ else if (type.type === "Boolean") {
781
+ return ".Boolean";
782
+ }
783
+ else if (type.type === "Integer") {
784
+ return ".Integer";
785
+ }
786
+ else if (type.type === "Float") {
787
+ return ".Float";
788
+ }
789
+ else if (type.type === "String") {
790
+ return ".String";
791
+ }
792
+ else if (type.type === "DateTime") {
793
+ return ".DateTime";
794
+ }
795
+ else if (type.type === "Blob") {
796
+ return ".Blob";
797
+ }
798
+ else if (type.type === "Ref") {
799
+ stack.push(type);
800
+ const ret = `.Ref ${printType(type.value, stack)}`;
801
+ stack.pop();
802
+ return ret;
803
+ }
804
+ else if (type.type === "Array") {
805
+ stack.push(type);
806
+ const ret = `.Array ${printType(type.value, stack)}`;
807
+ stack.pop();
808
+ return ret;
809
+ }
810
+ else if (type.type === "Set") {
811
+ stack.push(type);
812
+ const ret = `.Set ${printType(type.key, stack)}`;
813
+ stack.pop();
814
+ return ret;
815
+ }
816
+ else if (type.type === "Dict") {
817
+ stack.push(type);
818
+ const ret = `.Dict (key=${printType(type.key, stack)}, value=${printType(type.value, stack)})`;
819
+ stack.pop();
820
+ return ret;
821
+ }
822
+ else if (type.type === "Struct") {
823
+ stack.push(type);
824
+ const ret = `.Struct [${Object.entries(type.fields).map(([k, t]) => `(name=${JSON.stringify(k)}, type=${printType(t, stack)})`).join(", ")}]`;
825
+ stack.pop();
826
+ return ret;
827
+ }
828
+ else if (type.type === "Variant") {
829
+ stack.push(type);
830
+ const ret = `.Variant [${Object.entries(type.cases).map(([k, t]) => `(name=${JSON.stringify(k)}, type=${printType(t, stack)})`).join(", ")}]`;
831
+ stack.pop();
832
+ return ret;
833
+ }
834
+ else if (type.type === "Recursive") {
835
+ // TODO update for our new recursive type representation
836
+ const idx = stack.indexOf(type.node);
837
+ if (idx !== -1) {
838
+ // Recursive reference
839
+ return `.Recursive ${stack.length - idx}`;
840
+ }
841
+ return printType(type.node, stack);
842
+ }
843
+ else if (type.type === "Function") {
844
+ // Note: functions can't be inside recursive types
845
+ stack.push(type);
846
+ const ret = `.Function (inputs=[${type.inputs.map(t => printType(t, stack)).join(", ")}], output=${printType(type.output, stack)}, platforms=${type.platforms === null ? "null" : `[${type.platforms.map(p => JSON.stringify(p)).join(", ")}]`})`;
847
+ stack.pop();
848
+ return ret;
849
+ }
850
+ else {
851
+ throw new Error(`Unknown type encountered during type printing: ${type.type}`);
852
+ }
853
+ }
854
+ /**
855
+ * Checks if one East type is a subtype of another.
856
+ *
857
+ * @param t1 - The potential subtype
858
+ * @param t2 - The potential supertype
859
+ * @returns `true` if t1 is a subtype of t2, `false` otherwise
860
+ *
861
+ * @remarks
862
+ * Implements East's subtyping rules:
863
+ * - {@link NeverType} is a subtype of all types
864
+ * - Mutable collections ({@link ArrayType}, {@link SetType}, {@link DictType}) are invariant
865
+ * - {@link VariantType} supports width subtyping (more cases → fewer cases)
866
+ * - {@link FunctionType} uses contravariant inputs and covariant outputs
867
+ */
868
+ export function isSubtype(t1, t2) {
869
+ // Equi-recursive type subtyping: unfold recursive types when only one side is recursive
870
+ if (t1.type === "Recursive") {
871
+ if (t2.type === "Recursive") {
872
+ // Recursive types are invariant - we want any compatible heap-allocated objects to have exactly the same layout
873
+ return isTypeEqual(t1.node, t2.node);
874
+ }
875
+ else {
876
+ // The recursive type wrapper is "transparent", but invariant
877
+ return isTypeEqual(t1.node, t2);
878
+ }
879
+ }
880
+ else if (t2.type === "Recursive") {
881
+ // The recursive type wrapper is "transparent"
882
+ // If the head is "on the stack" we can do head covariance by unfolding once
883
+ return isSubtype(t1, t2.node);
884
+ }
885
+ else if (t1.type === "Never") {
886
+ // t2 must be a supertype of Never
887
+ return true;
888
+ }
889
+ else if (t1.type === "Null") {
890
+ return t2.type === "Null";
891
+ }
892
+ else if (t1.type === "Boolean") {
893
+ return t2.type === "Boolean";
894
+ }
895
+ else if (t1.type === "Integer") {
896
+ return t2.type === "Integer";
897
+ }
898
+ else if (t1.type === "Float") {
899
+ return t2.type === "Float";
900
+ }
901
+ else if (t1.type === "String") {
902
+ return t2.type === "String";
903
+ }
904
+ else if (t1.type === "DateTime") {
905
+ return t2.type === "DateTime";
906
+ }
907
+ else if (t1.type === "Blob") {
908
+ return t2.type === "Blob";
909
+ }
910
+ else if (t1.type === "Ref") {
911
+ if (t2.type === "Ref") {
912
+ return isTypeEqual(t1.value, t2.value);
913
+ }
914
+ else {
915
+ return false;
916
+ }
917
+ }
918
+ else if (t1.type === "Array") {
919
+ if (t2.type === "Array") {
920
+ return isTypeEqual(t1.value, t2.value);
921
+ }
922
+ else {
923
+ return false;
924
+ }
925
+ }
926
+ else if (t1.type === "Set") {
927
+ if (t2.type === "Set") {
928
+ return isTypeEqual(t1.key, t2.key);
929
+ }
930
+ else {
931
+ return false;
932
+ }
933
+ }
934
+ else if (t1.type === "Dict") {
935
+ if (t2.type === "Dict") {
936
+ return isTypeEqual(t1.key, t2.key) && isTypeEqual(t1.value, t2.value);
937
+ }
938
+ else {
939
+ return false;
940
+ }
941
+ }
942
+ else if (t1.type === "Struct") {
943
+ if (t2.type === "Struct") {
944
+ const e1 = Object.entries(t1.fields);
945
+ const e2 = Object.entries(t2.fields);
946
+ if (e1.length !== e2.length)
947
+ return false;
948
+ let i = 0;
949
+ for (const [k1, f1] of e1) {
950
+ const [k2, f2] = e2[i];
951
+ if (k1 !== k2)
952
+ return false;
953
+ if (!isSubtype(f1, f2))
954
+ return false;
955
+ i += 1;
956
+ }
957
+ return true;
958
+ }
959
+ else {
960
+ return false;
961
+ }
962
+ }
963
+ else if (t1.type === "Variant") {
964
+ if (t2.type === "Variant") {
965
+ // Unlike structs, we don't care about the order (they are sorted by name already) and we only need a subset of cases in t2
966
+ for (const [k, v1] of Object.entries(t1.cases)) {
967
+ const v2 = t2.cases[k] ?? NeverType;
968
+ if (!isSubtype(v1, v2))
969
+ return false;
970
+ }
971
+ return true;
972
+ }
973
+ else {
974
+ return false;
975
+ }
976
+ }
977
+ else if (t1.type === "Function") {
978
+ if (t2.type === "Function") {
979
+ return t1.inputs.length === t2.inputs.length && t1.inputs.every((t, i) => isSubtype(t2.inputs[i], t)) && isSubtype(t1.output, t2.output); // contravariant inputs and covariant output
980
+ }
981
+ else {
982
+ return false;
983
+ }
984
+ }
985
+ else {
986
+ throw new Error(`Unknown type encountered during subtype check: ${t1.type}`);
987
+ }
988
+ }
989
+ /**
990
+ * Computes the union of two East types at runtime.
991
+ *
992
+ * @typeParam T1 - First {@link EastType}
993
+ * @typeParam T2 - Second {@link EastType}
994
+ * @param t1 - First type
995
+ * @param t2 - Second type
996
+ * @returns The union type
997
+ * @throws {TypeMismatchError} When the types cannot be unified
998
+ *
999
+ * @remarks
1000
+ * This is the runtime version of the {@link TypeUnion} type.
1001
+ * Error messages include the full type path for debugging.
1002
+ */
1003
+ export function TypeUnion(t1, t2) {
1004
+ // TODO this is broken for recursive types (need to do cycle tracking in tandem with TypeEqual/TypeIntersect)
1005
+ try {
1006
+ if (t1.type === "Never") {
1007
+ return t2;
1008
+ }
1009
+ else if (t2.type === "Never") {
1010
+ return t1;
1011
+ }
1012
+ else if (t1.type === "Recursive") {
1013
+ if (t2.type === "Recursive") {
1014
+ // Both recursive - require exact match (heap invariance)
1015
+ return TypeEqual(t1, t2);
1016
+ // return RecursiveType(() => TypeEqual(t1.node, t2.node, t1.node, t2.node)) as TypeUnion<T1, T2>;
1017
+ }
1018
+ else {
1019
+ // Rec(A) ∪ NonRec: If NonRec <: A, union is Rec(A) (NonRec can be widened to heap type)
1020
+ if (isSubtype(t2, t1.node)) {
1021
+ return t1;
1022
+ }
1023
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1024
+ }
1025
+ }
1026
+ else if (t2.type === "Recursive") {
1027
+ // NonRec ∪ Rec(B): If NonRec <: B, union is Rec(B) (symmetric case)
1028
+ if (isSubtype(t1, t2.node)) {
1029
+ return t2;
1030
+ }
1031
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1032
+ }
1033
+ else if (t1.type === "Ref") {
1034
+ if (t2.type === "Ref") {
1035
+ return RefType(TypeEqual(t1.value, t2.value));
1036
+ }
1037
+ else {
1038
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1039
+ }
1040
+ }
1041
+ else if (t1.type === "Array") {
1042
+ if (t2.type === "Array") {
1043
+ return ArrayType(TypeEqual(t1.value, t2.value));
1044
+ }
1045
+ else {
1046
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1047
+ }
1048
+ }
1049
+ else if (t1.type === "Set") {
1050
+ if (t2.type === "Set") {
1051
+ return SetType(TypeEqual(t1.key, t2.key));
1052
+ }
1053
+ else {
1054
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1055
+ }
1056
+ }
1057
+ else if (t1.type === "Dict") {
1058
+ if (t2.type === "Dict") {
1059
+ return DictType(TypeEqual(t1.key, t2.key), TypeEqual(t1.value, t2.value));
1060
+ }
1061
+ else {
1062
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1063
+ }
1064
+ }
1065
+ else if (t1.type === "Struct") {
1066
+ if (t2.type === "Struct") {
1067
+ const e1 = Object.entries(t1.fields);
1068
+ const e2 = Object.entries(t2.fields);
1069
+ if (e1.length !== e2.length)
1070
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: structs contain different number of fields`);
1071
+ let i = 0;
1072
+ const e = {};
1073
+ for (const [k1, f1] of e1) {
1074
+ const [k2, f2] = e2[i];
1075
+ if (k1 !== k2)
1076
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: struct field ${i} has mismatched names ${printIdentifier(k1)} and ${printIdentifier(k2)}`);
1077
+ e[k1] = TypeUnion(f1, f2);
1078
+ i += 1;
1079
+ }
1080
+ return StructType(e);
1081
+ }
1082
+ else {
1083
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1084
+ }
1085
+ }
1086
+ else if (t1.type === "Variant") {
1087
+ if (t2.type === "Variant") {
1088
+ const cases = {};
1089
+ for (const [k1, f1] of Object.entries(t1.cases)) {
1090
+ const f2 = t2.cases[k1];
1091
+ if (f2 === undefined) {
1092
+ cases[k1] = f1;
1093
+ }
1094
+ else {
1095
+ cases[k1] = TypeUnion(f1, f2);
1096
+ }
1097
+ }
1098
+ for (const [k2, f2] of Object.entries(t2.cases)) {
1099
+ const f1 = t1.cases[k2];
1100
+ if (f1 === undefined) {
1101
+ cases[k2] = f2;
1102
+ }
1103
+ }
1104
+ return VariantType(cases);
1105
+ }
1106
+ else {
1107
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1108
+ }
1109
+ }
1110
+ else if (t1.type === "Function") {
1111
+ if (t2.type === "Function") {
1112
+ if (t1.inputs.length !== t2.inputs.length) {
1113
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1114
+ }
1115
+ let platforms;
1116
+ if (t1.platforms === null) {
1117
+ platforms = null;
1118
+ }
1119
+ else if (t2.platforms === null) {
1120
+ platforms = null;
1121
+ }
1122
+ else {
1123
+ platforms = [...new Set([...t1.platforms, ...t2.platforms])].sort();
1124
+ }
1125
+ return FunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output), platforms);
1126
+ }
1127
+ else {
1128
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1129
+ }
1130
+ }
1131
+ else {
1132
+ if (t1.type === t2.type) {
1133
+ return t1;
1134
+ }
1135
+ else {
1136
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1137
+ }
1138
+ }
1139
+ }
1140
+ catch (cause) {
1141
+ if (cause instanceof TypeMismatchError) {
1142
+ throw cause; // Don't wrap our own errors - they already have the full path
1143
+ }
1144
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}`, { cause });
1145
+ }
1146
+ }
1147
+ /**
1148
+ * Computes the intersection of two East types at runtime.
1149
+ *
1150
+ * @typeParam T1 - First {@link EastType}
1151
+ * @typeParam T2 - Second {@link EastType}
1152
+ * @param t1 - First type
1153
+ * @param t2 - Second type
1154
+ * @returns The intersection type
1155
+ * @throws {TypeMismatchError} When the types cannot be intersected
1156
+ *
1157
+ * @remarks
1158
+ * This is the runtime version of the {@link TypeIntersect} type.
1159
+ * For {@link VariantType}, returns {@link NeverType} if no common cases exist.
1160
+ */
1161
+ export function TypeIntersect(t1, t2) {
1162
+ // TODO this is broken for recursive types (need to do cycle tracking in tandem with TypeEqual/TypeUnion)
1163
+ try {
1164
+ if (t1.type === "Never") {
1165
+ return NeverType;
1166
+ }
1167
+ else if (t2.type === "Never") {
1168
+ return NeverType;
1169
+ }
1170
+ else if (t1.type === "Recursive") {
1171
+ if (t2.type === "Recursive") {
1172
+ // Both recursive - require exact match (heap invariance)
1173
+ return RecursiveType(() => TypeEqual(t1.node, t2.node, t1.node, t2.node));
1174
+ }
1175
+ else {
1176
+ // Rec(A) ∩ NonRec: If NonRec <: A, intersection is NonRec (the more specific type)
1177
+ if (isSubtype(t2, t1.node)) {
1178
+ return t2;
1179
+ }
1180
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1181
+ }
1182
+ }
1183
+ else if (t2.type === "Recursive") {
1184
+ // NonRec ∩ Rec(B): If NonRec <: B, intersection is NonRec (symmetric case)
1185
+ if (isSubtype(t1, t2.node)) {
1186
+ return t1;
1187
+ }
1188
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1189
+ }
1190
+ else if (t1.type === "Ref") {
1191
+ if (t2.type === "Ref") {
1192
+ return RefType(TypeEqual(t1.value, t2.value));
1193
+ }
1194
+ else {
1195
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1196
+ }
1197
+ }
1198
+ else if (t1.type === "Array") {
1199
+ if (t2.type === "Array") {
1200
+ return ArrayType(TypeEqual(t1.value, t2.value));
1201
+ }
1202
+ else {
1203
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1204
+ }
1205
+ }
1206
+ else if (t1.type === "Set") {
1207
+ if (t2.type === "Set") {
1208
+ return SetType(TypeEqual(t1.key, t2.key));
1209
+ }
1210
+ else {
1211
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1212
+ }
1213
+ }
1214
+ else if (t1.type === "Dict") {
1215
+ if (t2.type === "Dict") {
1216
+ return DictType(TypeEqual(t1.key, t2.key), TypeEqual(t1.value, t2.value));
1217
+ }
1218
+ else {
1219
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1220
+ }
1221
+ }
1222
+ else if (t1.type === "Struct") {
1223
+ if (t2.type === "Struct") {
1224
+ const e1 = Object.entries(t1.fields);
1225
+ const e2 = Object.entries(t2.fields);
1226
+ if (e1.length !== e2.length)
1227
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: structs contain different number of fields`);
1228
+ let i = 0;
1229
+ const e = {};
1230
+ for (const [k1, f1] of e1) {
1231
+ const [k2, f2] = e2[i];
1232
+ if (k1 !== k2)
1233
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: struct field ${i} has mismatched names ${printIdentifier(k1)} and ${printIdentifier(k2)}`);
1234
+ e[k1] = TypeIntersect(f1, f2);
1235
+ i += 1;
1236
+ }
1237
+ return StructType(e);
1238
+ }
1239
+ else {
1240
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1241
+ }
1242
+ }
1243
+ else if (t1.type === "Variant") {
1244
+ if (t2.type === "Variant") {
1245
+ const cases = {};
1246
+ let empty = true;
1247
+ for (const [k1, f1] of Object.entries(t1.cases)) {
1248
+ const f2 = t2.cases[k1];
1249
+ if (f2 !== undefined) {
1250
+ cases[k1] = TypeIntersect(f1, f2);
1251
+ empty = false;
1252
+ }
1253
+ }
1254
+ if (empty) {
1255
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: variants have no overlapping cases`);
1256
+ }
1257
+ return VariantType(cases);
1258
+ }
1259
+ else {
1260
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1261
+ }
1262
+ }
1263
+ else if (t1.type === "Function") {
1264
+ if (t2.type === "Function") {
1265
+ if (t1.inputs.length !== t2.inputs.length) {
1266
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1267
+ }
1268
+ let platforms;
1269
+ if (t1.platforms === null) {
1270
+ platforms = t2.platforms;
1271
+ }
1272
+ else if (t2.platforms === null) {
1273
+ platforms = t1.platforms;
1274
+ }
1275
+ else {
1276
+ platforms = t1.platforms.filter(p => t2.platforms.includes(p));
1277
+ }
1278
+ return FunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output), platforms);
1279
+ }
1280
+ else {
1281
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1282
+ }
1283
+ }
1284
+ else {
1285
+ if (t1.type === t2.type) {
1286
+ return t1;
1287
+ }
1288
+ else {
1289
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1290
+ }
1291
+ }
1292
+ }
1293
+ catch (cause) {
1294
+ if (cause instanceof TypeMismatchError) {
1295
+ throw cause; // Don't wrap our own errors - they already have the full path
1296
+ }
1297
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}`, { cause });
1298
+ }
1299
+ }
1300
+ /**
1301
+ * Asserts that two East types are equal, returning the first type.
1302
+ *
1303
+ * @typeParam T1 - First {@link EastType}
1304
+ * @typeParam T2 - Second {@link EastType}
1305
+ * @param t1 - First type
1306
+ * @param t2 - Second type
1307
+ * @param r1 - Internal parameter for tracking the first recursive type being compared
1308
+ * @param r2 - Internal parameter for tracking the second recursive type being compared
1309
+ * @returns The first type if types are equal
1310
+ * @throws {TypeMismatchError} When the types are not equal
1311
+ *
1312
+ * @remarks
1313
+ * This function recursively validates type equality and throws detailed errors
1314
+ * on mismatch. Used to enforce type constraints in compound type constructors.
1315
+ * Unlike {@link isTypeEqual}, this throws rather than returning a boolean.
1316
+ */
1317
+ export function TypeEqual(t1, t2, r1 = t1, r2 = t2) {
1318
+ // TODO this is broken for recursive types (need to do cycle tracking in tandem with TypeUnion/TypeIntersect)
1319
+ try {
1320
+ if (t1.type === "Ref") {
1321
+ if (t2.type === "Ref") {
1322
+ return RefType(TypeEqual(t1.value, t2.value, r1, r2));
1323
+ }
1324
+ else {
1325
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1326
+ }
1327
+ }
1328
+ else if (t1.type === "Array") {
1329
+ if (t2.type === "Array") {
1330
+ return ArrayType(TypeEqual(t1.value, t2.value, r1, r2));
1331
+ }
1332
+ else {
1333
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1334
+ }
1335
+ }
1336
+ else if (t1.type === "Set") {
1337
+ if (t2.type === "Set") {
1338
+ return SetType(TypeEqual(t1.key, t2.key, r1, r2));
1339
+ }
1340
+ else {
1341
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1342
+ }
1343
+ }
1344
+ else if (t1.type === "Dict") {
1345
+ if (t2.type === "Dict") {
1346
+ return DictType(TypeEqual(t1.key, t2.key, r1, r2), TypeEqual(t1.value, t2.value, r1, r2));
1347
+ }
1348
+ else {
1349
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1350
+ }
1351
+ }
1352
+ else if (t1.type === "Struct") {
1353
+ if (t2.type === "Struct") {
1354
+ const e1 = Object.entries(t1.fields);
1355
+ const e2 = Object.entries(t2.fields);
1356
+ if (e1.length !== e2.length)
1357
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: structs contain different number of fields`);
1358
+ let i = 0;
1359
+ const e = {};
1360
+ for (const [k1, f1] of e1) {
1361
+ const [k2, f2] = e2[i];
1362
+ if (k1 !== k2)
1363
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: struct field ${i} has mismatched names ${printIdentifier(k1)} and ${printIdentifier(k2)}`);
1364
+ e[k1] = TypeEqual(f1, f2, r1, r2);
1365
+ i += 1;
1366
+ }
1367
+ return StructType(e);
1368
+ }
1369
+ else {
1370
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1371
+ }
1372
+ }
1373
+ else if (t1.type === "Variant") {
1374
+ // We can reuse the logic for structs here, taking advantage of the ordering guarantees
1375
+ if (t2.type === "Variant") {
1376
+ const e1 = Object.entries(t1.cases);
1377
+ const e2 = Object.entries(t2.cases);
1378
+ if (e1.length !== e2.length)
1379
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: variants contain different number of cases`);
1380
+ let i = 0;
1381
+ const e = {};
1382
+ for (const [k1, f1] of e1) {
1383
+ const [k2, f2] = e2[i];
1384
+ if (k1 !== k2) {
1385
+ if (k1 < k2) {
1386
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: variant case ${k1} is not present in both variants`);
1387
+ }
1388
+ else {
1389
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: variant case ${k2} is not present in both variants`);
1390
+ }
1391
+ }
1392
+ e[k1] = TypeEqual(f1, f2, r1, r2);
1393
+ i += 1;
1394
+ }
1395
+ return VariantType(e);
1396
+ }
1397
+ else {
1398
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1399
+ }
1400
+ }
1401
+ else if (t1.type === "Recursive") {
1402
+ if (t2.type === "Recursive") {
1403
+ if (t1.node === r1) {
1404
+ if (t2.node === r2) {
1405
+ return t1; // both are references to the same recursive type
1406
+ }
1407
+ else {
1408
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: recursive types do not match`);
1409
+ }
1410
+ }
1411
+ else if (t2.node === r2) {
1412
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: recursive types do not match`);
1413
+ }
1414
+ // this is the root of a new recursive type - assert the node types are equal
1415
+ return RecursiveType(() => TypeEqual(t1.node, t2.node, t1.node, t2.node));
1416
+ }
1417
+ else {
1418
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1419
+ }
1420
+ }
1421
+ else if (t1.type === "Function") {
1422
+ if (t2.type === "Function") {
1423
+ if (t1.inputs.length !== t2.inputs.length) {
1424
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions take different number of arguments`);
1425
+ }
1426
+ if (t1.platforms === null) {
1427
+ if (t2.platforms !== null) {
1428
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions have different platform effects`);
1429
+ }
1430
+ }
1431
+ else if (t2.platforms === null) {
1432
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions have different platform effects`);
1433
+ }
1434
+ else {
1435
+ if (t1.platforms.length !== t2.platforms.length || !t1.platforms.every((p, i) => p === t2.platforms[i])) {
1436
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions have different platform effects`);
1437
+ }
1438
+ }
1439
+ return FunctionType(t1.inputs.map((t, i) => TypeEqual(t, t2.inputs[i], r1, r2)), TypeEqual(t1.output, t2.output, r1, r2), t1.platforms);
1440
+ }
1441
+ else {
1442
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1443
+ }
1444
+ }
1445
+ else {
1446
+ if (t1.type === t2.type) {
1447
+ return t1;
1448
+ }
1449
+ else {
1450
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1451
+ }
1452
+ }
1453
+ }
1454
+ catch (cause) {
1455
+ if (cause instanceof TypeMismatchError) {
1456
+ throw cause; // Don't wrap our own errors - they already have the full path
1457
+ }
1458
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}`, { cause });
1459
+ }
1460
+ }
1461
+ /**
1462
+ * Create a type wide enough to hold values from two East types at runtime.
1463
+ *
1464
+ * @param t1 - First type
1465
+ * @param t2 - Second type
1466
+ * @returns The union type
1467
+ * @throws {TypeMismatchError} When the types cannot be unified
1468
+ *
1469
+ * @remarks
1470
+ * This is a more forgiving version of the {@link TypeUnion} type, designed for inferring types from values.
1471
+ * Functions and recursive types are not supported.
1472
+ * Error messages include the full type path for debugging.
1473
+ */
1474
+ export function TypeWiden(t1, t2) {
1475
+ try {
1476
+ if (t1.type === "Never") {
1477
+ return t2;
1478
+ }
1479
+ else if (t2.type === "Never") {
1480
+ return t1;
1481
+ }
1482
+ else if (t1.type === "Recursive") {
1483
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: recursive types not supported`);
1484
+ }
1485
+ else if (t2.type === "Recursive") {
1486
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: recursive types not supported`);
1487
+ }
1488
+ else if (t1.type === "Ref") {
1489
+ if (t2.type === "Ref") {
1490
+ return RefType(TypeWiden(t1.value, t2.value));
1491
+ }
1492
+ else {
1493
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
1494
+ }
1495
+ }
1496
+ else if (t1.type === "Array") {
1497
+ if (t2.type === "Array") {
1498
+ return ArrayType(TypeWiden(t1.value, t2.value));
1499
+ }
1500
+ else {
1501
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
1502
+ }
1503
+ }
1504
+ else if (t1.type === "Set") {
1505
+ if (t2.type === "Set") {
1506
+ return SetType(TypeWiden(t1.key, t2.key));
1507
+ }
1508
+ else {
1509
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
1510
+ }
1511
+ }
1512
+ else if (t1.type === "Dict") {
1513
+ if (t2.type === "Dict") {
1514
+ return DictType(TypeWiden(t1.key, t2.key), TypeWiden(t1.value, t2.value));
1515
+ }
1516
+ else {
1517
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
1518
+ }
1519
+ }
1520
+ else if (t1.type === "Struct") {
1521
+ if (t2.type === "Struct") {
1522
+ const e1 = Object.entries(t1.fields);
1523
+ const e2 = Object.entries(t2.fields);
1524
+ if (e1.length !== e2.length)
1525
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: structs contain different number of fields`);
1526
+ let i = 0;
1527
+ const e = {};
1528
+ for (const [k1, f1] of e1) {
1529
+ const [k2, f2] = e2[i];
1530
+ if (k1 !== k2)
1531
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: struct field ${i} has mismatched names ${printIdentifier(k1)} and ${printIdentifier(k2)}`);
1532
+ e[k1] = TypeWiden(f1, f2);
1533
+ i += 1;
1534
+ }
1535
+ return StructType(e);
1536
+ }
1537
+ else {
1538
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
1539
+ }
1540
+ }
1541
+ else if (t1.type === "Variant") {
1542
+ if (t2.type === "Variant") {
1543
+ const cases = {};
1544
+ for (const [k1, f1] of Object.entries(t1.cases)) {
1545
+ const f2 = t2.cases[k1];
1546
+ if (f2 === undefined) {
1547
+ cases[k1] = f1;
1548
+ }
1549
+ else {
1550
+ cases[k1] = TypeWiden(f1, f2);
1551
+ }
1552
+ }
1553
+ for (const [k2, f2] of Object.entries(t2.cases)) {
1554
+ const f1 = t1.cases[k2];
1555
+ if (f1 === undefined) {
1556
+ cases[k2] = f2;
1557
+ }
1558
+ }
1559
+ return VariantType(cases);
1560
+ }
1561
+ else {
1562
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
1563
+ }
1564
+ }
1565
+ else if (t1.type === "Function") {
1566
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: functions not supported`);
1567
+ }
1568
+ else {
1569
+ if (t1.type === t2.type) {
1570
+ return t1;
1571
+ }
1572
+ else {
1573
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
1574
+ }
1575
+ }
1576
+ }
1577
+ catch (cause) {
1578
+ if (cause instanceof TypeMismatchError) {
1579
+ throw cause; // Don't wrap our own errors - they already have the full path
1580
+ }
1581
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}`, { cause });
1582
+ }
1583
+ }
1584
+ /**
1585
+ * Formats an identifier for display in type strings.
1586
+ *
1587
+ * @param x - The identifier to format
1588
+ * @returns The identifier, escaped with backticks if it contains special characters
1589
+ *
1590
+ * @remarks
1591
+ * Used by {@link printType} to format field and case names.
1592
+ * Identifiers matching `/^[a-zA-Z_][a-zA-Z0-9_]*$/` are returned as-is,
1593
+ * others are wrapped in backticks with escaping.
1594
+ */
1595
+ export function printIdentifier(x) {
1596
+ if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(x)) {
1597
+ return x;
1598
+ }
1599
+ else {
1600
+ return `\`${x.replace('\\', '\\\\').replace('`', '\\`')}\``;
1601
+ }
1602
+ }
1603
+ /** Singleton instance of {@link NoneType}. */
1604
+ export const NoneType = VariantType({ none: NullType });
1605
+ /**
1606
+ * Constructs a Some type wrapping a value type.
1607
+ *
1608
+ * @typeParam T - The type of the wrapped value
1609
+ * @param type - The {@link EastType} to wrap
1610
+ * @returns A SomeType variant
1611
+ */
1612
+ export function SomeType(type) {
1613
+ return VariantType({ some: type });
1614
+ }
1615
+ /**
1616
+ * Constructs an Option type for optional values.
1617
+ *
1618
+ * @typeParam T - The type of the value when present
1619
+ * @param type - The {@link EastType} of the wrapped value
1620
+ * @returns An OptionType variant with none and some cases
1621
+ *
1622
+ * @example
1623
+ * ```ts
1624
+ * const maybeInt = OptionType(IntegerType);
1625
+ * // Type: VariantType<{ none: NullType, some: IntegerType }>
1626
+ * ```
1627
+ */
1628
+ export function OptionType(type) {
1629
+ return VariantType({ none: NullType, some: type });
1630
+ }
1631
+ //# sourceMappingURL=types.js.map