@elaraai/east 0.0.1-beta.8 → 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
@@ -0,0 +1,621 @@
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 assert from "node:assert/strict";
6
+ import { describe, test } from "node:test";
7
+ import { NullType, BooleanType, IntegerType, FloatType, StringType, DateTimeType, BlobType, ArrayType, SetType, DictType, StructType, VariantType, RefType, RecursiveType, VectorType, OptionType, FunctionType, } from "../types.js";
8
+ import { variant, some, none } from "../containers/variant.js";
9
+ import { SortedSet } from "../containers/sortedset.js";
10
+ import { SortedMap } from "../containers/sortedmap.js";
11
+ import { ref } from "../containers/ref.js";
12
+ import { compareFor } from "../comparison.js";
13
+ import { diffFor } from "./diff.js";
14
+ import { walkPatch } from "./walk.js";
15
+ import { pathToString } from "./path.js";
16
+ function capture(type, before, after) {
17
+ const leaves = [];
18
+ const enters = [];
19
+ const exits = [];
20
+ walkPatch(type, diffFor(type)(before, after), {
21
+ enter: ({ type: t, path, leafCount }) => {
22
+ enters.push({ path: pathToString(path), typeKind: t.type, leafCount });
23
+ },
24
+ leaf: ({ type: t, path, op, before: b, after: a }) => {
25
+ leaves.push({ path: pathToString(path), op, before: b, after: a, typeKind: t.type });
26
+ },
27
+ exit: ({ path }) => { exits.push(pathToString(path)); },
28
+ });
29
+ return { leaves, enters, exits };
30
+ }
31
+ // ============================================================================
32
+ // Primitives — replace-only types: every changed value emits one update leaf.
33
+ // ============================================================================
34
+ describe("walkPatch: primitives", () => {
35
+ test("Null: identical → no leaves, no enters, no exits", () => {
36
+ const c = capture(NullType, null, null);
37
+ assert.equal(c.leaves.length, 0);
38
+ assert.equal(c.enters.length, 0);
39
+ assert.equal(c.exits.length, 0);
40
+ });
41
+ test("Boolean: changed → one update leaf carrying both values", () => {
42
+ const c = capture(BooleanType, true, false);
43
+ assert.equal(c.leaves.length, 1);
44
+ assert.equal(c.leaves[0].op, "update");
45
+ assert.equal(c.leaves[0].before, true);
46
+ assert.equal(c.leaves[0].after, false);
47
+ assert.equal(c.leaves[0].typeKind, "Boolean");
48
+ assert.equal(c.leaves[0].path, "");
49
+ });
50
+ test("Integer: changed → one update leaf carrying both bigints", () => {
51
+ const c = capture(IntegerType, 38n, 40n);
52
+ assert.equal(c.leaves.length, 1);
53
+ assert.equal(c.leaves[0].before, 38n);
54
+ assert.equal(c.leaves[0].after, 40n);
55
+ assert.equal(c.leaves[0].typeKind, "Integer");
56
+ });
57
+ test("Integer: large bigints round-trip without precision loss", () => {
58
+ const c = capture(IntegerType, 9007199254740993n, 9007199254740994n);
59
+ assert.equal(c.leaves[0].before, 9007199254740993n);
60
+ assert.equal(c.leaves[0].after, 9007199254740994n);
61
+ });
62
+ test("Float: NaN/Infinity edge cases produce a leaf when actually different", () => {
63
+ const c = capture(FloatType, 1.5, Infinity);
64
+ assert.equal(c.leaves.length, 1);
65
+ assert.equal(c.leaves[0].before, 1.5);
66
+ assert.equal(c.leaves[0].after, Infinity);
67
+ });
68
+ test("String: changed → one update leaf carrying both strings", () => {
69
+ const c = capture(StringType, "hello", "world");
70
+ assert.equal(c.leaves.length, 1);
71
+ assert.equal(c.leaves[0].before, "hello");
72
+ assert.equal(c.leaves[0].after, "world");
73
+ assert.equal(c.leaves[0].typeKind, "String");
74
+ });
75
+ test("String: empty → non-empty produces a leaf", () => {
76
+ const c = capture(StringType, "", "x");
77
+ assert.equal(c.leaves.length, 1);
78
+ });
79
+ test("DateTime: changed → one update leaf", () => {
80
+ const c = capture(DateTimeType, new Date("2025-01-01T00:00:00Z"), new Date("2026-01-01T00:00:00Z"));
81
+ assert.equal(c.leaves.length, 1);
82
+ assert.equal(c.leaves[0].typeKind, "DateTime");
83
+ });
84
+ test("Blob: changed → one update leaf", () => {
85
+ const c = capture(BlobType, new Uint8Array([1, 2]), new Uint8Array([3, 4]));
86
+ assert.equal(c.leaves.length, 1);
87
+ assert.equal(c.leaves[0].typeKind, "Blob");
88
+ });
89
+ test("Vector: changed → one update leaf (replace-only)", () => {
90
+ const T = VectorType(FloatType);
91
+ const c = capture(T, new Float64Array([1.0, 2.0, 3.0]), new Float64Array([4.0, 5.0, 6.0]));
92
+ assert.equal(c.leaves.length, 1);
93
+ assert.equal(c.leaves[0].typeKind, "Vector");
94
+ });
95
+ for (const [name, T, x] of [
96
+ ["Null", NullType, null],
97
+ ["Boolean", BooleanType, true],
98
+ ["Integer", IntegerType, 1n],
99
+ ["Float", FloatType, 1.0],
100
+ ["String", StringType, "x"],
101
+ ["DateTime", DateTimeType, new Date(0)],
102
+ ["Blob", BlobType, new Uint8Array([1])],
103
+ ]) {
104
+ test(`${name}: identical values → no leaves`, () => {
105
+ assert.equal(capture(T, x, x).leaves.length, 0);
106
+ });
107
+ }
108
+ test("Vector: identical → no leaves", () => {
109
+ const T = VectorType(FloatType);
110
+ assert.equal(capture(T, new Float64Array([1.0, 2.0]), new Float64Array([1.0, 2.0])).leaves.length, 0);
111
+ });
112
+ });
113
+ // ============================================================================
114
+ // Function types — replace-only by design (functions aren't structurally
115
+ // diffable, only opaquely replaceable).
116
+ // ============================================================================
117
+ describe("walkPatch: Function types", () => {
118
+ test("Function: diffFor always emits unchanged → walker sees no leaves", () => {
119
+ // East functions are opaque — `diffFor` returns unchanged regardless,
120
+ // so the walker has nothing to emit.
121
+ const T = FunctionType([IntegerType], IntegerType);
122
+ const f1 = (x) => x + 1n;
123
+ const f2 = (x) => x + 2n;
124
+ const c = capture(T, f1, f2);
125
+ assert.equal(c.leaves.length, 0);
126
+ });
127
+ });
128
+ // ============================================================================
129
+ // Struct
130
+ // ============================================================================
131
+ describe("walkPatch: Struct", () => {
132
+ const PolicyType = StructType({ maxHours: IntegerType, penalty: FloatType });
133
+ test("multiple fields changed → one leaf per changed field", () => {
134
+ const c = capture(PolicyType, { maxHours: 38n, penalty: 1.5 }, { maxHours: 40n, penalty: 2.0 });
135
+ assert.equal(c.leaves.length, 2);
136
+ const byPath = new Map(c.leaves.map(l => [l.path, l]));
137
+ assert.equal(byPath.get("maxHours").before, 38n);
138
+ assert.equal(byPath.get("maxHours").after, 40n);
139
+ assert.equal(byPath.get("penalty").before, 1.5);
140
+ assert.equal(byPath.get("penalty").after, 2.0);
141
+ });
142
+ test("one field changed, sibling unchanged → only the changed field appears", () => {
143
+ const c = capture(PolicyType, { maxHours: 38n, penalty: 1.5 }, { maxHours: 40n, penalty: 1.5 });
144
+ assert.equal(c.leaves.length, 1);
145
+ assert.equal(c.leaves[0].path, "maxHours");
146
+ });
147
+ test("identical struct → no leaves, no enters, no exits", () => {
148
+ const c = capture(PolicyType, { maxHours: 38n, penalty: 1.5 }, { maxHours: 38n, penalty: 1.5 });
149
+ assert.equal(c.leaves.length, 0);
150
+ assert.equal(c.enters.length, 0);
151
+ assert.equal(c.exits.length, 0);
152
+ });
153
+ test("enter/exit fire once with correct typeKind and leafCount", () => {
154
+ const c = capture(PolicyType, { maxHours: 38n, penalty: 1.5 }, { maxHours: 40n, penalty: 2.0 });
155
+ assert.equal(c.enters.length, 1);
156
+ assert.equal(c.enters[0].typeKind, "Struct");
157
+ assert.equal(c.enters[0].leafCount, 2);
158
+ assert.equal(c.exits.length, 1);
159
+ });
160
+ test("nested struct: leaves at qualified dot-paths", () => {
161
+ const Inner = StructType({ x: IntegerType, y: IntegerType });
162
+ const Outer = StructType({ name: StringType, inner: Inner });
163
+ const c = capture(Outer, { name: "a", inner: { x: 1n, y: 1n } }, { name: "b", inner: { x: 2n, y: 2n } });
164
+ const paths = c.leaves.map(l => l.path).sort();
165
+ assert.deepEqual(paths, ["inner.x", "inner.y", "name"]);
166
+ });
167
+ test("3-deep nesting: paths reflect the full chain", () => {
168
+ const L3 = StructType({ z: IntegerType });
169
+ const L2 = StructType({ inner: L3 });
170
+ const L1 = StructType({ outer: L2 });
171
+ const c = capture(L1, { outer: { inner: { z: 1n } } }, { outer: { inner: { z: 2n } } });
172
+ assert.equal(c.leaves.length, 1);
173
+ assert.equal(c.leaves[0].path, "outer.inner.z");
174
+ });
175
+ test("struct with all primitive types: every kind contributes one leaf", () => {
176
+ const Big = StructType({
177
+ s: StringType, i: IntegerType, f: FloatType, b: BooleanType,
178
+ d: DateTimeType, n: NullType,
179
+ });
180
+ const c = capture(Big, { s: "a", i: 1n, f: 1.0, b: false, d: new Date(0), n: null }, { s: "b", i: 2n, f: 2.0, b: true, d: new Date(1000), n: null });
181
+ const paths = c.leaves.map(l => l.path).sort();
182
+ assert.deepEqual(paths, ["b", "d", "f", "i", "s"]);
183
+ });
184
+ });
185
+ // ============================================================================
186
+ // Array
187
+ // ============================================================================
188
+ describe("walkPatch: Array", () => {
189
+ test("identical → no leaves", () => {
190
+ const c = capture(ArrayType(IntegerType), [1n, 2n, 3n], [1n, 2n, 3n]);
191
+ assert.equal(c.leaves.length, 0);
192
+ });
193
+ test("primitive element changed → leaf at the changed index", () => {
194
+ const c = capture(ArrayType(IntegerType), [10n, 20n, 30n], [10n, 25n, 30n]);
195
+ assert.ok(c.leaves.some(l => l.path === "[1]"));
196
+ });
197
+ test("element insert at end → one insert leaf carrying the inserted value", () => {
198
+ const c = capture(ArrayType(IntegerType), [1n, 2n], [1n, 2n, 3n]);
199
+ const inserts = c.leaves.filter(l => l.op === "insert");
200
+ assert.equal(inserts.length, 1);
201
+ assert.equal(inserts[0].after, 3n);
202
+ });
203
+ test("element insert at start → one insert leaf at index 0", () => {
204
+ const c = capture(ArrayType(IntegerType), [2n, 3n], [1n, 2n, 3n]);
205
+ const inserts = c.leaves.filter(l => l.op === "insert");
206
+ assert.equal(inserts.length, 1);
207
+ assert.equal(inserts[0].after, 1n);
208
+ });
209
+ test("element delete from middle → one delete leaf", () => {
210
+ const c = capture(ArrayType(IntegerType), [1n, 2n, 3n], [1n, 3n]);
211
+ const deletes = c.leaves.filter(l => l.op === "delete");
212
+ assert.equal(deletes.length, 1);
213
+ assert.equal(deletes[0].before, 2n);
214
+ });
215
+ test("delete from end → one delete leaf", () => {
216
+ const c = capture(ArrayType(IntegerType), [1n, 2n, 3n], [1n, 2n]);
217
+ const deletes = c.leaves.filter(l => l.op === "delete");
218
+ assert.equal(deletes.length, 1);
219
+ assert.equal(deletes[0].before, 3n);
220
+ });
221
+ test("clear all elements → delete leaves for every element", () => {
222
+ const c = capture(ArrayType(IntegerType), [1n, 2n, 3n], []);
223
+ const deletes = c.leaves.filter(l => l.op === "delete");
224
+ assert.equal(deletes.length, 3);
225
+ assert.deepEqual(deletes.map(l => l.before).sort(), [1n, 2n, 3n]);
226
+ });
227
+ test("populate empty array → insert leaves for every element", () => {
228
+ const c = capture(ArrayType(IntegerType), [], [1n, 2n, 3n]);
229
+ const inserts = c.leaves.filter(l => l.op === "insert");
230
+ assert.equal(inserts.length, 3);
231
+ });
232
+ test("array of structs: per-row field changes appear as nested paths", () => {
233
+ const Row = StructType({ id: StringType, n: IntegerType });
234
+ const c = capture(ArrayType(Row), [{ id: "a", n: 1n }, { id: "b", n: 2n }], [{ id: "a", n: 1n }, { id: "b", n: 5n }]);
235
+ assert.ok(c.leaves.some(l => l.path === "[1].n" && l.before === 2n && l.after === 5n));
236
+ });
237
+ test("two struct rows changed simultaneously → both indexed paths surface", () => {
238
+ const Row = StructType({ rate: FloatType });
239
+ const c = capture(ArrayType(Row), [{ rate: 1.0 }, { rate: 2.0 }, { rate: 3.0 }], [{ rate: 1.5 }, { rate: 2.5 }, { rate: 3.0 }]);
240
+ assert.ok(c.leaves.some(l => l.path === "[0].rate" && l.before === 1.0 && l.after === 1.5));
241
+ assert.ok(c.leaves.some(l => l.path === "[1].rate" && l.before === 2.0 && l.after === 2.5));
242
+ });
243
+ test("array of arrays: nested index paths", () => {
244
+ const T = ArrayType(ArrayType(IntegerType));
245
+ const c = capture(T, [[1n, 2n], [3n, 4n]], [[1n, 9n], [3n, 4n]]);
246
+ assert.ok(c.leaves.some(l => l.path === "[0][1]"));
247
+ });
248
+ test("array of dicts: index then key in path", () => {
249
+ const T = ArrayType(DictType(StringType, IntegerType));
250
+ const before = [new SortedMap([["a", 1n]], compareFor(StringType))];
251
+ const after = [new SortedMap([["a", 2n]], compareFor(StringType))];
252
+ const c = capture(T, before, after);
253
+ assert.ok(c.leaves.some(l => l.path === '[0]{"a"}'));
254
+ });
255
+ test("array of variants: index then @tag in path", () => {
256
+ const V = VariantType({ on: StructType({ since: IntegerType }), off: NullType });
257
+ const T = ArrayType(V);
258
+ const c = capture(T, [variant("on", { since: 100n })], [variant("on", { since: 200n })]);
259
+ assert.ok(c.leaves.some(l => l.path === "[0]@on.since"));
260
+ });
261
+ test("mixed insert + delete + paired-update in same chunk", () => {
262
+ // [1, A, B, 9] → [1, A', C, 9]
263
+ // chunk has: keep 1, update A→A', delete B, insert C, keep 9
264
+ const c = capture(ArrayType(IntegerType), [1n, 100n, 200n, 9n], [1n, 101n, 300n, 9n]);
265
+ const ops = c.leaves.map(l => l.op).sort();
266
+ // Expected: at least one update or replace event, plus delete+insert for unmatched
267
+ assert.ok(c.leaves.length >= 2);
268
+ assert.ok(ops.length > 0);
269
+ });
270
+ });
271
+ // ============================================================================
272
+ // Dict
273
+ // ============================================================================
274
+ describe("walkPatch: Dict", () => {
275
+ const PriceMap = DictType(StringType, FloatType);
276
+ test("update: leaf at the canonical key path", () => {
277
+ const c = capture(PriceMap, new SortedMap([["AU", 49.95], ["US", 39.95]], compareFor(StringType)), new SortedMap([["AU", 55.00], ["US", 39.95]], compareFor(StringType)));
278
+ assert.equal(c.leaves.length, 1);
279
+ assert.equal(c.leaves[0].path, '{"AU"}');
280
+ assert.equal(c.leaves[0].op, "update");
281
+ assert.equal(c.leaves[0].before, 49.95);
282
+ assert.equal(c.leaves[0].after, 55.00);
283
+ });
284
+ test("insert: leaf with op=insert at the new key", () => {
285
+ const c = capture(PriceMap, new SortedMap([["US", 39.95]], compareFor(StringType)), new SortedMap([["AU", 50.00], ["US", 39.95]], compareFor(StringType)));
286
+ assert.equal(c.leaves.length, 1);
287
+ assert.equal(c.leaves[0].op, "insert");
288
+ assert.equal(c.leaves[0].path, '{"AU"}');
289
+ assert.equal(c.leaves[0].after, 50.00);
290
+ });
291
+ test("delete: leaf with op=delete at the removed key", () => {
292
+ const c = capture(PriceMap, new SortedMap([["AU", 50.00], ["US", 39.95]], compareFor(StringType)), new SortedMap([["US", 39.95]], compareFor(StringType)));
293
+ assert.equal(c.leaves.length, 1);
294
+ assert.equal(c.leaves[0].op, "delete");
295
+ assert.equal(c.leaves[0].path, '{"AU"}');
296
+ assert.equal(c.leaves[0].before, 50.00);
297
+ });
298
+ test("dict-of-struct: nested field paths reach the leaves", () => {
299
+ const Item = StructType({ price: FloatType, qty: IntegerType });
300
+ const T = DictType(StringType, Item);
301
+ const c = capture(T, new SortedMap([
302
+ ["A", { price: 1.0, qty: 1n }],
303
+ ["B", { price: 2.0, qty: 2n }],
304
+ ], compareFor(StringType)), new SortedMap([
305
+ ["A", { price: 1.5, qty: 1n }],
306
+ ["B", { price: 2.0, qty: 5n }],
307
+ ], compareFor(StringType)));
308
+ assert.ok(c.leaves.some(l => l.path === '{"A"}.price' && l.before === 1.0 && l.after === 1.5));
309
+ assert.ok(c.leaves.some(l => l.path === '{"B"}.qty' && l.before === 2n && l.after === 5n));
310
+ });
311
+ test("identical dict → no leaves", () => {
312
+ const c = capture(PriceMap, new SortedMap([["AU", 50.0]], compareFor(StringType)), new SortedMap([["AU", 50.0]], compareFor(StringType)));
313
+ assert.equal(c.leaves.length, 0);
314
+ });
315
+ test("collapsed-replace (every entry deleted) decomposes into per-key delete leaves", () => {
316
+ const c = capture(PriceMap, new SortedMap([["AU", 50.0], ["US", 40.0]], compareFor(StringType)), new SortedMap([], compareFor(StringType)));
317
+ assert.equal(c.leaves.length, 2);
318
+ assert.ok(c.leaves.every(l => l.op === "delete"));
319
+ const paths = c.leaves.map(l => l.path).sort();
320
+ assert.deepEqual(paths, ['{"AU"}', '{"US"}']);
321
+ });
322
+ test("collapsed-replace (filling empty dict) decomposes into per-key insert leaves", () => {
323
+ const c = capture(PriceMap, new SortedMap([], compareFor(StringType)), new SortedMap([["AU", 50.0], ["US", 40.0]], compareFor(StringType)));
324
+ assert.equal(c.leaves.length, 2);
325
+ assert.ok(c.leaves.every(l => l.op === "insert"));
326
+ });
327
+ test("Integer-keyed dict produces non-quoted numeric keys in the path", () => {
328
+ const T = DictType(IntegerType, StringType);
329
+ const c = capture(T, new SortedMap([[1n, "a"]], compareFor(IntegerType)), new SortedMap([[1n, "b"]], compareFor(IntegerType)));
330
+ assert.equal(c.leaves.length, 1);
331
+ assert.equal(c.leaves[0].path, "{1}");
332
+ });
333
+ test("dict of arrays: key then index in path", () => {
334
+ const T = DictType(StringType, ArrayType(IntegerType));
335
+ const c = capture(T, new SortedMap([["row", [1n, 2n]]], compareFor(StringType)), new SortedMap([["row", [1n, 9n]]], compareFor(StringType)));
336
+ assert.ok(c.leaves.some(l => l.path === '{"row"}[1]'));
337
+ });
338
+ });
339
+ // ============================================================================
340
+ // Set
341
+ // ============================================================================
342
+ describe("walkPatch: Set", () => {
343
+ const T = SetType(StringType);
344
+ test("insert: one leaf per new element with op=insert", () => {
345
+ const c = capture(T, new SortedSet(["a", "b"], compareFor(StringType)), new SortedSet(["a", "b", "c"], compareFor(StringType)));
346
+ assert.equal(c.leaves.length, 1);
347
+ assert.equal(c.leaves[0].op, "insert");
348
+ assert.equal(c.leaves[0].after, "c");
349
+ });
350
+ test("delete: one leaf per removed element with op=delete", () => {
351
+ const c = capture(T, new SortedSet(["a", "b", "c"], compareFor(StringType)), new SortedSet(["a", "c"], compareFor(StringType)));
352
+ assert.equal(c.leaves.length, 1);
353
+ assert.equal(c.leaves[0].op, "delete");
354
+ assert.equal(c.leaves[0].before, "b");
355
+ });
356
+ test("mixed insert+delete: separate leaves with distinct paths and ops", () => {
357
+ const c = capture(T, new SortedSet(["a", "b"], compareFor(StringType)), new SortedSet(["b", "c"], compareFor(StringType)));
358
+ const inserts = c.leaves.filter(l => l.op === "insert");
359
+ const deletes = c.leaves.filter(l => l.op === "delete");
360
+ assert.equal(inserts.length, 1);
361
+ assert.equal(deletes.length, 1);
362
+ assert.equal(inserts[0].after, "c");
363
+ assert.equal(deletes[0].before, "a");
364
+ });
365
+ test("identical set → no leaves", () => {
366
+ const c = capture(T, new SortedSet(["a", "b"], compareFor(StringType)), new SortedSet(["a", "b"], compareFor(StringType)));
367
+ assert.equal(c.leaves.length, 0);
368
+ });
369
+ test("clear all elements → delete leaves for each", () => {
370
+ const c = capture(T, new SortedSet(["a", "b", "c"], compareFor(StringType)), new SortedSet([], compareFor(StringType)));
371
+ assert.equal(c.leaves.length, 3);
372
+ assert.ok(c.leaves.every(l => l.op === "delete"));
373
+ });
374
+ test("populate empty set → insert leaves for each", () => {
375
+ const c = capture(T, new SortedSet([], compareFor(StringType)), new SortedSet(["a", "b", "c"], compareFor(StringType)));
376
+ assert.equal(c.leaves.length, 3);
377
+ assert.ok(c.leaves.every(l => l.op === "insert"));
378
+ });
379
+ test("Set of integers: numeric paths", () => {
380
+ const Int = SetType(IntegerType);
381
+ const c = capture(Int, new SortedSet([1n, 2n], compareFor(IntegerType)), new SortedSet([1n, 2n, 3n], compareFor(IntegerType)));
382
+ assert.equal(c.leaves.length, 1);
383
+ assert.equal(c.leaves[0].path, "{3}");
384
+ });
385
+ });
386
+ // ============================================================================
387
+ // Variant
388
+ // ============================================================================
389
+ describe("walkPatch: Variant", () => {
390
+ const StatusType = VariantType({
391
+ pending: NullType,
392
+ active: StructType({ since: IntegerType }),
393
+ failed: StructType({ reason: StringType }),
394
+ });
395
+ test("tag change → one update leaf carrying both variants", () => {
396
+ const c = capture(StatusType, variant("pending", null), variant("failed", { reason: "oops" }));
397
+ assert.equal(c.leaves.length, 1);
398
+ assert.equal(c.leaves[0].op, "update");
399
+ assert.equal(c.leaves[0].typeKind, "Variant");
400
+ });
401
+ test("same tag, different inner struct field → leaf at @tag.field path", () => {
402
+ const c = capture(StatusType, variant("active", { since: 100n }), variant("active", { since: 200n }));
403
+ assert.ok(c.leaves.some(l => l.path === "@active.since" && l.before === 100n && l.after === 200n));
404
+ });
405
+ test("identical variant → no leaves", () => {
406
+ const c = capture(StatusType, variant("pending", null), variant("pending", null));
407
+ assert.equal(c.leaves.length, 0);
408
+ });
409
+ test("variant with NullType payload → tag transition has no inner leaves", () => {
410
+ const Light = VariantType({ on: NullType, off: NullType });
411
+ const c = capture(Light, variant("on", null), variant("off", null));
412
+ assert.equal(c.leaves.length, 1);
413
+ assert.equal(c.leaves[0].op, "update");
414
+ });
415
+ test("OptionType: none → some(value) emits a tag-change leaf", () => {
416
+ const T = OptionType(IntegerType);
417
+ const c = capture(T, none, some(42n));
418
+ assert.equal(c.leaves.length, 1);
419
+ assert.equal(c.leaves[0].op, "update");
420
+ });
421
+ test("OptionType: some(a) → some(b) emits leaf at @some path for primitive payload", () => {
422
+ const T = OptionType(IntegerType);
423
+ const c = capture(T, some(1n), some(2n));
424
+ assert.ok(c.leaves.length >= 1);
425
+ });
426
+ test("OptionType: some → none emits a tag-change leaf", () => {
427
+ const T = OptionType(IntegerType);
428
+ const c = capture(T, some(5n), none);
429
+ assert.equal(c.leaves.length, 1);
430
+ assert.equal(c.leaves[0].op, "update");
431
+ });
432
+ test("nested variant: outer @tag.inner@innerTag chain", () => {
433
+ const Inner = VariantType({ x: IntegerType, y: StringType });
434
+ const Outer = VariantType({ wrap: StructType({ inner: Inner }) });
435
+ const c = capture(Outer, variant("wrap", { inner: variant("x", 1n) }), variant("wrap", { inner: variant("x", 2n) }));
436
+ assert.ok(c.leaves.some(l => l.path === "@wrap.inner@x"));
437
+ });
438
+ });
439
+ // ============================================================================
440
+ // Ref
441
+ // ============================================================================
442
+ describe("walkPatch: Ref", () => {
443
+ test("Ref of primitive → unwraps to the inner type for the leaf", () => {
444
+ const T = RefType(IntegerType);
445
+ const c = capture(T, ref(1n), ref(2n));
446
+ assert.equal(c.leaves.length, 1);
447
+ assert.equal(c.leaves[0].typeKind, "Integer");
448
+ });
449
+ test("Ref of struct → leaves at the inner struct's fields", () => {
450
+ const T = RefType(StructType({ x: IntegerType, y: IntegerType }));
451
+ const c = capture(T, ref({ x: 1n, y: 1n }), ref({ x: 2n, y: 2n }));
452
+ const paths = c.leaves.map(l => l.path).sort();
453
+ assert.deepEqual(paths, ["x", "y"]);
454
+ });
455
+ test("Ref of array → indexed leaves", () => {
456
+ const T = RefType(ArrayType(IntegerType));
457
+ const c = capture(T, ref([1n, 2n]), ref([1n, 9n]));
458
+ assert.ok(c.leaves.some(l => l.path === "[1]"));
459
+ });
460
+ test("Ref of dict → keyed leaves", () => {
461
+ const T = RefType(DictType(StringType, IntegerType));
462
+ const c = capture(T, ref(new SortedMap([["a", 1n]], compareFor(StringType))), ref(new SortedMap([["a", 2n]], compareFor(StringType))));
463
+ assert.ok(c.leaves.some(l => l.path === '{"a"}'));
464
+ });
465
+ test("Ref of variant → @tag leaves", () => {
466
+ const V = VariantType({ on: NullType, off: NullType });
467
+ const T = RefType(V);
468
+ const c = capture(T, ref(variant("on", null)), ref(variant("off", null)));
469
+ assert.equal(c.leaves.length, 1);
470
+ assert.equal(c.leaves[0].op, "update");
471
+ });
472
+ test("Ref of Ref → unwraps both layers", () => {
473
+ const T = RefType(RefType(IntegerType));
474
+ const c = capture(T, ref(ref(1n)), ref(ref(2n)));
475
+ assert.equal(c.leaves.length, 1);
476
+ assert.equal(c.leaves[0].typeKind, "Integer");
477
+ });
478
+ });
479
+ // ============================================================================
480
+ // Recursive — replace-only by design (whole-subtree replacement)
481
+ // ============================================================================
482
+ describe("walkPatch: Recursive", () => {
483
+ test("Recursive type: changed value emits a single replace leaf at the recursive boundary", () => {
484
+ const Tree = RecursiveType(node => StructType({
485
+ value: IntegerType,
486
+ children: ArrayType(node),
487
+ }));
488
+ const before = { value: 1n, children: [] };
489
+ const after = { value: 2n, children: [] };
490
+ const c = capture(Tree, before, after);
491
+ assert.equal(c.leaves.length, 1);
492
+ assert.equal(c.leaves[0].op, "update");
493
+ });
494
+ test("Recursive type: deep tree change still emits a single update leaf", () => {
495
+ const Tree = RecursiveType(node => StructType({
496
+ value: IntegerType,
497
+ children: ArrayType(node),
498
+ }));
499
+ const before = { value: 1n, children: [{ value: 2n, children: [] }] };
500
+ const after = { value: 1n, children: [{ value: 99n, children: [] }] };
501
+ const c = capture(Tree, before, after);
502
+ assert.equal(c.leaves.length, 1);
503
+ assert.equal(c.leaves[0].op, "update");
504
+ });
505
+ });
506
+ // ============================================================================
507
+ // Visitor control flow
508
+ // ============================================================================
509
+ describe("walkPatch: visitor control", () => {
510
+ test("enter() returning false skips the subtree but processes siblings", () => {
511
+ const T = StructType({
512
+ outer: StructType({ a: IntegerType, b: IntegerType }),
513
+ sibling: IntegerType,
514
+ });
515
+ const seen = [];
516
+ walkPatch(T, diffFor(T)({ outer: { a: 1n, b: 2n }, sibling: 0n }, { outer: { a: 9n, b: 9n }, sibling: 5n }), {
517
+ enter: ({ path }) => {
518
+ if (pathToString(path) === "outer")
519
+ return false;
520
+ return undefined;
521
+ },
522
+ leaf: ({ path }) => seen.push(pathToString(path)),
523
+ });
524
+ assert.equal(seen.length, 1);
525
+ assert.equal(seen[0], "sibling");
526
+ });
527
+ test("enter.leafCount equals the total leaves under that subtree", () => {
528
+ const T = StructType({ outer: StructType({ a: IntegerType, b: IntegerType }) });
529
+ const enters = [];
530
+ let leafCount = 0;
531
+ walkPatch(T, diffFor(T)({ outer: { a: 1n, b: 2n } }, { outer: { a: 9n, b: 9n } }), {
532
+ enter: ({ path, leafCount: n }) => { enters.push({ path: pathToString(path), n }); },
533
+ leaf: () => { leafCount++; },
534
+ });
535
+ assert.equal(leafCount, 2);
536
+ const root = enters.find(e => e.path === "");
537
+ assert.equal(root.n, leafCount);
538
+ });
539
+ test("walking an unchanged patch produces no events", () => {
540
+ const events = [];
541
+ walkPatch(IntegerType, diffFor(IntegerType)(5n, 5n), {
542
+ enter: () => { events.push("enter"); },
543
+ leaf: () => { events.push("leaf"); },
544
+ exit: () => { events.push("exit"); },
545
+ });
546
+ assert.equal(events.length, 0);
547
+ });
548
+ test("enter / exit nesting matches container nesting (stacked correctly)", () => {
549
+ const T = StructType({ outer: StructType({ inner: IntegerType }) });
550
+ const events = [];
551
+ walkPatch(T, diffFor(T)({ outer: { inner: 1n } }, { outer: { inner: 2n } }), {
552
+ enter: ({ path }) => { events.push({ kind: "enter", path: pathToString(path) }); },
553
+ leaf: ({ path }) => { events.push({ kind: "leaf", path: pathToString(path) }); },
554
+ exit: ({ path }) => { events.push({ kind: "exit", path: pathToString(path) }); },
555
+ });
556
+ assert.deepEqual(events, [
557
+ { kind: "enter", path: "" },
558
+ { kind: "enter", path: "outer" },
559
+ { kind: "leaf", path: "outer.inner" },
560
+ { kind: "exit", path: "outer" },
561
+ { kind: "exit", path: "" },
562
+ ]);
563
+ });
564
+ test("optional enter / exit can be omitted; leaf is the only required hook", () => {
565
+ const T = StructType({ a: IntegerType, b: IntegerType });
566
+ const leaves = [];
567
+ walkPatch(T, diffFor(T)({ a: 1n, b: 2n }, { a: 9n, b: 9n }), {
568
+ leaf: ({ path }) => leaves.push(pathToString(path)),
569
+ });
570
+ assert.deepEqual(leaves.sort(), ["a", "b"]);
571
+ });
572
+ test("enter() returning false skips Array element subtrees too", () => {
573
+ const T = ArrayType(StructType({ a: IntegerType }));
574
+ const seen = [];
575
+ walkPatch(T, diffFor(T)([{ a: 1n }, { a: 2n }], [{ a: 9n }, { a: 9n }]), {
576
+ enter: ({ path }) => {
577
+ if (pathToString(path) === "[0]")
578
+ return false;
579
+ return undefined;
580
+ },
581
+ leaf: ({ path }) => seen.push(pathToString(path)),
582
+ });
583
+ // [0]'s leaf was pruned, [1].a survives
584
+ assert.ok(seen.every(p => !p.startsWith("[0]")));
585
+ });
586
+ });
587
+ // ============================================================================
588
+ // Typed PatchPath — leaf hooks receive structured segments, not strings
589
+ // ============================================================================
590
+ describe("walkPatch: typed PatchPath", () => {
591
+ test("leaf receives PatchPath (array of segments), not a string", () => {
592
+ const T = StructType({ a: ArrayType(IntegerType) });
593
+ const seen = [];
594
+ walkPatch(T, diffFor(T)({ a: [1n, 2n] }, { a: [1n, 5n] }), {
595
+ leaf: ({ path }) => { seen.push(path); },
596
+ });
597
+ assert.ok(seen.length > 0);
598
+ const first = seen[0][0];
599
+ assert.equal(first.kind, "field");
600
+ assert.equal(first.name, "a");
601
+ });
602
+ test("path segments expose every kind directly: field / index / key / variant", () => {
603
+ const T = StructType({
604
+ roster: ArrayType(StructType({
605
+ shifts: DictType(StringType, VariantType({ on: NullType, off: NullType }))
606
+ })),
607
+ });
608
+ const seen = [];
609
+ walkPatch(T, diffFor(T)({ roster: [{ shifts: new SortedMap([["m", variant("on", null)]], compareFor(StringType)) }] }, { roster: [{ shifts: new SortedMap([["m", variant("off", null)]], compareFor(StringType)) }] }), {
610
+ leaf: ({ path }) => { seen.push(path); },
611
+ });
612
+ assert.ok(seen.length > 0);
613
+ const segments = seen[0];
614
+ const kinds = segments.map(s => s.kind);
615
+ // Should hit field("roster") → index → field("shifts") → key("m") → variant("…")
616
+ assert.ok(kinds.includes("field"));
617
+ assert.ok(kinds.includes("index"));
618
+ assert.ok(kinds.includes("key"));
619
+ });
620
+ });
621
+ //# sourceMappingURL=walk.spec.js.map