@elaraai/east 0.0.1-beta.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (410) hide show
  1. package/LICENSE.md +15 -666
  2. package/README.md +113 -88
  3. package/dist/src/analyze.d.ts +3 -0
  4. package/dist/src/analyze.d.ts.map +1 -1
  5. package/dist/src/analyze.js +223 -111
  6. package/dist/src/analyze.js.map +1 -1
  7. package/dist/src/analyze.spec.d.ts +2 -0
  8. package/dist/src/analyze.spec.d.ts.map +1 -0
  9. package/dist/src/analyze.spec.js +445 -0
  10. package/dist/src/analyze.spec.js.map +1 -0
  11. package/dist/src/ast.d.ts +54 -35
  12. package/dist/src/ast.d.ts.map +1 -1
  13. package/dist/src/ast_to_ir.coerce_to.spec.d.ts +15 -0
  14. package/dist/src/ast_to_ir.coerce_to.spec.d.ts.map +1 -0
  15. package/dist/src/ast_to_ir.coerce_to.spec.js +420 -0
  16. package/dist/src/ast_to_ir.coerce_to.spec.js.map +1 -0
  17. package/dist/src/ast_to_ir.d.ts +18 -4
  18. package/dist/src/ast_to_ir.d.ts.map +1 -1
  19. package/dist/src/ast_to_ir.js +233 -178
  20. package/dist/src/ast_to_ir.js.map +1 -1
  21. package/dist/src/builtins.d.ts +1 -1
  22. package/dist/src/builtins.d.ts.map +1 -1
  23. package/dist/src/builtins.js +194 -2
  24. package/dist/src/builtins.js.map +1 -1
  25. package/dist/src/comparison.d.ts +2 -2
  26. package/dist/src/comparison.d.ts.map +1 -1
  27. package/dist/src/comparison.js +115 -40
  28. package/dist/src/comparison.js.map +1 -1
  29. package/dist/src/comparison.spec.d.ts +2 -0
  30. package/dist/src/comparison.spec.d.ts.map +1 -0
  31. package/dist/src/comparison.spec.js +1200 -0
  32. package/dist/src/comparison.spec.js.map +1 -0
  33. package/dist/src/compile.d.ts +31 -9
  34. package/dist/src/compile.d.ts.map +1 -1
  35. package/dist/src/compile.js +1048 -548
  36. package/dist/src/compile.js.map +1 -1
  37. package/dist/src/containers/matrix.d.ts +15 -0
  38. package/dist/src/containers/matrix.d.ts.map +1 -0
  39. package/dist/src/containers/matrix.js +15 -0
  40. package/dist/src/containers/matrix.js.map +1 -0
  41. package/dist/src/containers/ref.d.ts.map +1 -1
  42. package/dist/src/containers/ref.js +1 -1
  43. package/dist/src/containers/ref.js.map +1 -1
  44. package/dist/src/containers/sortedmap.spec.d.ts +2 -0
  45. package/dist/src/containers/sortedmap.spec.d.ts.map +1 -0
  46. package/dist/src/containers/sortedmap.spec.js +180 -0
  47. package/dist/src/containers/sortedmap.spec.js.map +1 -0
  48. package/dist/src/containers/sortedset.spec.d.ts +2 -0
  49. package/dist/src/containers/sortedset.spec.d.ts.map +1 -0
  50. package/dist/src/containers/sortedset.spec.js +228 -0
  51. package/dist/src/containers/sortedset.spec.js.map +1 -0
  52. package/dist/src/containers/variant.d.ts.map +1 -1
  53. package/dist/src/containers/variant.js +1 -1
  54. package/dist/src/containers/variant.js.map +1 -1
  55. package/dist/src/containers/variant.spec.d.ts +2 -0
  56. package/dist/src/containers/variant.spec.d.ts.map +1 -0
  57. package/dist/src/containers/variant.spec.js +217 -0
  58. package/dist/src/containers/variant.spec.js.map +1 -0
  59. package/dist/src/datetime_format/parse.spec.d.ts +6 -0
  60. package/dist/src/datetime_format/parse.spec.d.ts.map +1 -0
  61. package/dist/src/datetime_format/parse.spec.js +379 -0
  62. package/dist/src/datetime_format/parse.spec.js.map +1 -0
  63. package/dist/src/datetime_format/print.spec.d.ts +6 -0
  64. package/dist/src/datetime_format/print.spec.d.ts.map +1 -0
  65. package/dist/src/datetime_format/print.spec.js +257 -0
  66. package/dist/src/datetime_format/print.spec.js.map +1 -0
  67. package/dist/src/datetime_format/tokenize.spec.d.ts +2 -0
  68. package/dist/src/datetime_format/tokenize.spec.d.ts.map +1 -0
  69. package/dist/src/datetime_format/tokenize.spec.js +508 -0
  70. package/dist/src/datetime_format/tokenize.spec.js.map +1 -0
  71. package/dist/src/datetime_format/types.d.ts +23 -23
  72. package/dist/src/default.d.ts.map +1 -1
  73. package/dist/src/default.js +33 -0
  74. package/dist/src/default.js.map +1 -1
  75. package/dist/src/default.spec.d.ts +2 -0
  76. package/dist/src/default.spec.d.ts.map +1 -0
  77. package/dist/src/default.spec.js +178 -0
  78. package/dist/src/default.spec.js.map +1 -0
  79. package/dist/src/eastir.d.ts +7 -0
  80. package/dist/src/eastir.d.ts.map +1 -1
  81. package/dist/src/eastir.js +55 -7
  82. package/dist/src/eastir.js.map +1 -1
  83. package/dist/src/error.d.ts +14 -4
  84. package/dist/src/error.d.ts.map +1 -1
  85. package/dist/src/error.js +28 -3
  86. package/dist/src/error.js.map +1 -1
  87. package/dist/src/example.d.ts +87 -0
  88. package/dist/src/example.d.ts.map +1 -0
  89. package/dist/src/example.js +18 -0
  90. package/dist/src/example.js.map +1 -0
  91. package/dist/src/expr/array.d.ts +43 -1
  92. package/dist/src/expr/array.d.ts.map +1 -1
  93. package/dist/src/expr/array.js +103 -53
  94. package/dist/src/expr/array.js.map +1 -1
  95. package/dist/src/expr/ast.d.ts +1 -1
  96. package/dist/src/expr/ast.d.ts.map +1 -1
  97. package/dist/src/expr/ast.js +112 -54
  98. package/dist/src/expr/ast.js.map +1 -1
  99. package/dist/src/expr/asyncfunction.d.ts.map +1 -1
  100. package/dist/src/expr/asyncfunction.js +6 -4
  101. package/dist/src/expr/asyncfunction.js.map +1 -1
  102. package/dist/src/expr/blob.d.ts +48 -0
  103. package/dist/src/expr/blob.d.ts.map +1 -1
  104. package/dist/src/expr/blob.js +63 -8
  105. package/dist/src/expr/blob.js.map +1 -1
  106. package/dist/src/expr/block.d.ts +213 -12
  107. package/dist/src/expr/block.d.ts.map +1 -1
  108. package/dist/src/expr/block.js +796 -293
  109. package/dist/src/expr/block.js.map +1 -1
  110. package/dist/src/expr/boolean.d.ts +44 -0
  111. package/dist/src/expr/boolean.d.ts.map +1 -1
  112. package/dist/src/expr/boolean.js +58 -6
  113. package/dist/src/expr/boolean.js.map +1 -1
  114. package/dist/src/expr/datetime.d.ts +135 -0
  115. package/dist/src/expr/datetime.d.ts.map +1 -1
  116. package/dist/src/expr/datetime.js +184 -34
  117. package/dist/src/expr/datetime.js.map +1 -1
  118. package/dist/src/expr/dict.d.ts +47 -2
  119. package/dist/src/expr/dict.d.ts.map +1 -1
  120. package/dist/src/expr/dict.js +122 -61
  121. package/dist/src/expr/dict.js.map +1 -1
  122. package/dist/src/expr/expr.d.ts +3 -1
  123. package/dist/src/expr/expr.d.ts.map +1 -1
  124. package/dist/src/expr/expr.js +4 -3
  125. package/dist/src/expr/expr.js.map +1 -1
  126. package/dist/src/expr/float.d.ts +153 -0
  127. package/dist/src/expr/float.d.ts.map +1 -1
  128. package/dist/src/expr/float.js +191 -17
  129. package/dist/src/expr/float.js.map +1 -1
  130. package/dist/src/expr/function.d.ts +7 -2
  131. package/dist/src/expr/function.d.ts.map +1 -1
  132. package/dist/src/expr/function.js +6 -4
  133. package/dist/src/expr/function.js.map +1 -1
  134. package/dist/src/expr/index.d.ts +186 -2
  135. package/dist/src/expr/index.d.ts.map +1 -1
  136. package/dist/src/expr/index.js +183 -2
  137. package/dist/src/expr/index.js.map +1 -1
  138. package/dist/src/expr/integer.d.ts +180 -0
  139. package/dist/src/expr/integer.d.ts.map +1 -1
  140. package/dist/src/expr/integer.js +189 -18
  141. package/dist/src/expr/integer.js.map +1 -1
  142. package/dist/src/expr/libs/array.js +3 -3
  143. package/dist/src/expr/libs/array.js.map +1 -1
  144. package/dist/src/expr/libs/blob.js +3 -3
  145. package/dist/src/expr/libs/blob.js.map +1 -1
  146. package/dist/src/expr/libs/dict.js +1 -1
  147. package/dist/src/expr/libs/dict.js.map +1 -1
  148. package/dist/src/expr/libs/matrix.d.ts +17 -0
  149. package/dist/src/expr/libs/matrix.d.ts.map +1 -0
  150. package/dist/src/expr/libs/matrix.js +60 -0
  151. package/dist/src/expr/libs/matrix.js.map +1 -0
  152. package/dist/src/expr/libs/set.js +1 -1
  153. package/dist/src/expr/libs/set.js.map +1 -1
  154. package/dist/src/expr/libs/string.d.ts.map +1 -1
  155. package/dist/src/expr/libs/string.js +2 -1
  156. package/dist/src/expr/libs/string.js.map +1 -1
  157. package/dist/src/expr/libs/vector.d.ts +16 -0
  158. package/dist/src/expr/libs/vector.d.ts.map +1 -0
  159. package/dist/src/expr/libs/vector.js +44 -0
  160. package/dist/src/expr/libs/vector.js.map +1 -0
  161. package/dist/src/expr/matrix.d.ts +109 -0
  162. package/dist/src/expr/matrix.d.ts.map +1 -0
  163. package/dist/src/expr/matrix.js +224 -0
  164. package/dist/src/expr/matrix.js.map +1 -0
  165. package/dist/src/expr/recursive.d.ts +83 -0
  166. package/dist/src/expr/recursive.d.ts.map +1 -0
  167. package/dist/src/expr/recursive.js +99 -0
  168. package/dist/src/expr/recursive.js.map +1 -0
  169. package/dist/src/expr/ref.js +5 -5
  170. package/dist/src/expr/ref.js.map +1 -1
  171. package/dist/src/expr/set.d.ts +44 -2
  172. package/dist/src/expr/set.d.ts.map +1 -1
  173. package/dist/src/expr/set.js +102 -52
  174. package/dist/src/expr/set.js.map +1 -1
  175. package/dist/src/expr/string.d.ts +134 -0
  176. package/dist/src/expr/string.d.ts.map +1 -1
  177. package/dist/src/expr/string.js +173 -23
  178. package/dist/src/expr/string.js.map +1 -1
  179. package/dist/src/expr/struct.d.ts +1 -1
  180. package/dist/src/expr/struct.d.ts.map +1 -1
  181. package/dist/src/expr/struct.js +2 -2
  182. package/dist/src/expr/struct.js.map +1 -1
  183. package/dist/src/expr/types.d.ts +12 -9
  184. package/dist/src/expr/types.d.ts.map +1 -1
  185. package/dist/src/expr/variant.d.ts +108 -11
  186. package/dist/src/expr/variant.d.ts.map +1 -1
  187. package/dist/src/expr/variant.js +76 -38
  188. package/dist/src/expr/variant.js.map +1 -1
  189. package/dist/src/expr/vector.d.ts +102 -0
  190. package/dist/src/expr/vector.d.ts.map +1 -0
  191. package/dist/src/expr/vector.js +202 -0
  192. package/dist/src/expr/vector.js.map +1 -0
  193. package/dist/src/fuzz.d.ts +36 -2
  194. package/dist/src/fuzz.d.ts.map +1 -1
  195. package/dist/src/fuzz.js +409 -77
  196. package/dist/src/fuzz.js.map +1 -1
  197. package/dist/src/index.d.ts +4 -0
  198. package/dist/src/index.d.ts.map +1 -1
  199. package/dist/src/index.js +4 -0
  200. package/dist/src/index.js.map +1 -1
  201. package/dist/src/internal.d.ts +12 -2
  202. package/dist/src/internal.d.ts.map +1 -1
  203. package/dist/src/internal.js +13 -2
  204. package/dist/src/internal.js.map +1 -1
  205. package/dist/src/ir.d.ts +1754 -1484
  206. package/dist/src/ir.d.ts.map +1 -1
  207. package/dist/src/ir.js +51 -34
  208. package/dist/src/ir.js.map +1 -1
  209. package/dist/src/ir.spec.d.ts +2 -0
  210. package/dist/src/ir.spec.d.ts.map +1 -0
  211. package/dist/src/ir.spec.js +35 -0
  212. package/dist/src/ir.spec.js.map +1 -0
  213. package/dist/src/location.d.ts +56 -17
  214. package/dist/src/location.d.ts.map +1 -1
  215. package/dist/src/location.js +226 -29
  216. package/dist/src/location.js.map +1 -1
  217. package/dist/src/location.spec.d.ts +2 -0
  218. package/dist/src/location.spec.d.ts.map +1 -0
  219. package/dist/src/location.spec.js +102 -0
  220. package/dist/src/location.spec.js.map +1 -0
  221. package/dist/src/patch/apply.d.ts +15 -0
  222. package/dist/src/patch/apply.d.ts.map +1 -0
  223. package/dist/src/patch/apply.js +352 -0
  224. package/dist/src/patch/apply.js.map +1 -0
  225. package/dist/src/patch/compose.d.ts +15 -0
  226. package/dist/src/patch/compose.d.ts.map +1 -0
  227. package/dist/src/patch/compose.js +448 -0
  228. package/dist/src/patch/compose.js.map +1 -0
  229. package/dist/src/patch/diff.d.ts +15 -0
  230. package/dist/src/patch/diff.d.ts.map +1 -0
  231. package/dist/src/patch/diff.js +347 -0
  232. package/dist/src/patch/diff.js.map +1 -0
  233. package/dist/src/patch/fuzz.d.ts +73 -0
  234. package/dist/src/patch/fuzz.d.ts.map +1 -0
  235. package/dist/src/patch/fuzz.js +159 -0
  236. package/dist/src/patch/fuzz.js.map +1 -0
  237. package/dist/src/patch/index.d.ts +32 -0
  238. package/dist/src/patch/index.d.ts.map +1 -0
  239. package/dist/src/patch/index.js +48 -0
  240. package/dist/src/patch/index.js.map +1 -0
  241. package/dist/src/patch/invert.d.ts +15 -0
  242. package/dist/src/patch/invert.d.ts.map +1 -0
  243. package/dist/src/patch/invert.js +284 -0
  244. package/dist/src/patch/invert.js.map +1 -0
  245. package/dist/src/patch/merge.d.ts +56 -0
  246. package/dist/src/patch/merge.d.ts.map +1 -0
  247. package/dist/src/patch/merge.js +586 -0
  248. package/dist/src/patch/merge.js.map +1 -0
  249. package/dist/src/patch/merge.spec.d.ts +2 -0
  250. package/dist/src/patch/merge.spec.d.ts.map +1 -0
  251. package/dist/src/patch/merge.spec.js +1139 -0
  252. package/dist/src/patch/merge.spec.js.map +1 -0
  253. package/dist/src/patch/patch.spec.d.ts +2 -0
  254. package/dist/src/patch/patch.spec.d.ts.map +1 -0
  255. package/dist/src/patch/patch.spec.js +452 -0
  256. package/dist/src/patch/patch.spec.js.map +1 -0
  257. package/dist/src/patch/path.d.ts +115 -0
  258. package/dist/src/patch/path.d.ts.map +1 -0
  259. package/dist/src/patch/path.js +192 -0
  260. package/dist/src/patch/path.js.map +1 -0
  261. package/dist/src/patch/path.spec.d.ts +6 -0
  262. package/dist/src/patch/path.spec.d.ts.map +1 -0
  263. package/dist/src/patch/path.spec.js +322 -0
  264. package/dist/src/patch/path.spec.js.map +1 -0
  265. package/dist/src/patch/prune.d.ts +77 -0
  266. package/dist/src/patch/prune.d.ts.map +1 -0
  267. package/dist/src/patch/prune.js +115 -0
  268. package/dist/src/patch/prune.js.map +1 -0
  269. package/dist/src/patch/prune.spec.d.ts +6 -0
  270. package/dist/src/patch/prune.spec.d.ts.map +1 -0
  271. package/dist/src/patch/prune.spec.js +473 -0
  272. package/dist/src/patch/prune.spec.js.map +1 -0
  273. package/dist/src/patch/type_of_patch.d.ts +25 -0
  274. package/dist/src/patch/type_of_patch.d.ts.map +1 -0
  275. package/dist/src/patch/type_of_patch.js +144 -0
  276. package/dist/src/patch/type_of_patch.js.map +1 -0
  277. package/dist/src/patch/types.d.ts +220 -0
  278. package/dist/src/patch/types.d.ts.map +1 -0
  279. package/dist/src/patch/types.js +62 -0
  280. package/dist/src/patch/types.js.map +1 -0
  281. package/dist/src/patch/validate.d.ts +44 -0
  282. package/dist/src/patch/validate.d.ts.map +1 -0
  283. package/dist/src/patch/validate.js +190 -0
  284. package/dist/src/patch/validate.js.map +1 -0
  285. package/dist/src/patch/validate.spec.d.ts +6 -0
  286. package/dist/src/patch/validate.spec.d.ts.map +1 -0
  287. package/dist/src/patch/validate.spec.js +225 -0
  288. package/dist/src/patch/validate.spec.js.map +1 -0
  289. package/dist/src/patch/walk.d.ts +121 -0
  290. package/dist/src/patch/walk.d.ts.map +1 -0
  291. package/dist/src/patch/walk.js +385 -0
  292. package/dist/src/patch/walk.js.map +1 -0
  293. package/dist/src/patch/walk.spec.d.ts +6 -0
  294. package/dist/src/patch/walk.spec.d.ts.map +1 -0
  295. package/dist/src/patch/walk.spec.js +621 -0
  296. package/dist/src/patch/walk.spec.js.map +1 -0
  297. package/dist/src/platform.d.ts +6 -0
  298. package/dist/src/platform.d.ts.map +1 -1
  299. package/dist/src/platform.spec.d.ts +2 -0
  300. package/dist/src/platform.spec.d.ts.map +1 -0
  301. package/dist/src/platform.spec.js +330 -0
  302. package/dist/src/platform.spec.js.map +1 -0
  303. package/dist/src/serialization/beast.d.ts.map +1 -1
  304. package/dist/src/serialization/beast.js +12 -0
  305. package/dist/src/serialization/beast.js.map +1 -1
  306. package/dist/src/serialization/beast.spec.d.ts +6 -0
  307. package/dist/src/serialization/beast.spec.d.ts.map +1 -0
  308. package/dist/src/serialization/beast.spec.js +406 -0
  309. package/dist/src/serialization/beast.spec.js.map +1 -0
  310. package/dist/src/serialization/beast2/index.d.ts +50 -0
  311. package/dist/src/serialization/beast2/index.d.ts.map +1 -0
  312. package/dist/src/serialization/beast2/index.js +756 -0
  313. package/dist/src/serialization/beast2/index.js.map +1 -0
  314. package/dist/src/serialization/beast2/index.spec.d.ts +6 -0
  315. package/dist/src/serialization/beast2/index.spec.d.ts.map +1 -0
  316. package/dist/src/serialization/beast2/index.spec.js +528 -0
  317. package/dist/src/serialization/beast2/index.spec.js.map +1 -0
  318. package/dist/src/serialization/beast2/sourcemap-table.d.ts +29 -0
  319. package/dist/src/serialization/beast2/sourcemap-table.d.ts.map +1 -0
  320. package/dist/src/serialization/beast2/sourcemap-table.js +82 -0
  321. package/dist/src/serialization/beast2/sourcemap-table.js.map +1 -0
  322. package/dist/src/serialization/beast2/string-table.d.ts +16 -0
  323. package/dist/src/serialization/beast2/string-table.d.ts.map +1 -0
  324. package/dist/src/serialization/beast2/string-table.js +38 -0
  325. package/dist/src/serialization/beast2/string-table.js.map +1 -0
  326. package/dist/src/serialization/beast2/type-table.d.ts +86 -0
  327. package/dist/src/serialization/beast2/type-table.d.ts.map +1 -0
  328. package/dist/src/serialization/beast2/type-table.js +618 -0
  329. package/dist/src/serialization/beast2/type-table.js.map +1 -0
  330. package/dist/src/serialization/beast2/value-table.d.ts +40 -0
  331. package/dist/src/serialization/beast2/value-table.d.ts.map +1 -0
  332. package/dist/src/serialization/beast2/value-table.js +157 -0
  333. package/dist/src/serialization/beast2/value-table.js.map +1 -0
  334. package/dist/src/serialization/binary-utils.d.ts +33 -0
  335. package/dist/src/serialization/binary-utils.d.ts.map +1 -1
  336. package/dist/src/serialization/binary-utils.js +157 -0
  337. package/dist/src/serialization/binary-utils.js.map +1 -1
  338. package/dist/src/serialization/csv.d.ts +17 -17
  339. package/dist/src/serialization/csv.d.ts.map +1 -1
  340. package/dist/src/serialization/csv.js +10 -8
  341. package/dist/src/serialization/csv.js.map +1 -1
  342. package/dist/src/serialization/csv.spec.d.ts +2 -0
  343. package/dist/src/serialization/csv.spec.d.ts.map +1 -0
  344. package/dist/src/serialization/csv.spec.js +556 -0
  345. package/dist/src/serialization/csv.spec.js.map +1 -0
  346. package/dist/src/serialization/east-inferred.spec.d.ts +2 -0
  347. package/dist/src/serialization/east-inferred.spec.d.ts.map +1 -0
  348. package/dist/src/serialization/east-inferred.spec.js +322 -0
  349. package/dist/src/serialization/east-inferred.spec.js.map +1 -0
  350. package/dist/src/serialization/east.d.ts +1 -1
  351. package/dist/src/serialization/east.d.ts.map +1 -1
  352. package/dist/src/serialization/east.js +178 -26
  353. package/dist/src/serialization/east.js.map +1 -1
  354. package/dist/src/serialization/east.spec.d.ts +2 -0
  355. package/dist/src/serialization/east.spec.d.ts.map +1 -0
  356. package/dist/src/serialization/east.spec.js +1548 -0
  357. package/dist/src/serialization/east.spec.js.map +1 -0
  358. package/dist/src/serialization/index.d.ts +1 -2
  359. package/dist/src/serialization/index.d.ts.map +1 -1
  360. package/dist/src/serialization/index.js +1 -2
  361. package/dist/src/serialization/index.js.map +1 -1
  362. package/dist/src/serialization/json.d.ts +1 -1
  363. package/dist/src/serialization/json.d.ts.map +1 -1
  364. package/dist/src/serialization/json.js +123 -24
  365. package/dist/src/serialization/json.js.map +1 -1
  366. package/dist/src/serialization/json.spec.d.ts +2 -0
  367. package/dist/src/serialization/json.spec.d.ts.map +1 -0
  368. package/dist/src/serialization/json.spec.js +1297 -0
  369. package/dist/src/serialization/json.spec.js.map +1 -0
  370. package/dist/src/type_of_type.d.ts +72 -35
  371. package/dist/src/type_of_type.d.ts.map +1 -1
  372. package/dist/src/type_of_type.is_data.spec.d.ts +6 -0
  373. package/dist/src/type_of_type.is_data.spec.d.ts.map +1 -0
  374. package/dist/src/type_of_type.is_data.spec.js +221 -0
  375. package/dist/src/type_of_type.is_data.spec.js.map +1 -0
  376. package/dist/src/type_of_type.js +355 -80
  377. package/dist/src/type_of_type.js.map +1 -1
  378. package/dist/src/type_of_type.to_etv.spec.d.ts +6 -0
  379. package/dist/src/type_of_type.to_etv.spec.d.ts.map +1 -0
  380. package/dist/src/type_of_type.to_etv.spec.js +263 -0
  381. package/dist/src/type_of_type.to_etv.spec.js.map +1 -0
  382. package/dist/src/types.d.ts +82 -12
  383. package/dist/src/types.d.ts.map +1 -1
  384. package/dist/src/types.error.spec.d.ts +2 -0
  385. package/dist/src/types.error.spec.d.ts.map +1 -0
  386. package/dist/src/types.error.spec.js +665 -0
  387. package/dist/src/types.error.spec.js.map +1 -0
  388. package/dist/src/types.js +663 -156
  389. package/dist/src/types.js.map +1 -1
  390. package/dist/src/types.spec.d.ts +2 -0
  391. package/dist/src/types.spec.d.ts.map +1 -0
  392. package/dist/src/types.spec.js +946 -0
  393. package/dist/src/types.spec.js.map +1 -0
  394. package/dist/src/walker.d.ts +65 -0
  395. package/dist/src/walker.d.ts.map +1 -0
  396. package/dist/src/walker.js +165 -0
  397. package/dist/src/walker.js.map +1 -0
  398. package/dist/src/walker.spec.d.ts +6 -0
  399. package/dist/src/walker.spec.d.ts.map +1 -0
  400. package/dist/src/walker.spec.js +109 -0
  401. package/dist/src/walker.spec.js.map +1 -0
  402. package/package.json +16 -38
  403. package/dist/src/serialization/beast2-stream.d.ts +0 -38
  404. package/dist/src/serialization/beast2-stream.d.ts.map +0 -1
  405. package/dist/src/serialization/beast2-stream.js +0 -670
  406. package/dist/src/serialization/beast2-stream.js.map +0 -1
  407. package/dist/src/serialization/beast2.d.ts +0 -41
  408. package/dist/src/serialization/beast2.d.ts.map +0 -1
  409. package/dist/src/serialization/beast2.js +0 -495
  410. package/dist/src/serialization/beast2.js.map +0 -1
