@divmain/jdm-asm 0.2.1 → 0.2.3

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 (395) hide show
  1. package/dist/index.js +2227 -29
  2. package/dist/index.js.map +1 -1
  3. package/package.json +8 -2
  4. package/.github/workflows/ci.yml +0 -53
  5. package/.oxfmtrc.json +0 -16
  6. package/.oxlintrc.json +0 -183
  7. package/AGENTS.md +0 -81
  8. package/asconfig.json +0 -23
  9. package/benchmarks/fixtures.ts +0 -111
  10. package/benchmarks/input-fixtures.ts +0 -80
  11. package/benchmarks/run.ts +0 -913
  12. package/benchmarks/worker-pool.ts +0 -223
  13. package/benchmarks/worker.ts +0 -374
  14. package/scripts/run-all-tests.ts +0 -220
  15. package/src/compiler/EXPRESSION_SUBSETS.md +0 -228
  16. package/src/compiler/asc-compiler.ts +0 -315
  17. package/src/compiler/ast-types.ts +0 -215
  18. package/src/compiler/build.ts +0 -56
  19. package/src/compiler/cache.ts +0 -414
  20. package/src/compiler/code-generators.ts +0 -211
  21. package/src/compiler/codegen/index.ts +0 -15
  22. package/src/compiler/codegen/js-marshal.ts +0 -999
  23. package/src/compiler/codegen/js-validation.ts +0 -243
  24. package/src/compiler/codegen.ts +0 -19
  25. package/src/compiler/compile-time-validation.ts +0 -507
  26. package/src/compiler/cst-visitor.ts +0 -434
  27. package/src/compiler/errors.ts +0 -227
  28. package/src/compiler/expression-parser.ts +0 -536
  29. package/src/compiler/graph.ts +0 -197
  30. package/src/compiler/index.ts +0 -199
  31. package/src/compiler/input-validation.ts +0 -33
  32. package/src/compiler/marshal-gen.ts +0 -21
  33. package/src/compiler/nodes/context-resolvers.ts +0 -197
  34. package/src/compiler/nodes/decision-table.ts +0 -507
  35. package/src/compiler/nodes/decision.ts +0 -292
  36. package/src/compiler/nodes/expression-compiler.ts +0 -526
  37. package/src/compiler/nodes/expression.ts +0 -425
  38. package/src/compiler/nodes/function.ts +0 -316
  39. package/src/compiler/nodes/input.ts +0 -60
  40. package/src/compiler/nodes/switch.ts +0 -547
  41. package/src/compiler/optimizer.ts +0 -948
  42. package/src/compiler/orchestrator.ts +0 -352
  43. package/src/compiler/parser.ts +0 -115
  44. package/src/compiler/result-selection.ts +0 -161
  45. package/src/compiler/runtime/index.ts +0 -26
  46. package/src/compiler/runtime-codegen.ts +0 -211
  47. package/src/compiler/runtime-validation-codegen.ts +0 -294
  48. package/src/compiler/runtime.ts +0 -452
  49. package/src/compiler/schema.ts +0 -245
  50. package/src/compiler/switch-branch-detection.ts +0 -92
  51. package/src/compiler/types.ts +0 -136
  52. package/src/compiler/unary-ast-transforms.ts +0 -148
  53. package/src/compiler/unary-parser.ts +0 -301
  54. package/src/compiler/unary-transform.ts +0 -161
  55. package/src/compiler/utils.ts +0 -27
  56. package/src/compiler/virtual-fs.ts +0 -90
  57. package/src/compiler/wasm-instantiate.ts +0 -127
  58. package/src/index.ts +0 -1
  59. package/src/runtime/arrays.ts +0 -579
  60. package/src/runtime/context.ts +0 -189
  61. package/src/runtime/expressions.ts +0 -1811
  62. package/src/runtime/index.ts +0 -8
  63. package/src/runtime/memory.ts +0 -607
  64. package/src/runtime/strings.ts +0 -260
  65. package/src/runtime/tables.ts +0 -96
  66. package/src/runtime/tsconfig.json +0 -4
  67. package/src/runtime/values.ts +0 -209
  68. package/test-data/README.md +0 -83
  69. package/test-data/decision-tables/basic/8k.json +0 -87992
  70. package/test-data/decision-tables/basic/affiliate-commission-calculator.json +0 -228
  71. package/test-data/decision-tables/basic/airline-loyalty-points-calculations.json +0 -285
  72. package/test-data/decision-tables/basic/airline-upgrade-eligibility.json +0 -466
  73. package/test-data/decision-tables/basic/auto-insurance-premium-calculator.json +0 -412
  74. package/test-data/decision-tables/basic/booking-personalization-system.json +0 -553
  75. package/test-data/decision-tables/basic/care-team-assignment-system.json +0 -585
  76. package/test-data/decision-tables/basic/claim-validation-system.json +0 -307
  77. package/test-data/decision-tables/basic/clinical-lab-result-interpreter.json +0 -433
  78. package/test-data/decision-tables/basic/clinical-treatment-protocol.json +0 -474
  79. package/test-data/decision-tables/basic/credit-limit-adjustment.json +0 -479
  80. package/test-data/decision-tables/basic/customer-eligibility-engine.json +0 -551
  81. package/test-data/decision-tables/basic/customer-lifetime-value.json +0 -200
  82. package/test-data/decision-tables/basic/customer-onboarding-kyc-verification.json +0 -611
  83. package/test-data/decision-tables/basic/customer-service-escalation.json +0 -191
  84. package/test-data/decision-tables/basic/decision-table-discounts.json +0 -168
  85. package/test-data/decision-tables/basic/decision-table-shipping.json +0 -398
  86. package/test-data/decision-tables/basic/delivery-route-optimizer.json +0 -271
  87. package/test-data/decision-tables/basic/device-compatibility-checker.json +0 -303
  88. package/test-data/decision-tables/basic/disaster-relief-fund-allocation.json +0 -296
  89. package/test-data/decision-tables/basic/dynamic-fx-rate-pricing-system.json +0 -237
  90. package/test-data/decision-tables/basic/dynamic-marketplace-comission-calculator.json +0 -242
  91. package/test-data/decision-tables/basic/dynamic-shipping-cost-calculator.json +0 -378
  92. package/test-data/decision-tables/basic/dynamic-tarrif-engine.json +0 -289
  93. package/test-data/decision-tables/basic/dynamic-ticket-pricing.json +0 -325
  94. package/test-data/decision-tables/basic/empty-column-with-space.json +0 -100
  95. package/test-data/decision-tables/basic/empty-column-without-space.json +0 -100
  96. package/test-data/decision-tables/basic/environment-compliance-assessment.json +0 -386
  97. package/test-data/decision-tables/basic/expression-table-map.json +0 -313
  98. package/test-data/decision-tables/basic/flash-sale-eligibility.json +0 -366
  99. package/test-data/decision-tables/basic/flight-dispatch-decision-system.json +0 -455
  100. package/test-data/decision-tables/basic/flight-rebooking-fee-calculator.json +0 -406
  101. package/test-data/decision-tables/basic/government-assistance.json +0 -299
  102. package/test-data/decision-tables/basic/grant-funding-distribution.json +0 -307
  103. package/test-data/decision-tables/basic/hazardous-materials-management-system.json +0 -414
  104. package/test-data/decision-tables/basic/immigration-eligibility-evaluator.json +0 -765
  105. package/test-data/decision-tables/basic/import-duties-calculator.json +0 -318
  106. package/test-data/decision-tables/basic/insurance-agent-commission.json +0 -228
  107. package/test-data/decision-tables/basic/insurance-coverage-calculator.json +0 -362
  108. package/test-data/decision-tables/basic/insurance-underwriting-risk.json +0 -321
  109. package/test-data/decision-tables/basic/international-roaming-policy-manager.json +0 -199
  110. package/test-data/decision-tables/basic/legacy-plan-management.json +0 -434
  111. package/test-data/decision-tables/basic/marketplace-listing-verification-system.json +0 -334
  112. package/test-data/decision-tables/basic/medication-dosage-calculator.json +0 -318
  113. package/test-data/decision-tables/basic/merch-bags.json +0 -171
  114. package/test-data/decision-tables/basic/municipal-permit-evaluation-system.json +0 -364
  115. package/test-data/decision-tables/basic/mvno-partner-enablement.json +0 -313
  116. package/test-data/decision-tables/basic/partner-revenue-sharing.json +0 -244
  117. package/test-data/decision-tables/basic/payment-routing-and-fee-calculator.json +0 -475
  118. package/test-data/decision-tables/basic/policy-discount-calculator.json +0 -307
  119. package/test-data/decision-tables/basic/policy-eligibility-analyzer.json +0 -299
  120. package/test-data/decision-tables/basic/product-listing-scoring.json +0 -358
  121. package/test-data/decision-tables/basic/realtime-fraud-detection.json +0 -235
  122. package/test-data/decision-tables/basic/regional-compliance-manager.json +0 -278
  123. package/test-data/decision-tables/basic/returns-and-refund-policy.json +0 -366
  124. package/test-data/decision-tables/basic/returns-processing-system.json +0 -448
  125. package/test-data/decision-tables/basic/school-district-resource-allocation.json +0 -282
  126. package/test-data/decision-tables/basic/seat-map-optimization.json +0 -325
  127. package/test-data/decision-tables/basic/seller-fee-calculator.json +0 -307
  128. package/test-data/decision-tables/basic/service-level-agreement-enforcement.json +0 -575
  129. package/test-data/decision-tables/basic/smart-financial-product-matcher.json +0 -249
  130. package/test-data/decision-tables/basic/supply-chain-risk.json +0 -316
  131. package/test-data/decision-tables/basic/table-loop.json +0 -93
  132. package/test-data/decision-tables/basic/table.json +0 -76
  133. package/test-data/decision-tables/basic/traffic-violation-penalty-calculator.json +0 -436
  134. package/test-data/decision-tables/basic/transaction-compliance-classifier.json +0 -525
  135. package/test-data/decision-tables/basic/vehicle-claims-resolution.json +0 -310
  136. package/test-data/decision-tables/basic/warehouse-storage-location.json +0 -345
  137. package/test-data/decision-tables/hit-policy-collect/collect-multiple-matches.json +0 -127
  138. package/test-data/decision-tables/hit-policy-collect/collect-no-match.json +0 -95
  139. package/test-data/decision-tables/hit-policy-first/first-match.json +0 -103
  140. package/test-data/decision-tables/hit-policy-first/no-match.json +0 -95
  141. package/test-data/decision-tables/hit-policy-output-order/output-order-respected.json +0 -94
  142. package/test-data/decision-tables/hit-policy-output-order/string-output-order.json +0 -94
  143. package/test-data/decision-tables/hit-policy-priority/priority-respected.json +0 -86
  144. package/test-data/decision-tables/hit-policy-rule-order/rule-order-respected.json +0 -94
  145. package/test-data/decision-tables/hit-policy-unique/all-match-error.json +0 -89
  146. package/test-data/decision-tables/hit-policy-unique/multiple-match-error.json +0 -89
  147. package/test-data/decision-tables/hit-policy-unique/no-match.json +0 -88
  148. package/test-data/decision-tables/hit-policy-unique/unique-match.json +0 -99
  149. package/test-data/expressions/arithmetic/error-cyclic.json +0 -114
  150. package/test-data/expressions/arithmetic/error-missing-input.json +0 -54
  151. package/test-data/expressions/arithmetic/error-missing-output.json +0 -54
  152. package/test-data/expressions/arithmetic/expression-default.json +0 -93
  153. package/test-data/expressions/arithmetic/expression-fields.json +0 -94
  154. package/test-data/expressions/arithmetic/expression-loop.json +0 -94
  155. package/test-data/expressions/arithmetic/expression-passthrough.json +0 -108
  156. package/test-data/expressions/arithmetic/expression.json +0 -69
  157. package/test-data/expressions/arithmetic/nested-request.json +0 -125
  158. package/test-data/expressions/arithmetic/number-function.json +0 -58
  159. package/test-data/expressions/arithmetic/test-number-functions.json +0 -68
  160. package/test-data/expressions/functions/all.json +0 -149
  161. package/test-data/expressions/functions/avg.json +0 -89
  162. package/test-data/expressions/functions/filter.json +0 -109
  163. package/test-data/expressions/functions/flat.json +0 -167
  164. package/test-data/expressions/functions/map-strings.json +0 -65
  165. package/test-data/expressions/functions/map.json +0 -73
  166. package/test-data/expressions/functions/reduce.json +0 -49
  167. package/test-data/expressions/functions/some.json +0 -175
  168. package/test-data/expressions/functions/sort-strings.json +0 -97
  169. package/test-data/expressions/functions/sort.json +0 -97
  170. package/test-data/expressions/logical/logical-and.json +0 -116
  171. package/test-data/expressions/logical/logical-complex.json +0 -260
  172. package/test-data/expressions/logical/logical-not.json +0 -111
  173. package/test-data/expressions/logical/logical-or.json +0 -123
  174. package/test-data/expressions/string/string-comparison.json +0 -128
  175. package/test-data/expressions/string/string-concat.json +0 -106
  176. package/test-data/expressions/string/string-contains.json +0 -125
  177. package/test-data/expressions/string/string-endsWith.json +0 -113
  178. package/test-data/expressions/string/string-indexOf.json +0 -131
  179. package/test-data/expressions/string/string-join.json +0 -92
  180. package/test-data/expressions/string/string-lower.json +0 -94
  181. package/test-data/expressions/string/string-replace.json +0 -130
  182. package/test-data/expressions/string/string-split.json +0 -101
  183. package/test-data/expressions/string/string-startsWith.json +0 -113
  184. package/test-data/expressions/string/string-substring.json +0 -138
  185. package/test-data/expressions/string/string-trim.json +0 -100
  186. package/test-data/expressions/string/string-upper.json +0 -94
  187. package/test-data/other/custom.json +0 -51
  188. package/test-data/other/customer-input-schema.json +0 -34
  189. package/test-data/other/customer-output-schema.json +0 -34
  190. package/test-data/other/passthrough.json +0 -31
  191. package/test-data/sub-decisions/basic/$nodes-child.json +0 -31
  192. package/test-data/sub-decisions/basic/$nodes-parent.json +0 -49
  193. package/test-data/sub-decisions/basic/recursive-table1.json +0 -49
  194. package/test-data/sub-decisions/basic/recursive-table2.json +0 -49
  195. package/test-data/sub-decisions/complex-multi/approval-decision.json +0 -31
  196. package/test-data/sub-decisions/complex-multi/complex-dag.json +0 -175
  197. package/test-data/sub-decisions/complex-multi/credit-check.json +0 -31
  198. package/test-data/sub-decisions/complex-multi/customer-segmentation.json +0 -31
  199. package/test-data/sub-decisions/complex-multi/discount-eligibility.json +0 -31
  200. package/test-data/sub-decisions/complex-multi/eligibility-check.json +0 -31
  201. package/test-data/sub-decisions/complex-multi/final-offer.json +0 -31
  202. package/test-data/sub-decisions/complex-multi/income-verification.json +0 -31
  203. package/test-data/sub-decisions/complex-multi/linear-chain.json +0 -121
  204. package/test-data/sub-decisions/complex-multi/pricing-calculation.json +0 -31
  205. package/test-data/sub-decisions/complex-multi/product-eligibility.json +0 -31
  206. package/test-data/sub-decisions/complex-multi/risk-assessment.json +0 -31
  207. package/test-data/sub-decisions/complex-multi/shared-validation.json +0 -31
  208. package/test-data/sub-decisions/complex-multi/validation.json +0 -31
  209. package/test-data/sub-decisions/diamond/decision-a.json +0 -31
  210. package/test-data/sub-decisions/diamond/decision-b.json +0 -31
  211. package/test-data/sub-decisions/diamond/decision-c.json +0 -31
  212. package/test-data/sub-decisions/diamond/decision-shared.json +0 -31
  213. package/test-data/sub-decisions/diamond/diamond-pattern.json +0 -109
  214. package/test-data/sub-decisions/error-propagation/parent-calls-error.json +0 -44
  215. package/test-data/sub-decisions/error-propagation/sub-decision-with-error.json +0 -60
  216. package/test-data/switch-nodes/basic/account-dormancy-management.json +0 -245
  217. package/test-data/switch-nodes/basic/application-risk-assessment.json +0 -474
  218. package/test-data/switch-nodes/basic/cellular-data-rollover-system.json +0 -281
  219. package/test-data/switch-nodes/basic/clinical-pathway-selection.json +0 -454
  220. package/test-data/switch-nodes/basic/insurance-prior-authorization.json +0 -467
  221. package/test-data/switch-nodes/basic/last-mile-delivery-assignment.json +0 -373
  222. package/test-data/switch-nodes/basic/loan-approval.json +0 -469
  223. package/test-data/switch-nodes/basic/multi-switch.json +0 -498
  224. package/test-data/switch-nodes/basic/online-checkin-eligibility.json +0 -285
  225. package/test-data/switch-nodes/basic/order-consolidation-system.json +0 -493
  226. package/test-data/switch-nodes/basic/seller-approval-workflow.json +0 -383
  227. package/test-data/switch-nodes/basic/set-fee.json +0 -243
  228. package/test-data/switch-nodes/basic/shipping-carrier-selector.json +0 -379
  229. package/test-data/switch-nodes/basic/switch-node.json +0 -167
  230. package/test-data/switch-nodes/basic/switch-performance-2.json +0 -1307
  231. package/test-data/switch-nodes/basic/switch-performance.json +0 -691
  232. package/test-data/switch-nodes/basic/tax-exemption.json +0 -295
  233. package/test-data/switch-nodes/basic/warehouse-cross-docking.json +0 -313
  234. package/test-data/switch-nodes/default-cases/switch-with-default.json +0 -134
  235. package/test-data/zen-reference/$nodes-child.json +0 -69
  236. package/test-data/zen-reference/$nodes-parent.json +0 -34
  237. package/test-data/zen-reference/8k.json +0 -87992
  238. package/test-data/zen-reference/credit-analysis.json +0 -324
  239. package/test-data/zen-reference/custom.json +0 -51
  240. package/test-data/zen-reference/customer-input-schema.json +0 -34
  241. package/test-data/zen-reference/customer-output-schema.json +0 -34
  242. package/test-data/zen-reference/error-cyclic.json +0 -114
  243. package/test-data/zen-reference/error-missing-input.json +0 -54
  244. package/test-data/zen-reference/error-missing-output.json +0 -54
  245. package/test-data/zen-reference/expression.json +0 -69
  246. package/test-data/zen-reference/function-v2.json +0 -48
  247. package/test-data/zen-reference/function.json +0 -46
  248. package/test-data/zen-reference/graphs/account-dormancy-management.json +0 -245
  249. package/test-data/zen-reference/graphs/affiliate-commission-calculator.json +0 -228
  250. package/test-data/zen-reference/graphs/airline-loyalty-points-calculations.json +0 -285
  251. package/test-data/zen-reference/graphs/airline-upgrade-eligibility.json +0 -466
  252. package/test-data/zen-reference/graphs/aml.json +0 -537
  253. package/test-data/zen-reference/graphs/application-risk-assessment.json +0 -474
  254. package/test-data/zen-reference/graphs/auto-insurance-premium-calculator.json +0 -412
  255. package/test-data/zen-reference/graphs/booking-personalization-system.json +0 -553
  256. package/test-data/zen-reference/graphs/care-team-assignment-system.json +0 -585
  257. package/test-data/zen-reference/graphs/cellular-data-rollover-system.json +0 -281
  258. package/test-data/zen-reference/graphs/claim-validation-system.json +0 -307
  259. package/test-data/zen-reference/graphs/clinical-lab-result-interpreter.json +0 -433
  260. package/test-data/zen-reference/graphs/clinical-pathway-selection.json +0 -454
  261. package/test-data/zen-reference/graphs/clinical-treatment-protocol.json +0 -474
  262. package/test-data/zen-reference/graphs/company-analysis.json +0 -390
  263. package/test-data/zen-reference/graphs/credit-limit-adjustment.json +0 -479
  264. package/test-data/zen-reference/graphs/customer-eligibility-engine.json +0 -551
  265. package/test-data/zen-reference/graphs/customer-lifetime-value.json +0 -200
  266. package/test-data/zen-reference/graphs/customer-onboarding-kyc-verification.json +0 -611
  267. package/test-data/zen-reference/graphs/customer-service-escalation.json +0 -191
  268. package/test-data/zen-reference/graphs/decision-table-discounts.json +0 -168
  269. package/test-data/zen-reference/graphs/decision-table-shipping.json +0 -398
  270. package/test-data/zen-reference/graphs/delivery-route-optimizer.json +0 -271
  271. package/test-data/zen-reference/graphs/device-compatibility-checker.json +0 -303
  272. package/test-data/zen-reference/graphs/disaster-relief-fund-allocation.json +0 -296
  273. package/test-data/zen-reference/graphs/dynamic-fx-rate-pricing-system.json +0 -237
  274. package/test-data/zen-reference/graphs/dynamic-marketplace-comission-calculator.json +0 -242
  275. package/test-data/zen-reference/graphs/dynamic-shipping-cost-calculator.json +0 -378
  276. package/test-data/zen-reference/graphs/dynamic-tarrif-engine.json +0 -289
  277. package/test-data/zen-reference/graphs/dynamic-ticket-pricing.json +0 -325
  278. package/test-data/zen-reference/graphs/empty-column-with-space.json +0 -100
  279. package/test-data/zen-reference/graphs/empty-column-without-space.json +0 -100
  280. package/test-data/zen-reference/graphs/environment-compliance-assessment.json +0 -386
  281. package/test-data/zen-reference/graphs/expression-default.json +0 -93
  282. package/test-data/zen-reference/graphs/expression-fields.json +0 -94
  283. package/test-data/zen-reference/graphs/expression-loop.json +0 -94
  284. package/test-data/zen-reference/graphs/expression-passthrough.json +0 -108
  285. package/test-data/zen-reference/graphs/expression-table-map.json +0 -313
  286. package/test-data/zen-reference/graphs/flash-sale-eligibility.json +0 -366
  287. package/test-data/zen-reference/graphs/flight-dispatch-decision-system.json +0 -455
  288. package/test-data/zen-reference/graphs/flight-rebooking-fee-calculator.json +0 -406
  289. package/test-data/zen-reference/graphs/government-assistance.json +0 -299
  290. package/test-data/zen-reference/graphs/grant-funding-distribution.json +0 -307
  291. package/test-data/zen-reference/graphs/hazardous-materials-management-system.json +0 -414
  292. package/test-data/zen-reference/graphs/immigration-eligibility-evaluator.json +0 -765
  293. package/test-data/zen-reference/graphs/import-duties-calculator.json +0 -318
  294. package/test-data/zen-reference/graphs/insurance-agent-commission.json +0 -228
  295. package/test-data/zen-reference/graphs/insurance-breakdown.json +0 -421
  296. package/test-data/zen-reference/graphs/insurance-coverage-calculator.json +0 -362
  297. package/test-data/zen-reference/graphs/insurance-prior-authorization.json +0 -467
  298. package/test-data/zen-reference/graphs/insurance-underwriting-risk.json +0 -321
  299. package/test-data/zen-reference/graphs/international-roaming-policy-manager.json +0 -199
  300. package/test-data/zen-reference/graphs/last-mile-delivery-assignment.json +0 -373
  301. package/test-data/zen-reference/graphs/legacy-plan-management.json +0 -434
  302. package/test-data/zen-reference/graphs/loan-approval.json +0 -469
  303. package/test-data/zen-reference/graphs/marketplace-listing-verification-system.json +0 -334
  304. package/test-data/zen-reference/graphs/medication-dosage-calculator.json +0 -318
  305. package/test-data/zen-reference/graphs/merch-bags.json +0 -171
  306. package/test-data/zen-reference/graphs/multi-switch.json +0 -498
  307. package/test-data/zen-reference/graphs/municipal-permit-evaluation-system.json +0 -364
  308. package/test-data/zen-reference/graphs/mvno-partner-enablement.json +0 -313
  309. package/test-data/zen-reference/graphs/nested-request.json +0 -125
  310. package/test-data/zen-reference/graphs/online-checkin-eligibility.json +0 -285
  311. package/test-data/zen-reference/graphs/order-consolidation-system.json +0 -493
  312. package/test-data/zen-reference/graphs/partner-revenue-sharing.json +0 -244
  313. package/test-data/zen-reference/graphs/payment-routing-and-fee-calculator.json +0 -475
  314. package/test-data/zen-reference/graphs/policy-discount-calculator.json +0 -307
  315. package/test-data/zen-reference/graphs/policy-eligibility-analyzer.json +0 -299
  316. package/test-data/zen-reference/graphs/product-listing-scoring.json +0 -358
  317. package/test-data/zen-reference/graphs/realtime-fraud-detection.json +0 -235
  318. package/test-data/zen-reference/graphs/regional-compliance-manager.json +0 -278
  319. package/test-data/zen-reference/graphs/returns-and-refund-policy.json +0 -366
  320. package/test-data/zen-reference/graphs/returns-processing-system.json +0 -448
  321. package/test-data/zen-reference/graphs/school-district-resource-allocation.json +0 -282
  322. package/test-data/zen-reference/graphs/seat-map-optimization.json +0 -325
  323. package/test-data/zen-reference/graphs/seller-approval-workflow.json +0 -383
  324. package/test-data/zen-reference/graphs/seller-fee-calculator.json +0 -307
  325. package/test-data/zen-reference/graphs/service-level-agreement-enforcement.json +0 -575
  326. package/test-data/zen-reference/graphs/set-fee.json +0 -243
  327. package/test-data/zen-reference/graphs/shipping-carrier-selector.json +0 -379
  328. package/test-data/zen-reference/graphs/smart-financial-product-matcher.json +0 -249
  329. package/test-data/zen-reference/graphs/supply-chain-risk.json +0 -316
  330. package/test-data/zen-reference/graphs/table-loop.json +0 -93
  331. package/test-data/zen-reference/graphs/tax-exemption.json +0 -295
  332. package/test-data/zen-reference/graphs/traffic-violation-penalty-calculator.json +0 -436
  333. package/test-data/zen-reference/graphs/transaction-compliance-classifier.json +0 -525
  334. package/test-data/zen-reference/graphs/vehicle-claims-resolution.json +0 -310
  335. package/test-data/zen-reference/graphs/warehouse-cross-docking.json +0 -313
  336. package/test-data/zen-reference/graphs/warehouse-storage-location.json +0 -345
  337. package/test-data/zen-reference/http-function.json +0 -34
  338. package/test-data/zen-reference/infinite-function.json +0 -46
  339. package/test-data/zen-reference/js/imports.js +0 -25
  340. package/test-data/zen-reference/passthrough.json +0 -31
  341. package/test-data/zen-reference/recursive-table1.json +0 -49
  342. package/test-data/zen-reference/recursive-table2.json +0 -49
  343. package/test-data/zen-reference/sleep-function.json +0 -34
  344. package/test-data/zen-reference/switch-node.json +0 -167
  345. package/test-data/zen-reference/switch-performance-2.json +0 -1307
  346. package/test-data/zen-reference/switch-performance.json +0 -691
  347. package/test-data/zen-reference/table.json +0 -76
  348. package/tests/helpers/index.ts +0 -73
  349. package/tests/helpers/mock-context.ts +0 -231
  350. package/tests/helpers/round-trip.ts +0 -398
  351. package/tests/helpers/test-harness-comparison.ts +0 -325
  352. package/tests/helpers/test-harness-wasm.ts +0 -710
  353. package/tests/helpers/test-harness.ts +0 -28
  354. package/tests/helpers/wasm-test.ts +0 -659
  355. package/tests/integration/compilation-errors.test.ts +0 -864
  356. package/tests/integration/decision-tables.test.ts +0 -531
  357. package/tests/integration/edge-cases.test.ts +0 -787
  358. package/tests/integration/expressions.test.ts +0 -513
  359. package/tests/integration/function-node-integration.test.ts +0 -182
  360. package/tests/integration/sub-decisions.test.ts +0 -108
  361. package/tests/integration/switch-nodes.test.ts +0 -399
  362. package/tests/integration/unary-or-matching.test.ts +0 -53
  363. package/tests/integration/wasm-data-types.test.ts +0 -398
  364. package/tests/integration/wasm-errors.test.ts +0 -199
  365. package/tests/integration/wasm-execution.test.ts +0 -348
  366. package/tests/integration/wasm-memory.test.ts +0 -228
  367. package/tests/scripts/analyze-coverage.ts +0 -166
  368. package/tests/scripts/categorize-tests.ts +0 -396
  369. package/tests/scripts/coverage-analysis.ts +0 -836
  370. package/tests/unit/compiler/cache.test.ts +0 -238
  371. package/tests/unit/compiler/errors.test.ts +0 -316
  372. package/tests/unit/compiler/graph-scalability.test.ts +0 -510
  373. package/tests/unit/compiler/graph.test.ts +0 -878
  374. package/tests/unit/compiler/input-validation.test.ts +0 -447
  375. package/tests/unit/compiler/logical-and-parser.test.ts +0 -143
  376. package/tests/unit/compiler/logical-not-parser.test.ts +0 -107
  377. package/tests/unit/compiler/logical-or-parser.test.ts +0 -236
  378. package/tests/unit/compiler/marshal-gen/marshal-gen.test.ts +0 -97
  379. package/tests/unit/compiler/nodes/decision-table.test.ts +0 -103
  380. package/tests/unit/compiler/nodes/decision.test.ts +0 -182
  381. package/tests/unit/compiler/nodes/function-compile.test.ts +0 -204
  382. package/tests/unit/compiler/nodes/function.test.ts +0 -176
  383. package/tests/unit/compiler/nodes/input.test.ts +0 -30
  384. package/tests/unit/compiler/nodes/switch.test.ts +0 -127
  385. package/tests/unit/compiler/optimizer-cache.test.ts +0 -327
  386. package/tests/unit/compiler/optimizer-implementation.test.ts +0 -625
  387. package/tests/unit/compiler/parser.test.ts +0 -508
  388. package/tests/unit/compiler/runtime-error-cleanup.test.ts +0 -426
  389. package/tests/unit/compiler/runtime-validation.test.ts +0 -303
  390. package/tests/unit/compiler/runtime.test.ts +0 -221
  391. package/tests/unit/compiler/schema/schema.test.ts +0 -248
  392. package/tests/unit/compiler/unary-ast-transforms.test.ts +0 -245
  393. package/tsconfig.json +0 -27
  394. package/tsup.config.ts +0 -11
  395. package/vitest.config.ts +0 -12
