@divmain/jdm-asm 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (397) hide show
  1. package/.github/workflows/ci.yml +53 -0
  2. package/.oxfmtrc.json +16 -0
  3. package/.oxlintrc.json +183 -0
  4. package/AGENTS.md +81 -0
  5. package/README.md +769 -0
  6. package/asconfig.json +23 -0
  7. package/benchmarks/fixtures.ts +111 -0
  8. package/benchmarks/input-fixtures.ts +80 -0
  9. package/benchmarks/run.ts +913 -0
  10. package/benchmarks/worker-pool.ts +223 -0
  11. package/benchmarks/worker.ts +374 -0
  12. package/dist/index.d.ts +996 -0
  13. package/dist/index.js +12239 -0
  14. package/dist/index.js.map +1 -0
  15. package/package.json +49 -0
  16. package/scripts/run-all-tests.ts +220 -0
  17. package/src/compiler/EXPRESSION_SUBSETS.md +228 -0
  18. package/src/compiler/asc-compiler.ts +315 -0
  19. package/src/compiler/ast-types.ts +215 -0
  20. package/src/compiler/build.ts +56 -0
  21. package/src/compiler/cache.ts +414 -0
  22. package/src/compiler/code-generators.ts +211 -0
  23. package/src/compiler/codegen/index.ts +15 -0
  24. package/src/compiler/codegen/js-marshal.ts +999 -0
  25. package/src/compiler/codegen/js-validation.ts +243 -0
  26. package/src/compiler/codegen.ts +19 -0
  27. package/src/compiler/compile-time-validation.ts +507 -0
  28. package/src/compiler/cst-visitor.ts +434 -0
  29. package/src/compiler/errors.ts +227 -0
  30. package/src/compiler/expression-parser.ts +536 -0
  31. package/src/compiler/graph.ts +197 -0
  32. package/src/compiler/index.ts +199 -0
  33. package/src/compiler/input-validation.ts +33 -0
  34. package/src/compiler/marshal-gen.ts +21 -0
  35. package/src/compiler/nodes/context-resolvers.ts +197 -0
  36. package/src/compiler/nodes/decision-table.ts +507 -0
  37. package/src/compiler/nodes/decision.ts +292 -0
  38. package/src/compiler/nodes/expression-compiler.ts +526 -0
  39. package/src/compiler/nodes/expression.ts +425 -0
  40. package/src/compiler/nodes/function.ts +316 -0
  41. package/src/compiler/nodes/input.ts +60 -0
  42. package/src/compiler/nodes/switch.ts +547 -0
  43. package/src/compiler/optimizer.ts +948 -0
  44. package/src/compiler/orchestrator.ts +352 -0
  45. package/src/compiler/parser.ts +115 -0
  46. package/src/compiler/result-selection.ts +161 -0
  47. package/src/compiler/runtime/index.ts +26 -0
  48. package/src/compiler/runtime-codegen.ts +211 -0
  49. package/src/compiler/runtime-validation-codegen.ts +294 -0
  50. package/src/compiler/runtime.ts +452 -0
  51. package/src/compiler/schema.ts +245 -0
  52. package/src/compiler/switch-branch-detection.ts +92 -0
  53. package/src/compiler/types.ts +136 -0
  54. package/src/compiler/unary-ast-transforms.ts +148 -0
  55. package/src/compiler/unary-parser.ts +301 -0
  56. package/src/compiler/unary-transform.ts +161 -0
  57. package/src/compiler/utils.ts +27 -0
  58. package/src/compiler/virtual-fs.ts +90 -0
  59. package/src/compiler/wasm-instantiate.ts +127 -0
  60. package/src/index.ts +1 -0
  61. package/src/runtime/arrays.ts +579 -0
  62. package/src/runtime/context.ts +189 -0
  63. package/src/runtime/expressions.ts +1811 -0
  64. package/src/runtime/index.ts +8 -0
  65. package/src/runtime/memory.ts +607 -0
  66. package/src/runtime/strings.ts +260 -0
  67. package/src/runtime/tables.ts +96 -0
  68. package/src/runtime/tsconfig.json +4 -0
  69. package/src/runtime/values.ts +209 -0
  70. package/test-data/README.md +83 -0
  71. package/test-data/decision-tables/basic/8k.json +87992 -0
  72. package/test-data/decision-tables/basic/affiliate-commission-calculator.json +228 -0
  73. package/test-data/decision-tables/basic/airline-loyalty-points-calculations.json +285 -0
  74. package/test-data/decision-tables/basic/airline-upgrade-eligibility.json +466 -0
  75. package/test-data/decision-tables/basic/auto-insurance-premium-calculator.json +412 -0
  76. package/test-data/decision-tables/basic/booking-personalization-system.json +553 -0
  77. package/test-data/decision-tables/basic/care-team-assignment-system.json +585 -0
  78. package/test-data/decision-tables/basic/claim-validation-system.json +307 -0
  79. package/test-data/decision-tables/basic/clinical-lab-result-interpreter.json +433 -0
  80. package/test-data/decision-tables/basic/clinical-treatment-protocol.json +474 -0
  81. package/test-data/decision-tables/basic/credit-limit-adjustment.json +479 -0
  82. package/test-data/decision-tables/basic/customer-eligibility-engine.json +551 -0
  83. package/test-data/decision-tables/basic/customer-lifetime-value.json +200 -0
  84. package/test-data/decision-tables/basic/customer-onboarding-kyc-verification.json +611 -0
  85. package/test-data/decision-tables/basic/customer-service-escalation.json +191 -0
  86. package/test-data/decision-tables/basic/decision-table-discounts.json +168 -0
  87. package/test-data/decision-tables/basic/decision-table-shipping.json +398 -0
  88. package/test-data/decision-tables/basic/delivery-route-optimizer.json +271 -0
  89. package/test-data/decision-tables/basic/device-compatibility-checker.json +303 -0
  90. package/test-data/decision-tables/basic/disaster-relief-fund-allocation.json +296 -0
  91. package/test-data/decision-tables/basic/dynamic-fx-rate-pricing-system.json +237 -0
  92. package/test-data/decision-tables/basic/dynamic-marketplace-comission-calculator.json +242 -0
  93. package/test-data/decision-tables/basic/dynamic-shipping-cost-calculator.json +378 -0
  94. package/test-data/decision-tables/basic/dynamic-tarrif-engine.json +289 -0
  95. package/test-data/decision-tables/basic/dynamic-ticket-pricing.json +325 -0
  96. package/test-data/decision-tables/basic/empty-column-with-space.json +100 -0
  97. package/test-data/decision-tables/basic/empty-column-without-space.json +100 -0
  98. package/test-data/decision-tables/basic/environment-compliance-assessment.json +386 -0
  99. package/test-data/decision-tables/basic/expression-table-map.json +313 -0
  100. package/test-data/decision-tables/basic/flash-sale-eligibility.json +366 -0
  101. package/test-data/decision-tables/basic/flight-dispatch-decision-system.json +455 -0
  102. package/test-data/decision-tables/basic/flight-rebooking-fee-calculator.json +406 -0
  103. package/test-data/decision-tables/basic/government-assistance.json +299 -0
  104. package/test-data/decision-tables/basic/grant-funding-distribution.json +307 -0
  105. package/test-data/decision-tables/basic/hazardous-materials-management-system.json +414 -0
  106. package/test-data/decision-tables/basic/immigration-eligibility-evaluator.json +765 -0
  107. package/test-data/decision-tables/basic/import-duties-calculator.json +318 -0
  108. package/test-data/decision-tables/basic/insurance-agent-commission.json +228 -0
  109. package/test-data/decision-tables/basic/insurance-coverage-calculator.json +362 -0
  110. package/test-data/decision-tables/basic/insurance-underwriting-risk.json +321 -0
  111. package/test-data/decision-tables/basic/international-roaming-policy-manager.json +199 -0
  112. package/test-data/decision-tables/basic/legacy-plan-management.json +434 -0
  113. package/test-data/decision-tables/basic/marketplace-listing-verification-system.json +334 -0
  114. package/test-data/decision-tables/basic/medication-dosage-calculator.json +318 -0
  115. package/test-data/decision-tables/basic/merch-bags.json +171 -0
  116. package/test-data/decision-tables/basic/municipal-permit-evaluation-system.json +364 -0
  117. package/test-data/decision-tables/basic/mvno-partner-enablement.json +313 -0
  118. package/test-data/decision-tables/basic/partner-revenue-sharing.json +244 -0
  119. package/test-data/decision-tables/basic/payment-routing-and-fee-calculator.json +475 -0
  120. package/test-data/decision-tables/basic/policy-discount-calculator.json +307 -0
  121. package/test-data/decision-tables/basic/policy-eligibility-analyzer.json +299 -0
  122. package/test-data/decision-tables/basic/product-listing-scoring.json +358 -0
  123. package/test-data/decision-tables/basic/realtime-fraud-detection.json +235 -0
  124. package/test-data/decision-tables/basic/regional-compliance-manager.json +278 -0
  125. package/test-data/decision-tables/basic/returns-and-refund-policy.json +366 -0
  126. package/test-data/decision-tables/basic/returns-processing-system.json +448 -0
  127. package/test-data/decision-tables/basic/school-district-resource-allocation.json +282 -0
  128. package/test-data/decision-tables/basic/seat-map-optimization.json +325 -0
  129. package/test-data/decision-tables/basic/seller-fee-calculator.json +307 -0
  130. package/test-data/decision-tables/basic/service-level-agreement-enforcement.json +575 -0
  131. package/test-data/decision-tables/basic/smart-financial-product-matcher.json +249 -0
  132. package/test-data/decision-tables/basic/supply-chain-risk.json +316 -0
  133. package/test-data/decision-tables/basic/table-loop.json +93 -0
  134. package/test-data/decision-tables/basic/table.json +76 -0
  135. package/test-data/decision-tables/basic/traffic-violation-penalty-calculator.json +436 -0
  136. package/test-data/decision-tables/basic/transaction-compliance-classifier.json +525 -0
  137. package/test-data/decision-tables/basic/vehicle-claims-resolution.json +310 -0
  138. package/test-data/decision-tables/basic/warehouse-storage-location.json +345 -0
  139. package/test-data/decision-tables/hit-policy-collect/collect-multiple-matches.json +127 -0
  140. package/test-data/decision-tables/hit-policy-collect/collect-no-match.json +95 -0
  141. package/test-data/decision-tables/hit-policy-first/first-match.json +103 -0
  142. package/test-data/decision-tables/hit-policy-first/no-match.json +95 -0
  143. package/test-data/decision-tables/hit-policy-output-order/output-order-respected.json +94 -0
  144. package/test-data/decision-tables/hit-policy-output-order/string-output-order.json +94 -0
  145. package/test-data/decision-tables/hit-policy-priority/priority-respected.json +86 -0
  146. package/test-data/decision-tables/hit-policy-rule-order/rule-order-respected.json +94 -0
  147. package/test-data/decision-tables/hit-policy-unique/all-match-error.json +89 -0
  148. package/test-data/decision-tables/hit-policy-unique/multiple-match-error.json +89 -0
  149. package/test-data/decision-tables/hit-policy-unique/no-match.json +88 -0
  150. package/test-data/decision-tables/hit-policy-unique/unique-match.json +99 -0
  151. package/test-data/expressions/arithmetic/error-cyclic.json +114 -0
  152. package/test-data/expressions/arithmetic/error-missing-input.json +54 -0
  153. package/test-data/expressions/arithmetic/error-missing-output.json +54 -0
  154. package/test-data/expressions/arithmetic/expression-default.json +93 -0
  155. package/test-data/expressions/arithmetic/expression-fields.json +94 -0
  156. package/test-data/expressions/arithmetic/expression-loop.json +94 -0
  157. package/test-data/expressions/arithmetic/expression-passthrough.json +108 -0
  158. package/test-data/expressions/arithmetic/expression.json +69 -0
  159. package/test-data/expressions/arithmetic/nested-request.json +125 -0
  160. package/test-data/expressions/arithmetic/number-function.json +58 -0
  161. package/test-data/expressions/arithmetic/test-number-functions.json +68 -0
  162. package/test-data/expressions/functions/all.json +149 -0
  163. package/test-data/expressions/functions/avg.json +89 -0
  164. package/test-data/expressions/functions/filter.json +109 -0
  165. package/test-data/expressions/functions/flat.json +167 -0
  166. package/test-data/expressions/functions/map-strings.json +65 -0
  167. package/test-data/expressions/functions/map.json +73 -0
  168. package/test-data/expressions/functions/reduce.json +49 -0
  169. package/test-data/expressions/functions/some.json +175 -0
  170. package/test-data/expressions/functions/sort-strings.json +97 -0
  171. package/test-data/expressions/functions/sort.json +97 -0
  172. package/test-data/expressions/logical/logical-and.json +116 -0
  173. package/test-data/expressions/logical/logical-complex.json +260 -0
  174. package/test-data/expressions/logical/logical-not.json +111 -0
  175. package/test-data/expressions/logical/logical-or.json +123 -0
  176. package/test-data/expressions/string/string-comparison.json +128 -0
  177. package/test-data/expressions/string/string-concat.json +106 -0
  178. package/test-data/expressions/string/string-contains.json +125 -0
  179. package/test-data/expressions/string/string-endsWith.json +113 -0
  180. package/test-data/expressions/string/string-indexOf.json +131 -0
  181. package/test-data/expressions/string/string-join.json +92 -0
  182. package/test-data/expressions/string/string-lower.json +94 -0
  183. package/test-data/expressions/string/string-replace.json +130 -0
  184. package/test-data/expressions/string/string-split.json +101 -0
  185. package/test-data/expressions/string/string-startsWith.json +113 -0
  186. package/test-data/expressions/string/string-substring.json +138 -0
  187. package/test-data/expressions/string/string-trim.json +100 -0
  188. package/test-data/expressions/string/string-upper.json +94 -0
  189. package/test-data/other/custom.json +51 -0
  190. package/test-data/other/customer-input-schema.json +34 -0
  191. package/test-data/other/customer-output-schema.json +34 -0
  192. package/test-data/other/passthrough.json +31 -0
  193. package/test-data/sub-decisions/basic/$nodes-child.json +31 -0
  194. package/test-data/sub-decisions/basic/$nodes-parent.json +49 -0
  195. package/test-data/sub-decisions/basic/recursive-table1.json +49 -0
  196. package/test-data/sub-decisions/basic/recursive-table2.json +49 -0
  197. package/test-data/sub-decisions/complex-multi/approval-decision.json +31 -0
  198. package/test-data/sub-decisions/complex-multi/complex-dag.json +175 -0
  199. package/test-data/sub-decisions/complex-multi/credit-check.json +31 -0
  200. package/test-data/sub-decisions/complex-multi/customer-segmentation.json +31 -0
  201. package/test-data/sub-decisions/complex-multi/discount-eligibility.json +31 -0
  202. package/test-data/sub-decisions/complex-multi/eligibility-check.json +31 -0
  203. package/test-data/sub-decisions/complex-multi/final-offer.json +31 -0
  204. package/test-data/sub-decisions/complex-multi/income-verification.json +31 -0
  205. package/test-data/sub-decisions/complex-multi/linear-chain.json +121 -0
  206. package/test-data/sub-decisions/complex-multi/pricing-calculation.json +31 -0
  207. package/test-data/sub-decisions/complex-multi/product-eligibility.json +31 -0
  208. package/test-data/sub-decisions/complex-multi/risk-assessment.json +31 -0
  209. package/test-data/sub-decisions/complex-multi/shared-validation.json +31 -0
  210. package/test-data/sub-decisions/complex-multi/validation.json +31 -0
  211. package/test-data/sub-decisions/diamond/decision-a.json +31 -0
  212. package/test-data/sub-decisions/diamond/decision-b.json +31 -0
  213. package/test-data/sub-decisions/diamond/decision-c.json +31 -0
  214. package/test-data/sub-decisions/diamond/decision-shared.json +31 -0
  215. package/test-data/sub-decisions/diamond/diamond-pattern.json +109 -0
  216. package/test-data/sub-decisions/error-propagation/parent-calls-error.json +44 -0
  217. package/test-data/sub-decisions/error-propagation/sub-decision-with-error.json +60 -0
  218. package/test-data/switch-nodes/basic/account-dormancy-management.json +245 -0
  219. package/test-data/switch-nodes/basic/application-risk-assessment.json +474 -0
  220. package/test-data/switch-nodes/basic/cellular-data-rollover-system.json +281 -0
  221. package/test-data/switch-nodes/basic/clinical-pathway-selection.json +454 -0
  222. package/test-data/switch-nodes/basic/insurance-prior-authorization.json +467 -0
  223. package/test-data/switch-nodes/basic/last-mile-delivery-assignment.json +373 -0
  224. package/test-data/switch-nodes/basic/loan-approval.json +469 -0
  225. package/test-data/switch-nodes/basic/multi-switch.json +498 -0
  226. package/test-data/switch-nodes/basic/online-checkin-eligibility.json +285 -0
  227. package/test-data/switch-nodes/basic/order-consolidation-system.json +493 -0
  228. package/test-data/switch-nodes/basic/seller-approval-workflow.json +383 -0
  229. package/test-data/switch-nodes/basic/set-fee.json +243 -0
  230. package/test-data/switch-nodes/basic/shipping-carrier-selector.json +379 -0
  231. package/test-data/switch-nodes/basic/switch-node.json +167 -0
  232. package/test-data/switch-nodes/basic/switch-performance-2.json +1307 -0
  233. package/test-data/switch-nodes/basic/switch-performance.json +691 -0
  234. package/test-data/switch-nodes/basic/tax-exemption.json +295 -0
  235. package/test-data/switch-nodes/basic/warehouse-cross-docking.json +313 -0
  236. package/test-data/switch-nodes/default-cases/switch-with-default.json +134 -0
  237. package/test-data/zen-reference/$nodes-child.json +69 -0
  238. package/test-data/zen-reference/$nodes-parent.json +34 -0
  239. package/test-data/zen-reference/8k.json +87992 -0
  240. package/test-data/zen-reference/credit-analysis.json +324 -0
  241. package/test-data/zen-reference/custom.json +51 -0
  242. package/test-data/zen-reference/customer-input-schema.json +34 -0
  243. package/test-data/zen-reference/customer-output-schema.json +34 -0
  244. package/test-data/zen-reference/error-cyclic.json +114 -0
  245. package/test-data/zen-reference/error-missing-input.json +54 -0
  246. package/test-data/zen-reference/error-missing-output.json +54 -0
  247. package/test-data/zen-reference/expression.json +69 -0
  248. package/test-data/zen-reference/function-v2.json +48 -0
  249. package/test-data/zen-reference/function.json +46 -0
  250. package/test-data/zen-reference/graphs/account-dormancy-management.json +245 -0
  251. package/test-data/zen-reference/graphs/affiliate-commission-calculator.json +228 -0
  252. package/test-data/zen-reference/graphs/airline-loyalty-points-calculations.json +285 -0
  253. package/test-data/zen-reference/graphs/airline-upgrade-eligibility.json +466 -0
  254. package/test-data/zen-reference/graphs/aml.json +537 -0
  255. package/test-data/zen-reference/graphs/application-risk-assessment.json +474 -0
  256. package/test-data/zen-reference/graphs/auto-insurance-premium-calculator.json +412 -0
  257. package/test-data/zen-reference/graphs/booking-personalization-system.json +553 -0
  258. package/test-data/zen-reference/graphs/care-team-assignment-system.json +585 -0
  259. package/test-data/zen-reference/graphs/cellular-data-rollover-system.json +281 -0
  260. package/test-data/zen-reference/graphs/claim-validation-system.json +307 -0
  261. package/test-data/zen-reference/graphs/clinical-lab-result-interpreter.json +433 -0
  262. package/test-data/zen-reference/graphs/clinical-pathway-selection.json +454 -0
  263. package/test-data/zen-reference/graphs/clinical-treatment-protocol.json +474 -0
  264. package/test-data/zen-reference/graphs/company-analysis.json +390 -0
  265. package/test-data/zen-reference/graphs/credit-limit-adjustment.json +479 -0
  266. package/test-data/zen-reference/graphs/customer-eligibility-engine.json +551 -0
  267. package/test-data/zen-reference/graphs/customer-lifetime-value.json +200 -0
  268. package/test-data/zen-reference/graphs/customer-onboarding-kyc-verification.json +611 -0
  269. package/test-data/zen-reference/graphs/customer-service-escalation.json +191 -0
  270. package/test-data/zen-reference/graphs/decision-table-discounts.json +168 -0
  271. package/test-data/zen-reference/graphs/decision-table-shipping.json +398 -0
  272. package/test-data/zen-reference/graphs/delivery-route-optimizer.json +271 -0
  273. package/test-data/zen-reference/graphs/device-compatibility-checker.json +303 -0
  274. package/test-data/zen-reference/graphs/disaster-relief-fund-allocation.json +296 -0
  275. package/test-data/zen-reference/graphs/dynamic-fx-rate-pricing-system.json +237 -0
  276. package/test-data/zen-reference/graphs/dynamic-marketplace-comission-calculator.json +242 -0
  277. package/test-data/zen-reference/graphs/dynamic-shipping-cost-calculator.json +378 -0
  278. package/test-data/zen-reference/graphs/dynamic-tarrif-engine.json +289 -0
  279. package/test-data/zen-reference/graphs/dynamic-ticket-pricing.json +325 -0
  280. package/test-data/zen-reference/graphs/empty-column-with-space.json +100 -0
  281. package/test-data/zen-reference/graphs/empty-column-without-space.json +100 -0
  282. package/test-data/zen-reference/graphs/environment-compliance-assessment.json +386 -0
  283. package/test-data/zen-reference/graphs/expression-default.json +93 -0
  284. package/test-data/zen-reference/graphs/expression-fields.json +94 -0
  285. package/test-data/zen-reference/graphs/expression-loop.json +94 -0
  286. package/test-data/zen-reference/graphs/expression-passthrough.json +108 -0
  287. package/test-data/zen-reference/graphs/expression-table-map.json +313 -0
  288. package/test-data/zen-reference/graphs/flash-sale-eligibility.json +366 -0
  289. package/test-data/zen-reference/graphs/flight-dispatch-decision-system.json +455 -0
  290. package/test-data/zen-reference/graphs/flight-rebooking-fee-calculator.json +406 -0
  291. package/test-data/zen-reference/graphs/government-assistance.json +299 -0
  292. package/test-data/zen-reference/graphs/grant-funding-distribution.json +307 -0
  293. package/test-data/zen-reference/graphs/hazardous-materials-management-system.json +414 -0
  294. package/test-data/zen-reference/graphs/immigration-eligibility-evaluator.json +765 -0
  295. package/test-data/zen-reference/graphs/import-duties-calculator.json +318 -0
  296. package/test-data/zen-reference/graphs/insurance-agent-commission.json +228 -0
  297. package/test-data/zen-reference/graphs/insurance-breakdown.json +421 -0
  298. package/test-data/zen-reference/graphs/insurance-coverage-calculator.json +362 -0
  299. package/test-data/zen-reference/graphs/insurance-prior-authorization.json +467 -0
  300. package/test-data/zen-reference/graphs/insurance-underwriting-risk.json +321 -0
  301. package/test-data/zen-reference/graphs/international-roaming-policy-manager.json +199 -0
  302. package/test-data/zen-reference/graphs/last-mile-delivery-assignment.json +373 -0
  303. package/test-data/zen-reference/graphs/legacy-plan-management.json +434 -0
  304. package/test-data/zen-reference/graphs/loan-approval.json +469 -0
  305. package/test-data/zen-reference/graphs/marketplace-listing-verification-system.json +334 -0
  306. package/test-data/zen-reference/graphs/medication-dosage-calculator.json +318 -0
  307. package/test-data/zen-reference/graphs/merch-bags.json +171 -0
  308. package/test-data/zen-reference/graphs/multi-switch.json +498 -0
  309. package/test-data/zen-reference/graphs/municipal-permit-evaluation-system.json +364 -0
  310. package/test-data/zen-reference/graphs/mvno-partner-enablement.json +313 -0
  311. package/test-data/zen-reference/graphs/nested-request.json +125 -0
  312. package/test-data/zen-reference/graphs/online-checkin-eligibility.json +285 -0
  313. package/test-data/zen-reference/graphs/order-consolidation-system.json +493 -0
  314. package/test-data/zen-reference/graphs/partner-revenue-sharing.json +244 -0
  315. package/test-data/zen-reference/graphs/payment-routing-and-fee-calculator.json +475 -0
  316. package/test-data/zen-reference/graphs/policy-discount-calculator.json +307 -0
  317. package/test-data/zen-reference/graphs/policy-eligibility-analyzer.json +299 -0
  318. package/test-data/zen-reference/graphs/product-listing-scoring.json +358 -0
  319. package/test-data/zen-reference/graphs/realtime-fraud-detection.json +235 -0
  320. package/test-data/zen-reference/graphs/regional-compliance-manager.json +278 -0
  321. package/test-data/zen-reference/graphs/returns-and-refund-policy.json +366 -0
  322. package/test-data/zen-reference/graphs/returns-processing-system.json +448 -0
  323. package/test-data/zen-reference/graphs/school-district-resource-allocation.json +282 -0
  324. package/test-data/zen-reference/graphs/seat-map-optimization.json +325 -0
  325. package/test-data/zen-reference/graphs/seller-approval-workflow.json +383 -0
  326. package/test-data/zen-reference/graphs/seller-fee-calculator.json +307 -0
  327. package/test-data/zen-reference/graphs/service-level-agreement-enforcement.json +575 -0
  328. package/test-data/zen-reference/graphs/set-fee.json +243 -0
  329. package/test-data/zen-reference/graphs/shipping-carrier-selector.json +379 -0
  330. package/test-data/zen-reference/graphs/smart-financial-product-matcher.json +249 -0
  331. package/test-data/zen-reference/graphs/supply-chain-risk.json +316 -0
  332. package/test-data/zen-reference/graphs/table-loop.json +93 -0
  333. package/test-data/zen-reference/graphs/tax-exemption.json +295 -0
  334. package/test-data/zen-reference/graphs/traffic-violation-penalty-calculator.json +436 -0
  335. package/test-data/zen-reference/graphs/transaction-compliance-classifier.json +525 -0
  336. package/test-data/zen-reference/graphs/vehicle-claims-resolution.json +310 -0
  337. package/test-data/zen-reference/graphs/warehouse-cross-docking.json +313 -0
  338. package/test-data/zen-reference/graphs/warehouse-storage-location.json +345 -0
  339. package/test-data/zen-reference/http-function.json +34 -0
  340. package/test-data/zen-reference/infinite-function.json +46 -0
  341. package/test-data/zen-reference/js/imports.js +25 -0
  342. package/test-data/zen-reference/passthrough.json +31 -0
  343. package/test-data/zen-reference/recursive-table1.json +49 -0
  344. package/test-data/zen-reference/recursive-table2.json +49 -0
  345. package/test-data/zen-reference/sleep-function.json +34 -0
  346. package/test-data/zen-reference/switch-node.json +167 -0
  347. package/test-data/zen-reference/switch-performance-2.json +1307 -0
  348. package/test-data/zen-reference/switch-performance.json +691 -0
  349. package/test-data/zen-reference/table.json +76 -0
  350. package/tests/helpers/index.ts +73 -0
  351. package/tests/helpers/mock-context.ts +231 -0
  352. package/tests/helpers/round-trip.ts +398 -0
  353. package/tests/helpers/test-harness-comparison.ts +325 -0
  354. package/tests/helpers/test-harness-wasm.ts +710 -0
  355. package/tests/helpers/test-harness.ts +28 -0
  356. package/tests/helpers/wasm-test.ts +659 -0
  357. package/tests/integration/compilation-errors.test.ts +864 -0
  358. package/tests/integration/decision-tables.test.ts +531 -0
  359. package/tests/integration/edge-cases.test.ts +787 -0
  360. package/tests/integration/expressions.test.ts +513 -0
  361. package/tests/integration/function-node-integration.test.ts +182 -0
  362. package/tests/integration/sub-decisions.test.ts +108 -0
  363. package/tests/integration/switch-nodes.test.ts +399 -0
  364. package/tests/integration/unary-or-matching.test.ts +53 -0
  365. package/tests/integration/wasm-data-types.test.ts +398 -0
  366. package/tests/integration/wasm-errors.test.ts +199 -0
  367. package/tests/integration/wasm-execution.test.ts +348 -0
  368. package/tests/integration/wasm-memory.test.ts +228 -0
  369. package/tests/scripts/analyze-coverage.ts +166 -0
  370. package/tests/scripts/categorize-tests.ts +396 -0
  371. package/tests/scripts/coverage-analysis.ts +836 -0
  372. package/tests/unit/compiler/cache.test.ts +238 -0
  373. package/tests/unit/compiler/errors.test.ts +316 -0
  374. package/tests/unit/compiler/graph-scalability.test.ts +510 -0
  375. package/tests/unit/compiler/graph.test.ts +878 -0
  376. package/tests/unit/compiler/input-validation.test.ts +447 -0
  377. package/tests/unit/compiler/logical-and-parser.test.ts +143 -0
  378. package/tests/unit/compiler/logical-not-parser.test.ts +107 -0
  379. package/tests/unit/compiler/logical-or-parser.test.ts +236 -0
  380. package/tests/unit/compiler/marshal-gen/marshal-gen.test.ts +97 -0
  381. package/tests/unit/compiler/nodes/decision-table.test.ts +103 -0
  382. package/tests/unit/compiler/nodes/decision.test.ts +182 -0
  383. package/tests/unit/compiler/nodes/function-compile.test.ts +204 -0
  384. package/tests/unit/compiler/nodes/function.test.ts +176 -0
  385. package/tests/unit/compiler/nodes/input.test.ts +30 -0
  386. package/tests/unit/compiler/nodes/switch.test.ts +127 -0
  387. package/tests/unit/compiler/optimizer-cache.test.ts +327 -0
  388. package/tests/unit/compiler/optimizer-implementation.test.ts +625 -0
  389. package/tests/unit/compiler/parser.test.ts +508 -0
  390. package/tests/unit/compiler/runtime-error-cleanup.test.ts +426 -0
  391. package/tests/unit/compiler/runtime-validation.test.ts +303 -0
  392. package/tests/unit/compiler/runtime.test.ts +221 -0
  393. package/tests/unit/compiler/schema/schema.test.ts +248 -0
  394. package/tests/unit/compiler/unary-ast-transforms.test.ts +245 -0
  395. package/tsconfig.json +27 -0
  396. package/tsup.config.ts +11 -0
  397. package/vitest.config.ts +12 -0