package/dist/src/types.js CHANGED
@@ -6,6 +6,58 @@ import { isRef } from "./containers/ref.js";
6
6
  import { SortedMap } from "./containers/sortedmap.js";
7
7
  import { SortedSet } from "./containers/sortedset.js";
8
8
  import { variant_symbol } from "./containers/variant.js";
9
+ import { isMatrix } from "./containers/matrix.js";
10
+ // =============================================================================
11
+ // Type identity — every EastType gets a unique integer ID via a Symbol property.
12
+ // This enables O(1) identity-based lookup across independently constructed
13
+ // type objects that are structurally identical. Mirrors east-c's pointer interning.
14
+ // =============================================================================
15
+ export const type_id_symbol = Symbol.for("@elaraai/east/type_id");
16
+ let _next_type_id = 0;
17
+ /** Assign a unique type_id to an EastType. Called by every type constructor. */
18
+ export function assignTypeId(type) {
19
+ type[type_id_symbol] = _next_type_id++;
20
+ return type;
21
+ }
22
+ /** Get the type_id of an EastType, EastTypeValue, or any stamped object. */
23
+ export function getTypeId(type) {
24
+ return type?.[type_id_symbol];
25
+ }
26
+ // =============================================================================
27
+ // Type interning — ensures structurally identical compound types share identity.
28
+ // Mirrors east-c's pointer-based hash table interning.
29
+ // Uses integer hashing of child type_ids for O(1) lookup with zero GC pressure.
30
+ // =============================================================================
31
+ function hashCombine(h, v) {
32
+ return ((h ^ v) * 0x01000193) | 0;
33
+ }
34
+ // Intern table: hash → type. On collision we chain (store array).
35
+ const _intern = new Map();
36
+ function internLookup(hash, verify) {
37
+ const entry = _intern.get(hash);
38
+ if (entry === undefined)
39
+ return undefined;
40
+ if (Array.isArray(entry)) {
41
+ for (const candidate of entry) {
42
+ if (verify(candidate))
43
+ return candidate;
44
+ }
45
+ return undefined;
46
+ }
47
+ return verify(entry) ? entry : undefined;
48
+ }
49
+ function internStore(hash, type) {
50
+ const existing = _intern.get(hash);
51
+ if (existing === undefined) {
52
+ _intern.set(hash, type);
53
+ }
54
+ else if (Array.isArray(existing)) {
55
+ existing.push(type);
56
+ }
57
+ else {
58
+ _intern.set(hash, [existing, type]);
59
+ }
60
+ }
9
61
  /**
10
62
  * Error thrown when type operations encounter incompatible types.
11
63
  *
@@ -13,28 +65,52 @@ import { variant_symbol } from "./containers/variant.js";
13
65
  * Used by type union, intersection, and equality operations when types
14
66
  * cannot be combined or compared as requested.
15
67
  */