package/dist/index.js CHANGED
@@ -8490,41 +8490,2239 @@ import { createHash } from "crypto";
8490
8490
  import {
8491
8491
  existsSync,
8492
8492
  mkdirSync,
8493
- readFileSync as readFileSync2,
8493
+ readFileSync,
8494
8494
  writeFileSync,
8495
8495
  readdirSync,
8496
8496
  statSync,
8497
8497
  unlinkSync
8498
8498
  } from "fs";
8499
- import { join, dirname as dirname2 } from "path";
8500
-
8501
- // src/compiler/runtime/index.ts
8502
- import { readFileSync } from "fs";
8503
- import { resolve, dirname } from "path";
8504
- import { fileURLToPath } from "url";
8505
- var __dirname2 = dirname(fileURLToPath(import.meta.url));
8506
- function getAssemblyRuntimePath() {
8507
- return resolve(__dirname2, "..", "..", "runtime");
8508
- }
8509
- function loadRuntime(filename) {
8510
- const runtimePath = getAssemblyRuntimePath();
8511
- return readFileSync(resolve(runtimePath, filename), "utf-8");
8512
- }
8513
- var RUNTIME_VALUES = loadRuntime("values.ts");
8514
- var RUNTIME_CONTEXT = loadRuntime("context.ts");
8515
- var RUNTIME_EXPRESSIONS = loadRuntime("expressions.ts");
8516
- var RUNTIME_TABLES = loadRuntime("tables.ts");
8517
- var RUNTIME_MEMORY = loadRuntime("memory.ts");
8518
- var RUNTIME_STRINGS = loadRuntime("strings.ts");
8519
- var RUNTIME_ARRAYS = loadRuntime("arrays.ts");
8499
+ import { join, dirname } from "path";
8500
+
8501
+ // inline-runtime:inline-runtime
8502
+ var RUNTIME_VALUES = `// assembly/runtime/values.ts
8503
+ // Type tags for runtime type checking
8504
+ export const TYPE_NULL: i32 = 0;
8505
+ export const TYPE_BOOL: i32 = 1;
8506
+ export const TYPE_INT: i32 = 2;
8507
+ export const TYPE_FLOAT: i32 = 3;
8508
+ export const TYPE_STRING: i32 = 4;
8509
+ export const TYPE_ARRAY: i32 = 5;
8510
+ export const TYPE_OBJECT: i32 = 6;
8511
+
8512
+ // Pre-allocated singleton values for frequently used immutable types.
8513
+ // This avoids allocations in hot paths for null/boolean/small integer/common float values.
8514
+ let _nullSingleton: Value | null = null;
8515
+ let _trueSingleton: Value | null = null;
8516
+ let _falseSingleton: Value | null = null;
8517
+ let _emptyStringSingleton: Value | null = null;
8518
+ let _zeroFloatSingleton: Value | null = null;
8519
+ let _oneFloatSingleton: Value | null = null;
8520
+
8521
+ // Cache for small integers (-1 to 10) - covers common array indices, loop counters, etc.
8522
+ // Using 12-element array: index 0 = -1, index 1 = 0, ..., index 11 = 10
8523
+ const SMALL_INT_CACHE_MIN: i64 = -1;
8524
+ const SMALL_INT_CACHE_MAX: i64 = 10;
8525
+ const SMALL_INT_CACHE_SIZE: i32 = 12;
8526
+ let _smallIntCache: StaticArray<Value | null> | null = null;
8527
+
8528
+ /**
8529
+ * Tagged union for dynamic JSON-like values.
8530
+ * Only one field is valid based on the \`type\` tag.
8531
+ */
8532
+ export class Value {
8533
+ type: i32 = TYPE_NULL;
8534
+ boolVal: bool = false;
8535
+ intVal: i64 = 0;
8536
+ floatVal: f64 = 0.0;
8537
+ stringVal: string | null = null;
8538
+ arrayVal: Array<Value> | null = null;
8539
+ objectVal: Map<string, Value> | null = null;
8540
+
8541
+ static Null(): Value {
8542
+ // Return singleton null value to avoid allocations
8543
+ if (_nullSingleton === null) {
8544
+ const v = new Value();
8545
+ v.type = TYPE_NULL;
8546
+ _nullSingleton = v;
8547
+ }
8548
+ return _nullSingleton!;
8549
+ }
8550
+
8551
+ static Bool(val: bool): Value {
8552
+ // Return singleton boolean values to avoid allocations
8553
+ if (val) {
8554
+ if (_trueSingleton === null) {
8555
+ const v = new Value();
8556
+ v.type = TYPE_BOOL;
8557
+ v.boolVal = true;
8558
+ _trueSingleton = v;
8559
+ }
8560
+ return _trueSingleton!;
8561
+ } else {
8562
+ if (_falseSingleton === null) {
8563
+ const v = new Value();
8564
+ v.type = TYPE_BOOL;
8565
+ v.boolVal = false;
8566
+ _falseSingleton = v;
8567
+ }
8568
+ return _falseSingleton!;
8569
+ }
8570
+ }
8571
+
8572
+ static Int(val: i64): Value {
8573
+ // Fast path for common small integers to avoid allocations
8574
+ if (val >= SMALL_INT_CACHE_MIN && val <= SMALL_INT_CACHE_MAX) {
8575
+ if (_smallIntCache === null) {
8576
+ _smallIntCache = new StaticArray<Value | null>(SMALL_INT_CACHE_SIZE);
8577
+ }
8578
+ const idx = <i32>(val - SMALL_INT_CACHE_MIN);
8579
+ if (unchecked(_smallIntCache![idx]) === null) {
8580
+ const v = new Value();
8581
+ v.type = TYPE_INT;
8582
+ v.intVal = val;
8583
+ unchecked((_smallIntCache![idx] = v));
8584
+ }
8585
+ return unchecked(_smallIntCache![idx])!;
8586
+ }
8587
+ const v = new Value();
8588
+ v.type = TYPE_INT;
8589
+ v.intVal = val;
8590
+ return v;
8591
+ }
8592
+
8593
+ static Float(val: f64): Value {
8594
+ // Fast path for common float values (0.0, 1.0) to avoid allocations
8595
+ if (val == 0.0) {
8596
+ if (_zeroFloatSingleton === null) {
8597
+ const v = new Value();
8598
+ v.type = TYPE_FLOAT;
8599
+ v.floatVal = 0.0;
8600
+ _zeroFloatSingleton = v;
8601
+ }
8602
+ return _zeroFloatSingleton!;
8603
+ }
8604
+ if (val == 1.0) {
8605
+ if (_oneFloatSingleton === null) {
8606
+ const v = new Value();
8607
+ v.type = TYPE_FLOAT;
8608
+ v.floatVal = 1.0;
8609
+ _oneFloatSingleton = v;
8610
+ }
8611
+ return _oneFloatSingleton!;
8612
+ }
8613
+ const v = new Value();
8614
+ v.type = TYPE_FLOAT;
8615
+ v.floatVal = val;
8616
+ return v;
8617
+ }
8618
+
8619
+ static String(val: string): Value {
8620
+ // Fast path for empty string to avoid allocations
8621
+ if (val.length == 0) {
8622
+ if (_emptyStringSingleton === null) {
8623
+ const v = new Value();
8624
+ v.type = TYPE_STRING;
8625
+ v.stringVal = '';
8626
+ _emptyStringSingleton = v;
8627
+ }
8628
+ return _emptyStringSingleton!;
8629
+ }
8630
+ const v = new Value();
8631
+ v.type = TYPE_STRING;
8632
+ v.stringVal = val;
8633
+ return v;
8634
+ }
8635
+
8636
+ static Array(val: Array<Value>): Value {
8637
+ const v = new Value();
8638
+ v.type = TYPE_ARRAY;
8639
+ v.arrayVal = val;
8640
+ return v;
8641
+ }
8642
+
8643
+ static Object(val: Map<string, Value>): Value {
8644
+ const v = new Value();
8645
+ v.type = TYPE_OBJECT;
8646
+ v.objectVal = val;
8647
+ return v;
8648
+ }
8649
+
8650
+ // Type checking
8651
+ isNull(): bool {
8652
+ return this.type == TYPE_NULL;
8653
+ }
8654
+ isBool(): bool {
8655
+ return this.type == TYPE_BOOL;
8656
+ }
8657
+ isNumber(): bool {
8658
+ return this.type == TYPE_INT || this.type == TYPE_FLOAT;
8659
+ }
8660
+ isString(): bool {
8661
+ return this.type == TYPE_STRING;
8662
+ }
8663
+ isArray(): bool {
8664
+ return this.type == TYPE_ARRAY;
8665
+ }
8666
+ isObject(): bool {
8667
+ return this.type == TYPE_OBJECT;
8668
+ }
8669
+
8670
+ // Returning defaults (0, false, "") instead of throwing errors provides more
8671
+ // forgiving evaluation semantics matching JavaScript's type coercion behavior.
8672
+ asBool(): bool {
8673
+ return this.type == TYPE_BOOL ? this.boolVal : false;
8674
+ }
8675
+ asInt(): i64 {
8676
+ return this.type == TYPE_INT ? this.intVal : 0;
8677
+ }
8678
+ asFloat(): f64 {
8679
+ if (this.type == TYPE_FLOAT) return this.floatVal;
8680
+ if (this.type == TYPE_INT) return <f64>this.intVal;
8681
+ return 0.0;
8682
+ }
8683
+ asString(): string {
8684
+ // Convert value to string representation
8685
+ if (this.type == TYPE_STRING && this.stringVal != null) return this.stringVal!;
8686
+ if (this.type == TYPE_NULL) return '';
8687
+ if (this.type == TYPE_BOOL) return this.boolVal ? 'true' : 'false';
8688
+ if (this.type == TYPE_INT) return this.intVal.toString();
8689
+ if (this.type == TYPE_FLOAT) {
8690
+ // JavaScript developers expect "3" not "3.0" for integer-valued floats. We check if
8691
+ // the float is exactly representable as i64 (no fractional part, within range) and
8692
+ // format as integer in that case. Otherwise use AS's default float formatting.
8693
+ const f = this.floatVal;
8694
+ // Check if it's an integer
8695
+ if (f == <f64>(<i64>f) && f >= <f64>i64.MIN_VALUE && f <= <f64>i64.MAX_VALUE) {
8696
+ return (<i64>f).toString();
8697
+ }
8698
+ return f.toString();
8699
+ }
8700
+ return '';
8701
+ }
8702
+ asArray(): Array<Value> {
8703
+ return this.arrayVal != null ? this.arrayVal! : new Array<Value>();
8704
+ }
8705
+ asObject(): Map<string, Value> {
8706
+ return this.objectVal != null ? this.objectVal! : new Map<string, Value>();
8707
+ }
8708
+ }
8709
+
8710
+ export type Result = Map<string, Value>;
8711
+ `;
8712
+ var RUNTIME_CONTEXT = `// assembly/runtime/context.ts (AssemblyScript)
8713
+ import { Value, TYPE_OBJECT } from './values';
8714
+
8715
+ /**
8716
+ * Stores cached path resolution metadata to avoid redundant string splitting
8717
+ * and prefix matching on frequently-accessed paths.
8718
+ */
8719
+ class PathResolutionCache {
8720
+ // Cache entry: stores the matched prefix key, its length, and the split parts for a given path
8721
+ prefixKey: string;
8722
+ prefixLen: i32;
8723
+ parts: string[];
8724
+
8725
+ constructor(prefixKey: string, prefixLen: i32, parts: string[]) {
8726
+ this.prefixKey = prefixKey;
8727
+ this.prefixLen = prefixLen;
8728
+ this.parts = parts;
8729
+ }
8730
+ }
8731
+
8732
+ /**
8733
+ * Resolves a potentially dotted path against a data map with caching support.
8734
+ *
8735
+ * Keys like "$nodes.expression2" are stored as literal strings with dots in them,
8736
+ * not as nested objects. This function handles both cases:
8737
+ * 1. Exact key match (e.g., "$nodes.expression2")
8738
+ * 2. Nested path (e.g., "$nodes.expression2.L1" where "$nodes.expression2" is a key
8739
+ * and "L1" is a property inside the object stored at that key)
8740
+ *
8741
+ * The algorithm finds the longest matching prefix in the data map, then navigates
8742
+ * any remaining path segments through nested object properties.
8743
+ *
8744
+ * Performance: Uses an optional cache to avoid redundant path splitting and prefix
8745
+ * matching for frequently-accessed paths within a single evaluation.
8746
+ */
8747
+ export function resolvePathInData(
8748
+ data: Map<string, Value>,
8749
+ path: string,
8750
+ cache: Map<string, PathResolutionCache> | null = null,
8751
+ ): Value {
8752
+ // Check for exact key match first
8753
+ if (data.has(path)) {
8754
+ return data.get(path);
8755
+ }
8756
+
8757
+ // If no dot, the key doesn't exist
8758
+ if (!path.includes('.')) {
8759
+ return Value.Null();
8760
+ }
8761
+
8762
+ // Check cache if available
8763
+ let matchedPrefixLen: i32 = 0;
8764
+ let prefixKey: string = '';
8765
+ let parts: string[];
8766
+
8767
+ if (cache !== null && cache.has(path)) {
8768
+ // Cache hit - use cached resolution metadata AND cached parts array
8769
+ const cached = cache.get(path);
8770
+ prefixKey = cached.prefixKey;
8771
+ matchedPrefixLen = cached.prefixLen;
8772
+ parts = cached.parts;
8773
+ } else {
8774
+ // Cache miss - perform full path resolution
8775
+ parts = path.split('.');
8776
+
8777
+ // Try progressively longer prefixes to find a matching key
8778
+ // e.g., for "$nodes.expression2.L1", try:
8779
+ // 1. "$nodes" (no match)
8780
+ // 2. "$nodes.expression2" (match! then access .L1 inside)
8781
+ let currentPrefix = '';
8782
+ for (let i = 0; i < parts.length; i++) {
8783
+ if (i > 0) currentPrefix += '.';
8784
+ currentPrefix += parts[i];
8785
+ if (data.has(currentPrefix)) {
8786
+ matchedPrefixLen = i + 1;
8787
+ prefixKey = currentPrefix;
8788
+ }
8789
+ }
8790
+
8791
+ // Store in cache if available (including the parts array to avoid future splits)
8792
+ if (cache !== null) {
8793
+ cache.set(path, new PathResolutionCache(prefixKey, matchedPrefixLen, parts));
8794
+ }
8795
+ }
8796
+
8797
+ // If no prefix matched, path doesn't exist
8798
+ if (matchedPrefixLen == 0) {
8799
+ return Value.Null();
8800
+ }
8801
+
8802
+ // Get the value at the matched prefix
8803
+ let current = data.get(prefixKey);
8804
+
8805
+ // Navigate remaining parts as object properties
8806
+ for (let i = matchedPrefixLen; i < parts.length; i++) {
8807
+ if (!current.isObject()) return Value.Null();
8808
+ const obj = current.asObject();
8809
+ const key = parts[i];
8810
+ if (!obj.has(key)) return Value.Null();
8811
+ current = obj.get(key);
8812
+ }
8813
+ return current;
8814
+ }
8815
+
8816
+ export class Context {
8817
+ data: Map<string, Value>;
8818
+ // Cache for decision node results to avoid redundant evaluation. Caching prevents
8819
+ // exponential evaluation time for DAG structures where multiple nodes reference
8820
+ // the same upstream computation.
8821
+ decisionResults: Map<string, Value>;
8822
+ // Stack to track currently evaluating decisions for recursion detection
8823
+ decisionStack: string[];
8824
+ // When merging results, we need to know which keys are original inputs (should be
8825
+ // preserved) vs computed outputs (may be overwritten). This prevents input shadowing.
8826
+ inputKeys: Set<string>;
8827
+ // Cache for path resolution to avoid redundant string splitting and prefix matching.
8828
+ // For decisions with many field accesses (e.g., 100-rule decision table with 5 inputs
8829
+ // per rule = 500 path lookups), this eliminates most splitting overhead.
8830
+ pathCache: Map<string, PathResolutionCache>;
8831
+
8832
+ constructor(input: Map<string, Value>) {
8833
+ this.data = input;
8834
+ this.decisionResults = new Map<string, Value>();
8835
+ this.decisionStack = new Array<string>();
8836
+ this.pathCache = new Map<string, PathResolutionCache>();
8837
+ // Store original input keys
8838
+ this.inputKeys = new Set<string>();
8839
+ const keys = input.keys();
8840
+ for (let i = 0; i < keys.length; i++) {
8841
+ this.inputKeys.add(keys[i]);
8842
+ }
8843
+ }
8844
+
8845
+ // Unlike JS Map which returns undefined for missing keys, AS Map.get() throws.
8846
+ // We must check has() first or handle dots carefully to avoid runtime errors.
8847
+ get(path: string): Value {
8848
+ return resolvePathInData(this.data, path, this.pathCache);
8849
+ }
8850
+
8851
+ set(path: string, value: Value): void {
8852
+ this.data.set(path, value);
8853
+ }
8854
+
8855
+ // Merge another result into this context (for passthrough mode)
8856
+ merge(other: Map<string, Value>): void {
8857
+ const keys = other.keys();
8858
+ for (let i = 0; i < keys.length; i++) {
8859
+ const key = keys[i];
8860
+ this.data.set(key, other.get(key));
8861
+ }
8862
+ }
8863
+
8864
+ // Convert the context data to a Value (for passthrough sub-decisions)
8865
+ toValue(): Value {
8866
+ return Value.Object(this.data);
8867
+ }
8868
+ }
8869
+
8870
+ /**
8871
+ * ScopedContext is a lightweight context wrapper for scoped expression evaluation.
8872
+ * Scoped evaluation (loops, sub-object navigation) needs simple Map-based get/set
8873
+ * without decision caching/recursion tracking. Separate class avoids polluting
8874
+ * Context with unnecessary state and provides clearer semantics.
8875
+ *
8876
+ * Used for:
8877
+ * - inputField scoping (expressions evaluated within a sub-object scope)
8878
+ * - executionMode: "loop" (expressions evaluated for each array item)
8879
+ *
8880
+ * Performance: Includes path caching for loop-mode expressions where the same paths
8881
+ * are accessed repeatedly across array items.
8882
+ */
8883
+ export class ScopedContext {
8884
+ data: Map<string, Value>;
8885
+ // Path resolution cache for frequently-accessed paths in loops
8886
+ pathCache: Map<string, PathResolutionCache>;
8887
+
8888
+ constructor(data: Map<string, Value>) {
8889
+ this.data = data;
8890
+ this.pathCache = new Map<string, PathResolutionCache>();
8891
+ }
8892
+
8893
+ get(path: string): Value {
8894
+ return resolvePathInData(this.data, path, this.pathCache);
8895
+ }
8896
+
8897
+ set(path: string, value: Value): void {
8898
+ this.data.set(path, value);
8899
+ }
8900
+ }
8901
+ `;
8902
+ var RUNTIME_EXPRESSIONS = `// assembly/runtime/expressions.ts (AssemblyScript)
8903
+ import { Value, TYPE_ARRAY, TYPE_STRING, TYPE_FLOAT, TYPE_INT, TYPE_BOOL } from './values';
8904
+
8905
+ /**
8906
+ * Helper function to create an argument array.
8907
+ * AssemblyScript doesn't support true variadic functions or rest parameters, so we provide
8908
+ * fixed-arity helpers (makeArgs1, makeArgs2, makeArgs3) for common function arities.
8909
+ *
8910
+ * These use thread-local reusable arrays to avoid allocations in hot paths.
8911
+ * Since WASM evaluation is single-threaded, reusing arrays is safe.
8912
+ */
8913
+
8914
+ // Reusable argument arrays - lazily initialized
8915
+ let _args1: Array<Value> | null = null;
8916
+ let _args2: Array<Value> | null = null;
8917
+ let _args3: Array<Value> | null = null;
8918
+
8919
+ export function makeArgs(a: Value, b: Value, c: Value = Value.Null()): Array<Value> {
8920
+ if (_args3 === null) {
8921
+ _args3 = new Array<Value>(3);
8922
+ }
8923
+ unchecked((_args3![0] = a));
8924
+ unchecked((_args3![1] = b));
8925
+ unchecked((_args3![2] = c));
8926
+ return _args3!;
8927
+ }
8928
+
8929
+ export function makeArgs1(a: Value): Array<Value> {
8930
+ if (_args1 === null) {
8931
+ _args1 = new Array<Value>(1);
8932
+ }
8933
+ unchecked((_args1![0] = a));
8934
+ return _args1!;
8935
+ }
8936
+
8937
+ export function makeArgs2(a: Value, b: Value): Array<Value> {
8938
+ if (_args2 === null) {
8939
+ _args2 = new Array<Value>(2);
8940
+ }
8941
+ unchecked((_args2![0] = a));
8942
+ unchecked((_args2![1] = b));
8943
+ return _args2!;
8944
+ }
8945
+
8946
+ export function makeArgs3(a: Value, b: Value, c: Value): Array<Value> {
8947
+ if (_args3 === null) {
8948
+ _args3 = new Array<Value>(3);
8949
+ }
8950
+ unchecked((_args3![0] = a));
8951
+ unchecked((_args3![1] = b));
8952
+ unchecked((_args3![2] = c));
8953
+ return _args3!;
8954
+ }
8955
+
8956
+ // Type alias for function handlers
8957
+ type FunctionHandler = (args: Array<Value>) => Value;
8958
+
8959
+ // Global function registry - Map provides O(1) lookup instead of O(n) if-else chain
8960
+ const functionRegistry = new Map<string, FunctionHandler>();
8961
+
8962
+ /**
8963
+ * Initialize the function registry with all built-in functions.
8964
+ * This is called once at module initialization.
8965
+ */
8966
+ function initializeFunctionRegistry(): void {
8967
+ // Array functions
8968
+ functionRegistry.set('sum', (args: Array<Value>): Value => sumArray(args[0]));
8969
+ functionRegistry.set('avg', (args: Array<Value>): Value => avgArray(args[0]));
8970
+ functionRegistry.set('min', (args: Array<Value>): Value => minArray(args[0]));
8971
+ functionRegistry.set('max', (args: Array<Value>): Value => maxArray(args[0]));
8972
+ functionRegistry.set('sort', (args: Array<Value>): Value => sortArray(args[0]));
8973
+
8974
+ // Flatten has two aliases
8975
+ const flatHandler = (args: Array<Value>): Value => {
8976
+ const arg1 = args.length >= 2 ? args[1] : Value.Null();
8977
+ return flatArray(args[0], arg1);
8978
+ };
8979
+ functionRegistry.set('flat', flatHandler);
8980
+ functionRegistry.set('flatten', flatHandler);
8981
+
8982
+ // Count/length has two aliases
8983
+ const lengthHandler = (args: Array<Value>): Value => lengthOf(args[0]);
8984
+ functionRegistry.set('count', lengthHandler);
8985
+ functionRegistry.set('len', lengthHandler);
8986
+
8987
+ functionRegistry.set('contains', (args: Array<Value>): Value => {
8988
+ const arg1 = args.length >= 2 ? args[1] : Value.Null();
8989
+ return containsValue(args[0], arg1);
8990
+ });
8991
+
8992
+ // Type conversion functions
8993
+ functionRegistry.set('number', (args: Array<Value>): Value => numberValue(args[0]));
8994
+ functionRegistry.set('string', (args: Array<Value>): Value => stringValue(args[0]));
8995
+
8996
+ // Math functions
8997
+ functionRegistry.set('abs', (args: Array<Value>): Value => absValue(args[0]));
8998
+ functionRegistry.set('floor', (args: Array<Value>): Value => floorValue(args[0]));
8999
+ functionRegistry.set('ceil', (args: Array<Value>): Value => ceilValue(args[0]));
9000
+ functionRegistry.set('round', (args: Array<Value>): Value => roundValue(args[0]));
9001
+
9002
+ // String functions
9003
+ functionRegistry.set('upper', (args: Array<Value>): Value => upperString(args[0]));
9004
+ functionRegistry.set('lower', (args: Array<Value>): Value => lowerString(args[0]));
9005
+ functionRegistry.set('trim', (args: Array<Value>): Value => trimString(args[0]));
9006
+
9007
+ functionRegistry.set('substring', (args: Array<Value>): Value => {
9008
+ const arg1 = args.length >= 2 ? args[1] : Value.Null();
9009
+ const arg2 = args.length >= 3 ? args[2] : Value.Null();
9010
+ return substringString(args[0], arg1, arg2);
9011
+ });
9012
+
9013
+ functionRegistry.set('indexOf', (args: Array<Value>): Value => {
9014
+ const arg1 = args.length >= 2 ? args[1] : Value.Null();
9015
+ const arg2 = args.length >= 3 ? args[2] : Value.Null();
9016
+ return indexOfString(args[0], arg1, arg2.isNull() ? Value.Int(<i64>0) : arg2);
9017
+ });
9018
+
9019
+ functionRegistry.set('startsWith', (args: Array<Value>): Value => {
9020
+ const arg1 = args.length >= 2 ? args[1] : Value.Null();
9021
+ return startsWithString(args[0], arg1);
9022
+ });
9023
+
9024
+ functionRegistry.set('endsWith', (args: Array<Value>): Value => {
9025
+ const arg1 = args.length >= 2 ? args[1] : Value.Null();
9026
+ return endsWithString(args[0], arg1);
9027
+ });
9028
+
9029
+ functionRegistry.set('split', (args: Array<Value>): Value => {
9030
+ const arg1 = args.length >= 2 ? args[1] : Value.Null();
9031
+ return splitString(args[0], arg1);
9032
+ });
9033
+
9034
+ functionRegistry.set('join', (args: Array<Value>): Value => {
9035
+ const arg1 = args.length >= 2 ? args[1] : Value.Null();
9036
+ return joinArray(args[0], arg1);
9037
+ });
9038
+
9039
+ functionRegistry.set('replace', (args: Array<Value>): Value => {
9040
+ const arg1 = args.length >= 2 ? args[1] : Value.Null();
9041
+ const arg2 = args.length >= 3 ? args[2] : Value.Null();
9042
+ return replaceString(args[0], arg1, arg2);
9043
+ });
9044
+
9045
+ functionRegistry.set('replaceAll', (args: Array<Value>): Value => {
9046
+ const arg1 = args.length >= 2 ? args[1] : Value.Null();
9047
+ const arg2 = args.length >= 3 ? args[2] : Value.Null();
9048
+ return replaceAllString(args[0], arg1, arg2);
9049
+ });
9050
+
9051
+ // Date/time functions
9052
+ functionRegistry.set('date', (args: Array<Value>): Value => parseDate(args[0]));
9053
+
9054
+ // now() returns 0 because WASM doesn't have direct access to system time in a sandboxed way.
9055
+ // To get the current timestamp, use date("now") instead, which uses Date.now().
9056
+ functionRegistry.set('now', (args: Array<Value>): Value => Value.Float(0));
9057
+
9058
+ functionRegistry.set('time', (args: Array<Value>): Value => parseTime(args[0]));
9059
+ functionRegistry.set('duration', (args: Array<Value>): Value => parseDuration(args[0]));
9060
+
9061
+ // Object functions
9062
+ functionRegistry.set('values', (args: Array<Value>): Value => objectValues(args[0]));
9063
+ functionRegistry.set('keys', (args: Array<Value>): Value => objectKeys(args[0]));
9064
+ }
9065
+
9066
+ // Initialize the registry immediately
9067
+ initializeFunctionRegistry();
9068
+
9069
+ /**
9070
+ * Evaluate built-in functions.
9071
+ * All inputs and outputs use the Value tagged union.
9072
+ *
9073
+ * Uses a Map-based registry for O(1) function lookup instead of O(n) if-else chain.
9074
+ */
9075
+ export function evaluateFunction(name: string, args: Array<Value>): Value {
9076
+ // Safety check for args array
9077
+ if (args == null || args.length == 0) return Value.Null();
9078
+
9079
+ // Look up handler in the registry
9080
+ if (functionRegistry.has(name)) {
9081
+ const handler = functionRegistry.get(name);
9082
+ return handler(args);
9083
+ }
9084
+
9085
+ // Unknown function - return null
9086
+ return Value.Null();
9087
+ }
9088
+
9089
+ // === Object Functions ===
9090
+
9091
+ /**
9092
+ * Get all values from an object as an array.
9093
+ * If the input is not an object, returns an empty array.
9094
+ */
9095
+ export function objectValues(val: Value): Value {
9096
+ if (!val.isObject()) return Value.Array(new Array<Value>(0));
9097
+ const obj = val.asObject();
9098
+ const keys = obj.keys();
9099
+ const result = new Array<Value>(keys.length);
9100
+ for (let i = 0; i < keys.length; i++) {
9101
+ unchecked((result[i] = obj.get(unchecked(keys[i]))));
9102
+ }
9103
+ return Value.Array(result);
9104
+ }
9105
+
9106
+ /**
9107
+ * Get all keys from an object as an array of strings.
9108
+ * If the input is not an object, returns an empty array.
9109
+ */
9110
+ export function objectKeys(val: Value): Value {
9111
+ if (!val.isObject()) return Value.Array(new Array<Value>(0));
9112
+ const obj = val.asObject();
9113
+ const keys = obj.keys();
9114
+ const result = new Array<Value>(keys.length);
9115
+ for (let i = 0; i < keys.length; i++) {
9116
+ unchecked((result[i] = Value.String(unchecked(keys[i]))));
9117
+ }
9118
+ return Value.Array(result);
9119
+ }
9120
+
9121
+ // === Array Functions ===
9122
+
9123
+ export function sumArray(val: Value): Value {
9124
+ if (!val.isArray()) return Value.Float(0.0);
9125
+ const arr = val.asArray();
9126
+ let total: f64 = 0.0;
9127
+ for (let i = 0; i < arr.length; i++) {
9128
+ total += unchecked(arr[i]).asFloat();
9129
+ }
9130
+ return Value.Float(total);
9131
+ }
9132
+
9133
+ export function minArray(val: Value): Value {
9134
+ if (!val.isArray()) return Value.Null();
9135
+ const arr = val.asArray();
9136
+ if (arr.length == 0) return Value.Null();
9137
+ let min = arr[0].asFloat();
9138
+ for (let i = 1; i < arr.length; i++) {
9139
+ const v = unchecked(arr[i]).asFloat();
9140
+ if (v < min) min = v;
9141
+ }
9142
+ return Value.Float(min);
9143
+ }
9144
+
9145
+ export function maxArray(val: Value): Value {
9146
+ if (!val.isArray()) return Value.Null();
9147
+ const arr = val.asArray();
9148
+ if (arr.length == 0) return Value.Null();
9149
+ let max = arr[0].asFloat();
9150
+ for (let i = 1; i < arr.length; i++) {
9151
+ const v = unchecked(arr[i]).asFloat();
9152
+ if (v > max) max = v;
9153
+ }
9154
+ return Value.Float(max);
9155
+ }
9156
+
9157
+ export function avgArray(val: Value): Value {
9158
+ if (!val.isArray()) return Value.Null();
9159
+ const arr = val.asArray();
9160
+ if (arr.length == 0) return Value.Null();
9161
+ let total: f64 = 0.0;
9162
+ for (let i = 0; i < arr.length; i++) {
9163
+ total += unchecked(arr[i]).asFloat();
9164
+ }
9165
+ return Value.Float(total / <f64>arr.length);
9166
+ }
9167
+
9168
+ /**
9169
+ * Sort an array in ascending order.
9170
+ *
9171
+ * Supports sorting numbers and strings in ascending order.
9172
+ * Creates and returns a new sorted array without modifying the original.
9173
+ *
9174
+ * Parameters:
9175
+ * - val: The array value to sort
9176
+ *
9177
+ * Behavior:
9178
+ * - Numbers: sorted in ascending numeric order
9179
+ * - Strings: sorted in lexicographic (ASCII) order
9180
+ * - Empty arrays: returns a new empty array
9181
+ * - Non-array inputs: returns null
9182
+ *
9183
+ * Examples:
9184
+ * - sortArray([3, 1, 2]) -> [1, 2, 3]
9185
+ * - sortArray(["c", "a", "b"]) -> ["a", "b", "c"]
9186
+ * - sortArray([]) -> []
9187
+ *
9188
+ * @param val - The array value to sort
9189
+ * @returns A new sorted array, or null for invalid input
9190
+ */
9191
+ export function sortArray(val: Value): Value {
9192
+ if (!val.isArray()) return Value.Null();
9193
+ const arr = val.asArray();
9194
+
9195
+ // Create a copy of the array to avoid mutating the original
9196
+ const sorted = new Array<Value>(arr.length);
9197
+ for (let i = 0; i < arr.length; i++) {
9198
+ unchecked((sorted[i] = unchecked(arr[i])));
9199
+ }
9200
+
9201
+ // Sort the copy using the compareValues function
9202
+ sorted.sort((a: Value, b: Value): i32 => compareValues(a, b));
9203
+
9204
+ return Value.Array(sorted);
9205
+ }
9206
+
9207
+ /**
9208
+ * Flatten nested arrays into a single-level array.
9209
+ *
9210
+ * Parameters:
9211
+ * - val: The array value to flatten
9212
+ * - depthVal: Optional depth (default: 1). Specifies how many levels deep to flatten.
9213
+ *
9214
+ * Behavior:
9215
+ * - If depth is 0, returns a shallow copy of the array
9216
+ * - Flattens arrays recursively up to the specified depth
9217
+ * - Empty arrays are returned as empty arrays
9218
+ * - Non-array inputs return null
9219
+ *
9220
+ * Examples:
9221
+ * - flatArray([[1, 2], [3, 4]]) -> [1, 2, 3, 4] (depth 1)
9222
+ * - flatArray([1, [2, [3]]]) -> [1, 2, [3]] (depth 1)
9223
+ * - flatArray([1, [2, [3]]], 2) -> [1, 2, 3] (depth 2)
9224
+ * - flatArray([1, 2, 3]) -> [1, 2, 3] (no nesting, shallow copy)
9225
+ * - flatArray([]) -> [] (empty array)
9226
+ * - flatArray([[1], [2]], 0) -> [[1], [2]] (depth 0, no flattening)
9227
+ *
9228
+ * @param val - The array value to flatten
9229
+ * @param depthVal - Optional depth for flattening (default: 1)
9230
+ * @returns A new flattened array, or null for invalid input
9231
+ */
9232
+ export function flatArray(val: Value, depthVal: Value = Value.Int(<i64>1)): Value {
9233
+ if (!val.isArray()) return Value.Null();
9234
+ const arr = val.asArray();
9235
+
9236
+ // Parse depth parameter matches JavaScript Array.flat() default behavior, which
9237
+ // flattens one level deep unless specified otherwise. Most common use case.
9238
+ let depth: i32 = 1;
9239
+ if (!depthVal.isNull() && depthVal.isNumber()) {
9240
+ depth = <i32>depthVal.asFloat(); // Use asFloat() since numbers may come as floats
9241
+ // Clamp depth to non-negative values
9242
+ if (depth < 0) depth = 1;
9243
+ }
9244
+
9245
+ // Use a simple iterative approach that maintains order
9246
+ let result = new Array<Value>();
9247
+ flattenRecursive(arr, depth, result);
9248
+ return Value.Array(result);
9249
+ }
9250
+
9251
+ // Helper function for flatArray
9252
+ function flattenRecursive(arr: Array<Value>, currentDepth: i32, result: Array<Value>): void {
9253
+ for (let i = 0; i < arr.length; i++) {
9254
+ const element = unchecked(arr[i]);
9255
+ if (element.isArray() && currentDepth > 0) {
9256
+ // Recursively flatten nested array
9257
+ flattenRecursive(element.asArray(), currentDepth - 1, result);
9258
+ } else {
9259
+ // Add element to results
9260
+ result.push(element);
9261
+ }
9262
+ }
9263
+ }
9264
+
9265
+ export function lengthOf(val: Value): Value {
9266
+ if (val.isArray()) {
9267
+ return Value.Int(<i64>val.asArray().length);
9268
+ }
9269
+ if (val.isString()) {
9270
+ return Value.Int(<i64>val.asString().length);
9271
+ }
9272
+ return Value.Int(0);
9273
+ }
9274
+
9275
+ /**
9276
+ * Check if a value contains a subvalue.
9277
+ *
9278
+ * For strings: checks if the string contains a given substring.
9279
+ * For arrays: checks if the array contains the given value.
9280
+ *
9281
+ * Parameters:
9282
+ * - container: The string or array to search in
9283
+ * - needle: The substring or value to search for
9284
+ *
9285
+ * Behavior:
9286
+ * - If container is a string, checks if it contains the substring
9287
+ * - If container is an array, checks if any element equals the needle
9288
+ * - For strings, returns null if either argument is not a string
9289
+ * - For arrays, always returns a boolean
9290
+ *
9291
+ * String Examples:
9292
+ * - contains("hello world", "world") \u2192 true
9293
+ * - contains("hello world", "xyz") \u2192 false
9294
+ * - contains("abc", "abc") \u2192 true
9295
+ * - contains("hello", "") \u2192 true (empty string is always contained)
9296
+ * - contains("", "") \u2192 true
9297
+ * - contains("", "x") \u2192 false
9298
+ * - contains("Hello", "hello") \u2192 false (case-sensitive)
9299
+ * - contains("hello", 123) \u2192 null (needle not a string)
9300
+ * - contains(123, "hello") \u2192 null (container not a string)
9301
+ *
9302
+ * Array Examples:
9303
+ * - contains([1, 2, 3], 2) \u2192 true
9304
+ * - contains([1, 2, 3], 4) \u2192 false
9305
+ * - contains(["a"], "a") \u2192 true
9306
+ * - contains(123, 1) \u2192 false
9307
+ *
9308
+ * @param container - The string or array to search in
9309
+ * @param needle - The substring or value to search for
9310
+ * @returns true if contained, false if not, or null for invalid string arguments
9311
+ */
9312
+ export function containsValue(container: Value, needle: Value): Value {
9313
+ // Handle string containment
9314
+ if (container.isString()) {
9315
+ if (!needle.isString()) return Value.Null();
9316
+ const str = container.asString();
9317
+ const search = needle.asString();
9318
+ // Use indexOf to check containment (returns -1 if not found)
9319
+ const index = str.indexOf(search);
9320
+ return Value.Bool(index >= 0);
9321
+ }
9322
+
9323
+ // Handle array containment (original behavior)
9324
+ if (!container.isArray()) return Value.Bool(false);
9325
+ const arr = container.asArray();
9326
+ for (let i = 0; i < arr.length; i++) {
9327
+ if (valuesEqual(unchecked(arr[i]), needle)) return Value.Bool(true);
9328
+ }
9329
+ return Value.Bool(false);
9330
+ }
9331
+
9332
+ // === Date/Time Functions ===
9333
+
9334
+ /**
9335
+ * Parse a date string into a numeric timestamp.
9336
+ *
9337
+ * Supports ISO 8601 date format: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS[Z|+/-HH:MM]
9338
+ * Returns Unix timestamp in seconds since epoch (1970-01-01 00:00:00 UTC).
9339
+ * This matches zen-engine's behavior and allows date arithmetic operations.
9340
+ *
9341
+ * Examples:
9342
+ * - date("2025-03-20T10:30:00Z") \u2192 1742466600 (seconds since epoch)
9343
+ * - date("1970-01-01") \u2192 0
9344
+ * - date("1970-01-01T00:00:00Z") \u2192 0
9345
+ *
9346
+ * @param val - A string value containing a date
9347
+ * @returns A float Value representing seconds since epoch, or Null for invalid input
9348
+ */
9349
+ export function parseDate(val: Value): Value {
9350
+ if (!val.isString()) return Value.Null();
9351
+
9352
+ const str = val.asString();
9353
+
9354
+ // Handle special 'now' keyword - returns Unix timestamp in seconds
9355
+ // Using Date.now() which returns milliseconds, convert to seconds
9356
+ if (str == 'now') {
9357
+ return Value.Float(<f64>Date.now() / 1000.0);
9358
+ }
9359
+
9360
+ // Fast path for simple YYYY-MM-DD format (10 characters)
9361
+ // This avoids string allocations from split() and substring() for common cases
9362
+ if (str.length == 10) {
9363
+ // Check dash positions at indices 4 and 7
9364
+ const dashPos1 = unchecked(<i32>str.charCodeAt(4));
9365
+ const dashPos2 = unchecked(<i32>str.charCodeAt(7));
9366
+ if (dashPos1 == 45 && dashPos2 == 45) {
9367
+ // Hyphen is ASCII 45, digits start at ASCII 48
9368
+ // Parse year, month, day directly without split()
9369
+ const year =
9370
+ unchecked(<i32>str.charCodeAt(0) - 48) * 1000 +
9371
+ unchecked(<i32>str.charCodeAt(1) - 48) * 100 +
9372
+ unchecked(<i32>str.charCodeAt(2) - 48) * 10 +
9373
+ unchecked(<i32>str.charCodeAt(3) - 48);
9374
+ const month =
9375
+ unchecked(<i32>str.charCodeAt(5) - 48) * 10 + unchecked(<i32>str.charCodeAt(6) - 48);
9376
+ const day =
9377
+ unchecked(<i32>str.charCodeAt(8) - 48) * 10 + unchecked(<i32>str.charCodeAt(9) - 48);
9378
+
9379
+ // Validate ranges
9380
+ if (year >= 0 && month >= 1 && month <= 12 && day >= 1 && day <= 31) {
9381
+ const daysSinceEpoch = calculateDaysSinceEpoch(year, month, day);
9382
+ return Value.Float(<f64>daysSinceEpoch * 86400.0);
9383
+ }
9384
+ }
9385
+ }
9386
+
9387
+ // Parse ISO 8601 date format: YYYY-MM-DD
9388
+ // Also handle datetime format: YYYY-MM-DDTHH:MM:SS[Z|+/-HH:MM]
9389
+ let dateStr = str;
9390
+ let timeStr = '';
9391
+ let tzOffsetSeconds: i32 = 0; // Timezone offset in seconds (positive = ahead of UTC)
9392
+
9393
+ const tIndex = str.indexOf('T');
9394
+ if (tIndex >= 0) {
9395
+ dateStr = str.substring(0, tIndex);
9396
+ let timePart = str.substring(tIndex + 1);
9397
+
9398
+ // Handle timezone: Z, +HH:MM, -HH:MM
9399
+ const zIndex = timePart.indexOf('Z');
9400
+ if (zIndex >= 0) {
9401
+ timeStr = timePart.substring(0, zIndex);
9402
+ tzOffsetSeconds = 0; // UTC
9403
+ } else {
9404
+ const plusIndex = timePart.indexOf('+');
9405
+ const minusIndex = timePart.indexOf('-');
9406
+ // Find timezone offset (look for +/- after the time part, not in middle of time)
9407
+ let tzStartIndex = -1;
9408
+ if (plusIndex >= 5) {
9409
+ // At least HH:MM before timezone
9410
+ tzStartIndex = plusIndex;
9411
+ } else if (minusIndex >= 5) {
9412
+ tzStartIndex = minusIndex;
9413
+ }
9414
+
9415
+ if (tzStartIndex >= 0) {
9416
+ timeStr = timePart.substring(0, tzStartIndex);
9417
+ const tzStr = timePart.substring(tzStartIndex);
9418
+ // Parse timezone offset (+/-HH:MM or +/-HH)
9419
+ const isNegative = tzStr.charAt(0) == '-';
9420
+ const tzParts = tzStr.substring(1).split(':');
9421
+ if (tzParts.length >= 1) {
9422
+ const tzHours = parseIntValue(tzParts[0]);
9423
+ const tzMinutes = tzParts.length >= 2 ? parseIntValue(tzParts[1]) : 0;
9424
+ if (tzHours >= 0 && tzMinutes >= 0) {
9425
+ tzOffsetSeconds = tzHours * 3600 + tzMinutes * 60;
9426
+ if (isNegative) tzOffsetSeconds = -tzOffsetSeconds;
9427
+ }
9428
+ }
9429
+ } else {
9430
+ timeStr = timePart;
9431
+ }
9432
+ }
9433
+ }
9434
+
9435
+ // Split date by dash
9436
+ const parts = dateStr.split('-');
9437
+ if (parts.length < 3) return Value.Null();
9438
+
9439
+ // Parse year, month, day
9440
+ const year = parseIntValue(parts[0]);
9441
+ const month = parseIntValue(parts[1]);
9442
+ const day = parseIntValue(parts[2]);
9443
+
9444
+ if (year < 0 || month < 1 || month > 12 || day < 1 || day > 31) {
9445
+ return Value.Null();
9446
+ }
9447
+
9448
+ // Calculate days since epoch
9449
+ const daysSinceEpoch = calculateDaysSinceEpoch(year, month, day);
9450
+
9451
+ // Parse time if present (HH:MM:SS or HH:MM)
9452
+ let hours: i32 = 0;
9453
+ let minutes: i32 = 0;
9454
+ let seconds: i32 = 0;
9455
+
9456
+ if (timeStr.length > 0) {
9457
+ const timeParts = timeStr.split(':');
9458
+ if (timeParts.length >= 2) {
9459
+ hours = parseIntValue(timeParts[0]);
9460
+ minutes = parseIntValue(timeParts[1]);
9461
+ if (timeParts.length >= 3) {
9462
+ // Handle fractional seconds (e.g., "10:30:00.123")
9463
+ const secStr = timeParts[2];
9464
+ const dotIndex = secStr.indexOf('.');
9465
+ if (dotIndex >= 0) {
9466
+ seconds = parseIntValue(secStr.substring(0, dotIndex));
9467
+ } else {
9468
+ seconds = parseIntValue(secStr);
9469
+ }
9470
+ }
9471
+ }
9472
+ if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59) {
9473
+ // Invalid time - use 0
9474
+ hours = 0;
9475
+ minutes = 0;
9476
+ seconds = 0;
9477
+ }
9478
+ }
9479
+
9480
+ // Calculate total seconds since epoch
9481
+ // daysSinceEpoch * 86400 + hours * 3600 + minutes * 60 + seconds - tzOffsetSeconds
9482
+ // We subtract tzOffsetSeconds to convert local time to UTC
9483
+ const totalSeconds: f64 =
9484
+ <f64>daysSinceEpoch * 86400.0 +
9485
+ <f64>hours * 3600.0 +
9486
+ <f64>minutes * 60.0 +
9487
+ <f64>seconds -
9488
+ <f64>tzOffsetSeconds;
9489
+
9490
+ return Value.Float(totalSeconds);
9491
+ }
9492
+
9493
+ /**
9494
+ * Parse a time string into a numeric value.
9495
+ *
9496
+ * Supports format: HH:MM:SS or HH:MM
9497
+ * Returns the number of seconds since midnight.
9498
+ *
9499
+ * @param val - A string value containing a time
9500
+ * @returns A float Value representing seconds since midnight
9501
+ */
9502
+ export function parseTime(val: Value): Value {
9503
+ if (!val.isString()) return Value.Null();
9504
+
9505
+ const str = val.asString();
9506
+ const parts = str.split(':');
9507
+
9508
+ if (parts.length < 2) return Value.Null();
9509
+
9510
+ const hours = parseIntValue(parts[0]);
9511
+ const minutes = parseIntValue(parts[1]);
9512
+ const seconds = parts.length >= 3 ? parseIntValue(parts[2]) : 0;
9513
+
9514
+ if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59) {
9515
+ return Value.Null();
9516
+ }
9517
+
9518
+ const totalSeconds = hours * 3600 + minutes * 60 + seconds;
9519
+ return Value.Float(<f64>totalSeconds);
9520
+ }
9521
+
9522
+ /**
9523
+ * Parse a duration string into a numeric value.
9524
+ *
9525
+ * Supports ISO 8601 duration format: P[n]Y[n]M[n]DT[n]H[n]M[n]S
9526
+ * Also supports simple formats like "30d", "24h", "60m", "3600s"
9527
+ *
9528
+ * @param val - A string value containing a duration
9529
+ * @returns A float Value representing duration in seconds
9530
+ */
9531
+ export function parseDuration(val: Value): Value {
9532
+ if (!val.isString()) return Value.Null();
9533
+
9534
+ const str = val.asString();
9535
+
9536
+ // Handle simple formats: "30d", "24h", "60m", "3600s"
9537
+ if (str.length > 1) {
9538
+ const unit = str.charAt(str.length - 1);
9539
+ const numStr = str.substring(0, str.length - 1);
9540
+ const num = parseFloatValue(numStr);
9541
+
9542
+ if (num >= 0) {
9543
+ if (unit == 's') return Value.Float(num);
9544
+ if (unit == 'm') return Value.Float(num * 60);
9545
+ if (unit == 'h') return Value.Float(num * 3600);
9546
+ if (unit == 'd') return Value.Float(num * 86400);
9547
+ }
9548
+ }
9549
+
9550
+ // Full ISO 8601 duration format (P[n]Y[n]M[n]DT[n]H[n]M[n]S) not yet supported
9551
+ return Value.Null();
9552
+ }
9553
+
9554
+ /**
9555
+ * Parse a string to an integer value.
9556
+ */
9557
+ function parseIntValue(str: string): i32 {
9558
+ let result: i32 = 0;
9559
+ let negative = false;
9560
+ let i = 0;
9561
+
9562
+ // Handle leading whitespace
9563
+ while (i < str.length && str.charCodeAt(i) == 32) {
9564
+ i++;
9565
+ }
9566
+
9567
+ // Handle negative sign
9568
+ if (i < str.length && str.charCodeAt(i) == 45) {
9569
+ // '-'
9570
+ negative = true;
9571
+ i++;
9572
+ }
9573
+
9574
+ // Parse digits
9575
+ let hasDigits = false;
9576
+ while (i < str.length) {
9577
+ const c = str.charCodeAt(i);
9578
+ if (c >= 48 && c <= 57) {
9579
+ // '0'-'9'
9580
+ result = result * 10 + (c - 48);
9581
+ hasDigits = true;
9582
+ i++;
9583
+ } else {
9584
+ break;
9585
+ }
9586
+ }
9587
+
9588
+ // AssemblyScript doesn't have optional types or Result<T,E>, so we use -1 as sentinel
9589
+ // value for parse errors. Callers check for negative values to detect failures.
9590
+ if (!hasDigits) return -1;
9591
+
9592
+ return negative ? -result : result;
9593
+ }
9594
+
9595
+ /**
9596
+ * Parse a string to a float value.
9597
+ * Returns NaN for invalid input.
9598
+ */
9599
+ function parseFloatValue(str: string): f64 {
9600
+ return parseFloat(str);
9601
+ }
9602
+
9603
+ /**
9604
+ * Calculate days since Unix epoch (1970-01-01).
9605
+ * Uses a simplified algorithm that handles leap years.
9606
+ */
9607
+ function calculateDaysSinceEpoch(year: i32, month: i32, day: i32): i32 {
9608
+ // Days in each month (non-leap year)
9609
+ const daysInMonth: i32[] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
9610
+
9611
+ // Calculate days from years
9612
+ let days: i32 = 0;
9613
+
9614
+ // Calculate years from 1970 to target year
9615
+ if (year >= 1970) {
9616
+ for (let y = 1970; y < year; y++) {
9617
+ days += isLeapYear(y) ? 366 : 365;
9618
+ }
9619
+ } else {
9620
+ for (let y = year; y < 1970; y++) {
9621
+ days -= isLeapYear(y) ? 366 : 365;
9622
+ }
9623
+ }
9624
+
9625
+ // Add days from months
9626
+ for (let m = 1; m < month; m++) {
9627
+ days += unchecked(daysInMonth[m - 1]);
9628
+ // Add leap day for February in leap years
9629
+ if (m == 2 && isLeapYear(year)) {
9630
+ days += 1;
9631
+ }
9632
+ }
9633
+
9634
+ // Add days
9635
+ days += day - 1; // day 1 is the first day, so subtract 1
9636
+
9637
+ return days;
9638
+ }
9639
+
9640
+ /**
9641
+ * Check if a year is a leap year.
9642
+ */
9643
+ function isLeapYear(year: i32): bool {
9644
+ return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
9645
+ }
9646
+
9647
+ // === Math Functions ===
9648
+
9649
+ export function absValue(val: Value): Value {
9650
+ if (val.isNumber()) {
9651
+ const n = val.asFloat();
9652
+ return Value.Float(n < 0 ? -n : n);
9653
+ }
9654
+ return Value.Null();
9655
+ }
9656
+
9657
+ export function floorValue(val: Value): Value {
9658
+ if (val.isNumber()) {
9659
+ return Value.Float(Math.floor(val.asFloat()));
9660
+ }
9661
+ return Value.Null();
9662
+ }
9663
+
9664
+ export function ceilValue(val: Value): Value {
9665
+ if (val.isNumber()) {
9666
+ return Value.Float(Math.ceil(val.asFloat()));
9667
+ }
9668
+ return Value.Null();
9669
+ }
9670
+
9671
+ export function roundValue(val: Value): Value {
9672
+ if (val.isNumber()) {
9673
+ return Value.Float(Math.round(val.asFloat()));
9674
+ }
9675
+ return Value.Null();
9676
+ }
9677
+
9678
+ // === String Functions ===
9679
+
9680
+ /**
9681
+ * Convert a string to uppercase.
9682
+ *
9683
+ * Non-alphabetic characters (numbers, symbols, spaces) remain unchanged.
9684
+ * If the input is not a string, returns null.
9685
+ *
9686
+ * Examples:
9687
+ * - upper("hello") \u2192 "HELLO"
9688
+ * - upper("Hello World") \u2192 "HELLO WORLD"
9689
+ * - upper("123abc") \u2192 "123ABC"
9690
+ * - upper("!@#") \u2192 "!@#"
9691
+ * - upper(123) \u2192 null
9692
+ * - upper(null) \u2192 null
9693
+ *
9694
+ * @param val - The value to convert to uppercase
9695
+ * @returns The uppercase string, or null if input is not a string
9696
+ */
9697
+ export function upperString(val: Value): Value {
9698
+ if (!val.isString()) return Value.Null();
9699
+ const str = val.asString();
9700
+ return Value.String(str.toUpperCase());
9701
+ }
9702
+
9703
+ /**
9704
+ * Convert a string to lowercase.
9705
+ *
9706
+ * Non-alphabetic characters (numbers, symbols, spaces) remain unchanged.
9707
+ * If the input is not a string, returns null.
9708
+ *
9709
+ * Examples:
9710
+ * - lower("HELLO") \u2192 "hello"
9711
+ * - lower("Hello World") \u2192 "hello world"
9712
+ * - lower("123ABC") \u2192 "123abc"
9713
+ * - lower("!@#") \u2192 "!@#"
9714
+ * - lower(123) \u2192 null
9715
+ * - lower(null) \u2192 null
9716
+ *
9717
+ * @param val - The value to convert to lowercase
9718
+ * @returns The lowercase string, or null if input is not a string
9719
+ */
9720
+ export function lowerString(val: Value): Value {
9721
+ if (!val.isString()) return Value.Null();
9722
+ const str = val.asString();
9723
+ return Value.String(str.toLowerCase());
9724
+ }
9725
+
9726
+ /**
9727
+ * Remove leading and trailing whitespace from a string.
9728
+ *
9729
+ * Whitespace includes spaces, tabs, newlines, and other whitespace characters.
9730
+ * If the input is not a string, returns null.
9731
+ *
9732
+ * Examples:
9733
+ * - trim(" hello ") \u2192 "hello"
9734
+ * - trim("\\t\\nhello\\n\\t") \u2192 "hello"
9735
+ * - trim("hello") \u2192 "hello" (no change)
9736
+ * - trim(" hello world ") \u2192 "hello world"
9737
+ * - trim("") \u2192 "" (empty string)
9738
+ * - trim(" ") \u2192 "" (only whitespace)
9739
+ * - trim(123) \u2192 null
9740
+ * - trim(null) \u2192 null
9741
+ *
9742
+ * @param val - The value to trim whitespace from
9743
+ * @returns The trimmed string, or null if input is not a string
9744
+ */
9745
+ export function trimString(val: Value): Value {
9746
+ if (!val.isString()) return Value.Null();
9747
+ const str = val.asString();
9748
+ return Value.String(str.trim());
9749
+ }
9750
+
9751
+ /**
9752
+ * Extract a portion of a string.
9753
+ *
9754
+ * Returns a substring starting at the specified index and optionally
9755
+ * ending at the specified index (or length).
9756
+ *
9757
+ * Parameters:
9758
+ * - str: The input string
9759
+ * - start: The starting index (0-based, inclusive)
9760
+ * - endOrLength: Optional end index (exclusive) or length. If omitted,
9761
+ * extracts from start to the end of the string.
9762
+ *
9763
+ * Behavior:
9764
+ * - If start is negative, it's treated as an offset from the end of the string
9765
+ * - If start < 0, it's clamped to 0 (empty prefix)
9766
+ * - If start >= str.length, returns empty string
9767
+ * - If endOrLength is omitted or null, extracts to end of string
9768
+ * - If endOrLength is negative, it's treated as an offset from the end
9769
+ * - If endOrLength > str.length, it's clamped to str.length
9770
+ * - If endOrLength <= start, returns empty string
9771
+ *
9772
+ * Examples:
9773
+ * - substring("hello", 0, 2) \u2192 "he"
9774
+ * - substring("hello", 1, 3) \u2192 "el"
9775
+ * - substring("hello", 2) \u2192 "llo"
9776
+ * - substring("hello", 0) \u2192 "hello"
9777
+ * - substring("hello", 0, 5) \u2192 "hello"
9778
+ * - substring("hello", 0, 10) \u2192 "hello" (clamped to end)
9779
+ * - substring("hello", 3, 5) \u2192 "lo"
9780
+ * - substring("hello", -3) \u2192 "llo" (from end)
9781
+ * - substring("hello", 0, -2) \u2192 "hel" (from end)
9782
+ * - substring("hello", 10) \u2192 "" (start beyond length)
9783
+ * - substring("", 0, 5) \u2192 "" (empty string)
9784
+ * - substring(123, 0, 2) \u2192 null (not a string)
9785
+ * - substring(null, 0, 2) \u2192 null (not a string)
9786
+ *
9787
+ * @param strVal - The string value to extract from
9788
+ * @param startVal - The starting index
9789
+ * @param endOrLengthVal - Optional end index (exclusive) or length
9790
+ * @returns The substring, or null if input is not a string
9791
+ */
9792
+ export function substringString(strVal: Value, startVal: Value, endOrLengthVal: Value): Value {
9793
+ if (!strVal.isString()) return Value.Null();
9794
+ const str = strVal.asString();
9795
+ const len = str.length;
9796
+
9797
+ if (len == 0) return Value.String('');
9798
+
9799
+ // Parse start index - use asFloat() to handle both Int and Float types
9800
+ let start: i32 = 0;
9801
+ if (startVal.isNumber()) {
9802
+ start = <i32>startVal.asFloat();
9803
+ }
9804
+
9805
+ // Handle negative start (offset from end)
9806
+ if (start < 0) {
9807
+ start = len + start;
9808
+ if (start < 0) start = 0; // Clamp to 0
9809
+ }
9810
+
9811
+ // Handle start beyond length
9812
+ if (start >= len) {
9813
+ return Value.String('');
9814
+ }
9815
+
9816
+ // Parse end index if provided
9817
+ let hasEnd = !endOrLengthVal.isNull();
9818
+ let end: i32 = len; // Default to end of string
9819
+
9820
+ if (hasEnd && endOrLengthVal.isNumber()) {
9821
+ end = <i32>endOrLengthVal.asFloat();
9822
+
9823
+ // Handle negative end (offset from end)
9824
+ if (end < 0) {
9825
+ end = len + end;
9826
+ if (end < 0) end = 0;
9827
+ }
9828
+
9829
+ // Clamp end to string length
9830
+ if (end > len) end = len;
9831
+
9832
+ // Ensure end >= start
9833
+ if (end < start) end = start;
9834
+ }
9835
+
9836
+ // Extract substring
9837
+ return Value.String(str.substring(start, end));
9838
+ }
9839
+
9840
+ /**
9841
+ * Find the index of the first occurrence of a substring.
9842
+ *
9843
+ * Returns the index of the first occurrence of the search string,
9844
+ * starting from the specified position. Returns -1 if not found.
9845
+ *
9846
+ * Parameters:
9847
+ * - str: The string to search in
9848
+ * - searchVal: The substring to search for
9849
+ * - startVal: Optional starting position (default: 0)
9850
+ *
9851
+ * Behavior:
9852
+ * - If the source string is not a string, returns null
9853
+ * - If the search value is not a string, returns null
9854
+ * - If start is negative or omitted, defaults to 0
9855
+ * - If start > str.length, returns -1 (not found)
9856
+ * - If search string is empty, returns the start position
9857
+ * - Returns -1 if the substring is not found
9858
+ *
9859
+ * Examples:
9860
+ * - indexOf("hello", "l") \u2192 2
9861
+ * - indexOf("hello", "x") \u2192 -1
9862
+ * - indexOf("hello", "l", 3) \u2192 3
9863
+ * - indexOf("hello", "ll") \u2192 2
9864
+ * - indexOf("hello", "lo", 0) \u2192 3
9865
+ * - indexOf("hello", "") \u2192 0 (empty search string)
9866
+ * - indexOf("hello", "o", 5) \u2192 -1 (start at end)
9867
+ * - indexOf("hello", "o", 10) \u2192 -1 (start beyond length)
9868
+ * - indexOf("", "x") \u2192 -1 (empty source)
9869
+ * - indexOf("", "") \u2192 0 (both empty)
9870
+ * - indexOf(123, "1") \u2192 null (not a string)
9871
+ *
9872
+ * @param strVal - The string value to search in
9873
+ * @param searchVal - The substring value to search for
9874
+ * @param startVal - Optional starting position
9875
+ * @returns The index of the first occurrence, or -1 if not found, or null if inputs are not strings
9876
+ */
9877
+ export function indexOfString(strVal: Value, searchVal: Value, startVal: Value): Value {
9878
+ if (!strVal.isString() || !searchVal.isString()) return Value.Null();
9879
+ const str = strVal.asString();
9880
+ const search = searchVal.asString();
9881
+
9882
+ // Parse start index
9883
+ let start: i32 = 0;
9884
+ if (!startVal.isNull() && startVal.isNumber()) {
9885
+ start = <i32>startVal.asFloat();
9886
+ }
9887
+
9888
+ // Handle negative start
9889
+ if (start < 0) {
9890
+ start = 0;
9891
+ }
9892
+
9893
+ // Special case: empty search string (matches JavaScript behavior)
9894
+ // When searching for empty string, return the start position clamped to string length
9895
+ if (search.length == 0) {
9896
+ const len = <i32>str.length;
9897
+ if (start >= len) {
9898
+ return Value.Int(<i64>len);
9899
+ }
9900
+ return Value.Int(<i64>start);
9901
+ }
9902
+
9903
+ // AssemblyScript's indexOf returns -1 for not found
9904
+ const index = str.indexOf(search, start);
9905
+ return Value.Int(<i64>index);
9906
+ }
9907
+
9908
+ /**
9909
+ * Check if a string starts with a given prefix.
9910
+ *
9911
+ * Returns true if the string begins with the specified prefix, false otherwise.
9912
+ *
9913
+ * Parameters:
9914
+ * - str: The string to check
9915
+ * - prefix: The prefix to look for
9916
+ *
9917
+ * Behavior:
9918
+ * - If either input is not a string, returns null
9919
+ * - If the prefix is an empty string, returns true
9920
+ * - Case-sensitive comparison
9921
+ *
9922
+ * Examples:
9923
+ * - startsWith("hello", "he") \u2192 true
9924
+ * - startsWith("hello", "lo") \u2192 false
9925
+ * - startsWith("hello", "hello") \u2192 true
9926
+ * - startsWith("hello", "hello world") \u2192 false (prefix longer than string)
9927
+ * - startsWith("", "") \u2192 true (empty strings)
9928
+ * - startsWith("hello", "") \u2192 true (empty prefix)
9929
+ * - startsWith("", "he") \u2192 false (prefix longer than empty string)
9930
+ * - startsWith("Hello", "he") \u2192 false (case-sensitive)
9931
+ * - startsWith(123, "1") \u2192 null (not a string)
9932
+ * - startsWith(null, "he") \u2192 null (not a string)
9933
+ *
9934
+ * @param strVal - The string value to check
9935
+ * @param prefixVal - The prefix value to look for
9936
+ * @returns true if the string starts with the prefix, false otherwise, or null if inputs are not strings
9937
+ */
9938
+ export function startsWithString(strVal: Value, prefixVal: Value): Value {
9939
+ if (!strVal.isString() || !prefixVal.isString()) return Value.Null();
9940
+ const str = strVal.asString();
9941
+ const prefix = prefixVal.asString();
9942
+
9943
+ // AssemblyScript's startsWith returns a bool
9944
+ const result = str.startsWith(prefix);
9945
+ return Value.Bool(result);
9946
+ }
9947
+
9948
+ /**
9949
+ * Check if a string ends with a given suffix.
9950
+ *
9951
+ * Returns true if the string ends with the specified suffix, false otherwise.
9952
+ *
9953
+ * Parameters:
9954
+ * - str: The string to check
9955
+ * - suffix: The suffix to look for
9956
+ *
9957
+ * Behavior:
9958
+ * - If either input is not a string, returns null
9959
+ * - If the suffix is an empty string, returns true
9960
+ * - Case-sensitive comparison
9961
+ *
9962
+ * Examples:
9963
+ * - endsWith("hello", "lo") \u2192 true
9964
+ * - endsWith("hello", "he") \u2192 false
9965
+ * - endsWith("hello", "hello") \u2192 true
9966
+ * - endsWith("hello", "hello world") \u2192 false (suffix longer than string)
9967
+ * - endsWith("", "") \u2192 true (empty strings)
9968
+ * - endsWith("hello", "") \u2192 true (empty suffix)
9969
+ * - endsWith("", "lo") \u2192 false (suffix longer than empty string)
9970
+ * - endsWith("Hello", "LO") \u2192 false (case-sensitive)
9971
+ * - endsWith(123, "1") \u2192 null (not a string)
9972
+ * - endsWith(null, "lo") \u2192 null (not a string)
9973
+ *
9974
+ * @param strVal - The string value to check
9975
+ * @param suffixVal - The suffix value to look for
9976
+ * @returns true if the string ends with the suffix, false otherwise, or null if inputs are not strings
9977
+ */
9978
+ export function endsWithString(strVal: Value, suffixVal: Value): Value {
9979
+ if (!strVal.isString() || !suffixVal.isString()) return Value.Null();
9980
+ const str = strVal.asString();
9981
+ const suffix = suffixVal.asString();
9982
+
9983
+ // AssemblyScript's endsWith returns a bool
9984
+ const result = str.endsWith(suffix);
9985
+ return Value.Bool(result);
9986
+ }
9987
+
9988
+ /**
9989
+ * Split a string into an array based on a delimiter.
9990
+ *
9991
+ * Returns an array of substrings by dividing the input string at each
9992
+ * occurrence of the specified delimiter.
9993
+ *
9994
+ * Parameters:
9995
+ * - str: The string to split
9996
+ * - delimiter: The delimiter to split on
9997
+ *
9998
+ * Behavior:
9999
+ * - If the input string is not a string, returns null
10000
+ * - If the delimiter is not a string, returns null
10001
+ * - If the delimiter is an empty string, returns an array of individual characters
10002
+ * - If the string and delimiter are both empty, returns an array with one empty string
10003
+ * - If the delimiter is not found, returns an array with the original string
10004
+ *
10005
+ * Examples:
10006
+ * - split("a,b,c", ",") \u2192 ["a", "b", "c"]
10007
+ * - split("hello", "") \u2192 ["h", "e", "l", "l", "o"]
10008
+ * - split("a-b-c", "-") \u2192 ["a", "b", "c"]
10009
+ * - split("word", "x") \u2192 ["word"]
10010
+ * - split("", ",") \u2192 [""]
10011
+ * - split("", "") \u2192 [""] (special case: empty string splits into array with one empty string)
10012
+ * - split("a,,b", ",") \u2192 ["a", "", "b"] (preserves empty segments)
10013
+ * - split(null, ",") \u2192 null
10014
+ * - split("hello", null) \u2192 null
10015
+ * - split("hello", 123) \u2192 null
10016
+ *
10017
+ * @param strVal - The string value to split
10018
+ * @param delimiterVal - The delimiter value to split on
10019
+ * @returns An array of strings, or null if inputs are invalid
10020
+ */
10021
+ export function splitString(strVal: Value, delimiterVal: Value): Value {
10022
+ if (!strVal.isString() || !delimiterVal.isString()) return Value.Null();
10023
+ const str = strVal.asString();
10024
+ const delimiter = delimiterVal.asString();
10025
+
10026
+ const parts = str.split(delimiter);
10027
+
10028
+ // Convert string array to Value array
10029
+ const result = new Array<Value>(parts.length);
10030
+ for (let i = 0; i < parts.length; i++) {
10031
+ unchecked((result[i] = Value.String(unchecked(parts[i]))));
10032
+ }
10033
+
10034
+ return Value.Array(result);
10035
+ }
10036
+
10037
+ /**
10038
+ * Join array elements into a string with a separator.
10039
+ *
10040
+ * Returns a string that concatenates all elements of the array,
10041
+ * separated by the specified delimiter.
10042
+ *
10043
+ * Parameters:
10044
+ * - arr: The array to join
10045
+ * - delimiter: The delimiter to insert between elements
10046
+ *
10047
+ * Behavior:
10048
+ * - If the input is not an array, returns null
10049
+ * - If the delimiter is not a string, returns null
10050
+ * - If the array is empty, returns an empty string
10051
+ * - Each array element is converted to its string representation
10052
+ * - The delimiter is placed between each adjacent element, not at the start or end
10053
+ *
10054
+ * Examples:
10055
+ * - join(["a", "b", "c"], "-") \u2192 "a-b-c"
10056
+ * - join(["hello", "world"], " ") \u2192 "hello world"
10057
+ * - join(["a"], ",") \u2192 "a"
10058
+ * - join([], ",") \u2192 ""
10059
+ * - join([1, 2, 3], "-") \u2192 "1-2-3" (numbers converted to strings)
10060
+ * - join([true, false], " ") \u2192 "true false" (booleans converted to strings)
10061
+ * - join(["a", null, "b"], "-") \u2192 "a-null-b" (null converted to "null")
10062
+ * - join(null, ",") \u2192 null
10063
+ * - join(["a"], null) \u2192 null
10064
+ * - join(["a"], 123) \u2192 null
10065
+ *
10066
+ * @param arrVal - The array value to join
10067
+ * @param delimiterVal - The delimiter to insert between elements
10068
+ * @returns The concatenated string, or null if inputs are invalid
10069
+ */
10070
+ export function joinArray(arrVal: Value, delimiterVal: Value): Value {
10071
+ if (!arrVal.isArray() || !delimiterVal.isString()) return Value.Null();
10072
+ const arr = arrVal.asArray();
10073
+ const delimiter = delimiterVal.asString();
10074
+
10075
+ // Handle empty array
10076
+ if (arr.length == 0) {
10077
+ return Value.String('');
10078
+ }
10079
+
10080
+ // Build the result string
10081
+ let result: string = '';
10082
+ for (let i = 0; i < arr.length; i++) {
10083
+ // Append delimiter if not first element
10084
+ if (i > 0) {
10085
+ result += delimiter;
10086
+ }
10087
+ // Convert value to string and append
10088
+ result += valueToString(unchecked(arr[i]));
10089
+ }
10090
+
10091
+ return Value.String(result);
10092
+ }
10093
+
10094
+ /**
10095
+ * Convert a string value to a number.
10096
+ *
10097
+ * Parses a string containing a numeric value and returns it as a float.
10098
+ * Supports decimal numbers and negative numbers.
10099
+ *
10100
+ * Parameters:
10101
+ * - val: The value to convert to a number (must be a string)
10102
+ *
10103
+ * Behavior:
10104
+ * - If input is not a string, returns null
10105
+ * - If string cannot be parsed as a number, returns NaN
10106
+ * - Returns f64 (float) for all numeric values
10107
+ *
10108
+ * Examples:
10109
+ * - number("123") \u2192 123.0
10110
+ * - number("3.14") \u2192 3.14
10111
+ * - number("-42") \u2192 -42.0
10112
+ * - number("0.5") \u2192 0.5
10113
+ * - number("not a number") \u2192 NaN
10114
+ * - number(null) \u2192 null
10115
+ * - number(123) \u2192 null (not a string)
10116
+ *
10117
+ * @param val - The value to convert to a number
10118
+ * @returns The numeric value as a float, or null if input is not a string
10119
+ */
10120
+ export function numberValue(val: Value): Value {
10121
+ if (val.isString()) {
10122
+ const str = val.asString();
10123
+ // Parse the string using the same logic as parseIntValue
10124
+ let result: i64 = 0;
10125
+ let negative = false;
10126
+ let i = 0;
10127
+
10128
+ // Handle leading whitespace
10129
+ while (i < str.length && str.charCodeAt(i) == 32) {
10130
+ i++;
10131
+ }
10132
+
10133
+ // Handle negative sign
10134
+ if (i < str.length && str.charCodeAt(i) == 45) {
10135
+ // '-'
10136
+ negative = true;
10137
+ i++;
10138
+ }
10139
+
10140
+ // Parse digits
10141
+ let hasDigits = false;
10142
+ while (i < str.length) {
10143
+ const c = str.charCodeAt(i);
10144
+ if (c >= 48 && c <= 57) {
10145
+ // '0'-'9'
10146
+ result = result * 10 + (c - 48);
10147
+ hasDigits = true;
10148
+ i++;
10149
+ } else {
10150
+ break;
10151
+ }
10152
+ }
10153
+
10154
+ if (!hasDigits) return Value.Null();
10155
+
10156
+ const finalResult = negative ? -result : result;
10157
+ return Value.Float(<f64>finalResult);
10158
+ } else if (val.isNumber()) {
10159
+ return val;
10160
+ } else if (val.isBool()) {
10161
+ return val.asBool() ? Value.Float(1.0) : Value.Float(0.0);
10162
+ }
10163
+ return Value.Null();
10164
+ }
10165
+
10166
+ /**
10167
+ * Convert any value to a string.
10168
+ *
10169
+ * Converts the input value to its string representation.
10170
+ *
10171
+ * Parameters:
10172
+ * - val: Any value to convert to string
10173
+ *
10174
+ * Behavior:
10175
+ * - Boolean: "true" or "false"
10176
+ * - Number: numeric string representation
10177
+ * - String: returned as-is
10178
+ * - Null: null (returns null, not the string "null")
10179
+ * - Array/Object: "[object]"
10180
+ *
10181
+ * Examples:
10182
+ * - string(42) \u2192 "42"
10183
+ * - string(3.14) \u2192 "3.14"
10184
+ * - string(true) \u2192 "true"
10185
+ * - string(false) \u2192 "false"
10186
+ * - string("hello") \u2192 "hello"
10187
+ * - string(null) \u2192 null
10188
+ *
10189
+ * @param val - The value to convert to a string
10190
+ * @returns The string representation, or null if input is null
10191
+ */
10192
+ export function stringValue(val: Value): Value {
10193
+ if (val.isNull()) {
10194
+ return Value.Null();
10195
+ }
10196
+ if (val.isBool()) {
10197
+ return Value.String(val.asBool() ? 'true' : 'false');
10198
+ }
10199
+ if (val.isNumber()) {
10200
+ // Converting to string matches JavaScript String() coercion for consistency with JS engines.
10201
+ // Integers print without decimals (3 not 3.0), floats use AS toString() formatting.
10202
+ if (val.type == TYPE_INT) {
10203
+ return Value.String(val.intVal.toString());
10204
+ }
10205
+ const f = val.asFloat();
10206
+ // Check if it's a whole number by testing if remainder is 0
10207
+ if (f % 1.0 == 0.0) {
10208
+ return Value.String(i64(<i64>f).toString());
10209
+ }
10210
+ return Value.String(f.toString());
10211
+ }
10212
+ if (val.isString()) {
10213
+ return val;
10214
+ }
10215
+ // Arrays and objects fall back to a placeholder
10216
+ return Value.String('[object]');
10217
+ }
10218
+
10219
+ /**
10220
+ * Replace the first occurrence of a substring with another string.
10221
+ *
10222
+ * Returns a new string where the first occurrence of the search string
10223
+ * is replaced by the replacement string.
10224
+ *
10225
+ * Parameters:
10226
+ * - str: The string to modify
10227
+ * - search: The substring to search for
10228
+ * - replacement: The string to replace with
10229
+ *
10230
+ * Behavior:
10231
+ * - If the source string is not a string, returns null
10232
+ * - If the search value is not a string, returns null
10233
+ * - If the replacement value is not a string, returns null
10234
+ * - If the search string is empty, the replacement is inserted at the beginning
10235
+ * - If the search string is not found, returns the original string unchanged
10236
+ * - Only the first occurrence is replaced
10237
+ *
10238
+ * Examples:
10239
+ * - replace("hello", "l", "L") \u2192 "heLlo"
10240
+ * - replace("hello", "lo", "LO") \u2192 "heLLO"
10241
+ * - replace("hello", "x", "X") \u2192 "hello" (not found, unchanged)
10242
+ * - replace("hello hello", "l", "L") \u2192 "heLlo hello" (only first occurrence)
10243
+ * - replace("hello", "", "X") \u2192 "Xhello" (empty search, insert at start)
10244
+ * - replace("", "x", "X") \u2192 "" (empty source, no match)
10245
+ * - replace(null, "l", "L") \u2192 null (not a string)
10246
+ * - replace("hello", null, "L") \u2192 null (not a string)
10247
+ * - replace("hello", "l", null) \u2192 null (not a string)
10248
+ *
10249
+ * @param strVal - The string value to modify
10250
+ * @param searchVal - The substring value to search for
10251
+ * @param replacementVal - The replacement string value
10252
+ * @returns The modified string with first occurrence replaced, or null if inputs are invalid
10253
+ */
10254
+ export function replaceString(strVal: Value, searchVal: Value, replacementVal: Value): Value {
10255
+ if (!strVal.isString() || !searchVal.isString() || !replacementVal.isString())
10256
+ return Value.Null();
10257
+ const str = strVal.asString();
10258
+ const search = searchVal.asString();
10259
+ const replacement = replacementVal.asString();
10260
+
10261
+ // AssemblyScript's replace() replaces all occurrences by default
10262
+ // We need to replace only the first occurrence, so we use indexOf and string concatenation
10263
+ const index = str.indexOf(search);
10264
+
10265
+ // Search string not found - return original string
10266
+ if (index < 0) {
10267
+ return Value.String(str);
10268
+ }
10269
+
10270
+ // Build result: prefix + replacement + suffix
10271
+ const prefix = str.substring(0, index);
10272
+ const suffix = str.substring(index + search.length);
10273
+ return Value.String(prefix + replacement + suffix);
10274
+ }
10275
+
10276
+ /**
10277
+ * Replace all occurrences of a substring with another string.
10278
+ *
10279
+ * Returns a new string where all occurrences of the search string
10280
+ * are replaced by the replacement string.
10281
+ *
10282
+ * Parameters:
10283
+ * - str: The string to modify
10284
+ * - search: The substring to search for
10285
+ * - replacement: The string to replace with
10286
+ *
10287
+ * Behavior:
10288
+ * - If the source string is not a string, returns null
10289
+ * - If the search value is not a string, returns null
10290
+ * - If the replacement value is not a string, returns null
10291
+ * - If the search string is empty, the replacement is inserted between each character
10292
+ * - If the search string is not found, returns the original string unchanged
10293
+ * - All occurrences are replaced
10294
+ *
10295
+ * Examples:
10296
+ * - replaceAll("hello", "l", "L") \u2192 "heLLo"
10297
+ * - replaceAll("hello hello", "l", "L") \u2192 "heLLo heLLo"
10298
+ * - replaceAll("hello", "lo", "LO") \u2192 "heLLO"
10299
+ * - replaceAll("hello", "x", "X") \u2192 "hello" (not found, unchanged)
10300
+ * - replaceAll("hello", "", "X") \u2192 "XhXeXlXlXoX" (empty search, insert between chars)
10301
+ * - replaceAll("", "x", "X") \u2192 "" (empty source, no match)
10302
+ * - replaceAll(null, "l", "L") \u2192 null (not a string)
10303
+ * - replaceAll("hello", null, "L") \u2192 null (not a string)
10304
+ * - replaceAll("hello", "l", null) \u2192 null (not a string)
10305
+ *
10306
+ * @param strVal - The string value to modify
10307
+ * @param searchVal - The substring value to search for
10308
+ * @param replacementVal - The replacement string value
10309
+ * @returns The modified string with all occurrences replaced, or null if inputs are invalid
10310
+ */
10311
+ export function replaceAllString(strVal: Value, searchVal: Value, replacementVal: Value): Value {
10312
+ if (!strVal.isString() || !searchVal.isString() || !replacementVal.isString())
10313
+ return Value.Null();
10314
+ let str = strVal.asString();
10315
+ const search = searchVal.asString();
10316
+ const replacement = replacementVal.asString();
10317
+
10318
+ // Handle empty search string - insert replacement between each character
10319
+ if (search.length == 0) {
10320
+ let result = replacement;
10321
+ for (let i = 0; i < str.length; i++) {
10322
+ result += str.charAt(i) + replacement;
10323
+ }
10324
+ return Value.String(result);
10325
+ }
10326
+
10327
+ // Use split and join for O(n) complexity instead of quadratic substring approach
10328
+ const parts = str.split(search);
10329
+ return Value.String(parts.join(replacement));
10330
+ }
10331
+
10332
+ // === Value Type Conversion ===
10333
+
10334
+ /**
10335
+ * Convert a Value to its string representation.
10336
+ *
10337
+ * This follows JavaScript's String() conversion rules:
10338
+ * - Number: converted using JS toString()
10339
+ * - Boolean: "true" or "false"
10340
+ * - String: returned as-is
10341
+ * - Null: "null"
10342
+ *
10343
+ * @param val - The value to convert
10344
+ * @returns The string representation
10345
+ */
10346
+ function valueToString(val: Value): string {
10347
+ if (val.isNull()) {
10348
+ return 'null';
10349
+ }
10350
+ if (val.isBool()) {
10351
+ return val.asBool() ? 'true' : 'false';
10352
+ }
10353
+ if (val.isNumber()) {
10354
+ // Converting to string matches JavaScript String() coercion for consistency with JS engines.
10355
+ // Integers print without decimals (3 not 3.0), floats use AS toString() formatting.
10356
+ if (val.type == TYPE_INT) {
10357
+ return val.intVal.toString();
10358
+ }
10359
+ const f = val.asFloat();
10360
+ // Check if it's a whole number by testing if remainder is 0
10361
+ if (f % 1.0 == 0.0) {
10362
+ return i64(<i64>f).toString();
10363
+ }
10364
+ return f.toString();
10365
+ }
10366
+ if (val.isString()) {
10367
+ return val.asString();
10368
+ }
10369
+ // Arrays and objects fall back to a placeholder
10370
+ return '[object]';
10371
+ }
10372
+
10373
+ // === Comparison Helpers ===
10374
+
10375
+ export function valuesEqual(a: Value, b: Value): bool {
10376
+ if (a.type != b.type) {
10377
+ // Allow int/float comparison
10378
+ if (a.isNumber() && b.isNumber()) {
10379
+ return a.asFloat() == b.asFloat();
10380
+ }
10381
+ return false;
10382
+ }
10383
+ if (a.isBool()) return a.boolVal == b.boolVal;
10384
+ if (a.type == TYPE_INT) return a.intVal == b.intVal;
10385
+ if (a.type == TYPE_FLOAT) return a.floatVal == b.floatVal;
10386
+ if (a.isString()) return a.asString() == b.asString();
10387
+ if (a.isNull()) return true; // null == null
10388
+ return false; // Arrays/objects: reference equality not supported
10389
+ }
10390
+
10391
+ /**
10392
+ * Compare two values for ordering.
10393
+ *
10394
+ * Returns:
10395
+ * - Negative number if a < b
10396
+ * - Zero if a == b
10397
+ * - Positive number if a > b
10398
+ *
10399
+ * Comparison rules:
10400
+ * - Strings: Lexicographic comparison using UTF-16 code units (case-sensitive)
10401
+ * - Numbers: Numeric comparison (int and float are comparable)
10402
+ * - Booleans: false < true
10403
+ * - Null: Treated as less than all other values
10404
+ * - Mixed types: Comparisons between incompatible types return 0 (not recommended)
10405
+ *
10406
+ * Examples:
10407
+ * - compareValues(String("apple"), String("banana")) \u2192 negative
10408
+ * - compareValues(String("zebra"), String("apple")) \u2192 positive
10409
+ * - compareValues(String("abc"), String("abc")) \u2192 0
10410
+ * - compareValues(Float(3.14), Float(2.71)) \u2192 positive
10411
+ * - compareValues(Bool(false), Bool(true)) \u2192 negative
10412
+ *
10413
+ * @param a - The left operand to compare
10414
+ * @param b - The right operand to compare
10415
+ * @returns Comparison result (-1, 0, or 1)
10416
+ */
10417
+ export function compareValues(a: Value, b: Value): i32 {
10418
+ // Handle null - null is less than all other values
10419
+ if (a.isNull()) return b.isNull() ? 0 : -1;
10420
+ if (b.isNull()) return 1;
10421
+
10422
+ // Allow int/float comparison for numbers
10423
+ if (a.isNumber() && b.isNumber()) {
10424
+ const aNum = a.asFloat();
10425
+ const bNum = b.asFloat();
10426
+ if (aNum < bNum) return -1;
10427
+ if (aNum > bNum) return 1;
10428
+ return 0;
10429
+ }
10430
+
10431
+ // String comparison - lexicographic, case-sensitive
10432
+ if (a.isString() && b.isString()) {
10433
+ const aStr = a.asString();
10434
+ const bStr = b.asString();
10435
+ if (aStr < bStr) return -1;
10436
+ if (aStr > bStr) return 1;
10437
+ return 0;
10438
+ }
10439
+
10440
+ // Boolean comparison
10441
+ if (a.isBool() && b.isBool()) {
10442
+ const aBool = a.asBool();
10443
+ const bBool = b.asBool();
10444
+ if (!aBool && bBool) return -1;
10445
+ if (aBool && !bBool) return 1;
10446
+ return 0;
10447
+ }
10448
+
10449
+ // Mixed types or unsupported types - treat as equal (but probably shouldn't happen)
10450
+ return 0;
10451
+ }
10452
+
10453
+ // === Arithmetic Operators ===
10454
+
10455
+ /**
10456
+ * Add two values, supporting both numeric addition and string concatenation.
10457
+ *
10458
+ * Type coercion rules:
10459
+ * - If either operand is a string, both are converted to strings and concatenated
10460
+ * - Otherwise, both operands are treated as numbers and added together
10461
+ *
10462
+ * Examples:
10463
+ * - "hello" + " " + "world" \u2192 "hello world"
10464
+ * - "count: " + 5 \u2192 "count: 5"
10465
+ * - 1 + 2 \u2192 3
10466
+ * - true + false \u2192 1 (true=1, false=0, numeric addition)
10467
+ * - null + 5 \u2192 5 (null=0, numeric addition)
10468
+ * - "prefix" + null \u2192 "prefixnull" (string concatenation)
10469
+ *
10470
+ * @param a - The left operand
10471
+ * @param b - The right operand
10472
+ * @returns The sum or concatenation result
10473
+ */
10474
+ export function addValues(a: Value, b: Value): Value {
10475
+ // String concatenation takes priority over numeric addition
10476
+ // If either operand is a string, concatenate both as strings
10477
+ if (a.isString() || b.isString()) {
10478
+ const leftStr = a.isString() ? a.asString() : valueToString(a);
10479
+ const rightStr = b.isString() ? b.asString() : valueToString(b);
10480
+ return Value.String(leftStr + rightStr);
10481
+ }
10482
+
10483
+ // Numeric addition
10484
+ const aNum = a.isNumber() ? a.asFloat() : a.isBool() ? (a.asBool() ? 1.0 : 0.0) : 0.0;
10485
+ const bNum = b.isNumber() ? b.asFloat() : b.isBool() ? (b.asBool() ? 1.0 : 0.0) : 0.0;
10486
+ return Value.Float(aNum + bNum);
10487
+ }
10488
+
10489
+ export function subtractValues(a: Value, b: Value): Value {
10490
+ if (!a.isNumber() || !b.isNumber()) return Value.Null();
10491
+ const aVal = a.asFloat();
10492
+ const bVal = b.asFloat();
10493
+ return Value.Float(aVal - bVal);
10494
+ }
10495
+
10496
+ export function multiplyValues(a: Value, b: Value): Value {
10497
+ if (!a.isNumber() || !b.isNumber()) return Value.Null();
10498
+ const aVal = a.asFloat();
10499
+ const bVal = b.asFloat();
10500
+ return Value.Float(aVal * bVal);
10501
+ }
10502
+
10503
+ export function divideValues(a: Value, b: Value): Value {
10504
+ if (!a.isNumber() || !b.isNumber()) return Value.Null();
10505
+ const aVal = a.asFloat();
10506
+ const bVal = b.asFloat();
10507
+ if (bVal == 0) return Value.Null(); // Division by zero
10508
+ return Value.Float(aVal / bVal);
10509
+ }
10510
+
10511
+ export function moduloValues(a: Value, b: Value): Value {
10512
+ if (!a.isNumber() || !b.isNumber()) return Value.Null();
10513
+ const aVal = a.asFloat();
10514
+ const bVal = b.asFloat();
10515
+ if (bVal == 0) return Value.Null(); // Division by zero
10516
+ // Use fmod for floating point modulo
10517
+ const result = aVal - Math.trunc(aVal / bVal) * bVal;
10518
+ return Value.Float(result);
10519
+ }
10520
+
10521
+ export function powerValues(a: Value, b: Value): Value {
10522
+ if (!a.isNumber() || !b.isNumber()) return Value.Null();
10523
+ const aVal = a.asFloat();
10524
+ const bVal = b.asFloat();
10525
+ const result = Math.pow(aVal, bVal);
10526
+ return Value.Float(result);
10527
+ }
10528
+
10529
+ export function negateValue(a: Value): Value {
10530
+ if (!a.isNumber()) return Value.Null();
10531
+ const n = a.asFloat();
10532
+ return Value.Float(-n);
10533
+ }
10534
+
10535
+ /**
10536
+ * Get a property from an object Value.
10537
+ *
10538
+ * @param obj - The Value to get the property from (should be an object)
10539
+ * @param property - The property name to get
10540
+ * @returns The Value of the property, or Null if not found or obj is null/not an object
10541
+ */
10542
+ export function getProperty(obj: Value, property: string): Value {
10543
+ if (obj.isNull()) return Value.Null();
10544
+ if (!obj.isObject()) return Value.Null();
10545
+
10546
+ const map = obj.asObject();
10547
+ if (map.has(property)) {
10548
+ return map.get(property);
10549
+ }
10550
+ return Value.Null();
10551
+ }
10552
+
10553
+ /**
10554
+ * Get an element from an array at the specified index.
10555
+ *
10556
+ * @param arr - The Value to get the element from (should be an array)
10557
+ * @param idx - The index to get (must be a number Value)
10558
+ * @returns The Value at the index, or Null if out of bounds or arr is null/not an array
10559
+ */
10560
+ export function getIndex(arr: Value, idx: Value): Value {
10561
+ if (arr.isNull()) return Value.Null();
10562
+ if (idx.isNull()) return Value.Null();
10563
+
10564
+ // Handle object access with string keys (e.g., baseCosts["FastTrack"])
10565
+ if (arr.isObject()) {
10566
+ if (idx.isString()) {
10567
+ const obj = arr.asObject();
10568
+ const key = idx.asString();
10569
+ if (obj.has(key)) {
10570
+ return obj.get(key);
10571
+ }
10572
+ return Value.Null();
10573
+ }
10574
+ // If idx is a number, convert to string for object key lookup
10575
+ // This handles cases like obj[0] where obj is {"0": value}
10576
+ const keyStr = idx.asString();
10577
+ const obj = arr.asObject();
10578
+ if (obj.has(keyStr)) {
10579
+ return obj.get(keyStr);
10580
+ }
10581
+ return Value.Null();
10582
+ }
10583
+
10584
+ if (!arr.isArray()) return Value.Null();
10585
+
10586
+ const array = arr.asArray();
10587
+ const index = idx.asFloat();
10588
+
10589
+ // Handle negative indices (from end of array)
10590
+ let actualIndex = <i32>index;
10591
+ if (actualIndex < 0) {
10592
+ actualIndex = <i32>(array.length + actualIndex);
10593
+ }
10594
+
10595
+ // Check bounds
10596
+ if (actualIndex < 0 || actualIndex >= array.length) {
10597
+ return Value.Null();
10598
+ }
10599
+
10600
+ return array[actualIndex];
10601
+ }
10602
+
10603
+ /**
10604
+ * Create an interval object for range checking.
10605
+ *
10606
+ * @param start - Start value of the interval
10607
+ * @param end - End value of the interval
10608
+ * @param startInclusive - Whether the start is inclusive (true) or exclusive (false)
10609
+ * @param endInclusive - Whether the end is inclusive (true) or exclusive (false)
10610
+ * @returns An object representing the interval
10611
+ */
10612
+ export function createInterval(
10613
+ start: Value,
10614
+ end: Value,
10615
+ startInclusive: bool,
10616
+ endInclusive: bool,
10617
+ ): Value {
10618
+ const intervalMap = new Map<string, Value>();
10619
+ intervalMap.set('__isInterval', Value.Bool(true));
10620
+ intervalMap.set('start', start);
10621
+ intervalMap.set('end', end);
10622
+ intervalMap.set('startInclusive', Value.Bool(startInclusive));
10623
+ intervalMap.set('endInclusive', Value.Bool(endInclusive));
10624
+ return Value.Object(intervalMap);
10625
+ }
10626
+
10627
+ /**
10628
+ * Check if a value is within an interval.
10629
+ *
10630
+ * @param needle - The value to check
10631
+ * @param interval - The interval object created by createInterval
10632
+ * @returns true if the value is within the interval, false otherwise
10633
+ */
10634
+ function valueInInterval(needle: Value, interval: Value): bool {
10635
+ if (!interval.isObject()) return false;
10636
+
10637
+ const intervalObj = interval.asObject();
10638
+
10639
+ // Check if this is actually an interval object
10640
+ if (!intervalObj.has('__isInterval')) return false;
10641
+
10642
+ const start = intervalObj.get('start');
10643
+ const end = intervalObj.get('end');
10644
+ const startInclusive = intervalObj.get('startInclusive').asBool();
10645
+ const endInclusive = intervalObj.get('endInclusive').asBool();
10646
+
10647
+ const cmpStart = compareValues(needle, start);
10648
+ const cmpEnd = compareValues(needle, end);
10649
+
10650
+ // Check start boundary
10651
+ let passesStart: bool;
10652
+ if (startInclusive) {
10653
+ passesStart = cmpStart >= 0;
10654
+ } else {
10655
+ passesStart = cmpStart > 0;
10656
+ }
10657
+
10658
+ // Check end boundary
10659
+ let passesEnd: bool;
10660
+ if (endInclusive) {
10661
+ passesEnd = cmpEnd <= 0;
10662
+ } else {
10663
+ passesEnd = cmpEnd < 0;
10664
+ }
10665
+
10666
+ return passesStart && passesEnd;
10667
+ }
10668
+
10669
+ /**
10670
+ * Check if a value is in an array, a substring is in a string, or a value is in an interval.
10671
+ * For arrays: checks if the exact value exists in the array
10672
+ * For strings: checks if the needle string is a substring of the haystack
10673
+ * For intervals: checks if the value is within the range
10674
+ *
10675
+ * @param needle - The Value or string to search for
10676
+ * @param haystack - The Value to search in (should be an array, string, or interval)
10677
+ * @returns true if found, false otherwise
10678
+ */
10679
+ export function valueIn(needle: Value, haystack: Value): Value {
10680
+ if (needle.isNull() || haystack.isNull()) return Value.Bool(false);
10681
+
10682
+ // Check if haystack is an interval
10683
+ if (haystack.isObject()) {
10684
+ const obj = haystack.asObject();
10685
+ if (obj.has('__isInterval')) {
10686
+ return Value.Bool(valueInInterval(needle, haystack));
10687
+ }
10688
+ }
10689
+
10690
+ // Check if haystack is an array
10691
+ if (haystack.isArray()) {
10692
+ const array = haystack.asArray();
10693
+ for (let i = 0; i < array.length; i++) {
10694
+ if (valuesEqual(needle, unchecked(array[i]))) {
10695
+ return Value.Bool(true);
10696
+ }
10697
+ }
10698
+ return Value.Bool(false);
10699
+ }
10700
+
10701
+ // Check if haystack is a string
10702
+ if (haystack.isString() && needle.isString()) {
10703
+ const hayStr = haystack.asString();
10704
+ const needStr = needle.asString();
10705
+ if (hayStr.indexOf(needStr) >= 0) {
10706
+ return Value.Bool(true);
10707
+ }
10708
+ return Value.Bool(false);
10709
+ }
10710
+
10711
+ return Value.Bool(false);
10712
+ }
10713
+ `;
10714
+ var RUNTIME_TABLES = "// assembly/runtime/tables.ts (AssemblyScript)\nimport { Value } from './values';\nimport { Context } from './context';\nimport { valuesEqual } from './expressions';\n\n/**\n * Condition for decision table row evaluation.\n * Uses Value tagged union consistently.\n */\nexport class Condition {\n field: string;\n op: string;\n value: Value;\n\n constructor(field: string, op: string, value: Value) {\n this.field = field;\n this.op = op;\n this.value = value;\n }\n\n evaluate(ctx: Context): bool {\n const actual = ctx.get(this.field);\n const expected = this.value;\n\n // Handle null cases\n if (actual.isNull()) {\n if (this.op == '==' && expected.isNull()) return true;\n if (this.op == '!=' && !expected.isNull()) return true;\n return false;\n }\n\n // Numeric comparisons\n if (actual.isNumber() && expected.isNumber()) {\n const a = actual.asFloat();\n const e = expected.asFloat();\n\n if (this.op == '==') return a == e;\n if (this.op == '!=') return a != e;\n if (this.op == '<') return a < e;\n if (this.op == '<=') return a <= e;\n if (this.op == '>') return a > e;\n if (this.op == '>=') return a >= e;\n }\n\n // String comparisons\n if (actual.isString() && expected.isString()) {\n const a = actual.asString();\n const e = expected.asString();\n\n if (this.op == '==') return a == e;\n if (this.op == '!=') return a != e;\n // Lexicographic comparison for strings\n if (this.op == '<') return a < e;\n if (this.op == '<=') return a <= e;\n if (this.op == '>') return a > e;\n if (this.op == '>=') return a >= e;\n }\n\n // Boolean equality\n if (actual.isBool() && expected.isBool()) {\n if (this.op == '==') return actual.boolVal == expected.boolVal;\n if (this.op == '!=') return actual.boolVal != expected.boolVal;\n }\n\n // Generic equality via valuesEqual\n if (this.op == '==') return valuesEqual(actual, expected);\n if (this.op == '!=') return !valuesEqual(actual, expected);\n\n return false;\n }\n}\n\n/**\n * Check if a value is within an interval (range).\n * Supports open/closed boundaries: [a..b], (a..b), [a..b), (a..b]\n */\nexport function inInterval(\n val: Value,\n start: Value,\n end: Value,\n startInclusive: bool,\n endInclusive: bool,\n): bool {\n if (!val.isNumber() || !start.isNumber() || !end.isNumber()) {\n return false;\n }\n\n const v = val.asFloat();\n const s = start.asFloat();\n const e = end.asFloat();\n\n const aboveStart = startInclusive ? v >= s : v > s;\n const belowEnd = endInclusive ? v <= e : v < e;\n\n return aboveStart && belowEnd;\n}\n";
10715
+ var RUNTIME_MEMORY = "// assembly/runtime/memory.ts (AssemblyScript)\n// Memory layout and management utilities for WASM linear memory\n\nimport { writeString } from './strings';\n\n// ============================================================================\n// Memory Layout Constants\n// ============================================================================\n\n// Reserved space for null pointer trap (first 4 bytes)\n// Accessing address 0 should trap, catching null/nullish pointer bugs\nexport const NULL_POINTER_TRAP_SIZE: usize = 4;\n\n// Fixed memory region offsets\nexport const INPUT_BUFFER_START: usize = NULL_POINTER_TRAP_SIZE; // 0x0004\n\n// Schema-specific field layouts require different buffer sizes. Computing sizes at\n// compile time based on actual schema ensures optimal memory usage per decision.\nexport let INPUT_BUFFER_SIZE: usize = 0;\n\n// Variable-length input data starts after the fixed input buffer\nexport let INPUT_VAR_START: usize = 0;\n\n// Output buffer follows input variable data\nexport let OUTPUT_START: usize = 0;\n\n// Computed at compilation time based on input/output schema field sizes\nexport let OUTPUT_BUFFER_SIZE: usize = 0;\n\n// Variable-length output data starts after the fixed output buffer\nexport let OUTPUT_VAR_START: usize = 0;\n\n// Runtime heap for intermediate values (temporary arrays, string concat buffers, etc.)\nexport let HEAP_START: usize = 0;\n\n// ============================================================================\n// Header Constants\n// ============================================================================\n\n// Schema hash size in bytes\nexport const SCHEMA_HASH_SIZE: usize = 8;\n\n// String descriptor format: [offset (4 bytes)] [length (4 bytes)]\nexport const STRING_DESC_SIZE: usize = 8;\n\n// Array descriptor format: [offset (4 bytes)] [length (4 bytes)]\nexport const ARRAY_DESC_SIZE: usize = 8;\n\n// ============================================================================\n// Memory Allocation\n// ============================================================================\n\n// Decision evaluation is single-pass (no GC needed), and most allocations are temporary\n// (freed after result is marshaled). Linear bump allocator is fast and simple.\nlet heapPointer: usize = 0;\n\n/**\n * Initialize memory layout.\n *\n * This function must be called once at startup with the computed sizes\n * for the input and output buffers based on the schema.\n *\n * @param inputFixedSize - Size of fixed input fields in bytes\n * @param outputFixedSize - Size of fixed output fields in bytes\n */\nexport function initializeMemoryLayout(inputFixedSize: usize, outputFixedSize: usize): void {\n INPUT_BUFFER_SIZE = inputFixedSize;\n INPUT_VAR_START = INPUT_BUFFER_START + inputFixedSize;\n\n OUTPUT_BUFFER_SIZE = outputFixedSize;\n OUTPUT_START = INPUT_VAR_START; // Output follows input variable data\n OUTPUT_VAR_START = OUTPUT_START + outputFixedSize;\n\n HEAP_START = OUTPUT_VAR_START; // Heap follows output variable data\n heapPointer = HEAP_START; // Reset heap pointer\n}\n\n/**\n * Get the current memory layout info.\n *\n * @returns Object with all memory region offsets and sizes\n */\nexport function getMemoryLayout(): MemoryLayout {\n return new MemoryLayout(\n NULL_POINTER_TRAP_SIZE,\n INPUT_BUFFER_START,\n INPUT_BUFFER_SIZE,\n INPUT_VAR_START,\n OUTPUT_START,\n OUTPUT_BUFFER_SIZE,\n OUTPUT_VAR_START,\n HEAP_START,\n );\n}\n\n/**\n * Memory layout information structure.\n */\nexport class MemoryLayout {\n nullPointerTrapSize: usize;\n inputBufferStart: usize;\n inputBufferSize: usize;\n inputVarStart: usize;\n outputStart: usize;\n outputBufferSize: usize;\n outputVarStart: usize;\n heapStart: usize;\n\n constructor(\n nullPointerTrapSize: usize,\n inputBufferStart: usize,\n inputBufferSize: usize,\n inputVarStart: usize,\n outputStart: usize,\n outputBufferSize: usize,\n outputVarStart: usize,\n heapStart: usize,\n ) {\n this.nullPointerTrapSize = nullPointerTrapSize;\n this.inputBufferStart = inputBufferStart;\n this.inputBufferSize = inputBufferSize;\n this.inputVarStart = inputVarStart;\n this.outputStart = outputStart;\n this.outputBufferSize = outputBufferSize;\n this.outputVarStart = outputVarStart;\n this.heapStart = heapStart;\n }\n}\n\n// ============================================================================\n// Simple Linear Heap Allocator\n// ============================================================================\n\n/**\n * Allocate memory from the runtime heap.\n *\n * A simple linear allocator - memory is never freed during a single evaluation.\n * Suitable for short-lived operations like decision table evaluation.\n *\n * Memory is never freed during a single evaluation because:\n * (1) evaluations are short-lived (milliseconds),\n * (2) heap is reset between evaluations, and\n * (3) avoiding free list management simplifies allocator and improves performance.\n *\n * @param size - Number of bytes to allocate (must be > 0)\n * @returns Pointer to allocated memory\n * @throws Calls abort() on OUT_OF_MEMORY - this is a critical error that halts execution\n */\nexport function heapAlloc(size: usize): usize {\n if (size == 0) return 0;\n\n // Get current memory size in bytes (1 page = 64KB = 65536 bytes)\n const memorySize: usize = (<usize>memory.size()) << 16;\n\n // Align the heap pointer to 8-byte boundary\n const alignedPtr = alignPtr(heapPointer, 8);\n\n // Check for overflow when computing aligned pointer\n // If alignedPtr wrapped around (became smaller than heapPointer), we've overflowed\n if (alignedPtr < heapPointer) {\n SetLastError(RuntimeErrorCode.OUT_OF_MEMORY);\n return 0;\n }\n\n // Check for overflow when computing new heap pointer\n // If newHeapPointer would wrap around, we've overflowed\n const maxAllowedSize = memorySize - alignedPtr;\n if (size > maxAllowedSize) {\n SetLastError(RuntimeErrorCode.OUT_OF_MEMORY);\n return 0;\n }\n\n const newHeapPointer = alignedPtr + size;\n\n // Bounds check: ensure new heap pointer doesn't exceed memory\n if (newHeapPointer > memorySize) {\n SetLastError(RuntimeErrorCode.OUT_OF_MEMORY);\n return 0;\n }\n\n // All checks passed, update heap pointer and return allocation\n heapPointer = newHeapPointer;\n return alignedPtr;\n}\n\n/**\n * Get the current heap pointer (next allocation will start here).\n *\n * @returns Current heap pointer address\n */\nexport function getHeapPointer(): usize {\n return heapPointer;\n}\n\n/**\n * Reset the heap pointer to the start of the heap region.\n *\n * This allows reusing heap memory for multiple evaluations.\n */\nexport function resetHeap(): void {\n heapPointer = HEAP_START;\n}\n\n/**\n * Save the current heap pointer state.\n *\n * @returns Current heap pointer address\n */\nexport function saveHeapPointer(): usize {\n return heapPointer;\n}\n\n/**\n * Restore the heap pointer to a previously saved state.\n *\n * @param saved - Saved heap pointer address from saveHeapPointer()\n */\nexport function restoreHeapPointer(saved: usize): usize {\n const old = heapPointer;\n heapPointer = saved;\n return old;\n}\n\n// ============================================================================\n// Pointer Utilities\n// ============================================================================\n\n/**\n * Align a pointer to the specified alignment boundary.\n *\n * @param ptr - Pointer to align\n * @param alignment - Alignment boundary (must be a power of 2)\n * @returns Aligned pointer\n */\nexport function alignPtr(ptr: usize, alignment: usize): usize {\n return (ptr + alignment - 1) & ~(alignment - 1);\n}\n\n/**\n * Check if a pointer is null (0).\n *\n * @param ptr - Pointer to check\n * @returns true if pointer is 0, false otherwise\n */\nexport function isNullPtr(ptr: usize): bool {\n return ptr == 0;\n}\n\n/**\n * Check if a pointer is valid (non-null and aligned).\n *\n * @param ptr - Pointer to check\n * @param alignment - Required alignment (default: 4 bytes)\n * @returns true if pointer is valid, false otherwise\n */\nexport function isValidPtr(ptr: usize, alignment: usize = 4): bool {\n if (ptr == 0) return false;\n return (ptr & (alignment - 1)) == 0;\n}\n\n// ============================================================================\n// Buffer Access Helpers\n// ============================================================================\n\n/**\n * Get the pointer to a schema hash field.\n *\n * @param basePtr - Base pointer of the buffer\n * @returns Pointer to the schema hash field\n */\nexport function getSchemaHashPtr(basePtr: usize): usize {\n return basePtr;\n}\n\n/**\n * Read a schema hash from a buffer.\n *\n * @param basePtr - Base pointer of the buffer\n * @returns Schema hash as u64\n */\nexport function readSchemaHash(basePtr: usize): u64 {\n return load<u64>(basePtr);\n}\n\n/**\n * Write a schema hash to a buffer.\n *\n * @param basePtr - Base pointer of the buffer\n * @param hash - Schema hash to write\n */\nexport function writeSchemaHash(basePtr: usize, hash: u64): void {\n store<u64>(basePtr, hash);\n}\n\n/**\n * Get the pointer to a field in a fixed-size buffer.\n *\n * @param bufferPtr - Base pointer of the buffer\n * @param fieldOffset - Offset of the field from the buffer start\n * @returns Pointer to the field\n */\nexport function getFieldPtr(bufferPtr: usize, fieldOffset: usize): usize {\n return bufferPtr + fieldOffset;\n}\n\n// ============================================================================\n// String Descriptor Helpers\n// ============================================================================\n\n/**\n * Get the offset and length components of a string descriptor.\n *\n * String descriptor format: [offset (4 bytes)] [length (4 bytes)]\n *\n * @param descPtr - Pointer to the string descriptor\n * @returns Object with offset and length\n */\nexport function readStringDesc(descPtr: usize): StringDesc {\n return new StringDesc(load<u32>(descPtr), load<u32>(descPtr + 4));\n}\n\n/**\n * Write a string descriptor.\n *\n * @param descPtr - Pointer to the string descriptor\n * @param offset - Offset to the string data\n * @param length - Length of the string data\n */\nexport function writeStringDesc(descPtr: usize, offset: u32, length: u32): void {\n store<u32>(descPtr, offset);\n store<u32>(descPtr + 4, length);\n}\n\n/**\n * String descriptor structure.\n */\nexport class StringDesc {\n offset: u32;\n length: u32;\n\n constructor(offset: u32, length: u32) {\n this.offset = offset;\n this.length = length;\n }\n\n isEmpty(): bool {\n return this.length == 0;\n }\n}\n\n// ============================================================================\n// Array Descriptor Helpers\n// ============================================================================\n\n/**\n * Get the offset and length components of an array descriptor.\n *\n * Array descriptor format: [offset (4 bytes)] [length (4 bytes)]\n *\n * @param descPtr - Pointer to the array descriptor\n * @returns Object with offset and length\n */\nexport function readArrayDesc(descPtr: usize): ArrayDesc {\n return new ArrayDesc(load<u32>(descPtr), load<u32>(descPtr + 4));\n}\n\n/**\n * Write an array descriptor.\n *\n * @param descPtr - Pointer to the array descriptor\n * @param offset - Offset to the array data\n * @param length - Number of elements in the array\n */\nexport function writeArrayDesc(descPtr: usize, offset: u32, length: u32): void {\n store<u32>(descPtr, offset);\n store<u32>(descPtr + 4, length);\n}\n\n/**\n * Array descriptor structure.\n */\nexport class ArrayDesc {\n offset: u32;\n length: u32;\n\n constructor(offset: u32, length: u32) {\n this.offset = offset;\n this.length = length;\n }\n\n isEmpty(): bool {\n return this.length == 0;\n }\n}\n\n// ============================================================================\n// Runtime Error Handling\n// ============================================================================\n\n// Error codes for runtime failures\nexport enum RuntimeErrorCode {\n NONE = 0,\n SCHEMA_MISMATCH = 1,\n NULL_POINTER = 2,\n INVALID_ACCESS = 3,\n EVALUATION_ERROR = 4,\n OUT_OF_MEMORY = 5,\n}\n\n// Last error that occurred during evaluation\nlet lastErrorCode: RuntimeErrorCode = RuntimeErrorCode.NONE;\n\n// Optional error message (allocated on heap for variable-length messages)\nlet errorMessagePtr: usize = 0;\n\n/**\n * Check if an error code represents a critical error that should abort execution.\n *\n * Critical errors are unrecoverable conditions where continuing execution would\n * likely lead to memory corruption, crashes, or incorrect results. These errors\n * call abort() to immediately halt the WASM module.\n *\n * Critical errors:\n * - OUT_OF_MEMORY: Memory allocation failed; continuing could corrupt state\n * - NULL_POINTER: Null pointer dereference; memory safety violation\n * - INVALID_ACCESS: Invalid memory access; memory safety violation\n *\n * Non-critical errors (set flag, caller must check hasError()):\n * - SCHEMA_MISMATCH: Input doesn't match schema; recoverable\n * - EVALUATION_ERROR: Logic error in decision evaluation; recoverable\n *\n * @param code - The error code to check\n * @returns true if the error is critical and should abort\n */\nexport function isCriticalError(code: RuntimeErrorCode): bool {\n return (\n code === RuntimeErrorCode.OUT_OF_MEMORY ||\n code === RuntimeErrorCode.NULL_POINTER ||\n code === RuntimeErrorCode.INVALID_ACCESS\n );\n}\n\n/**\n * Get the last error code that occurred during evaluation.\n *\n * @returns The last error code\n */\nexport function getLastError(): RuntimeErrorCode {\n return lastErrorCode;\n}\n\n/**\n * Set the last error code and optional message.\n *\n * For critical errors (OUT_OF_MEMORY, NULL_POINTER, INVALID_ACCESS), this function\n * calls abort() to immediately halt execution. Critical errors indicate unrecoverable\n * conditions where continuing would lead to memory corruption or undefined behavior.\n *\n * For non-critical errors (SCHEMA_MISMATCH, EVALUATION_ERROR), callers MUST check\n * hasError() after operations that can fail and handle the error appropriately.\n * Failure to check error state may result in incorrect evaluation results.\n *\n * @param code - The error code\n * @param message - Optional error message (allocated on heap)\n */\nexport function SetLastError(code: RuntimeErrorCode, message: usize = 0): void {\n lastErrorCode = code;\n errorMessagePtr = message;\n\n // Critical errors abort immediately to prevent corrupted state propagation\n if (isCriticalError(code)) {\n abort();\n }\n}\n\n/**\n * Clear the last error (reset to NONE).\n */\nexport function clearLastError(): void {\n lastErrorCode = RuntimeErrorCode.NONE;\n errorMessagePtr = 0;\n}\n\n/**\n * Set an error with a string message.\n *\n * This is a convenience function that combines SetLastError() with writeString().\n * The error message is allocated on the heap and will be freed by the next error.\n *\n * For critical errors (OUT_OF_MEMORY, NULL_POINTER, INVALID_ACCESS), this function\n * will call abort() after setting the error, immediately halting execution.\n *\n * For non-critical errors (SCHEMA_MISMATCH, EVALUATION_ERROR), callers MUST check\n * hasError() after operations that can fail.\n *\n * @param code - The error code\n * @param message - The error message string\n */\nexport function SetLastErrorWithMessage(code: RuntimeErrorCode, message: string): void {\n const messagePtr = writeString(message);\n SetLastError(code, messagePtr);\n}\n\n/**\n * Get the pointer to the error message.\n *\n * @returns Pointer to error message (0 if no message)\n */\nexport function getErrorMessagePtr(): usize {\n return errorMessagePtr;\n}\n\n/**\n * Check if an error occurred.\n *\n * Callers MUST check this function after operations that can set non-critical errors\n * (SCHEMA_MISMATCH, EVALUATION_ERROR). Critical errors (OUT_OF_MEMORY, NULL_POINTER,\n * INVALID_ACCESS) abort immediately and never reach this check.\n *\n * Example usage:\n * ```assemblyscript\n * let result = someOperationThatCanFail();\n * if (hasError()) {\n * // Handle error - return early, propagate error, etc.\n * return Value.Null();\n * }\n * // Continue with valid result\n * ```\n *\n * @returns true if a non-critical error occurred, false otherwise\n */\nexport function hasError(): bool {\n return lastErrorCode !== RuntimeErrorCode.NONE;\n}\n\n// ============================================================================\n// Memory Validation Helpers\n// ============================================================================\n\n/**\n * Check if a pointer is within the valid memory range.\n *\n * This is a basic check that can help catch out-of-bounds accesses.\n * Note: In production, you may want to disable these checks for performance.\n *\n * @param ptr - Pointer to check\n * @param size - Size of the region to check\n * @return true if pointer is within valid range, false otherwise\n */\nexport function isValidMemoryRange(ptr: usize, size: usize): bool {\n if (ptr == 0) return false;\n\n // Get current memory size (AssemblyScript runtime provides this)\n const memorySize: usize = memory.size() << 16; // 1 page = 64KB\n\n return ptr + size <= memorySize;\n}\n\n/**\n * Check if a pointer is within the input buffer region.\n *\n * @param ptr - Pointer to check\n * @return true if pointer is within input buffer, false otherwise\n */\nexport function isWithinInputBuffer(ptr: usize): bool {\n return ptr >= INPUT_BUFFER_START && ptr < INPUT_VAR_START;\n}\n\n/**\n * Check if a pointer is within the input variable data region.\n *\n * @param ptr - Pointer to check\n * @return true if pointer is within input variable data, false otherwise\n */\nexport function isWithinInputVar(ptr: usize): bool {\n return ptr >= INPUT_VAR_START && ptr < OUTPUT_START;\n}\n\n/**\n * Check if a pointer is within the output buffer region.\n *\n * @param ptr - Pointer to check\n * @return true if pointer is within output buffer, false otherwise\n */\nexport function isWithinOutputBuffer(ptr: usize): bool {\n return ptr >= OUTPUT_START && ptr < OUTPUT_VAR_START;\n}\n\n/**\n * Check if a pointer is within the output variable data region.\n *\n * @param ptr - Pointer to check\n * @return true if pointer is within output variable data, false otherwise\n */\nexport function isWithinOutputVar(ptr: usize): bool {\n return ptr >= OUTPUT_VAR_START && ptr < HEAP_START;\n}\n\n/**\n * Check if a pointer is within the heap region.\n *\n * @param ptr - Pointer to check\n * @return true if pointer is within heap, false otherwise\n */\nexport function isWithinHeap(ptr: usize): bool {\n return ptr >= HEAP_START;\n}\n";
10716
+ var RUNTIME_STRINGS = "// assembly/runtime/strings.ts (AssemblyScript)\n// UTF-16 string handling for WASM linear memory\n\nimport { heapAlloc } from './memory';\n\n// ============================================================================\n// String Memory Format\n// ============================================================================\n\n/**\n * String memory layout: [length: u32][utf16 chars...]\n *\n * We use a length-prefixed format; this allows O(1) length checks and efficient substring\n * operations without scanning for null terminators. Also matches AssemblyScript's native\n * string representation. The first 4 bytes contain the length (number of UTF-16 code units).\n * Following that are the UTF-16 code units, each 2 bytes.\n */\n\n// ============================================================================\n// String Constants\n// ============================================================================\n\n/**\n * Size of the length field header in bytes.\n */\nexport const STRING_LENGTH_SIZE: usize = 4;\n\n/**\n * Size of each UTF-16 code unit in bytes.\n */\nexport const UTF16_CODE_UNIT_SIZE: usize = 2;\n\n/**\n * Minimum string length to use bulk memory operations.\n * For strings shorter than this, character-by-character writes are used\n * as the setup overhead dominates. For longer strings, bulk operations\n * provide significant performance benefits.\n */\nconst MIN_BULK_COPY_STRING_LENGTH: usize = 32;\n\n// ============================================================================\n// String Writing\n// ============================================================================\n\n/**\n * Write a string to memory and return its pointer.\n *\n * Memory is allocated from the runtime heap using heapAlloc().\n * The memory is aligned to 4 bytes for efficient access.\n *\n * @param s - The string to write to memory\n * @returns Pointer to the allocated string data, or 0 if allocation failed\n */\nexport function writeString(s: string): usize {\n const len: usize = <usize>s.length;\n\n // Calculate total size: length field (4 bytes) + UTF-16 characters (2 bytes each)\n const totalSize = STRING_LENGTH_SIZE + len * UTF16_CODE_UNIT_SIZE;\n\n // Four-byte alignment ensures proper alignment for u32 loads/stores, avoiding unaligned access\n // penalties on some architectures. WASM requires aligned memory access for multi-byte types.\n // Allocate memory from heap\n const ptr = heapAlloc(totalSize);\n\n // Return 0 if allocation failed\n if (ptr == 0) {\n return 0;\n }\n\n // Write length (number of UTF-16 code units)\n store<u32>(ptr, len);\n\n // Handle empty string case (len == 0)\n if (len == 0) {\n return ptr;\n }\n\n // For long strings, use bulk memory copy for better performance.\n // AssemblyScript strings are internally UTF-16, matching our memory format.\n if (len >= MIN_BULK_COPY_STRING_LENGTH) {\n // Get pointer to the string's internal UTF-16 data\n const srcPtr = changetype<usize>(s);\n // Copy the UTF-16 data directly\n memory.copy(ptr + STRING_LENGTH_SIZE, srcPtr, len * UTF16_CODE_UNIT_SIZE);\n } else {\n // For short strings, character-by-character writes avoid bulk operation overhead\n for (let i: usize = 0; i < len; i++) {\n const codeUnit: u16 = unchecked(<u16>s.charCodeAt(<i32>i));\n store<u16>(ptr + STRING_LENGTH_SIZE + i * UTF16_CODE_UNIT_SIZE, codeUnit);\n }\n }\n\n return ptr;\n}\n\n// ============================================================================\n// String Reading\n// ============================================================================\n\n/**\n * Read a string from memory.\n *\n * @param ptr - Pointer to the string data in memory\n * @returns The read string, or empty string if pointer is null\n */\nexport function readString(ptr: usize): string {\n // Return empty string for null pointer\n if (ptr == 0) {\n return '';\n }\n\n // Read length (number of UTF-16 code units)\n const len: usize = <usize>load<u32>(ptr);\n\n // Handle empty string case\n if (len == 0) {\n return '';\n }\n\n // Use bulk memory read for O(n) performance instead of O(n\xB2) string concatenation.\n // String.UTF16.decodeUnsafe reads UTF-16 data directly from memory in a single allocation,\n // avoiding the repeated string allocations that occur with character-by-character concatenation.\n return String.UTF16.decodeUnsafe(ptr + STRING_LENGTH_SIZE, len * UTF16_CODE_UNIT_SIZE);\n}\n\n// ============================================================================\n// String Utility Functions\n// ============================================================================\n\n/**\n * Calculate the size in bytes needed to store a string in memory.\n *\n * @param len - The length of the string (number of UTF-16 code units)\n * @returns The total size in bytes including the length header\n */\nexport function getStringSize(len: usize): usize {\n return STRING_LENGTH_SIZE + len * UTF16_CODE_UNIT_SIZE;\n}\n\n/**\n * Calculate the length of a string from its pointer in memory.\n *\n * @param ptr - Pointer to the string data in memory\n * @returns The length of the string (number of UTF-16 code units), or 0 if null\n */\nexport function getStringLength(ptr: usize): usize {\n if (ptr == 0) {\n return 0;\n }\n return load<u32>(ptr);\n}\n\n/**\n * Check if a string pointer points to an empty string.\n *\n * @param ptr - Pointer to the string data in memory\n * @returns true if the string is empty or pointer is null, false otherwise\n */\nexport function isStringEmpty(ptr: usize): bool {\n if (ptr == 0) {\n return true;\n }\n return load<u32>(ptr) == 0;\n}\n\n/**\n * Compare two strings in memory.\n *\n * @param ptr1 - Pointer to the first string\n * @param ptr2 - Pointer to the second string\n * @returns true if the strings are equal, false otherwise\n */\nexport function stringsEqual(ptr1: usize, ptr2: usize): bool {\n // If both are null, they're equal\n if (ptr1 == 0 && ptr2 == 0) {\n return true;\n }\n\n // If only one is null, they're not equal\n if (ptr1 == 0 || ptr2 == 0) {\n return false;\n }\n\n // Compare lengths\n const len1 = load<u32>(ptr1);\n const len2 = load<u32>(ptr2);\n\n if (len1 != len2) {\n return false;\n }\n\n // Compare each UTF-16 code unit\n for (let i: usize = 0; i < <usize>len1; i++) {\n const offset = STRING_LENGTH_SIZE + i * UTF16_CODE_UNIT_SIZE;\n const char1 = load<u16>(ptr1 + offset);\n const char2 = load<u16>(ptr2 + offset);\n if (char1 != char2) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Calculate a simple hash of a string in memory.\n *\n * This can be used for quick comparisons or deduplication.\n * Uses a simple polynomial rolling hash algorithm.\n *\n * @param ptr - Pointer to the string data in memory\n * @returns A hash value for the string (0 if null)\n */\nexport function hashString(ptr: usize): u32 {\n if (ptr == 0) {\n return 0;\n }\n\n const len: usize = <usize>load<u32>(ptr);\n let hash: u32 = 5381;\n\n for (let i: usize = 0; i < len; i++) {\n const charCode = load<u16>(ptr + STRING_LENGTH_SIZE + i * UTF16_CODE_UNIT_SIZE);\n hash = unchecked((hash << 5) + hash + unchecked(charCode)); // hash * 33 + charCode\n }\n\n return hash;\n}\n\n/**\n * Copy a string within memory.\n *\n * Creates a new copy of the string at a new location in the heap.\n *\n * @param srcPtr - Pointer to the source string\n * @returns Pointer to the newly allocated copy, or 0 if allocation failed\n */\nexport function copyString(srcPtr: usize): usize {\n if (srcPtr == 0) {\n return 0;\n }\n\n // Get the length of the source string\n const len: usize = <usize>load<u32>(srcPtr);\n\n // Calculate total size\n const totalSize = STRING_LENGTH_SIZE + len * UTF16_CODE_UNIT_SIZE;\n\n // Allocate new memory\n const dstPtr = heapAlloc(totalSize);\n\n if (dstPtr == 0) {\n return 0;\n }\n\n // Copy the entire string data (length header + UTF-16 code units)\n memory.copy(dstPtr, srcPtr, totalSize);\n\n return dstPtr;\n}\n";
10717
+ var RUNTIME_ARRAYS = "// assembly/runtime/arrays.ts (AssemblyScript)\n// Array handling for WASM linear memory\n\nimport { heapAlloc } from './memory';\nimport { writeString, readString } from './strings';\nimport {\n Value,\n TYPE_NULL,\n TYPE_BOOL,\n TYPE_INT,\n TYPE_FLOAT,\n TYPE_STRING,\n TYPE_ARRAY,\n TYPE_OBJECT,\n} from './values';\n\n// ============================================================================\n// Array Constants\n// ============================================================================\n\n/**\n * Size of the length field header in bytes for arrays.\n */\nexport const ARRAY_LENGTH_SIZE: usize = 4;\n\n/**\n * Size of each F64 element in bytes.\n */\nexport const F64_ELEMENT_SIZE: usize = 8;\n\n/**\n * Size of each element pointer (usize) in bytes for generic arrays.\n */\nexport const ELEMENT_PTR_SIZE: usize = 4;\n\n/**\n * Size of the type tag field in bytes for serialized Values.\n */\nexport const VALUE_TYPE_SIZE: usize = 4;\n\n/**\n * Size of the bool field in bytes for serialized Values.\n */\nexport const VALUE_BOOL_SIZE: usize = 1;\n\n/**\n * Size of the int field in bytes for serialized Values.\n */\nexport const VALUE_INT_SIZE: usize = 8;\n\n/**\n * Size of the float field in bytes for serialized Values.\n */\nexport const VALUE_FLOAT_SIZE: usize = 8;\n\n/**\n * Size of the pointer field in bytes for serialized Values (string, array, object).\n */\nexport const VALUE_PTR_SIZE: usize = 4;\n\n// ============================================================================\n// F64 Array Handling\n// ============================================================================\n\n/**\n * Write a numeric array (Array<f64>) to memory.\n *\n * The array is stored in the format: [length: u32][f64 elements...]\n *\n * Memory is allocated from the runtime heap using heapAlloc().\n * The memory is aligned to 8 bytes for efficient access.\n *\n * @param arr - The array to write to memory\n * @returns Pointer to the allocated array data, or 0 if allocation failed\n */\nexport function writeF64Array(arr: Array<f64>): usize {\n const len = <u32>arr.length;\n\n // Calculate total size: length field (4 bytes) + f64 elements (8 bytes each)\n const totalSize = ARRAY_LENGTH_SIZE + <usize>len * F64_ELEMENT_SIZE;\n\n // Allocate memory from heap\n const ptr = heapAlloc(totalSize);\n\n // Return 0 if allocation failed\n if (ptr == 0) {\n return 0;\n }\n\n // Write length\n store<u32>(ptr, len);\n\n // Write each f64 element\n for (let i: u32 = 0; i < len; i++) {\n store<f64>(ptr + ARRAY_LENGTH_SIZE + <usize>i * F64_ELEMENT_SIZE, arr[i]);\n }\n\n return ptr;\n}\n\n/**\n * Read a numeric array (Array<f64>) from memory.\n *\n * The array must be stored in the format: [length: u32][f64 elements...]\n *\n * @param ptr - Pointer to the array data in memory\n * @returns The read array, or empty array if pointer is null\n */\nexport function readF64Array(ptr: usize): Array<f64> {\n // Return empty array for null pointer\n if (ptr == 0) {\n return new Array<f64>();\n }\n\n // Read length\n const len = load<u32>(ptr);\n\n // Create array and populate it\n const result = new Array<f64>(len);\n for (let i: u32 = 0; i < len; i++) {\n result[i] = load<f64>(ptr + ARRAY_LENGTH_SIZE + <usize>i * F64_ELEMENT_SIZE);\n }\n\n return result;\n}\n\n/**\n * Calculate the size in bytes needed to store an F64 array in memory.\n *\n * @param len - The length of the array\n * @returns The total size in bytes including the length header\n */\nexport function getF64ArraySize(len: usize): usize {\n return ARRAY_LENGTH_SIZE + len * F64_ELEMENT_SIZE;\n}\n\n// ============================================================================\n// Value Serialization\n// ============================================================================\n\n/**\n * Write a Value to memory and return its pointer.\n *\n * For complex types (string/array/object), storing the actual data inline would make\n * Value structs variable-sized and unpredictable. Storing pointers provides fixed-size\n * Values (predictable offsets) while allowing arbitrary complex data on the heap.\n *\n * The Value is stored in the format: [type: u32][value_data...]\n *\n * For primitive types (bool, int, float), value_data is the actual value.\n * For complex types (string, array, object), value_data is a pointer to the data.\n *\n * Memory is allocated from the runtime heap using heapAlloc().\n *\n * @param v - The Value to write to memory\n * @returns Pointer to the allocated Value data, or 0 if allocation failed\n */\nexport function writeValue(v: Value): usize {\n const type = v.type;\n\n if (type == TYPE_NULL) {\n // For null, just write the type tag\n const ptr = heapAlloc(VALUE_TYPE_SIZE);\n if (ptr == 0) return 0;\n store<u32>(ptr, type);\n return ptr;\n } else if (type == TYPE_BOOL) {\n // Format: [type: u32][bool: u8]\n const ptr = heapAlloc(VALUE_TYPE_SIZE + VALUE_BOOL_SIZE);\n if (ptr == 0) return 0;\n store<u32>(ptr, type);\n store<u8>(ptr + VALUE_TYPE_SIZE, v.boolVal ? 1 : 0);\n return ptr;\n } else if (type == TYPE_INT) {\n // Format: [type: u32][int: i64]\n const ptr = heapAlloc(VALUE_TYPE_SIZE + VALUE_INT_SIZE);\n if (ptr == 0) return 0;\n store<u32>(ptr, type);\n store<i64>(ptr + VALUE_TYPE_SIZE, v.intVal);\n return ptr;\n } else if (type == TYPE_FLOAT) {\n // Format: [type: u32][float: f64]\n const ptr = heapAlloc(VALUE_TYPE_SIZE + VALUE_FLOAT_SIZE);\n if (ptr == 0) return 0;\n store<u32>(ptr, type);\n store<f64>(ptr + VALUE_TYPE_SIZE, v.floatVal);\n return ptr;\n } else if (type == TYPE_STRING) {\n // Format: [type: u32][stringPtr: u32]\n const ptr = heapAlloc(VALUE_TYPE_SIZE + VALUE_PTR_SIZE);\n if (ptr == 0) return 0;\n store<u32>(ptr, type);\n const stringVal = v.stringVal != null ? v.stringVal! : '';\n const stringPtr = writeString(stringVal);\n store<u32>(ptr + VALUE_TYPE_SIZE, stringPtr);\n return ptr;\n } else if (type == TYPE_ARRAY) {\n // Format: [type: u32][arrayPtr: u32]\n const ptr = heapAlloc(VALUE_TYPE_SIZE + VALUE_PTR_SIZE);\n if (ptr == 0) return 0;\n store<u32>(ptr, type);\n const arrayVal = v.arrayVal != null ? v.arrayVal! : new Array<Value>();\n const arrayPtr = writeValueArray(arrayVal);\n store<u32>(ptr + VALUE_TYPE_SIZE, arrayPtr);\n return ptr;\n } else if (type == TYPE_OBJECT) {\n // Format: [type: u32][objectPtr: u32]\n const ptr = heapAlloc(VALUE_TYPE_SIZE + VALUE_PTR_SIZE);\n if (ptr == 0) return 0;\n store<u32>(ptr, type);\n const objectVal = v.objectVal != null ? v.objectVal! : new Map<string, Value>();\n const objectPtr = writeValueMap(objectVal);\n store<u32>(ptr + VALUE_TYPE_SIZE, objectPtr);\n return ptr;\n }\n\n return 0; // Unknown type\n}\n\n/**\n * Read a Value from memory.\n *\n * The Value must be stored in the format: [type: u32][value_data...]\n *\n * @param ptr - Pointer to the Value data in memory\n * @returns The read Value, or null if pointer is invalid\n */\nexport function readValue(ptr: usize): Value {\n // Return null for null pointer\n if (ptr == 0) {\n return Value.Null();\n }\n\n // Read type\n const type = load<u32>(ptr);\n\n if (type == TYPE_NULL) {\n return Value.Null();\n } else if (type == TYPE_BOOL) {\n const boolVal = load<u8>(ptr + VALUE_TYPE_SIZE) != 0;\n return Value.Bool(boolVal);\n } else if (type == TYPE_INT) {\n const intVal = load<i64>(ptr + VALUE_TYPE_SIZE);\n return Value.Int(intVal);\n } else if (type == TYPE_FLOAT) {\n const floatVal = load<f64>(ptr + VALUE_TYPE_SIZE);\n return Value.Float(floatVal);\n } else if (type == TYPE_STRING) {\n const stringPtr = load<u32>(ptr + VALUE_TYPE_SIZE);\n const stringVal = readString(stringPtr);\n return Value.String(stringVal);\n } else if (type == TYPE_ARRAY) {\n const arrayPtr = load<u32>(ptr + VALUE_TYPE_SIZE);\n const arrayVal = readValueArray(arrayPtr);\n return Value.Array(arrayVal);\n } else if (type == TYPE_OBJECT) {\n const objectPtr = load<u32>(ptr + VALUE_TYPE_SIZE);\n const objectVal = readValueMap(objectPtr);\n return Value.Object(objectVal);\n }\n\n return Value.Null(); // Unknown type\n}\n\n// ============================================================================\n// Value Array Handling\n// ============================================================================\n\n// Format marker for flat numeric arrays (high bit set in length field)\nconst FLAT_ARRAY_MARKER: u32 = 0x80000000;\n\n/**\n * Detect if an array contains only floats/ints (homogeneous numeric array).\n *\n * @param arr - The array to check\n * @returns true if all elements are TYPE_FLOAT or TYPE_INT, false otherwise\n */\nfunction isHomogeneousNumericArray(arr: Array<Value>): bool {\n const len = arr.length;\n if (len == 0) return false;\n\n for (let i = 0; i < len; i++) {\n const type = arr[i].type;\n if (type != TYPE_FLOAT && type != TYPE_INT) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Write a homogeneous numeric array in flat format.\n *\n * Format: [length | FLAT_ARRAY_MARKER: u32][f64 elements...]\n *\n * The high bit of the length field is set to indicate flat format.\n * This specialized format avoids individual Value allocations and type tags,\n * reducing memory overhead and fragmentation significantly.\n *\n * @param arr - The array to write (must be homogeneous numeric)\n * @returns Pointer to the allocated array data, or 0 if allocation failed\n */\nfunction writeHomogeneousNumericArray(arr: Array<Value>): usize {\n const len = <u32>arr.length;\n\n // Calculate total size: length with marker (4) + f64 elements (8 each)\n const totalSize = ARRAY_LENGTH_SIZE + <usize>len * F64_ELEMENT_SIZE;\n\n // Single allocation for entire array\n const ptr = heapAlloc(totalSize);\n if (ptr == 0) return 0;\n\n // Write length with high bit set to indicate flat format\n store<u32>(ptr, len | FLAT_ARRAY_MARKER);\n\n // Write all numeric values as f64 directly (no Value wrapping)\n for (let i: u32 = 0; i < len; i++) {\n const val = arr[i];\n const numVal = val.type == TYPE_FLOAT ? val.floatVal : <f64>val.intVal;\n store<f64>(ptr + ARRAY_LENGTH_SIZE + <usize>i * F64_ELEMENT_SIZE, numVal);\n }\n\n return ptr;\n}\n\n/**\n * Read a homogeneous numeric array from flat format.\n *\n * Format: [length | FLAT_ARRAY_MARKER: u32][f64 elements...]\n *\n * @param ptr - Pointer to the array data in memory\n * @param len - The actual length (without the marker bit)\n * @returns The read array of Value objects\n */\nfunction readHomogeneousNumericArray(ptr: usize, len: u32): Array<Value> {\n const result = new Array<Value>(len);\n\n // Read all f64 values and wrap them as Value.Float\n for (let i: u32 = 0; i < len; i++) {\n const numVal = load<f64>(ptr + ARRAY_LENGTH_SIZE + <usize>i * F64_ELEMENT_SIZE);\n result[i] = Value.Float(numVal);\n }\n\n return result;\n}\n\n/**\n * Write a Value array to memory.\n *\n * The array is stored in one of two formats:\n *\n * 1. Flat numeric format (for homogeneous numeric arrays):\n * [length | FLAT_ARRAY_MARKER: u32][f64 elements...]\n *\n * 2. Generic format (for mixed-type arrays):\n * [length: u32][Value pointers...]\n *\n * Homogeneous numeric arrays are detected and serialized using a flat format\n * that eliminates per-element allocations and type tags, significantly reducing\n * memory overhead (from ~16+ bytes per element to 8 bytes) and fragmentation.\n * The flat format is indicated by setting the high bit of the length field.\n *\n * Memory is allocated from the runtime heap using heapAlloc().\n *\n * @param arr - The array to write to memory\n * @returns Pointer to the allocated array data, or 0 if allocation failed\n */\nexport function writeValueArray(arr: Array<Value>): usize {\n const len = <u32>arr.length;\n\n // Empty array (generic format)\n if (len == 0) {\n const ptr = heapAlloc(ARRAY_LENGTH_SIZE);\n if (ptr == 0) return 0;\n store<u32>(ptr, 0);\n return ptr;\n }\n\n // Optimization: detect homogeneous numeric arrays and use flat format\n if (isHomogeneousNumericArray(arr)) {\n return writeHomogeneousNumericArray(arr);\n }\n\n // Generic path: mixed-type array\n // Format: [length: u32][Value pointers...]\n const totalSize = ARRAY_LENGTH_SIZE + <usize>len * ELEMENT_PTR_SIZE;\n\n const ptr = heapAlloc(totalSize);\n if (ptr == 0) return 0;\n\n store<u32>(ptr, len);\n\n // Write each Value pointer\n for (let i: u32 = 0; i < len; i++) {\n const valuePtr = writeValue(arr[i]);\n store<u32>(ptr + ARRAY_LENGTH_SIZE + <usize>i * ELEMENT_PTR_SIZE, valuePtr);\n }\n\n return ptr;\n}\n\n/**\n * Read a Value array from memory.\n *\n * Handles both flat numeric format and generic format:\n *\n * 1. Flat numeric format: [length | FLAT_ARRAY_MARKER: u32][f64 elements...]\n * 2. Generic format: [length: u32][Value pointers...]\n *\n * The format is detected by checking if the high bit of the length is set.\n *\n * @param ptr - Pointer to the array data in memory\n * @returns The read array, or empty array if pointer is null\n */\nexport function readValueArray(ptr: usize): Array<Value> {\n // Return empty array for null pointer\n if (ptr == 0) {\n return new Array<Value>();\n }\n\n // Read length (with potential marker bit)\n const lengthField = load<u32>(ptr);\n\n // Check if this is a flat numeric array (high bit set)\n if ((lengthField & FLAT_ARRAY_MARKER) != 0) {\n // Flat numeric format - extract actual length\n const len = lengthField & ~FLAT_ARRAY_MARKER;\n return readHomogeneousNumericArray(ptr, len);\n }\n\n // Generic format - length is used as-is\n const len = lengthField;\n\n // Empty array\n if (len == 0) {\n return new Array<Value>();\n }\n\n // Read array of Value pointers\n const result = new Array<Value>(len);\n for (let i: u32 = 0; i < len; i++) {\n const valuePtr = load<u32>(ptr + ARRAY_LENGTH_SIZE + <usize>i * ELEMENT_PTR_SIZE);\n result[i] = readValue(valuePtr);\n }\n\n return result;\n}\n\n/**\n * Calculate the size in bytes needed to store a Value array in memory.\n *\n * Note: This calculates the size for the generic format.\n * For flat numeric arrays, the actual size may be different.\n *\n * @param len - The length of the array\n * @returns The total size in bytes including the length header (but not the individual Values)\n */\nexport function getValueArraySize(len: usize): usize {\n return ARRAY_LENGTH_SIZE + len * ELEMENT_PTR_SIZE;\n}\n\n// ============================================================================\n// Value Map (Object) Handling\n// ============================================================================\n\n/**\n * Write a Value map to memory.\n *\n * The map is stored in the format: [length: u32][keyPtr1: u32][valuePtr1: u32][keyPtr2: u32][valuePtr2: u32]...\n *\n * Keys are always strings and written to memory separately using writeString().\n * Values are serialized using writeValue() separately.\n *\n * Memory is allocated from the runtime heap using heapAlloc().\n *\n * JavaScript object property order is insertion-ordered (ES2015+), so we must preserve\n * order when serializing to match JS semantics and ensure deterministic output.\n *\n * @param map - The map to write to memory\n * @returns Pointer to the allocated map data, or 0 if allocation failed\n */\nexport function writeValueMap(map: Map<string, Value>): usize {\n const len = <u32>map.size;\n\n // Calculate total size: length field (4 bytes) + key/value pairs (8 bytes each)\n const totalSize = ARRAY_LENGTH_SIZE + <usize>len * 2 * ELEMENT_PTR_SIZE;\n\n // Allocate memory from heap\n const ptr = heapAlloc(totalSize);\n\n // Return 0 if allocation failed\n if (ptr == 0) {\n return 0;\n }\n\n // Write length\n store<u32>(ptr, len);\n\n // Get keys and write each key/value pair\n const keys = map.keys();\n for (let i: u32 = 0; i < <u32>keys.length; i++) {\n const key = keys[i];\n const offset = ptr + ARRAY_LENGTH_SIZE + <usize>i * 2 * ELEMENT_PTR_SIZE;\n\n // Write key pointer\n const keyPtr = writeString(key);\n store<u32>(offset, keyPtr);\n\n // Write value pointer\n const valuePtr = writeValue(map.get(key));\n store<u32>(offset + ELEMENT_PTR_SIZE, valuePtr);\n }\n\n return ptr;\n}\n\n/**\n * Read a Value map from memory.\n *\n * The map must be stored in the format: [length: u32][keyPtr1: u32][valuePtr1: u32][keyPtr2: u32][valuePtr2: u32]...\n *\n * @param ptr - Pointer to the map data in memory\n * @returns The read map, or empty map if pointer is null\n */\nexport function readValueMap(ptr: usize): Map<string, Value> {\n // Return empty map for null pointer\n if (ptr == 0) {\n return new Map<string, Value>();\n }\n\n // Read length\n const len = load<u32>(ptr);\n\n // Create map and populate it\n const result = new Map<string, Value>();\n for (let i: u32 = 0; i < len; i++) {\n const keyPtr = load<u32>(ptr + ARRAY_LENGTH_SIZE + <usize>i * 2 * ELEMENT_PTR_SIZE);\n const valuePtr = load<u32>(\n ptr + ARRAY_LENGTH_SIZE + <usize>i * 2 * ELEMENT_PTR_SIZE + ELEMENT_PTR_SIZE,\n );\n\n const key = readString(keyPtr);\n const value = readValue(valuePtr);\n\n result.set(key, value);\n }\n\n return result;\n}\n\n// ============================================================================\n// Array Utility Functions\n// ============================================================================\n\n/**\n * Calculate the length of an array from its pointer in memory.\n *\n * @param ptr - Pointer to the array data in memory\n * @returns The length of the array, or 0 if null\n */\nexport function getArrayLength(ptr: usize): usize {\n if (ptr == 0) {\n return 0;\n }\n return load<u32>(ptr);\n}\n\n/**\n * Check if an array pointer points to an empty array.\n *\n * @param ptr - Pointer to the array data in memory\n * @returns true if the array is empty or pointer is null, false otherwise\n */\nexport function isArrayEmpty(ptr: usize): bool {\n if (ptr == 0) {\n return true;\n }\n return load<u32>(ptr) == 0;\n}\n";
8520
10718
 