@@ -0,0 +1,452 @@
1
+ // src/compiler/runtime.ts
2
+ // Runtime execution support for evaluating compiled JDM decisions
3
+
4
+ import type { CompilationResult } from './types';
5
+ import {
6
+ loadGeneratedMarshaling,
7
+ loadGeneratedValidation,
8
+ type MarshalFn,
9
+ type UnmarshalFn,
10
+ type ValidateFn,
11
+ } from './runtime-codegen';
12
+
13
+ // ============================================================================
14
+ // Memory Constants
15
+ // ============================================================================
16
+
17
+ /**
18
+ * WebAssembly page size in bytes (64KB).
19
+ * This is a fixed constant defined by the WebAssembly specification.
20
+ * Used to calculate memory requirements and convert between pages and bytes.
21
+ */
22
+ const WASM_PAGE_SIZE_BYTES = 65536;
23
+
24
+ /**
25
+ * Default maximum memory pages for compiled decisions (256 pages = 16MB).
26
+ *
27
+ * This limit balances:
28
+ * 1. Allowing large decision tables with many rules
29
+ * 2. Preventing unbounded memory growth
30
+ * 3. Browser compatibility (most browsers support at least 32MB per WASM instance)
31
+ *
32
+ * Can be overridden via CompiledDecisionOptions.maximumMemoryPages.
33
+ */
34
+ const DEFAULT_MAX_MEMORY_PAGES = 256;
35
+
36
+ /**
37
+ * Input buffer start offset in bytes (after null pointer trap).
38
+ *
39
+ * Must match INPUT_BUFFER_START in src/runtime/memory.ts (which equals
40
+ * NULL_POINTER_TRAP_SIZE = 4 bytes). This ensures correct memory layout
41
+ * coordination between JavaScript marshaling and WASM memory access.
42
+ *
43
+ * The first 4 bytes are reserved as a null pointer trap zone - accessing
44
+ * address 0 will read/write this trap zone, helping catch null pointer bugs.
45
+ */
46
+ const INPUT_BUFFER_START_OFFSET = 4;
47
+
48
+ /**
49
+ * Error type for runtime evaluation errors.
50
+ */
51
+ export class RuntimeError extends Error {
52
+ constructor(
53
+ message: string,
54
+ public readonly code: number = -1,
55
+ public readonly context?: Record<string, unknown>,
56
+ ) {
57
+ super(message);
58
+ this.name = 'RuntimeError';
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Ensure WASM memory is large enough for the required size.
64
+ *
65
+ * This function checks if the current memory buffer is large enough
66
+ * and grows it by 64KB pages if needed.
67
+ *
68
+ * @param memory - The WebAssembly.Memory instance
69
+ * @param required - The required memory size in bytes
70
+ * @throws {RuntimeError} If memory cannot be grown (e.g., exceeds maximum)
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * ensureMemory(memory, 131072); // Ensure at least 128KB is available
75
+ * ```
76
+ */
77
+ export function ensureMemory(memory: WebAssembly.Memory, required: number): void {
78
+ const current = memory.buffer.byteLength;
79
+ if (required <= current) {
80
+ return; // Already have enough memory
81
+ }
82
+
83
+ // Calculate how many pages to grow
84
+ const pagesNeeded = Math.ceil((required - current) / WASM_PAGE_SIZE_BYTES);
85
+
86
+ // Attempt to grow memory
87
+ try {
88
+ const oldPages = memory.grow(pagesNeeded);
89
+ if (oldPages < 0) {
90
+ throw new RuntimeError(`Failed to grow memory by ${pagesNeeded} pages`, 5); // OUT_OF_MEMORY
91
+ }
92
+ } catch (error) {
93
+ // Re-throw RuntimeError as-is
94
+ if (error instanceof RuntimeError) {
95
+ throw error;
96
+ }
97
+ throw new RuntimeError(
98
+ `Cannot grow WASM memory: required ${required} bytes, ` +
99
+ `current ${current} bytes, need ${pagesNeeded} more pages. ` +
100
+ (error instanceof Error ? error.message : String(error)),
101
+ 5, // OUT_OF_MEMORY
102
+ );
103
+ }
104
+ }
105
+
106
+ // Re-export type definitions from runtime-codegen for backward compatibility
107
+ export type { MarshalFn, UnmarshalFn, ValidateFn } from './runtime-codegen';
108
+
109
+ /**
110
+ * Options for creating a CompiledDecision instance.
111
+ */
112
+ export interface CompiledDecisionOptions {
113
+ /** The compiled WASM binary */
114
+ wasm: Uint8Array;
115
+ /** Schema hash for validation */
116
+ schemaHash: bigint;
117
+ /** Function to marshal input to WASM memory */
118
+ marshal: MarshalFn;
119
+ /** Function to unmarshal output from WASM memory */
120
+ unmarshal: UnmarshalFn;
121
+ /** Function to validate input against schema */
122
+ validate?: ValidateFn;
123
+ /** Initial memory size in pages (default: 1 page = 64KB) */
124
+ initialMemoryPages?: number;
125
+ /** Maximum memory pages (default: DEFAULT_MAX_MEMORY_PAGES = 256 pages = 16MB) */
126
+ maximumMemoryPages?: number;
127
+ }
128
+
129
+ /**
130
+ * A compiled JDM decision ready for evaluation.
131
+ *
132
+ * This class manages the lifecycle of evaluating a compiled decision:
133
+ * 1. Validates input against schema (fail fast with good error)
134
+ * 2. Marshals input to WASM memory
135
+ * 3. Resets heap for fresh evaluation
136
+ * 4. Runs evaluation
137
+ * 5. Checks for runtime errors (schema mismatch, etc.)
138
+ * 6. Unmarshals output from WASM memory
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * const decision = new CompiledDecision({
143
+ * wasm: compilationResult.wasm,
144
+ * schemaHash: compilationResult.schemaHash,
145
+ * marshal: marshalInput,
146
+ * unmarshal: unmarshalOutput,
147
+ * validate: validateInput,
148
+ * });
149
+ *
150
+ * const output = await decision.evaluate({ age: 25, name: 'Alice' });
151
+ * ```
152
+ */
153
+ export class CompiledDecision {
154
+ private instance: WebAssembly.Instance | null = null;
155
+ private memory: WebAssembly.Memory | null = null;
156
+ private module: WebAssembly.Module;
157
+ private marshal: MarshalFn;
158
+ private unmarshal: UnmarshalFn;
159
+ private validate?: ValidateFn;
160
+
161
+ constructor(private options: CompiledDecisionOptions) {
162
+ // WebAssembly.Module requires an ArrayBuffer starting at offset 0, but Uint8Array may
163
+ // be a view into a larger buffer at a non-zero offset, which would cause Module
164
+ // construction to fail. Create a proper Uint8Array view that the Module constructor
165
+ // can safely access, ensuring we only pass the exact bytes of the WASM binary.
166
+ const buffer = new Uint8Array(
167
+ options.wasm.buffer,
168
+ options.wasm.byteOffset,
169
+ options.wasm.byteLength,
170
+ );
171
+ this.module = new WebAssembly.Module(buffer as unknown as ArrayBuffer);
172
+
173
+ this.marshal = options.marshal;
174
+ this.unmarshal = options.unmarshal;
175
+ this.validate = options.validate;
176
+ }
177
+
178
+ /**
179
+ * Initialize the WebAssembly instance.
180
+ * This is done lazily on first evaluation, or can be called explicitly.
181
+ */
182
+ private async initialize(): Promise<void> {
183
+ if (this.instance !== null) {
184
+ return;
185
+ }
186
+
187
+ // Don't provide memory via import - let the WASM module use its own exported memory.
188
+ // AssemblyScript's incremental GC runtime requires specific memory initialization
189
+ // that only works correctly with the module's own memory.
190
+ this.instance = await WebAssembly.instantiate(this.module, {
191
+ env: {
192
+ abort: () => {
193
+ throw new RuntimeError('WASM abort called');
194
+ },
195
+ seed: () => {
196
+ return Date.now();
197
+ },
198
+ 'Date.now': () => Date.now(),
199
+ },
200
+ // Empty index module provided for AssemblyScript compatibility
201
+ index: {},
202
+ });
203
+
204
+ // Get memory from the WASM module's exports
205
+ this.memory = this.instance.exports.memory as WebAssembly.Memory;
206
+ }
207
+
208
+ /**
209
+ * Evaluate the decision with the given input.
210
+ *
211
+ * @param input - The input data to evaluate
212
+ * @returns The output of the decision evaluation
213
+ * @throws {RuntimeError} If evaluation fails
214
+ * @throws {ValidationError} If input validation fails (if validate is provided)
215
+ */
216
+ async evaluate<I = Record<string, unknown>, O = Record<string, unknown>>(input: I): Promise<O> {
217
+ await this.initialize();
218
+
219
+ if (!this.instance || !this.memory) {
220
+ throw new RuntimeError('WASM instance not initialized');
221
+ }
222
+
223
+ // Local references to avoid null checks in the hot path
224
+ const memory = this.memory;
225
+ const instance = this.instance;
226
+
227
+ // 1. Validate input against schema (fail fast with good error)
228
+ if (this.validate) {
229
+ try {
230
+ this.validate(input);
231
+ } catch (error) {
232
+ if (error instanceof Error) {
233
+ throw new RuntimeError(`Input validation failed: ${error.message}`, 1, { input });
234
+ }
235
+ throw error;
236
+ }
237
+ }
238
+
239
+ // Clear any stale error state from previous evaluations before starting new evaluation.
240
+ // This ensures each evaluation starts with a clean slate.
241
+ const clearLastErrorFn = instance.exports.clearLastError as () => void;
242
+ if (clearLastErrorFn) {
243
+ clearLastErrorFn();
244
+ }
245
+
246
+ try {
247
+ // 2. Marshal input to WASM memory
248
+ const inputPtr: number = this.marshal(input as Record<string, unknown>, memory);
249
+
250
+ // Verify input pointer is valid
251
+ if (inputPtr < INPUT_BUFFER_START_OFFSET) {
252
+ throw new RuntimeError(
253
+ `Invalid input pointer: ${inputPtr} (must be >= ${INPUT_BUFFER_START_OFFSET})`,
254
+ );
255
+ }
256
+
257
+ // 3. Reset heap for fresh evaluation
258
+ const resetHeapFn = instance.exports.resetHeap as () => void;
259
+ if (resetHeapFn) {
260
+ resetHeapFn();
261
+ }
262
+
263
+ // 4. Run evaluation
264
+ const evaluateFn = instance.exports.evaluate as (ptr: number) => number;
265
+ if (!evaluateFn) {
266
+ throw new RuntimeError('evaluate function not exported from WASM module');
267
+ }
268
+
269
+ const outputPtr: number = evaluateFn(inputPtr);
270
+
271
+ // 5. Check for runtime errors (schema mismatch, etc.)
272
+ const getLastErrorFn = instance.exports.getLastError as () => number;
273
+ if (getLastErrorFn) {
274
+ const errorCode = getLastErrorFn();
275
+ if (errorCode !== 0) {
276
+ throw new RuntimeError(this.getErrorMessage(errorCode), errorCode, { input, outputPtr });
277
+ }
278
+ }
279
+
280
+ // Verify output pointer is valid
281
+ // Output pointer validity is verified during unmarshal - we only check it's within
282
+ // addressable memory here to avoid segfault.
283
+ if (outputPtr < memory.buffer.byteLength) {
284
+ // Output pointer should be within a reasonable range
285
+ // We'll verify the unmarshal works properly
286
+ }
287
+
288
+ // 6. Unmarshal output from WASM memory
289
+ const output = this.unmarshal(outputPtr, memory);
290
+
291
+ return output as O;
292
+ } finally {
293
+ // Always clear error state after evaluation, even if an error was thrown.
294
+ // This prevents stale error state from affecting subsequent evaluations.
295
+ if (clearLastErrorFn) {
296
+ clearLastErrorFn();
297
+ }
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Get a human-readable error message for a given error code.
303
+ *
304
+ * @param errorCode - The error code from getLastError()
305
+ * @returns A human-readable error message
306
+ */
307
+ private getErrorMessage(errorCode: number): string {
308
+ // Error codes duplicated from assembly/runtime/errors.ts because WASM cannot export
309
+ // enum metadata - these must stay synchronized.
310
+ // NONE = 0, SCHEMA_MISMATCH = 1, NULL_POINTER = 2, INVALID_ACCESS = 3, EVALUATION_ERROR = 4, OUT_OF_MEMORY = 5
311
+ const errorMessages: Record<number, string> = {
312
+ 0: 'No error',
313
+ 1: 'Schema mismatch: input data does not match expected schema',
314
+ 2: 'Null pointer dereference',
315
+ 3: 'Invalid memory access',
316
+ 4: 'Evaluation error',
317
+ 5: 'Out of memory',
318
+ };
319
+
320
+ return errorMessages[errorCode] ?? `Unknown error code: ${errorCode}`;
321
+ }
322
+
323
+ /**
324
+ * Get the current WebAssembly memory instance.
325
+ * This can be useful for debugging or direct memory inspection.
326
+ *
327
+ * @returns The WebAssembly.Memory instance (null if not initialized)
328
+ */
329
+ getMemory(): WebAssembly.Memory | null {
330
+ return this.memory;
331
+ }
332
+
333
+ /**
334
+ * Get the current WebAssembly instance.
335
+ *
336
+ * @returns The WebAssembly.Instance instance (null if not initialized)
337
+ */
338
+ getInstance(): WebAssembly.Instance | null {
339
+ return this.instance;
340
+ }
341
+
342
+ /**
343
+ * Dispose of this CompiledDecision instance.
344
+ * This clears the WebAssembly instance and allows garbage collection.
345
+ */
346
+ dispose(): void {
347
+ this.instance = null;
348
+ }
349
+
350
+ /**
351
+ * Get memory usage statistics.
352
+ *
353
+ * @returns Object with memory usage information
354
+ */
355
+ getMemoryStats(): { current: number; maximum: number; used: number } {
356
+ // Note: 'used' equals 'current' because WebAssembly.Memory doesn't expose actual heap
357
+ // usage - tracking used bytes requires instrumentation in AssemblyScript allocator.
358
+ const currentBytes = this.memory?.buffer.byteLength ?? 0;
359
+ return {
360
+ current: currentBytes,
361
+ maximum: (this.options.maximumMemoryPages ?? DEFAULT_MAX_MEMORY_PAGES) * WASM_PAGE_SIZE_BYTES,
362
+ used: currentBytes,
363
+ };
364
+ }
365
+
366
+ /**
367
+ * Get the input buffer pointer, ensuring sufficient memory is available.
368
+ *
369
+ * This method doesn't allocate new buffers on each call - it always returns the
370
+ * same INPUT_BUFFER_START_OFFSET constant after ensuring the memory can accommodate the
371
+ * required size. Single-allocation design chosen because: (1) heap is reset between
372
+ * evaluations so fragmentation isn't an issue, (2) simpler than maintaining a free
373
+ * list, (3) marshal code controls actual layout in WASM memory. All input data is
374
+ * marshaled into a single buffer starting at INPUT_BUFFER_START_OFFSET.
375
+ *
376
+ * @param required - The required memory size in bytes
377
+ * @returns The base pointer of the input buffer (always INPUT_BUFFER_START_OFFSET)
378
+ * @throws {RuntimeError} If memory cannot be grown to accommodate the required size
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * const ptr = decision.getInputBuffer(1024); // Ensure 1KB available
383
+ * ```
384
+ */
385
+ getInputBuffer(required: number): number {
386
+ if (!this.memory) {
387
+ throw new RuntimeError('Memory not initialized - call evaluate() first', 5);
388
+ }
389
+
390
+ // Ensure enough memory
391
+ try {
392
+ ensureMemory(this.memory, required);
393
+ } catch (error) {
394
+ throw new RuntimeError(
395
+ `Memory allocation failed: ${error instanceof Error ? error.message : String(error)}`,
396
+ 5, // OUT_OF_MEMORY error code
397
+ );
398
+ }
399
+
400
+ // Single-allocation design: return the INPUT_BUFFER_START_OFFSET
401
+ return INPUT_BUFFER_START_OFFSET;
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Create a CompiledDecision instance from a CompilationResult.
407
+ *
408
+ * This is a convenience function that creates the necessary marshal,
409
+ * unmarshal, and validation functions from the generated code.
410
+ *
411
+ * @param result - The compilation result
412
+ * @param validate - Optional validation function (defaults to generated validation code)
413
+ * @returns A CompiledDecision instance ready for evaluation
414
+ *
415
+ * @example
416
+ * ```typescript
417
+ * const result = await compile({
418
+ * jdm: myJdm,
419
+ * inputSchema: MyInputSchema,
420
+ * outputSchema: MyOutputSchema,
421
+ * });
422
+ *
423
+ * const decision = createCompiledDecision(result);
424
+ * const output = await decision.evaluate({ age: 25 });
425
+ * ```
426
+ */
427
+ export async function createCompiledDecision(
428
+ result: CompilationResult,
429
+ validate?: ValidateFn,
430
+ ): Promise<CompiledDecision> {
431
+ // Load the marshal and unmarshal functions from the generated code
432
+ const { marshal, unmarshal } = loadGeneratedMarshaling(result.marshalCode);
433
+
434
+ // Load or use provided validation function
435
+ const validator = validate ?? (await loadGeneratedValidation(result.validationCode));
436
+
437
+ return new CompiledDecision({
438
+ wasm: result.wasm,
439
+ schemaHash: result.schemaHash,
440
+ marshal,
441
+ unmarshal,
442
+ validate: validator,
443
+ });
444
+ }
445
+
446
+ // Re-export compile-time code generation functions for backward compatibility
447
+ export { loadGeneratedMarshaling, loadGeneratedValidation } from './runtime-codegen';
448
+
449
+ /**
450
+ * Re-export types from dependencies
451
+ */
452
+ export type { TSchema } from '@sinclair/typebox';
@@ -0,0 +1,245 @@
1
+ import { type TSchema, Kind } from '@sinclair/typebox';
2
+ import type { FlattenedLayout } from './types';
3
+ import { createHash } from 'crypto';
4
+
5
+ /**
6
+ * Schema field information for memory layout
7
+ */
8
+ interface SchemaField {
9
+ schema: TSchema;
10
+ path: string;
11
+ flatName: string;
12
+ }
13
+
14
+ /**
15
+ * Flattens a TypeBox schema to a memory layout.
16
+ *
17
+ * This function processes a schema and produces a flattened representation
18
+ * that can be used for direct memory access in WebAssembly.
19
+ *
20
+ * Memory structure:
21
+ * ┌─────────────────────────────────────────────────────────────┐
22
+ * │ Fixed Fields (numbers, bools, enum indices) │
23
+ * │ customer_age: f64 (8 bytes) │
24
+ * │ customer_tier: i32 (4 bytes) - enum index │
25
+ * ├─────────────────────────────────────────────────────────────┤
26
+ * │ String/Array Pointers (offset, length pairs) │
27
+ * │ order_items_ptr: i32, order_items_len: i32 │
28
+ * ├─────────────────────────────────────────────────────────────┤
29
+ * │ Variable-length data (UTF-16 strings, array contents) │
30
+ * └─────────────────────────────────────────────────────────────┘
31
+ *
32
+ * @param schema - The TypeBox schema to flatten
33
+ * @param _prefix - Internal parameter for recursive flattening with dotted paths
34
+ * @returns The flattened memory layout
35
+ */
36
+ export function flattenSchema(schema: TSchema): FlattenedLayout {
37
+ const layout: FlattenedLayout = {
38
+ fields: [],
39
+ };
40
+
41
+ // Collect all fields from the schema
42
+ const fields: SchemaField[] = collectFields(schema, '');
43
+
44
+ // Track current offset for field layout
45
+ let currentOffset = 0;
46
+
47
+ // Process fields: first pass - fixed types
48
+ for (const field of fields) {
49
+ const typeInfo = getFieldType(field.schema);
50
+ if (typeInfo.isFixed) {
51
+ layout.fields.push({
52
+ path: field.path,
53
+ flatName: field.flatName,
54
+ type: typeInfo.asType,
55
+ offset: currentOffset,
56
+ size: typeInfo.size,
57
+ });
58
+ currentOffset += typeInfo.size;
59
+ }
60
+ }
61
+
62
+ // Second pass - variable types (strings, arrays)
63
+ // These get pointers (2 x i32 = 8 bytes each for offset + length)
64
+ for (const field of fields) {
65
+ const typeInfo = getFieldType(field.schema);
66
+ if (!typeInfo.isFixed) {
67
+ layout.fields.push({
68
+ path: field.path,
69
+ flatName: field.flatName,
70
+ type: typeInfo.asType,
71
+ offset: currentOffset,
72
+ size: -1, // Variable length
73
+ });
74
+ currentOffset += 8; // 4 bytes offset + 4 bytes length
75
+ }
76
+ }
77
+
78
+ return layout;
79
+ }
80
+
81
+ /**
82
+ * Collect all fields from a schema recursively
83
+ */
84
+ function collectFields(schema: TSchema, path: string): SchemaField[] {
85
+ const fields: SchemaField[] = [];
86
+
87
+ const kind = (schema as any)[Kind];
88
+
89
+ if (kind === 'Object') {
90
+ const properties = (schema as any).properties || {};
91
+ for (const [name, fieldSchema] of Object.entries(properties)) {
92
+ const fieldPath = path ? `${path}.${name}` : name;
93
+ const flatName = path ? `${path}_${name}` : name;
94
+
95
+ const nestedFields = collectFields(fieldSchema as TSchema, fieldPath);
96
+ if (nestedFields.length > 0) {
97
+ fields.push(...nestedFields);
98
+ } else {
99
+ fields.push({
100
+ schema: fieldSchema as TSchema,
101
+ path: fieldPath,
102
+ flatName,
103
+ });
104
+ }
105
+ }
106
+ }
107
+
108
+ // For non-objects, return empty array (handled by parent)
109
+ return fields;
110
+ }
111
+
112
+ /**
113
+ * Get type information for a schema field
114
+ */
115
+ function getFieldType(schema: TSchema): {
116
+ isFixed: boolean;
117
+ asType: 'f64' | 'i32' | 'i64' | 'bool' | 'string' | 'array';
118
+ size: number;
119
+ } {
120
+ const kind = (schema as any)[Kind];
121
+
122
+ switch (kind) {
123
+ case 'Number':
124
+ return { isFixed: true, asType: 'f64', size: 8 };
125
+
126
+ case 'Integer':
127
+ return { isFixed: true, asType: 'i32', size: 4 };
128
+
129
+ case 'Boolean':
130
+ // Booleans stored as i32 (4 bytes) to ensure natural alignment and match AssemblyScript's
131
+ // bool representation
132
+ return { isFixed: true, asType: 'bool', size: 4 };
133
+
134
+ case 'String':
135
+ return { isFixed: false, asType: 'string', size: -1 };
136
+
137
+ case 'Array':
138
+ return { isFixed: false, asType: 'array', size: -1 };
139
+
140
+ case 'Literal':
141
+ // Literals are stored as i32 indices (for boolean-like or enum-like)
142
+ return { isFixed: true, asType: 'i32', size: 4 };
143
+
144
+ case 'Union':
145
+ // First, handle unions of Literal types (enum-like) as i32 indices for efficiency
146
+ const anyOf = (schema as any).anyOf;
147
+ if (Array.isArray(anyOf) && anyOf.length > 0) {
148
+ const firstKind = (anyOf[0] as any)[Kind];
149
+ if (firstKind === 'Literal') {
150
+ return { isFixed: true, asType: 'i32', size: 4 };
151
+ }
152
+ }
153
+ // Default to string for heterogeneous unions - can represent multiple incompatible types
154
+ // (e.g., string | number | object)
155
+ return { isFixed: false, asType: 'string', size: -1 };
156
+
157
+ default:
158
+ // Default to string for unknown types
159
+ return { isFixed: false, asType: 'string', size: -1 };
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Compute a hash of the input and output schemas for runtime validation.
165
+ *
166
+ * This hash is embedded in the compiled WASM module and can be used to
167
+ * verify that the runtime data matches the expected schema.
168
+ *
169
+ * Uses SHA256 to create a canonical hash, then takes the first 8 bytes as u64.
170
+ *
171
+ * @param inputSchema - The input schema
172
+ * @param outputSchema - The output schema
173
+ * @returns A 64-bit hash value
174
+ */
175
+ export function computeSchemaHash(inputSchema: TSchema, outputSchema: TSchema): bigint {
176
+ // Create canonical representation with sorted keys
177
+ const canonical = JSON.stringify(
178
+ {
179
+ input: canonicalizeSchema(inputSchema),
180
+ output: canonicalizeSchema(outputSchema),
181
+ },
182
+ null,
183
+ 2,
184
+ );
185
+
186
+ // Compute SHA256 hash
187
+ const hash = createHash('sha256').update(canonical).digest();
188
+
189
+ // Take first 8 bytes as u64 (little-endian)
190
+ return hash.readBigUInt64LE(0);
191
+ }
192
+
193
+ /**
194
+ * Convert a schema to a canonical form for hashing by sorting keys and normalizing structure
195
+ */
196
+ function canonicalizeSchema(schema: TSchema): unknown {
197
+ const kind = (schema as any)[Kind];
198
+
199
+ if (kind === 'Object') {
200
+ const properties = (schema as any).properties || {};
201
+ const canonical: Record<string, unknown> = {};
202
+ const keys = Object.keys(properties).sort();
203
+
204
+ for (const key of keys) {
205
+ canonical[key] = canonicalizeSchema(properties[key]);
206
+ }
207
+
208
+ return {
209
+ type: 'object',
210
+ properties: canonical,
211
+ required: ((schema as any).required || []).sort(),
212
+ };
213
+ }
214
+
215
+ if (kind === 'Array') {
216
+ return {
217
+ type: 'array',
218
+ items: canonicalizeSchema((schema as any).items),
219
+ };
220
+ }
221
+
222
+ if (kind === 'Enum') {
223
+ return {
224
+ type: 'enum',
225
+ values: ((schema as any).anyOf || []).map((val: TSchema) => (val as any).const).sort(),
226
+ };
227
+ }
228
+
229
+ if (kind === 'Literal') {
230
+ return {
231
+ type: 'literal',
232
+ value: (schema as any).const,
233
+ };
234
+ }
235
+
236
+ // For primitive types, return a simple representation
237
+ const primitiveTypes: Record<string, string> = {
238
+ Number: 'number',
239
+ Integer: 'integer',
240
+ String: 'string',
241
+ Boolean: 'boolean',
242
+ };
243
+
244
+ return primitiveTypes[kind] || kind;
245
+ }