16
- class TypeMismatchError extends Error {
17
- constructor(message, options) {
18
- super(message, options);
68
+ export class TypeMismatchError extends Error {
69
+ path;
70
+ reason;
71
+ constructor(reason, options) {
72
+ super(reason, options);
19
73
  this.name = "TypeMismatchError";
74
+ this.reason = reason;
75
+ this.path = [];
76
+ }
77
+ addPathSegment(segment) {
78
+ this.path.unshift(segment);
79
+ this.message = `at ${this.path.join("")}: ${this.reason}`;
80
+ }
81
+ }
82
+ function withPathSegment(segment, fn) {
83
+ try {
84
+ return fn();
85
+ }
86
+ catch (err) {
87
+ if (err instanceof TypeMismatchError)
88
+ err.addPathSegment(segment);
89
+ throw err;
20
90
  }
21
91
  }
22
92
  /** Singleton instance of the Never type. */
23
- export const NeverType = { type: "Never" };
93
+ export const NeverType = assignTypeId({ type: "Never" });
24
94
  /** Singleton instance of the Null type. */
25
- export const NullType = { type: "Null" };
95
+ export const NullType = assignTypeId({ type: "Null" });
26
96
  /** Singleton instance of the Boolean type. */
27
- export const BooleanType = { type: "Boolean" };
97
+ export const BooleanType = assignTypeId({ type: "Boolean" });
28
98
  /** Singleton instance of the Integer type. */
29
- export const IntegerType = { type: "Integer" };
99
+ export const IntegerType = assignTypeId({ type: "Integer" });
30
100
  /** Singleton instance of the Float type. */
31
- export const FloatType = { type: "Float" };
101
+ export const FloatType = assignTypeId({ type: "Float" });
32
102
  /** Singleton instance of the String type. */
33
- export const StringType = { type: "String" };
103
+ export const StringType = assignTypeId({ type: "String" });
34
104
  /** Singleton instance of the DateTime type. */
35
- export const DateTimeType = { type: "DateTime" };
105
+ export const DateTimeType = assignTypeId({ type: "DateTime" });
36
106
  /** Singleton instance of the Blob type. */
37
- export const BlobType = { type: "Blob" };
107
+ export const BlobType = assignTypeId({ type: "Blob" });
108
+ /** Returns true if the type is a primitive (no child types, no refs possible). */
109
+ export function isPrimitiveType(type) {
110
+ const t = type.type;
111
+ return t === "Never" || t === "Null" || t === "Boolean" || t === "Integer" ||
112
+ t === "Float" || t === "String" || t === "DateTime" || t === "Blob";
113
+ }
38
114
  /**
39
115
  * Constructs a reference (or cell) type with the specified element type.
40
116
  *
@@ -48,7 +124,13 @@ export const BlobType = { type: "Blob" };
48
124
  * @throws When the element type contains functions
49
125
  */
50
126
  export function RefType(type) {
51
- return { type: "Ref", value: type };
127
+ const h = hashCombine(0x52, type?.[type_id_symbol] ?? 0);
128
+ const cached = internLookup(h, (c) => c.type === "Ref" && c.value === type);
129
+ if (cached)
130
+ return cached;
131
+ const result = assignTypeId({ type: "Ref", value: type });
132
+ internStore(h, result);
133
+ return result;
52
134
  }
53
135
  ;
54
136
  /**
@@ -60,7 +142,13 @@ export function RefType(type) {
60
142
  * @throws When the element type contains functions
61
143
  */
62
144
  export function ArrayType(type) {
63
- return { type: "Array", value: type };
145
+ const h = hashCombine(0x41, type?.[type_id_symbol] ?? 0);
146
+ const cached = internLookup(h, (c) => c.type === "Array" && c.value === type);
147
+ if (cached)
148
+ return cached;
149
+ const result = assignTypeId({ type: "Array", value: type });
150
+ internStore(h, result);
151
+ return result;
64
152
  }
65
153
  ;
66
154
  /**
@@ -75,7 +163,13 @@ export function SetType(type) {
75
163
  if (typeof type !== "string" && !isImmutableType(type)) {
76
164
  throw new Error(`Set key type must be an immutable type, got ${printType(type)}`);
77
165
  }
78
- return { type: "Set", key: type };
166
+ const h = hashCombine(0x53, type?.[type_id_symbol] ?? 0);
167
+ const cached = internLookup(h, (c) => c.type === "Set" && c.key === type);
168
+ if (cached)
169
+ return cached;
170
+ const result = assignTypeId({ type: "Set", key: type });
171
+ internStore(h, result);
172
+ return result;
79
173
  }
80
174
  ;
81
175
  /**
@@ -92,7 +186,14 @@ export function DictType(key, value) {
92
186
  if (typeof key !== "string" && !isImmutableType(key)) {
93
187
  throw new Error(`Dict key type must be an immutable type, got ${printType(key)}`);
94
188
  }
95
- return { type: "Dict", key, value };
189
+ let h = hashCombine(0x44, key?.[type_id_symbol] ?? 0);
190
+ h = hashCombine(h, value?.[type_id_symbol] ?? 0);
191
+ const cached = internLookup(h, (c) => c.type === "Dict" && c.key === key && c.value === value);
192
+ if (cached)
193
+ return cached;
194
+ const result = assignTypeId({ type: "Dict", key, value });
195
+ internStore(h, result);
196
+ return result;
96
197
  }
97
198
  ;
98
199
  /**
@@ -103,7 +204,35 @@ export function DictType(key, value) {
103
204
  * @returns A Struct type
104
205
  */
105
206
  export function StructType(field_types) {
106
- return { type: "Struct", fields: field_types };
207
+ // Intern: structurally identical struct types (same field order, same names,
208
+ // same child type_ids) share a single canonical object. This mirrors the
209
+ // existing interning for Array/Dict/Function etc. and is required for the
210
+ // beast2 v2 encoder to produce a canonical type table — otherwise calling
211
+ // StructType({ x: Int, y: Int }) twice would yield two distinct entries.
212
+ const keys = Object.keys(field_types);
213
+ let h = hashCombine(0x09, keys.length);
214
+ for (const k of keys) {
215
+ h = hashCombine(h, field_types[k]?.[type_id_symbol] ?? 0);
216
+ }
217
+ const cached = internLookup(h, (c) => {
218
+ if (c.type !== "Struct")
219
+ return false;
220
+ const ck = Object.keys(c.fields);
221
+ if (ck.length !== keys.length)
222
+ return false;
223
+ for (let i = 0; i < keys.length; i++) {
224
+ if (ck[i] !== keys[i])
225
+ return false;
226
+ if (c.fields[keys[i]] !== field_types[keys[i]])
227
+ return false;
228
+ }
229
+ return true;
230
+ });
231
+ if (cached)
232
+ return cached;
233
+ const result = assignTypeId({ type: "Struct", fields: field_types });
234
+ internStore(h, result);
235
+ return result;
107
236
  }
108
237
  ;
109
238
  /**
@@ -116,7 +245,33 @@ export function StructType(field_types) {
116
245
  export function VariantType(case_types) {
117
246
  // Cases are sorted alphabetically by their name
118
247
  const cases_sorted = Object.fromEntries(Object.entries(case_types).sort((x, y) => x[0] < y[0] ? -1 : x[0] === y[0] ? 0 : 1));
119
- return { type: "Variant", cases: cases_sorted };
248
+ // Intern: match Struct. Hash is computed over the sorted keys so two calls
249
+ // with cases in different orders produce the same hash, and verify confirms
250
+ // identical names and child type_ids.
251
+ const keys = Object.keys(cases_sorted);
252
+ let h = hashCombine(0x08, keys.length);
253
+ for (const k of keys) {
254
+ h = hashCombine(h, cases_sorted[k]?.[type_id_symbol] ?? 0);
255
+ }
256
+ const cached = internLookup(h, (c) => {
257
+ if (c.type !== "Variant")
258
+ return false;
259
+ const ck = Object.keys(c.cases);
260
+ if (ck.length !== keys.length)
261
+ return false;
262
+ for (let i = 0; i < keys.length; i++) {
263
+ if (ck[i] !== keys[i])
264
+ return false;
265
+ if (c.cases[keys[i]] !== cases_sorted[keys[i]])
266
+ return false;
267
+ }
268
+ return true;
269
+ });
270
+ if (cached)
271
+ return cached;
272
+ const result = assignTypeId({ type: "Variant", cases: cases_sorted });
273
+ internStore(h, result);
274
+ return result;
120
275
  }
121
276
  ;
122
277
  /**
@@ -197,6 +352,9 @@ function validateNotMutuallyRecursive(type, allowedMarker) {
197
352
  t.inputs.forEach(input => check(input, allowed));
198
353
  check(t.output, allowed);
199
354
  }
355
+ else if (t.type === "Vector" || t.type === "Matrix") {
356
+ check(t.element, allowed);
357
+ }
200
358
  // Primitive types don't need recursion
201
359
  }
202
360
  check(type, true);
@@ -237,14 +395,24 @@ function validateNotMutuallyRecursive(type, allowedMarker) {
237
395
  * ```
238
396
  */
239
397
  export function RecursiveType(f) {
240
- const ret = { type: "Recursive", node: undefined };
398
+ const ret = assignTypeId({ type: "Recursive", node: undefined });
241
399
  const type = f(ret);
242
400
  ret.node = type;
243
401
  // Validate SCC size 1 (no nested RecursiveTypes with cross-references)
244
402
  validateNotMutuallyRecursive(type, ret);
403
+ // Intern: if a structurally identical RecursiveType already exists, reuse it.
404
+ // This ensures two independently-created RecursiveTypes with the same structure
405
+ // share a single type_id, making id-based Recursive refs work correctly.
406
+ for (const existing of _recursiveIntern) {
407
+ if (isTypeEqual(existing, ret)) {
408
+ return existing;
409
+ }
410
+ }
411
+ _recursiveIntern.push(ret);
245
412
  return ret;
246
413
  }
247
414
  ;
415
+ const _recursiveIntern = [];
248
416
  /**
249
417
  * Constructs a Function type with the specified input and output types.
250
418
  *
@@ -255,7 +423,17 @@ export function RecursiveType(f) {
255
423
  * @see {@link AsyncFunctionType} for asynchronous functions.
256
424
  */
257
425
  export function FunctionType(inputs, output) {
258
- return { type: "Function", inputs, output };
426
+ let h = hashCombine(0x46, inputs.length);
427
+ for (const inp of inputs)
428
+ h = hashCombine(h, inp?.[type_id_symbol] ?? 0);
429
+ h = hashCombine(h, output?.[type_id_symbol] ?? 0);
430
+ const cached = internLookup(h, (c) => c.type === "Function" && c.inputs.length === inputs.length && c.output === output &&
431
+ c.inputs.every((inp, i) => inp === inputs[i]));
432
+ if (cached)
433
+ return cached;
434
+ const result = assignTypeId({ type: "Function", inputs, output });
435
+ internStore(h, result);
436
+ return result;
259
437
  }
260
438
  ;
261
439
  /**
@@ -268,7 +446,43 @@ export function FunctionType(inputs, output) {
268
446
  * @see {@link AsyncFunctionType} for asynchronous functions.
269
447
  */
270
448
  export function AsyncFunctionType(inputs, output) {
271
- return { type: "AsyncFunction", inputs, output };
449
+ let h = hashCombine(0x61, inputs.length);
450
+ for (const inp of inputs)
451
+ h = hashCombine(h, inp?.[type_id_symbol] ?? 0);
452
+ h = hashCombine(h, output?.[type_id_symbol] ?? 0);
453
+ const cached = internLookup(h, (c) => c.type === "AsyncFunction" && c.inputs.length === inputs.length && c.output === output &&
454
+ c.inputs.every((inp, i) => inp === inputs[i]));
455
+ if (cached)
456
+ return cached;
457
+ const result = assignTypeId({ type: "AsyncFunction", inputs, output });
458
+ internStore(h, result);
459
+ return result;
460
+ }
461
+ ;
462
+ export function VectorType(element) {
463
+ if (typeof element !== "string" && element.type !== "Float" && element.type !== "Integer" && element.type !== "Boolean") {
464
+ throw new Error(`Vector element type must be Float, Integer, or Boolean, got ${printType(element)}`);
465
+ }
466
+ const h = hashCombine(0x56, element?.[type_id_symbol] ?? 0);
467
+ const cached = internLookup(h, (c) => c.type === "Vector" && c.element === element);
468
+ if (cached)
469
+ return cached;
470
+ const result = assignTypeId({ type: "Vector", element });
471
+ internStore(h, result);
472
+ return result;
473
+ }
474
+ ;
475
+ export function MatrixType(element) {
476
+ if (typeof element !== "string" && element.type !== "Float" && element.type !== "Integer" && element.type !== "Boolean") {
477
+ throw new Error(`Matrix element type must be Float, Integer, or Boolean, got ${printType(element)}`);
478
+ }
479
+ const h = hashCombine(0x4d, element?.[type_id_symbol] ?? 0);
480
+ const cached = internLookup(h, (c) => c.type === "Matrix" && c.element === element);
481
+ if (cached)
482
+ return cached;
483
+ const result = assignTypeId({ type: "Matrix", element });
484
+ internStore(h, result);
485
+ return result;
272
486
  }
273
487
  ;
274
488
  /**
@@ -319,6 +533,9 @@ export function isDataType(type, recursive_type) {
319
533
  else if (type.type === "Recursive") {
320
534
  return type.node === recursive_type ? true : isDataType(type.node, type);
321
535
  }
536
+ else if (type.type === "Vector" || type.type === "Matrix") {
537
+ return true;
538
+ }
322
539
  else if (type.type === "Function") {
323
540
  return false;
324
541
  }
@@ -361,6 +578,9 @@ export function isImmutableType(type, recursive_type) {
361
578
  // Dict are mutable
362
579
  return false;
363
580
  }
581
+ else if (type.type === "Vector" || type.type === "Matrix") {
582
+ return false;
583
+ }
364
584
  else if (type.type === "Struct") {
365
585
  for (const field_type of Object.values(type.fields)) {
366
586
  if (!isImmutableType(field_type, recursive_type)) {
@@ -424,6 +644,9 @@ export function EastTypeOf(value) {
424
644
  if (value instanceof Date) {
425
645
  return DateTimeType;
426
646
  }
647
+ else if (value instanceof Uint8ClampedArray) {
648
+ return VectorType(BooleanType);
649
+ }
427
650
  else if (value instanceof Uint8Array) {
428
651
  return BlobType;
429
652
  }
@@ -472,28 +695,32 @@ export function EastTypeOf(value) {
472
695
  * For {@link RecursiveType}, uses cycle tracking to handle recursive references.
473
696
  */
474
697
  // Cache for memoizing isTypeEqual results (top-level calls only)
475
- const typeEqualCache = new WeakMap();
698
+ const typeEqualCache = new Map();
476
699
  export function isTypeEqual(t1, t2, r1 = t1, r2 = t2) {
477
700
  // Fast path: reference equality
478
701
  if (t1 === t2)
479
702
  return true;
703
+ // Fast path: type_id equality (interned types with same id are structurally equal)
704
+ const tid1 = getTypeId(t1);
705
+ const tid2 = getTypeId(t2);
706
+ if (tid1 !== undefined && tid1 === tid2)
707
+ return true;
480
708
  // Memoization for top-level calls only (r1/r2 are just for internal cycle detection)
481
709
  const isTopLevel = r1 === t1 && r2 === t2;
482
- if (isTopLevel) {
483
- const innerCache = typeEqualCache.get(t1);
710
+ if (isTopLevel && tid1 !== undefined && tid2 !== undefined) {
711
+ const innerCache = typeEqualCache.get(tid1);
484
712
  if (innerCache) {
485
- const cached = innerCache.get(t2);
713
+ const cached = innerCache.get(tid2);
486
714
  if (cached !== undefined)
487
715
  return cached;
488
716
  }
489
717
  const result = isTypeEqualImpl(t1, t2, r1, r2);
490
- // Store result in cache
491
- let cache = typeEqualCache.get(t1);
718
+ let cache = typeEqualCache.get(tid1);
492
719
  if (!cache) {
493
- cache = new WeakMap();
494
- typeEqualCache.set(t1, cache);
720
+ cache = new Map();
721
+ typeEqualCache.set(tid1, cache);
495
722
  }
496
- cache.set(t2, result);
723
+ cache.set(tid2, result);
497
724
  return result;
498
725
  }
499
726
  return isTypeEqualImpl(t1, t2, r1, r2);
@@ -576,6 +803,22 @@ function isTypeEqualImpl(t1, t2, r1, r2) {
576
803
  return false;
577
804
  }
578
805
  }
806
+ else if (t1.type === "Vector") {
807
+ if (t2.type === "Vector") {
808
+ return isTypeEqual(t1.element, t2.element, r1, r2);
809
+ }
810
+ else {
811
+ return false;
812
+ }
813
+ }
814
+ else if (t1.type === "Matrix") {
815
+ if (t2.type === "Matrix") {
816
+ return isTypeEqual(t1.element, t2.element, r1, r2);
817
+ }
818
+ else {
819
+ return false;
820
+ }
821
+ }
579
822
  else if (t1.type === "Struct") {
580
823
  if (t2.type === "Struct") {
581
824
  const e1 = Object.entries(t1.fields);
@@ -766,6 +1009,26 @@ export function isValueOf(value, type, node_type, nodes_visited) {
766
1009
  ;
767
1010
  return isValueOf(value.value, t, node_type, nodes_visited);
768
1011
  }
1012
+ else if (type.type === "Vector") {
1013
+ if (type.element.type === "Float")
1014
+ return value instanceof Float64Array;
1015
+ if (type.element.type === "Integer")
1016
+ return value instanceof BigInt64Array;
1017
+ if (type.element.type === "Boolean")
1018
+ return value instanceof Uint8ClampedArray;
1019
+ return false;
1020
+ }
1021
+ else if (type.type === "Matrix") {
1022
+ if (!isMatrix(value))
1023
+ return false;
1024
+ if (type.element.type === "Float")
1025
+ return value.data instanceof Float64Array;
1026
+ if (type.element.type === "Integer")
1027
+ return value.data instanceof BigInt64Array;
1028
+ if (type.element.type === "Boolean")
1029
+ return value.data instanceof Uint8ClampedArray;
1030
+ return false;
1031
+ }
769
1032
  else if (type.type === "Recursive") {
770
1033
  if (node_type === type.node) {
771
1034
  if (nodes_visited.has(value)) {
@@ -866,14 +1129,21 @@ export function printType(type, stack = []) {
866
1129
  stack.pop();
867
1130
  return ret;
868
1131
  }
1132
+ else if (type.type === "Vector") {
1133
+ return `.Vector ${printType(type.element, stack)}`;
1134
+ }
1135
+ else if (type.type === "Matrix") {
1136
+ return `.Matrix ${printType(type.element, stack)}`;
1137
+ }
869
1138
  else if (type.type === "Recursive") {
870
- // TODO update for our new recursive type representation
871
- const idx = stack.indexOf(type.node);
1139
+ const idx = stack.indexOf(type);
872
1140
  if (idx !== -1) {
873
- // Recursive reference
874
- return `.Recursive ${stack.length - idx}`;
1141
+ return `.Recursive .ref ${getTypeId(type)}`;
875
1142
  }
876
- return printType(type.node, stack);
1143
+ stack.push(type);
1144
+ const inner = printType(type.node, stack);
1145
+ stack.pop();
1146
+ return `.Recursive .wrapper (id=${getTypeId(type)}, inner=${inner})`;
877
1147
  }
878
1148
  else if (type.type === "Function") {
879
1149
  // Note: functions can't be inside recursive types
@@ -893,6 +1163,140 @@ export function printType(type, stack = []) {
893
1163
  throw new Error(`Unknown type encountered during type printing: ${type.type}`);
894
1164
  }
895
1165
  }
1166
+ /**
1167
+ * Converts an East type to a truncated string representation for error messages.
1168
+ *
1169
+ * @param type - The {@link EastType} to print
1170
+ * @param maxDepth - Maximum depth to recurse into compound types (default 1)
1171
+ * @param maxFields - Maximum number of fields/cases to show before truncating (default 3)
1172
+ * @param stack - Internal parameter for tracking recursive types
1173
+ * @returns A human-readable, truncated string representation of the type
1174
+ *
1175
+ * @remarks
1176
+ * Like {@link printType} but truncates large types for readable error messages.
1177
+ * At depth 0, compound types show summary form (e.g., `.Struct [N fields]`).
1178
+ * Shows first `maxFields` entries then `... and N more`.
1179
+ */
1180
+ export function printTypeSummary(type, maxDepth = 1, maxFields = 3, stack = []) {
1181
+ if (type.type === "Never") {
1182
+ return ".Never";
1183
+ }
1184
+ else if (type.type === "Null") {
1185
+ return ".Null";
1186
+ }
1187
+ else if (type.type === "Boolean") {
1188
+ return ".Boolean";
1189
+ }
1190
+ else if (type.type === "Integer") {
1191
+ return ".Integer";
1192
+ }
1193
+ else if (type.type === "Float") {
1194
+ return ".Float";
1195
+ }
1196
+ else if (type.type === "String") {
1197
+ return ".String";
1198
+ }
1199
+ else if (type.type === "DateTime") {
1200
+ return ".DateTime";
1201
+ }
1202
+ else if (type.type === "Blob") {
1203
+ return ".Blob";
1204
+ }
1205
+ else if (type.type === "Ref") {
1206
+ if (maxDepth <= 0)
1207
+ return ".Ref ...";
1208
+ stack.push(type);
1209
+ const ret = `.Ref ${printTypeSummary(type.value, maxDepth - 1, maxFields, stack)}`;
1210
+ stack.pop();
1211
+ return ret;
1212
+ }
1213
+ else if (type.type === "Array") {
1214
+ if (maxDepth <= 0)
1215
+ return ".Array ...";
1216
+ stack.push(type);
1217
+ const ret = `.Array ${printTypeSummary(type.value, maxDepth - 1, maxFields, stack)}`;
1218
+ stack.pop();
1219
+ return ret;
1220
+ }
1221
+ else if (type.type === "Set") {
1222
+ if (maxDepth <= 0)
1223
+ return ".Set ...";
1224
+ stack.push(type);
1225
+ const ret = `.Set ${printTypeSummary(type.key, maxDepth - 1, maxFields, stack)}`;
1226
+ stack.pop();
1227
+ return ret;
1228
+ }
1229
+ else if (type.type === "Dict") {
1230
+ if (maxDepth <= 0)
1231
+ return ".Dict ...";
1232
+ stack.push(type);
1233
+ const ret = `.Dict (key=${printTypeSummary(type.key, maxDepth - 1, maxFields, stack)}, value=${printTypeSummary(type.value, maxDepth - 1, maxFields, stack)})`;
1234
+ stack.pop();
1235
+ return ret;
1236
+ }
1237
+ else if (type.type === "Struct") {
1238
+ const entries = Object.entries(type.fields);
1239
+ if (maxDepth <= 0)
1240
+ return `.Struct [${entries.length} fields]`;
1241
+ stack.push(type);
1242
+ const shown = entries.slice(0, maxFields).map(([k, t]) => `(name=${JSON.stringify(k)}, type=${printTypeSummary(t, maxDepth - 1, maxFields, stack)})`);
1243
+ stack.pop();
1244
+ const remaining = entries.length - maxFields;
1245
+ if (remaining > 0) {
1246
+ return `.Struct [${shown.join(", ")}, ... and ${remaining} more]`;
1247
+ }
1248
+ return `.Struct [${shown.join(", ")}]`;
1249
+ }
1250
+ else if (type.type === "Variant") {
1251
+ const entries = Object.entries(type.cases);
1252
+ if (maxDepth <= 0) {
1253
+ return `.Variant (${entries.map(([k]) => k).join(" | ")})`;
1254
+ }
1255
+ stack.push(type);
1256
+ const shown = entries.slice(0, maxFields).map(([k, t]) => `(name=${JSON.stringify(k)}, type=${printTypeSummary(t, maxDepth - 1, maxFields, stack)})`);
1257
+ stack.pop();
1258
+ const remaining = entries.length - maxFields;
1259
+ if (remaining > 0) {
1260
+ return `.Variant [${shown.join(", ")}, ... and ${remaining} more]`;
1261
+ }
1262
+ return `.Variant [${shown.join(", ")}]`;
1263
+ }
1264
+ else if (type.type === "Vector") {
1265
+ return `.Vector ${printTypeSummary(type.element, maxDepth, maxFields, stack)}`;
1266
+ }
1267
+ else if (type.type === "Matrix") {
1268
+ return `.Matrix ${printTypeSummary(type.element, maxDepth, maxFields, stack)}`;
1269
+ }
1270
+ else if (type.type === "Recursive") {
1271
+ const idx = stack.indexOf(type);
1272
+ if (idx !== -1) {
1273
+ return `.Recursive .ref ${getTypeId(type)}`;
1274
+ }
1275
+ stack.push(type);
1276
+ const inner = printTypeSummary(type.node, maxDepth, maxFields, stack);
1277
+ stack.pop();
1278
+ return `.Recursive .wrapper (id=${getTypeId(type)}, inner=${inner})`;
1279
+ }
1280
+ else if (type.type === "Function") {
1281
+ if (maxDepth <= 0)
1282
+ return `.Function ...`;
1283
+ stack.push(type);
1284
+ const ret = `.Function (inputs=[${type.inputs.map(t => printTypeSummary(t, maxDepth - 1, maxFields, stack)).join(", ")}], output=${printTypeSummary(type.output, maxDepth - 1, maxFields, stack)})`;
1285
+ stack.pop();
1286
+ return ret;
1287
+ }
1288
+ else if (type.type === "AsyncFunction") {
1289
+ if (maxDepth <= 0)
1290
+ return `.AsyncFunction ...`;
1291
+ stack.push(type);
1292
+ const ret = `.AsyncFunction (inputs=[${type.inputs.map(t => printTypeSummary(t, maxDepth - 1, maxFields, stack)).join(", ")}], output=${printTypeSummary(type.output, maxDepth - 1, maxFields, stack)})`;
1293
+ stack.pop();
1294
+ return ret;
1295
+ }
1296
+ else {
1297
+ throw new Error(`Unknown type encountered during type printing: ${type.type}`);
1298
+ }
1299
+ }
896
1300
  /**
897
1301
  * Checks if one East type is a subtype of another.
898
1302
  *
@@ -908,27 +1312,34 @@ export function printType(type, stack = []) {
908
1312
  * - {@link FunctionType} uses contravariant inputs and covariant outputs
909
1313
  */
910
1314
  // Cache for memoizing isSubtype results
911
- const isSubtypeCache = new WeakMap();
1315
+ const isSubtypeCache = new Map();
912
1316
  export function isSubtype(t1, t2) {
913
1317
  // Fast path: reference equality (reflexivity)
914
1318
  if (t1 === t2)
915
1319
  return true;
1320
+ // Fast path: type_id equality
1321
+ const tid1 = getTypeId(t1);
1322
+ const tid2 = getTypeId(t2);
1323
+ if (tid1 !== undefined && tid1 === tid2)
1324
+ return true;
916
1325
  // Check cache
917
- const innerCache = isSubtypeCache.get(t1);
918
- if (innerCache) {
919
- const cached = innerCache.get(t2);
920
- if (cached !== undefined)
921
- return cached;
922
- }
923
- const result = isSubtypeImpl(t1, t2);
924
- // Store in cache
925
- let cache = isSubtypeCache.get(t1);
926
- if (!cache) {
927
- cache = new WeakMap();
928
- isSubtypeCache.set(t1, cache);
929
- }
930
- cache.set(t2, result);
931
- return result;
1326
+ if (tid1 !== undefined && tid2 !== undefined) {
1327
+ const innerCache = isSubtypeCache.get(tid1);
1328
+ if (innerCache) {
1329
+ const cached = innerCache.get(tid2);
1330
+ if (cached !== undefined)
1331
+ return cached;
1332
+ }
1333
+ const result = isSubtypeImpl(t1, t2);
1334
+ let cache = isSubtypeCache.get(tid1);
1335
+ if (!cache) {
1336
+ cache = new Map();
1337
+ isSubtypeCache.set(tid1, cache);
1338
+ }
1339
+ cache.set(tid2, result);
1340
+ return result;
1341
+ }
1342
+ return isSubtypeImpl(t1, t2);
932
1343
  }
933
1344
  function isSubtypeImpl(t1, t2) {
934
1345
  // Equi-recursive type subtyping: unfold recursive types when only one side is recursive
@@ -988,6 +1399,22 @@ function isSubtypeImpl(t1, t2) {
988
1399
  return false;
989
1400
  }
990
1401
  }
1402
+ else if (t1.type === "Vector") {
1403
+ if (t2.type === "Vector") {
1404
+ return isTypeEqual(t1.element, t2.element);
1405
+ }
1406
+ else {
1407
+ return false;
1408
+ }
1409
+ }
1410
+ else if (t1.type === "Matrix") {
1411
+ if (t2.type === "Matrix") {
1412
+ return isTypeEqual(t1.element, t2.element);
1413
+ }
1414
+ else {
1415
+ return false;
1416
+ }
1417
+ }
991
1418
  else if (t1.type === "Set") {
992
1419
  if (t2.type === "Set") {
993
1420
  return isTypeEqual(t1.key, t2.key);
@@ -1074,6 +1501,8 @@ function isSubtypeImpl(t1, t2) {
1074
1501
  * Error messages include the full type path for debugging.
1075
1502
  */
1076
1503
  export function TypeUnion(t1, t2) {
1504
+ if (t1 === t2)
1505
+ return t1;
1077
1506
  // TODO this is broken for recursive types (need to do cycle tracking in tandem with TypeEqual/TypeIntersect)
1078
1507
  try {
1079
1508
  if (t1.type === "Never") {
@@ -1093,7 +1522,7 @@ export function TypeUnion(t1, t2) {
1093
1522
  if (isSubtype(t2, t1.node)) {
1094
1523
  return t1;
1095
1524
  }
1096
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1525
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1097
1526
  }
1098
1527
  }
1099
1528
  else if (t2.type === "Recursive") {
@@ -1101,38 +1530,54 @@ export function TypeUnion(t1, t2) {
1101
1530
  if (isSubtype(t1, t2.node)) {
1102
1531
  return t2;
1103
1532
  }
1104
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1533
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1105
1534
  }
1106
1535
  else if (t1.type === "Ref") {
1107
1536
  if (t2.type === "Ref") {
1108
- return RefType(TypeEqual(t1.value, t2.value));
1537
+ return RefType(withPathSegment("[ref]", () => TypeEqual(t1.value, t2.value)));
1109
1538
  }
1110
1539
  else {
1111
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1540
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1112
1541
  }
1113
1542
  }
1114
1543
  else if (t1.type === "Array") {
1115
1544
  if (t2.type === "Array") {
1116
- return ArrayType(TypeEqual(t1.value, t2.value));
1545
+ return ArrayType(withPathSegment("[element]", () => TypeEqual(t1.value, t2.value)));
1546
+ }
1547
+ else {
1548
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1549
+ }
1550
+ }
1551
+ else if (t1.type === "Vector") {
1552
+ if (t2.type === "Vector") {
1553
+ return VectorType(withPathSegment("[element]", () => TypeEqual(t1.element, t2.element)));
1117
1554
  }
1118
1555
  else {
1119
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1556
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1557
+ }
1558
+ }
1559
+ else if (t1.type === "Matrix") {
1560
+ if (t2.type === "Matrix") {
1561
+ return MatrixType(withPathSegment("[element]", () => TypeEqual(t1.element, t2.element)));
1562
+ }
1563
+ else {
1564
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1120
1565
  }
1121
1566
  }
1122
1567
  else if (t1.type === "Set") {
1123
1568
  if (t2.type === "Set") {
1124
- return SetType(TypeEqual(t1.key, t2.key));
1569
+ return SetType(withPathSegment("[key]", () => TypeEqual(t1.key, t2.key)));
1125
1570
  }
1126
1571
  else {
1127
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1572
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1128
1573
  }
1129
1574
  }
1130
1575
  else if (t1.type === "Dict") {
1131
1576
  if (t2.type === "Dict") {
1132
- return DictType(TypeEqual(t1.key, t2.key), TypeEqual(t1.value, t2.value));
1577
+ return DictType(withPathSegment("[key]", () => TypeEqual(t1.key, t2.key)), withPathSegment("[value]", () => TypeEqual(t1.value, t2.value)));
1133
1578
  }
1134
1579
  else {
1135
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1580
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1136
1581
  }
1137
1582
  }
1138
1583
  else if (t1.type === "Struct") {
@@ -1140,20 +1585,20 @@ export function TypeUnion(t1, t2) {
1140
1585
  const e1 = Object.entries(t1.fields);
1141
1586
  const e2 = Object.entries(t2.fields);
1142
1587
  if (e1.length !== e2.length)
1143
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: structs contain different number of fields`);
1588
+ throw new TypeMismatchError(`structs contain different number of fields (${e1.length} vs ${e2.length})`);
1144
1589
  let i = 0;
1145
1590
  const e = {};
1146
1591
  for (const [k1, f1] of e1) {
1147
1592
  const [k2, f2] = e2[i];
1148
1593
  if (k1 !== k2)
1149
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: struct field ${i} has mismatched names ${printIdentifier(k1)} and ${printIdentifier(k2)}`);
1150
- e[k1] = TypeUnion(f1, f2);
1594
+ throw new TypeMismatchError(`struct field ${i} has mismatched names ${printIdentifier(k1)} and ${printIdentifier(k2)}`);
1595
+ e[k1] = withPathSegment(`.${k1}`, () => TypeUnion(f1, f2));
1151
1596
  i += 1;
1152
1597
  }
1153
1598
  return StructType(e);
1154
1599
  }
1155
1600
  else {
1156
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1601
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1157
1602
  }
1158
1603
  }
1159
1604
  else if (t1.type === "Variant") {
@@ -1165,7 +1610,7 @@ export function TypeUnion(t1, t2) {
1165
1610
  cases[k1] = f1;
1166
1611
  }
1167
1612
  else {
1168
- cases[k1] = TypeUnion(f1, f2);
1613
+ cases[k1] = withPathSegment(`.${k1}`, () => TypeUnion(f1, f2));
1169
1614
  }
1170
1615
  }
1171
1616
  for (const [k2, f2] of Object.entries(t2.cases)) {
@@ -1177,41 +1622,41 @@ export function TypeUnion(t1, t2) {
1177
1622
  return VariantType(cases);
1178
1623
  }
1179
1624
  else {
1180
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1625
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1181
1626
  }
1182
1627
  }
1183
1628
  else if (t1.type === "Function") {
1184
1629
  if (t2.type === "Function") {
1185
1630
  if (t1.inputs.length !== t2.inputs.length) {
1186
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1631
+ throw new TypeMismatchError(`functions take different number of arguments (${t1.inputs.length} vs ${t2.inputs.length})`);
1187
1632
  }
1188
- return FunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output));
1633
+ return FunctionType(t1.inputs.map((t, i) => withPathSegment(`[input ${i}]`, () => TypeIntersect(t, t2.inputs[i]))), withPathSegment("[output]", () => TypeUnion(t1.output, t2.output)));
1189
1634
  }
1190
1635
  else if (t2.type === "AsyncFunction") {
1191
1636
  if (t1.inputs.length !== t2.inputs.length) {
1192
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1637
+ throw new TypeMismatchError(`functions take different number of arguments (${t1.inputs.length} vs ${t2.inputs.length})`);
1193
1638
  }
1194
- return AsyncFunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output));
1639
+ return AsyncFunctionType(t1.inputs.map((t, i) => withPathSegment(`[input ${i}]`, () => TypeIntersect(t, t2.inputs[i]))), withPathSegment("[output]", () => TypeUnion(t1.output, t2.output)));
1195
1640
  }
1196
1641
  else {
1197
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1642
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1198
1643
  }
1199
1644
  }
1200
1645
  else if (t1.type === "AsyncFunction") {
1201
1646
  if (t2.type === "Function") {
1202
1647
  if (t1.inputs.length !== t2.inputs.length) {
1203
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1648
+ throw new TypeMismatchError(`functions take different number of arguments (${t1.inputs.length} vs ${t2.inputs.length})`);
1204
1649
  }
1205
- return AsyncFunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output));
1650
+ return AsyncFunctionType(t1.inputs.map((t, i) => withPathSegment(`[input ${i}]`, () => TypeIntersect(t, t2.inputs[i]))), withPathSegment("[output]", () => TypeUnion(t1.output, t2.output)));
1206
1651
  }
1207
1652
  else if (t2.type === "AsyncFunction") {
1208
1653
  if (t1.inputs.length !== t2.inputs.length) {
1209
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1654
+ throw new TypeMismatchError(`functions take different number of arguments (${t1.inputs.length} vs ${t2.inputs.length})`);
1210
1655
  }
1211
- return AsyncFunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output));
1656
+ return AsyncFunctionType(t1.inputs.map((t, i) => withPathSegment(`[input ${i}]`, () => TypeIntersect(t, t2.inputs[i]))), withPathSegment("[output]", () => TypeUnion(t1.output, t2.output)));
1212
1657
  }
1213
1658
  else {
1214
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1659
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1215
1660
  }
1216
1661
  }
1217
1662
  else {
@@ -1219,7 +1664,7 @@ export function TypeUnion(t1, t2) {
1219
1664
  return t1;
1220
1665
  }
1221
1666
  else {
1222
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1667
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1223
1668
  }
1224
1669
  }
1225
1670
  }
@@ -1227,7 +1672,7 @@ export function TypeUnion(t1, t2) {
1227
1672
  if (cause instanceof TypeMismatchError) {
1228
1673
  throw cause; // Don't wrap our own errors - they already have the full path
1229
1674
  }
1230
- throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}`, { cause });
1675
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}`, { cause });
1231
1676
  }
1232
1677
  }