8521
10719
  // src/compiler/cache.ts
8522
10720
  var packageVersion = "";
8523
10721
  function getPackageVersion() {
8524
10722
  if (packageVersion === "") {
8525
10723
  try {
8526
- const packagePath = join(dirname2(__dirname), "..", "package.json");
8527
- const pkg = JSON.parse(readFileSync2(packagePath, "utf-8"));
10724
+ const packagePath = join(dirname(__dirname), "..", "package.json");
10725
+ const pkg = JSON.parse(readFileSync(packagePath, "utf-8"));
8528
10726
  packageVersion = pkg.version || "unknown";
8529
10727
  } catch {
8530
10728
  packageVersion = "unknown";
@@ -8581,7 +10779,7 @@ function readCacheFromDisk(cacheKey) {
8581
10779
  return null;
8582
10780
  }
8583
10781
  try {
8584
- const content = readFileSync2(cachePath, "utf-8");
10782
+ const content = readFileSync(cachePath, "utf-8");
8585
10783
  const entry = JSON.parse(content);
8586
10784
  if (entry.version !== getPackageVersion()) {
8587
10785
  unlinkSync(cachePath);
@@ -8795,8 +10993,8 @@ function createASCFileSystemCallbacks(virtualFS) {
8795
10993
  const key = baseDir ? `${baseDir}/${name}` : name;
8796
10994
  virtualFS.set(key, data2);
8797
10995
  },
8798
- listFiles(dirname3, baseDir) {
8799
- const prefix = baseDir ? `${baseDir}/${dirname3}` : dirname3;
10996
+ listFiles(dirname2, baseDir) {
10997
+ const prefix = baseDir ? `${baseDir}/${dirname2}` : dirname2;
8800
10998
  return [...virtualFS.keys()].filter((f) => f.startsWith(prefix));
8801
10999
  }
8802
11000
  };
@@ -10428,7 +12626,7 @@ function canonicalizeSchema(schema) {
10428
12626
  }
10429
12627
 
10430
12628
  // src/compiler/index.ts
10431
- import { readFileSync as readFileSync3 } from "fs";
12629
+ import { readFileSync as readFileSync2 } from "fs";
10432
12630
  import path2 from "path";
10433
12631
 
10434
12632
  // src/compiler/ast-types.ts
@@ -12120,7 +14318,7 @@ function createFilesystemLoader(testDataRoot) {
12120
14318
  for (const basePath of possiblePaths) {
12121
14319
  try {
12122
14320
  const filePath = path2.join(basePath, key);
12123
- const content = readFileSync3(filePath, "utf-8");
14321
+ const content = readFileSync2(filePath, "utf-8");
12124
14322
  return parseJDM(content);
12125
14323
  } catch (error) {
12126
14324
  if (isFileNotFoundError(error)) {