1233
1678
  /**
@@ -1245,6 +1690,8 @@ export function TypeUnion(t1, t2) {
1245
1690
  * For {@link VariantType}, returns {@link NeverType} if no common cases exist.
1246
1691
  */
1247
1692
  export function TypeIntersect(t1, t2) {
1693
+ if (t1 === t2)
1694
+ return t1;
1248
1695
  // TODO this is broken for recursive types (need to do cycle tracking in tandem with TypeEqual/TypeUnion)
1249
1696
  try {
1250
1697
  if (t1.type === "Never") {
@@ -1263,7 +1710,7 @@ export function TypeIntersect(t1, t2) {
1263
1710
  if (isSubtype(t2, t1.node)) {
1264
1711
  return t2;
1265
1712
  }
1266
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1713
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1267
1714
  }
1268
1715
  }
1269
1716
  else if (t2.type === "Recursive") {
@@ -1271,38 +1718,54 @@ export function TypeIntersect(t1, t2) {
1271
1718
  if (isSubtype(t1, t2.node)) {
1272
1719
  return t1;
1273
1720
  }
1274
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1721
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1275
1722
  }
1276
1723
  else if (t1.type === "Ref") {
1277
1724
  if (t2.type === "Ref") {
1278
- return RefType(TypeEqual(t1.value, t2.value));
1725
+ return RefType(withPathSegment("[ref]", () => TypeEqual(t1.value, t2.value)));
1279
1726
  }
1280
1727
  else {
1281
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1728
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1282
1729
  }
1283
1730
  }
1284
1731
  else if (t1.type === "Array") {
1285
1732
  if (t2.type === "Array") {
1286
- return ArrayType(TypeEqual(t1.value, t2.value));
1733
+ return ArrayType(withPathSegment("[element]", () => TypeEqual(t1.value, t2.value)));
1734
+ }
1735
+ else {
1736
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1737
+ }
1738
+ }
1739
+ else if (t1.type === "Vector") {
1740
+ if (t2.type === "Vector") {
1741
+ return VectorType(withPathSegment("[element]", () => TypeEqual(t1.element, t2.element)));
1742
+ }
1743
+ else {
1744
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1745
+ }
1746
+ }
1747
+ else if (t1.type === "Matrix") {
1748
+ if (t2.type === "Matrix") {
1749
+ return MatrixType(withPathSegment("[element]", () => TypeEqual(t1.element, t2.element)));
1287
1750
  }
1288
1751
  else {
1289
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1752
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1290
1753
  }
1291
1754
  }
1292
1755
  else if (t1.type === "Set") {
1293
1756
  if (t2.type === "Set") {
1294
- return SetType(TypeEqual(t1.key, t2.key));
1757
+ return SetType(withPathSegment("[key]", () => TypeEqual(t1.key, t2.key)));
1295
1758
  }
1296
1759
  else {
1297
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1760
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1298
1761
  }
1299
1762
  }
1300
1763
  else if (t1.type === "Dict") {
1301
1764
  if (t2.type === "Dict") {
1302
- return DictType(TypeEqual(t1.key, t2.key), TypeEqual(t1.value, t2.value));
1765
+ return DictType(withPathSegment("[key]", () => TypeEqual(t1.key, t2.key)), withPathSegment("[value]", () => TypeEqual(t1.value, t2.value)));
1303
1766
  }
1304
1767
  else {
1305
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1768
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1306
1769
  }
1307
1770
  }
1308
1771
  else if (t1.type === "Struct") {
@@ -1310,20 +1773,20 @@ export function TypeIntersect(t1, t2) {
1310
1773
  const e1 = Object.entries(t1.fields);
1311
1774
  const e2 = Object.entries(t2.fields);
1312
1775
  if (e1.length !== e2.length)
1313
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: structs contain different number of fields`);
1776
+ throw new TypeMismatchError(`structs contain different number of fields (${e1.length} vs ${e2.length})`);
1314
1777
  let i = 0;
1315
1778
  const e = {};
1316
1779
  for (const [k1, f1] of e1) {
1317
1780
  const [k2, f2] = e2[i];
1318
1781
  if (k1 !== k2)
1319
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: struct field ${i} has mismatched names ${printIdentifier(k1)} and ${printIdentifier(k2)}`);
1320
- e[k1] = TypeIntersect(f1, f2);
1782
+ throw new TypeMismatchError(`struct field ${i} has mismatched names ${printIdentifier(k1)} and ${printIdentifier(k2)}`);
1783
+ e[k1] = withPathSegment(`.${k1}`, () => TypeIntersect(f1, f2));
1321
1784
  i += 1;
1322
1785
  }
1323
1786
  return StructType(e);
1324
1787
  }
1325
1788
  else {
1326
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1789
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1327
1790
  }
1328
1791
  }
1329
1792
  else if (t1.type === "Variant") {
@@ -1333,51 +1796,51 @@ export function TypeIntersect(t1, t2) {
1333
1796
  for (const [k1, f1] of Object.entries(t1.cases)) {
1334
1797
  const f2 = t2.cases[k1];
1335
1798
  if (f2 !== undefined) {
1336
- cases[k1] = TypeIntersect(f1, f2);
1799
+ cases[k1] = withPathSegment(`.${k1}`, () => TypeIntersect(f1, f2));
1337
1800
  empty = false;
1338
1801
  }
1339
1802
  }
1340
1803
  if (empty) {
1341
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: variants have no overlapping cases`);
1804
+ throw new TypeMismatchError(`variants have no overlapping cases`);
1342
1805
  }
1343
1806
  return VariantType(cases);
1344
1807
  }
1345
1808
  else {
1346
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1809
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1347
1810
  }
1348
1811
  }
1349
1812
  else if (t1.type === "Function") {
1350
1813
  if (t2.type === "Function") {
1351
1814
  if (t1.inputs.length !== t2.inputs.length) {
1352
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1815
+ throw new TypeMismatchError(`functions take different number of arguments (${t1.inputs.length} vs ${t2.inputs.length})`);
1353
1816
  }
1354
- return FunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output));
1817
+ return FunctionType(t1.inputs.map((t, i) => withPathSegment(`[input ${i}]`, () => TypeUnion(t, t2.inputs[i]))), withPathSegment("[output]", () => TypeIntersect(t1.output, t2.output)));
1355
1818
  }
1356
1819
  else if (t2.type === "AsyncFunction") {
1357
1820
  if (t1.inputs.length !== t2.inputs.length) {
1358
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1821
+ throw new TypeMismatchError(`functions take different number of arguments (${t1.inputs.length} vs ${t2.inputs.length})`);
1359
1822
  }
1360
- return FunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output));
1823
+ return FunctionType(t1.inputs.map((t, i) => withPathSegment(`[input ${i}]`, () => TypeUnion(t, t2.inputs[i]))), withPathSegment("[output]", () => TypeIntersect(t1.output, t2.output)));
1361
1824
  }
1362
1825
  else {
1363
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1826
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1364
1827
  }
1365
1828
  }
1366
1829
  else if (t1.type === "AsyncFunction") {
1367
1830
  if (t2.type === "Function") {
1368
1831
  if (t1.inputs.length !== t2.inputs.length) {
1369
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1832
+ throw new TypeMismatchError(`functions take different number of arguments (${t1.inputs.length} vs ${t2.inputs.length})`);
1370
1833
  }
1371
- return FunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output));
1834
+ return FunctionType(t1.inputs.map((t, i) => withPathSegment(`[input ${i}]`, () => TypeUnion(t, t2.inputs[i]))), withPathSegment("[output]", () => TypeIntersect(t1.output, t2.output)));
1372
1835
  }
1373
1836
  else if (t2.type === "AsyncFunction") {
1374
1837
  if (t1.inputs.length !== t2.inputs.length) {
1375
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1838
+ throw new TypeMismatchError(`functions take different number of arguments (${t1.inputs.length} vs ${t2.inputs.length})`);
1376
1839
  }
1377
- return AsyncFunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output));
1840
+ return AsyncFunctionType(t1.inputs.map((t, i) => withPathSegment(`[input ${i}]`, () => TypeUnion(t, t2.inputs[i]))), withPathSegment("[output]", () => TypeIntersect(t1.output, t2.output)));
1378
1841
  }
1379
1842
  else {
1380
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1843
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1381
1844
  }
1382
1845
  }
1383
1846
  else {
@@ -1385,7 +1848,7 @@ export function TypeIntersect(t1, t2) {
1385
1848
  return t1;
1386
1849
  }
1387
1850
  else {
1388
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1851
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1389
1852
  }
1390
1853
  }
1391
1854
  }
@@ -1393,7 +1856,7 @@ export function TypeIntersect(t1, t2) {
1393
1856
  if (cause instanceof TypeMismatchError) {
1394
1857
  throw cause; // Don't wrap our own errors - they already have the full path
1395
1858
  }
1396
- throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}`, { cause });
1859
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}`, { cause });
1397
1860
  }
1398
1861
  }
1399
1862
  /**
@@ -1414,38 +1877,56 @@ export function TypeIntersect(t1, t2) {
1414
1877
  * Unlike {@link isTypeEqual}, this throws rather than returning a boolean.
1415
1878
  */
1416
1879
  export function TypeEqual(t1, t2, r1 = t1, r2 = t2) {
1880
+ if (t1 === t2)
1881
+ return t1;
1417
1882
  // TODO this is broken for recursive types (need to do cycle tracking in tandem with TypeUnion/TypeIntersect)
1418
1883
  try {
1419
1884
  if (t1.type === "Ref") {
1420
1885
  if (t2.type === "Ref") {
1421
- return RefType(TypeEqual(t1.value, t2.value, r1, r2));
1886
+ return RefType(withPathSegment("[ref]", () => TypeEqual(t1.value, t2.value, r1, r2)));
1422
1887
  }
1423
1888
  else {
1424
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1889
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1425
1890
  }
1426
1891
  }
1427
1892
  else if (t1.type === "Array") {
1428
1893
  if (t2.type === "Array") {
1429
- return ArrayType(TypeEqual(t1.value, t2.value, r1, r2));
1894
+ return ArrayType(withPathSegment("[element]", () => TypeEqual(t1.value, t2.value, r1, r2)));
1895
+ }
1896
+ else {
1897
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1898
+ }
1899
+ }
1900
+ else if (t1.type === "Vector") {
1901
+ if (t2.type === "Vector") {
1902
+ return VectorType(withPathSegment("[element]", () => TypeEqual(t1.element, t2.element, r1, r2)));
1903
+ }
1904
+ else {
1905
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1906
+ }
1907
+ }
1908
+ else if (t1.type === "Matrix") {
1909
+ if (t2.type === "Matrix") {
1910
+ return MatrixType(withPathSegment("[element]", () => TypeEqual(t1.element, t2.element, r1, r2)));
1430
1911
  }
1431
1912
  else {
1432
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1913
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1433
1914
  }
1434
1915
  }
1435
1916
  else if (t1.type === "Set") {
1436
1917
  if (t2.type === "Set") {
1437
- return SetType(TypeEqual(t1.key, t2.key, r1, r2));
1918
+ return SetType(withPathSegment("[key]", () => TypeEqual(t1.key, t2.key, r1, r2)));
1438
1919
  }
1439
1920
  else {
1440
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1921
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1441
1922
  }
1442
1923
  }
1443
1924
  else if (t1.type === "Dict") {
1444
1925
  if (t2.type === "Dict") {
1445
- return DictType(TypeEqual(t1.key, t2.key, r1, r2), TypeEqual(t1.value, t2.value, r1, r2));
1926
+ return DictType(withPathSegment("[key]", () => TypeEqual(t1.key, t2.key, r1, r2)), withPathSegment("[value]", () => TypeEqual(t1.value, t2.value, r1, r2)));
1446
1927
  }
1447
1928
  else {
1448
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1929
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1449
1930
  }
1450
1931
  }
1451
1932
  else if (t1.type === "Struct") {
@@ -1453,20 +1934,20 @@ export function TypeEqual(t1, t2, r1 = t1, r2 = t2) {
1453
1934
  const e1 = Object.entries(t1.fields);
1454
1935
  const e2 = Object.entries(t2.fields);
1455
1936
  if (e1.length !== e2.length)
1456
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: structs contain different number of fields`);
1937
+ throw new TypeMismatchError(`structs contain different number of fields (${e1.length} vs ${e2.length})`);
1457
1938
  let i = 0;
1458
1939
  const e = {};
1459
1940
  for (const [k1, f1] of e1) {
1460
1941
  const [k2, f2] = e2[i];
1461
1942
  if (k1 !== k2)
1462
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: struct field ${i} has mismatched names ${printIdentifier(k1)} and ${printIdentifier(k2)}`);
1463
- e[k1] = TypeEqual(f1, f2, r1, r2);
1943
+ throw new TypeMismatchError(`struct field ${i} has mismatched names ${printIdentifier(k1)} and ${printIdentifier(k2)}`);
1944
+ e[k1] = withPathSegment(`.${k1}`, () => TypeEqual(f1, f2, r1, r2));
1464
1945
  i += 1;
1465
1946
  }
1466
1947
  return StructType(e);
1467
1948
  }
1468
1949
  else {
1469
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1950
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1470
1951
  }
1471
1952
  }
1472
1953
  else if (t1.type === "Variant") {
@@ -1474,27 +1955,37 @@ export function TypeEqual(t1, t2, r1 = t1, r2 = t2) {
1474
1955
  if (t2.type === "Variant") {
1475
1956
  const e1 = Object.entries(t1.cases);
1476
1957
  const e2 = Object.entries(t2.cases);
1477
- if (e1.length !== e2.length)
1478
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: variants contain different number of cases`);
1958
+ if (e1.length !== e2.length) {
1959
+ let msg = `variants contain different number of cases (${e1.length} vs ${e2.length})`;
1960
+ const names1 = new Set(e1.map(([k]) => k));
1961
+ const names2 = new Set(e2.map(([k]) => k));
1962
+ const onlyIn1 = e1.map(([k]) => k).filter(k => !names2.has(k));
1963
+ const onlyIn2 = e2.map(([k]) => k).filter(k => !names1.has(k));
1964
+ if (onlyIn1.length > 0)
1965
+ msg += `; only in expected: ${onlyIn1.join(", ")}`;
1966
+ if (onlyIn2.length > 0)
1967
+ msg += `; only in actual: ${onlyIn2.join(", ")}`;
1968
+ throw new TypeMismatchError(msg);
1969
+ }
1479
1970
  let i = 0;
1480
1971
  const e = {};
1481
1972
  for (const [k1, f1] of e1) {
1482
1973
  const [k2, f2] = e2[i];
1483
1974
  if (k1 !== k2) {
1484
1975
  if (k1 < k2) {
1485
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: variant case ${k1} is not present in both variants`);
1976
+ throw new TypeMismatchError(`variant case ${k1} is not present in both variants`);
1486
1977
  }
1487
1978
  else {
1488
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: variant case ${k2} is not present in both variants`);
1979
+ throw new TypeMismatchError(`variant case ${k2} is not present in both variants`);
1489
1980
  }
1490
1981
  }
1491
- e[k1] = TypeEqual(f1, f2, r1, r2);
1982
+ e[k1] = withPathSegment(`.${k1}`, () => TypeEqual(f1, f2, r1, r2));
1492
1983
  i += 1;
1493
1984
  }
1494
1985
  return VariantType(e);
1495
1986
  }
1496
1987
  else {
1497
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1988
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1498
1989
  }
1499
1990
  }
1500
1991
  else if (t1.type === "Recursive") {
@@ -1504,39 +1995,39 @@ export function TypeEqual(t1, t2, r1 = t1, r2 = t2) {
1504
1995
  return t1; // both are references to the same recursive type
1505
1996
  }
1506
1997
  else {
1507
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: recursive types do not match`);
1998
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: recursive types do not match`);
1508
1999
  }
1509
2000
  }
1510
2001
  else if (t2.node === r2) {
1511
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: recursive types do not match`);
2002
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: recursive types do not match`);
1512
2003
  }
1513
2004
  // this is the root of a new recursive type - assert the node types are equal
1514
2005
  return RecursiveType(() => TypeEqual(t1.node, t2.node, t1.node, t2.node));
1515
2006
  }
1516
2007
  else {
1517
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
2008
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1518
2009
  }
1519
2010
  }
1520
2011
  else if (t1.type === "Function") {
1521
2012
  if (t2.type === "Function") {
1522
2013
  if (t1.inputs.length !== t2.inputs.length) {
1523
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions take different number of arguments`);
2014
+ throw new TypeMismatchError(`functions take different number of arguments (${t1.inputs.length} vs ${t2.inputs.length})`);
1524
2015
  }
1525
- return FunctionType(t1.inputs.map((t, i) => TypeEqual(t, t2.inputs[i], r1, r2)), TypeEqual(t1.output, t2.output, r1, r2));
2016
+ return FunctionType(t1.inputs.map((t, i) => withPathSegment(`[input ${i}]`, () => TypeEqual(t, t2.inputs[i], r1, r2))), withPathSegment("[output]", () => TypeEqual(t1.output, t2.output, r1, r2)));
1526
2017
  }
1527
2018
  else {
1528
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
2019
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1529
2020
  }
1530
2021
  }
1531
2022
  else if (t1.type === "AsyncFunction") {
1532
2023
  if (t2.type === "AsyncFunction") {
1533
2024
  if (t1.inputs.length !== t2.inputs.length) {
1534
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions take different number of arguments`);
2025
+ throw new TypeMismatchError(`functions take different number of arguments (${t1.inputs.length} vs ${t2.inputs.length})`);
1535
2026
  }
1536
- return AsyncFunctionType(t1.inputs.map((t, i) => TypeEqual(t, t2.inputs[i], r1, r2)), TypeEqual(t1.output, t2.output, r1, r2));
2027
+ return AsyncFunctionType(t1.inputs.map((t, i) => withPathSegment(`[input ${i}]`, () => TypeEqual(t, t2.inputs[i], r1, r2))), withPathSegment("[output]", () => TypeEqual(t1.output, t2.output, r1, r2)));
1537
2028
  }
1538
2029
  else {
1539
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
2030
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1540
2031
  }
1541
2032
  }
1542
2033
  else {
@@ -1544,7 +2035,7 @@ export function TypeEqual(t1, t2, r1 = t1, r2 = t2) {
1544
2035
  return t1;
1545
2036
  }
1546
2037
  else {
1547
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
2038
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1548
2039
  }
1549
2040
  }
1550
2041
  }
@@ -1552,7 +2043,7 @@ export function TypeEqual(t1, t2, r1 = t1, r2 = t2) {
1552
2043
  if (cause instanceof TypeMismatchError) {
1553
2044
  throw cause; // Don't wrap our own errors - they already have the full path
1554
2045
  }
1555
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}`, { cause });
2046
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}`, { cause });
1556
2047
  }
1557
2048
  }
1558
2049
  /**
@@ -1577,41 +2068,57 @@ export function TypeWiden(t1, t2) {
1577
2068
  return t1;
1578
2069
  }
1579
2070
  else if (t1.type === "Recursive") {
1580
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: recursive types not supported`);
2071
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: recursive types not supported`);
1581
2072
  }
1582
2073
  else if (t2.type === "Recursive") {
1583
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: recursive types not supported`);
2074
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: recursive types not supported`);
1584
2075
  }
1585
2076
  else if (t1.type === "Ref") {
1586
2077
  if (t2.type === "Ref") {
1587
- return RefType(TypeWiden(t1.value, t2.value));
2078
+ return RefType(withPathSegment("[ref]", () => TypeWiden(t1.value, t2.value)));
1588
2079
  }
1589
2080
  else {
1590
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
2081
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1591
2082
  }
1592
2083
  }
1593
2084
  else if (t1.type === "Array") {
1594
2085
  if (t2.type === "Array") {
1595
- return ArrayType(TypeWiden(t1.value, t2.value));
2086
+ return ArrayType(withPathSegment("[element]", () => TypeWiden(t1.value, t2.value)));
2087
+ }
2088
+ else {
2089
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
2090
+ }
2091
+ }
2092
+ else if (t1.type === "Vector") {
2093
+ if (t2.type === "Vector") {
2094
+ return VectorType(withPathSegment("[element]", () => TypeEqual(t1.element, t2.element)));
2095
+ }
2096
+ else {
2097
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
2098
+ }
2099
+ }
2100
+ else if (t1.type === "Matrix") {
2101
+ if (t2.type === "Matrix") {
2102
+ return MatrixType(withPathSegment("[element]", () => TypeEqual(t1.element, t2.element)));
1596
2103
  }
1597
2104
  else {
1598
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
2105
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1599
2106
  }
1600
2107
  }
1601
2108
  else if (t1.type === "Set") {
1602
2109
  if (t2.type === "Set") {
1603
- return SetType(TypeWiden(t1.key, t2.key));
2110
+ return SetType(withPathSegment("[key]", () => TypeWiden(t1.key, t2.key)));
1604
2111
  }
1605
2112
  else {
1606
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
2113
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1607
2114
  }
1608
2115
  }
1609
2116
  else if (t1.type === "Dict") {
1610
2117
  if (t2.type === "Dict") {
1611
- return DictType(TypeWiden(t1.key, t2.key), TypeWiden(t1.value, t2.value));
2118
+ return DictType(withPathSegment("[key]", () => TypeWiden(t1.key, t2.key)), withPathSegment("[value]", () => TypeWiden(t1.value, t2.value)));
1612
2119
  }
1613
2120
  else {
1614
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
2121
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1615
2122
  }
1616
2123
  }
1617
2124
  else if (t1.type === "Struct") {
@@ -1619,20 +2126,20 @@ export function TypeWiden(t1, t2) {
1619
2126
  const e1 = Object.entries(t1.fields);
1620
2127
  const e2 = Object.entries(t2.fields);
1621
2128
  if (e1.length !== e2.length)
1622
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: structs contain different number of fields`);
2129
+ throw new TypeMismatchError(`structs contain different number of fields (${e1.length} vs ${e2.length})`);
1623
2130
  let i = 0;
1624
2131
  const e = {};
1625
2132
  for (const [k1, f1] of e1) {
1626
2133
  const [k2, f2] = e2[i];
1627
2134
  if (k1 !== k2)
1628
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: struct field ${i} has mismatched names ${printIdentifier(k1)} and ${printIdentifier(k2)}`);
1629
- e[k1] = TypeWiden(f1, f2);
2135
+ throw new TypeMismatchError(`struct field ${i} has mismatched names ${printIdentifier(k1)} and ${printIdentifier(k2)}`);
2136
+ e[k1] = withPathSegment(`.${k1}`, () => TypeWiden(f1, f2));
1630
2137
  i += 1;
1631
2138
  }
1632
2139
  return StructType(e);
1633
2140
  }
1634
2141
  else {
1635
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
2142
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1636
2143
  }
1637
2144
  }
1638
2145
  else if (t1.type === "Variant") {
@@ -1644,7 +2151,7 @@ export function TypeWiden(t1, t2) {
1644
2151
  cases[k1] = f1;
1645
2152
  }
1646
2153
  else {
1647
- cases[k1] = TypeWiden(f1, f2);
2154
+ cases[k1] = withPathSegment(`.${k1}`, () => TypeWiden(f1, f2));
1648
2155
  }
1649
2156
  }
1650
2157
  for (const [k2, f2] of Object.entries(t2.cases)) {
@@ -1656,21 +2163,21 @@ export function TypeWiden(t1, t2) {
1656
2163
  return VariantType(cases);
1657
2164
  }
1658
2165
  else {
1659
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
2166
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1660
2167
  }
1661
2168
  }
1662
2169
  else if (t1.type === "Function") {
1663
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: functions not supported`);
2170
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: functions not supported`);
1664
2171
  }
1665
2172
  else if (t1.type === "AsyncFunction") {
1666
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: functions not supported`);
2173
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: functions not supported`);
1667
2174
  }
1668
2175
  else {
1669
2176
  if (t1.type === t2.type) {
1670
2177
  return t1;
1671
2178
  }
1672
2179
  else {
1673
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: incompatible types`);
2180
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}: incompatible types`);
1674
2181
  }
1675
2182
  }
1676
2183
  }
@@ -1678,7 +2185,7 @@ export function TypeWiden(t1, t2) {
1678
2185
  if (cause instanceof TypeMismatchError) {
1679
2186
  throw cause; // Don't wrap our own errors - they already have the full path
1680
2187
  }
1681
- throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}`, { cause });
2188
+ throw new TypeMismatchError(`expected ${printTypeSummary(t1)} but got ${printTypeSummary(t2)}`, { cause });
1682
2189
  }
1683
2190
  }
1684
2191
  /**