@divmain/jdm-asm 0.2.0 → 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
@@ -1,1811 +0,0 @@
1
- // assembly/runtime/expressions.ts (AssemblyScript)
2
- import { Value, TYPE_ARRAY, TYPE_STRING, TYPE_FLOAT, TYPE_INT, TYPE_BOOL } from './values';
3
-
4
- /**
5
- * Helper function to create an argument array.
6
- * AssemblyScript doesn't support true variadic functions or rest parameters, so we provide
7
- * fixed-arity helpers (makeArgs1, makeArgs2, makeArgs3) for common function arities.
8
- *
9
- * These use thread-local reusable arrays to avoid allocations in hot paths.
10
- * Since WASM evaluation is single-threaded, reusing arrays is safe.
11
- */
12
-
13
- // Reusable argument arrays - lazily initialized
14
- let _args1: Array<Value> | null = null;
15
- let _args2: Array<Value> | null = null;
16
- let _args3: Array<Value> | null = null;
17
-
18
- export function makeArgs(a: Value, b: Value, c: Value = Value.Null()): Array<Value> {
19
- if (_args3 === null) {
20
- _args3 = new Array<Value>(3);
21
- }
22
- unchecked((_args3![0] = a));
23
- unchecked((_args3![1] = b));
24
- unchecked((_args3![2] = c));
25
- return _args3!;
26
- }
27
-
28
- export function makeArgs1(a: Value): Array<Value> {
29
- if (_args1 === null) {
30
- _args1 = new Array<Value>(1);
31
- }
32
- unchecked((_args1![0] = a));
33
- return _args1!;
34
- }
35
-
36
- export function makeArgs2(a: Value, b: Value): Array<Value> {
37
- if (_args2 === null) {
38
- _args2 = new Array<Value>(2);
39
- }
40
- unchecked((_args2![0] = a));
41
- unchecked((_args2![1] = b));
42
- return _args2!;
43
- }
44
-
45
- export function makeArgs3(a: Value, b: Value, c: Value): Array<Value> {
46
- if (_args3 === null) {
47
- _args3 = new Array<Value>(3);
48
- }
49
- unchecked((_args3![0] = a));
50
- unchecked((_args3![1] = b));
51
- unchecked((_args3![2] = c));
52
- return _args3!;
53
- }
54
-
55
- // Type alias for function handlers
56
- type FunctionHandler = (args: Array<Value>) => Value;
57
-
58
- // Global function registry - Map provides O(1) lookup instead of O(n) if-else chain
59
- const functionRegistry = new Map<string, FunctionHandler>();
60
-
61
- /**
62
- * Initialize the function registry with all built-in functions.
63
- * This is called once at module initialization.
64
- */
65
- function initializeFunctionRegistry(): void {
66
- // Array functions
67
- functionRegistry.set('sum', (args: Array<Value>): Value => sumArray(args[0]));
68
- functionRegistry.set('avg', (args: Array<Value>): Value => avgArray(args[0]));
69
- functionRegistry.set('min', (args: Array<Value>): Value => minArray(args[0]));
70
- functionRegistry.set('max', (args: Array<Value>): Value => maxArray(args[0]));
71
- functionRegistry.set('sort', (args: Array<Value>): Value => sortArray(args[0]));
72
-
73
- // Flatten has two aliases
74
- const flatHandler = (args: Array<Value>): Value => {
75
- const arg1 = args.length >= 2 ? args[1] : Value.Null();
76
- return flatArray(args[0], arg1);
77
- };
78
- functionRegistry.set('flat', flatHandler);
79
- functionRegistry.set('flatten', flatHandler);
80
-
81
- // Count/length has two aliases
82
- const lengthHandler = (args: Array<Value>): Value => lengthOf(args[0]);
83
- functionRegistry.set('count', lengthHandler);
84
- functionRegistry.set('len', lengthHandler);
85
-
86
- functionRegistry.set('contains', (args: Array<Value>): Value => {
87
- const arg1 = args.length >= 2 ? args[1] : Value.Null();
88
- return containsValue(args[0], arg1);
89
- });
90
-
91
- // Type conversion functions
92
- functionRegistry.set('number', (args: Array<Value>): Value => numberValue(args[0]));
93
- functionRegistry.set('string', (args: Array<Value>): Value => stringValue(args[0]));
94
-
95
- // Math functions
96
- functionRegistry.set('abs', (args: Array<Value>): Value => absValue(args[0]));
97
- functionRegistry.set('floor', (args: Array<Value>): Value => floorValue(args[0]));
98
- functionRegistry.set('ceil', (args: Array<Value>): Value => ceilValue(args[0]));
99
- functionRegistry.set('round', (args: Array<Value>): Value => roundValue(args[0]));
100
-
101
- // String functions
102
- functionRegistry.set('upper', (args: Array<Value>): Value => upperString(args[0]));
103
- functionRegistry.set('lower', (args: Array<Value>): Value => lowerString(args[0]));
104
- functionRegistry.set('trim', (args: Array<Value>): Value => trimString(args[0]));
105
-
106
- functionRegistry.set('substring', (args: Array<Value>): Value => {
107
- const arg1 = args.length >= 2 ? args[1] : Value.Null();
108
- const arg2 = args.length >= 3 ? args[2] : Value.Null();
109
- return substringString(args[0], arg1, arg2);
110
- });
111
-
112
- functionRegistry.set('indexOf', (args: Array<Value>): Value => {
113
- const arg1 = args.length >= 2 ? args[1] : Value.Null();
114
- const arg2 = args.length >= 3 ? args[2] : Value.Null();
115
- return indexOfString(args[0], arg1, arg2.isNull() ? Value.Int(<i64>0) : arg2);
116
- });
117
-
118
- functionRegistry.set('startsWith', (args: Array<Value>): Value => {
119
- const arg1 = args.length >= 2 ? args[1] : Value.Null();
120
- return startsWithString(args[0], arg1);
121
- });
122
-
123
- functionRegistry.set('endsWith', (args: Array<Value>): Value => {
124
- const arg1 = args.length >= 2 ? args[1] : Value.Null();
125
- return endsWithString(args[0], arg1);
126
- });
127
-
128
- functionRegistry.set('split', (args: Array<Value>): Value => {
129
- const arg1 = args.length >= 2 ? args[1] : Value.Null();
130
- return splitString(args[0], arg1);
131
- });
132
-
133
- functionRegistry.set('join', (args: Array<Value>): Value => {
134
- const arg1 = args.length >= 2 ? args[1] : Value.Null();
135
- return joinArray(args[0], arg1);
136
- });
137
-
138
- functionRegistry.set('replace', (args: Array<Value>): Value => {
139
- const arg1 = args.length >= 2 ? args[1] : Value.Null();
140
- const arg2 = args.length >= 3 ? args[2] : Value.Null();
141
- return replaceString(args[0], arg1, arg2);
142
- });
143
-
144
- functionRegistry.set('replaceAll', (args: Array<Value>): Value => {
145
- const arg1 = args.length >= 2 ? args[1] : Value.Null();
146
- const arg2 = args.length >= 3 ? args[2] : Value.Null();
147
- return replaceAllString(args[0], arg1, arg2);
148
- });
149
-
150
- // Date/time functions
151
- functionRegistry.set('date', (args: Array<Value>): Value => parseDate(args[0]));
152
-
153
- // now() returns 0 because WASM doesn't have direct access to system time in a sandboxed way.
154
- // To get the current timestamp, use date("now") instead, which uses Date.now().
155
- functionRegistry.set('now', (args: Array<Value>): Value => Value.Float(0));
156
-
157
- functionRegistry.set('time', (args: Array<Value>): Value => parseTime(args[0]));
158
- functionRegistry.set('duration', (args: Array<Value>): Value => parseDuration(args[0]));
159
-
160
- // Object functions
161
- functionRegistry.set('values', (args: Array<Value>): Value => objectValues(args[0]));
162
- functionRegistry.set('keys', (args: Array<Value>): Value => objectKeys(args[0]));
163
- }
164
-
165
- // Initialize the registry immediately
166
- initializeFunctionRegistry();
167
-
168
- /**
169
- * Evaluate built-in functions.
170
- * All inputs and outputs use the Value tagged union.
171
- *
172
- * Uses a Map-based registry for O(1) function lookup instead of O(n) if-else chain.
173
- */
174
- export function evaluateFunction(name: string, args: Array<Value>): Value {
175
- // Safety check for args array
176
- if (args == null || args.length == 0) return Value.Null();
177
-
178
- // Look up handler in the registry
179
- if (functionRegistry.has(name)) {
180
- const handler = functionRegistry.get(name);
181
- return handler(args);
182
- }
183
-
184
- // Unknown function - return null
185
- return Value.Null();
186
- }
187
-
188
- // === Object Functions ===
189
-
190
- /**
191
- * Get all values from an object as an array.
192
- * If the input is not an object, returns an empty array.
193
- */
194
- export function objectValues(val: Value): Value {
195
- if (!val.isObject()) return Value.Array(new Array<Value>(0));
196
- const obj = val.asObject();
197
- const keys = obj.keys();
198
- const result = new Array<Value>(keys.length);
199
- for (let i = 0; i < keys.length; i++) {
200
- unchecked((result[i] = obj.get(unchecked(keys[i]))));
201
- }
202
- return Value.Array(result);
203
- }
204
-
205
- /**
206
- * Get all keys from an object as an array of strings.
207
- * If the input is not an object, returns an empty array.
208
- */
209
- export function objectKeys(val: Value): Value {
210
- if (!val.isObject()) return Value.Array(new Array<Value>(0));
211
- const obj = val.asObject();
212
- const keys = obj.keys();
213
- const result = new Array<Value>(keys.length);
214
- for (let i = 0; i < keys.length; i++) {
215
- unchecked((result[i] = Value.String(unchecked(keys[i]))));
216
- }
217
- return Value.Array(result);
218
- }
219
-
220
- // === Array Functions ===
221
-
222
- export function sumArray(val: Value): Value {
223
- if (!val.isArray()) return Value.Float(0.0);
224
- const arr = val.asArray();
225
- let total: f64 = 0.0;
226
- for (let i = 0; i < arr.length; i++) {
227
- total += unchecked(arr[i]).asFloat();
228
- }
229
- return Value.Float(total);
230
- }
231
-
232
- export function minArray(val: Value): Value {
233
- if (!val.isArray()) return Value.Null();
234
- const arr = val.asArray();
235
- if (arr.length == 0) return Value.Null();
236
- let min = arr[0].asFloat();
237
- for (let i = 1; i < arr.length; i++) {
238
- const v = unchecked(arr[i]).asFloat();
239
- if (v < min) min = v;
240
- }
241
- return Value.Float(min);
242
- }
243
-
244
- export function maxArray(val: Value): Value {
245
- if (!val.isArray()) return Value.Null();
246
- const arr = val.asArray();
247
- if (arr.length == 0) return Value.Null();
248
- let max = arr[0].asFloat();
249
- for (let i = 1; i < arr.length; i++) {
250
- const v = unchecked(arr[i]).asFloat();
251
- if (v > max) max = v;
252
- }
253
- return Value.Float(max);
254
- }
255
-
256
- export function avgArray(val: Value): Value {
257
- if (!val.isArray()) return Value.Null();
258
- const arr = val.asArray();
259
- if (arr.length == 0) return Value.Null();
260
- let total: f64 = 0.0;
261
- for (let i = 0; i < arr.length; i++) {
262
- total += unchecked(arr[i]).asFloat();
263
- }
264
- return Value.Float(total / <f64>arr.length);
265
- }
266
-
267
- /**
268
- * Sort an array in ascending order.
269
- *
270
- * Supports sorting numbers and strings in ascending order.
271
- * Creates and returns a new sorted array without modifying the original.
272
- *
273
- * Parameters:
274
- * - val: The array value to sort
275
- *
276
- * Behavior:
277
- * - Numbers: sorted in ascending numeric order
278
- * - Strings: sorted in lexicographic (ASCII) order
279
- * - Empty arrays: returns a new empty array
280
- * - Non-array inputs: returns null
281
- *
282
- * Examples:
283
- * - sortArray([3, 1, 2]) -> [1, 2, 3]
284
- * - sortArray(["c", "a", "b"]) -> ["a", "b", "c"]
285
- * - sortArray([]) -> []
286
- *
287
- * @param val - The array value to sort
288
- * @returns A new sorted array, or null for invalid input
289
- */
290
- export function sortArray(val: Value): Value {
291
- if (!val.isArray()) return Value.Null();
292
- const arr = val.asArray();
293
-
294
- // Create a copy of the array to avoid mutating the original
295
- const sorted = new Array<Value>(arr.length);
296
- for (let i = 0; i < arr.length; i++) {
297
- unchecked((sorted[i] = unchecked(arr[i])));
298
- }
299
-
300
- // Sort the copy using the compareValues function
301
- sorted.sort((a: Value, b: Value): i32 => compareValues(a, b));
302
-
303
- return Value.Array(sorted);
304
- }
305
-
306
- /**
307
- * Flatten nested arrays into a single-level array.
308
- *
309
- * Parameters:
310
- * - val: The array value to flatten
311
- * - depthVal: Optional depth (default: 1). Specifies how many levels deep to flatten.
312
- *
313
- * Behavior:
314
- * - If depth is 0, returns a shallow copy of the array
315
- * - Flattens arrays recursively up to the specified depth
316
- * - Empty arrays are returned as empty arrays
317
- * - Non-array inputs return null
318
- *
319
- * Examples:
320
- * - flatArray([[1, 2], [3, 4]]) -> [1, 2, 3, 4] (depth 1)
321
- * - flatArray([1, [2, [3]]]) -> [1, 2, [3]] (depth 1)
322
- * - flatArray([1, [2, [3]]], 2) -> [1, 2, 3] (depth 2)
323
- * - flatArray([1, 2, 3]) -> [1, 2, 3] (no nesting, shallow copy)
324
- * - flatArray([]) -> [] (empty array)
325
- * - flatArray([[1], [2]], 0) -> [[1], [2]] (depth 0, no flattening)
326
- *
327
- * @param val - The array value to flatten
328
- * @param depthVal - Optional depth for flattening (default: 1)
329
- * @returns A new flattened array, or null for invalid input
330
- */
331
- export function flatArray(val: Value, depthVal: Value = Value.Int(<i64>1)): Value {
332
- if (!val.isArray()) return Value.Null();
333
- const arr = val.asArray();
334
-
335
- // Parse depth parameter matches JavaScript Array.flat() default behavior, which
336
- // flattens one level deep unless specified otherwise. Most common use case.
337
- let depth: i32 = 1;
338
- if (!depthVal.isNull() && depthVal.isNumber()) {
339
- depth = <i32>depthVal.asFloat(); // Use asFloat() since numbers may come as floats
340
- // Clamp depth to non-negative values
341
- if (depth < 0) depth = 1;
342
- }
343
-
344
- // Use a simple iterative approach that maintains order
345
- let result = new Array<Value>();
346
- flattenRecursive(arr, depth, result);
347
- return Value.Array(result);
348
- }
349
-
350
- // Helper function for flatArray
351
- function flattenRecursive(arr: Array<Value>, currentDepth: i32, result: Array<Value>): void {
352
- for (let i = 0; i < arr.length; i++) {
353
- const element = unchecked(arr[i]);
354
- if (element.isArray() && currentDepth > 0) {
355
- // Recursively flatten nested array
356
- flattenRecursive(element.asArray(), currentDepth - 1, result);
357
- } else {
358
- // Add element to results
359
- result.push(element);
360
- }
361
- }
362
- }
363
-
364
- export function lengthOf(val: Value): Value {
365
- if (val.isArray()) {
366
- return Value.Int(<i64>val.asArray().length);
367
- }
368
- if (val.isString()) {
369
- return Value.Int(<i64>val.asString().length);
370
- }
371
- return Value.Int(0);
372
- }
373
-
374
- /**
375
- * Check if a value contains a subvalue.
376
- *
377
- * For strings: checks if the string contains a given substring.
378
- * For arrays: checks if the array contains the given value.
379
- *
380
- * Parameters:
381
- * - container: The string or array to search in
382
- * - needle: The substring or value to search for
383
- *
384
- * Behavior:
385
- * - If container is a string, checks if it contains the substring
386
- * - If container is an array, checks if any element equals the needle
387
- * - For strings, returns null if either argument is not a string
388
- * - For arrays, always returns a boolean
389
- *
390
- * String Examples:
391
- * - contains("hello world", "world") → true
392
- * - contains("hello world", "xyz") → false
393
- * - contains("abc", "abc") → true
394
- * - contains("hello", "") → true (empty string is always contained)
395
- * - contains("", "") → true
396
- * - contains("", "x") → false
397
- * - contains("Hello", "hello") → false (case-sensitive)
398
- * - contains("hello", 123) → null (needle not a string)
399
- * - contains(123, "hello") → null (container not a string)
400
- *
401
- * Array Examples:
402
- * - contains([1, 2, 3], 2) → true
403
- * - contains([1, 2, 3], 4) → false
404
- * - contains(["a"], "a") → true
405
- * - contains(123, 1) → false
406
- *
407
- * @param container - The string or array to search in
408
- * @param needle - The substring or value to search for
409
- * @returns true if contained, false if not, or null for invalid string arguments
410
- */
411
- export function containsValue(container: Value, needle: Value): Value {
412
- // Handle string containment
413
- if (container.isString()) {
414
- if (!needle.isString()) return Value.Null();
415
- const str = container.asString();
416
- const search = needle.asString();
417
- // Use indexOf to check containment (returns -1 if not found)
418
- const index = str.indexOf(search);
419
- return Value.Bool(index >= 0);
420
- }
421
-
422
- // Handle array containment (original behavior)
423
- if (!container.isArray()) return Value.Bool(false);
424
- const arr = container.asArray();
425
- for (let i = 0; i < arr.length; i++) {
426
- if (valuesEqual(unchecked(arr[i]), needle)) return Value.Bool(true);
427
- }
428
- return Value.Bool(false);
429
- }
430
-
431
- // === Date/Time Functions ===
432
-
433
- /**
434
- * Parse a date string into a numeric timestamp.
435
- *
436
- * Supports ISO 8601 date format: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS[Z|+/-HH:MM]
437
- * Returns Unix timestamp in seconds since epoch (1970-01-01 00:00:00 UTC).
438
- * This matches zen-engine's behavior and allows date arithmetic operations.
439
- *
440
- * Examples:
441
- * - date("2025-03-20T10:30:00Z") → 1742466600 (seconds since epoch)
442
- * - date("1970-01-01") → 0
443
- * - date("1970-01-01T00:00:00Z") → 0
444
- *
445
- * @param val - A string value containing a date
446
- * @returns A float Value representing seconds since epoch, or Null for invalid input
447
- */
448
- export function parseDate(val: Value): Value {
449
- if (!val.isString()) return Value.Null();
450
-
451
- const str = val.asString();
452
-
453
- // Handle special 'now' keyword - returns Unix timestamp in seconds
454
- // Using Date.now() which returns milliseconds, convert to seconds
455
- if (str == 'now') {
456
- return Value.Float(<f64>Date.now() / 1000.0);
457
- }
458
-
459
- // Fast path for simple YYYY-MM-DD format (10 characters)
460
- // This avoids string allocations from split() and substring() for common cases
461
- if (str.length == 10) {
462
- // Check dash positions at indices 4 and 7
463
- const dashPos1 = unchecked(<i32>str.charCodeAt(4));
464
- const dashPos2 = unchecked(<i32>str.charCodeAt(7));
465
- if (dashPos1 == 45 && dashPos2 == 45) {
466
- // Hyphen is ASCII 45, digits start at ASCII 48
467
- // Parse year, month, day directly without split()
468
- const year =
469
- unchecked(<i32>str.charCodeAt(0) - 48) * 1000 +
470
- unchecked(<i32>str.charCodeAt(1) - 48) * 100 +
471
- unchecked(<i32>str.charCodeAt(2) - 48) * 10 +
472
- unchecked(<i32>str.charCodeAt(3) - 48);
473
- const month =
474
- unchecked(<i32>str.charCodeAt(5) - 48) * 10 + unchecked(<i32>str.charCodeAt(6) - 48);
475
- const day =
476
- unchecked(<i32>str.charCodeAt(8) - 48) * 10 + unchecked(<i32>str.charCodeAt(9) - 48);
477
-
478
- // Validate ranges
479
- if (year >= 0 && month >= 1 && month <= 12 && day >= 1 && day <= 31) {
480
- const daysSinceEpoch = calculateDaysSinceEpoch(year, month, day);
481
- return Value.Float(<f64>daysSinceEpoch * 86400.0);
482
- }
483
- }
484
- }
485
-
486
- // Parse ISO 8601 date format: YYYY-MM-DD
487
- // Also handle datetime format: YYYY-MM-DDTHH:MM:SS[Z|+/-HH:MM]
488
- let dateStr = str;
489
- let timeStr = '';
490
- let tzOffsetSeconds: i32 = 0; // Timezone offset in seconds (positive = ahead of UTC)
491
-
492
- const tIndex = str.indexOf('T');
493
- if (tIndex >= 0) {
494
- dateStr = str.substring(0, tIndex);
495
- let timePart = str.substring(tIndex + 1);
496
-
497
- // Handle timezone: Z, +HH:MM, -HH:MM
498
- const zIndex = timePart.indexOf('Z');
499
- if (zIndex >= 0) {
500
- timeStr = timePart.substring(0, zIndex);
501
- tzOffsetSeconds = 0; // UTC
502
- } else {
503
- const plusIndex = timePart.indexOf('+');
504
- const minusIndex = timePart.indexOf('-');
505
- // Find timezone offset (look for +/- after the time part, not in middle of time)
506
- let tzStartIndex = -1;
507
- if (plusIndex >= 5) {
508
- // At least HH:MM before timezone
509
- tzStartIndex = plusIndex;
510
- } else if (minusIndex >= 5) {
511
- tzStartIndex = minusIndex;
512
- }
513
-
514
- if (tzStartIndex >= 0) {
515
- timeStr = timePart.substring(0, tzStartIndex);
516
- const tzStr = timePart.substring(tzStartIndex);
517
- // Parse timezone offset (+/-HH:MM or +/-HH)
518
- const isNegative = tzStr.charAt(0) == '-';
519
- const tzParts = tzStr.substring(1).split(':');
520
- if (tzParts.length >= 1) {
521
- const tzHours = parseIntValue(tzParts[0]);
522
- const tzMinutes = tzParts.length >= 2 ? parseIntValue(tzParts[1]) : 0;
523
- if (tzHours >= 0 && tzMinutes >= 0) {
524
- tzOffsetSeconds = tzHours * 3600 + tzMinutes * 60;
525
- if (isNegative) tzOffsetSeconds = -tzOffsetSeconds;
526
- }
527
- }
528
- } else {
529
- timeStr = timePart;
530
- }
531
- }
532
- }
533
-
534
- // Split date by dash
535
- const parts = dateStr.split('-');
536
- if (parts.length < 3) return Value.Null();
537
-
538
- // Parse year, month, day
539
- const year = parseIntValue(parts[0]);
540
- const month = parseIntValue(parts[1]);
541
- const day = parseIntValue(parts[2]);
542
-
543
- if (year < 0 || month < 1 || month > 12 || day < 1 || day > 31) {
544
- return Value.Null();
545
- }
546
-
547
- // Calculate days since epoch
548
- const daysSinceEpoch = calculateDaysSinceEpoch(year, month, day);
549
-
550
- // Parse time if present (HH:MM:SS or HH:MM)
551
- let hours: i32 = 0;
552
- let minutes: i32 = 0;
553
- let seconds: i32 = 0;
554
-
555
- if (timeStr.length > 0) {
556
- const timeParts = timeStr.split(':');
557
- if (timeParts.length >= 2) {
558
- hours = parseIntValue(timeParts[0]);
559
- minutes = parseIntValue(timeParts[1]);
560
- if (timeParts.length >= 3) {
561
- // Handle fractional seconds (e.g., "10:30:00.123")
562
- const secStr = timeParts[2];
563
- const dotIndex = secStr.indexOf('.');
564
- if (dotIndex >= 0) {
565
- seconds = parseIntValue(secStr.substring(0, dotIndex));
566
- } else {
567
- seconds = parseIntValue(secStr);
568
- }
569
- }
570
- }
571
- if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59) {
572
- // Invalid time - use 0
573
- hours = 0;
574
- minutes = 0;
575
- seconds = 0;
576
- }
577
- }
578
-
579
- // Calculate total seconds since epoch
580
- // daysSinceEpoch * 86400 + hours * 3600 + minutes * 60 + seconds - tzOffsetSeconds
581
- // We subtract tzOffsetSeconds to convert local time to UTC
582
- const totalSeconds: f64 =
583
- <f64>daysSinceEpoch * 86400.0 +
584
- <f64>hours * 3600.0 +
585
- <f64>minutes * 60.0 +
586
- <f64>seconds -
587
- <f64>tzOffsetSeconds;
588
-
589
- return Value.Float(totalSeconds);
590
- }
591
-
592
- /**
593
- * Parse a time string into a numeric value.
594
- *
595
- * Supports format: HH:MM:SS or HH:MM
596
- * Returns the number of seconds since midnight.
597
- *
598
- * @param val - A string value containing a time
599
- * @returns A float Value representing seconds since midnight
600
- */
601
- export function parseTime(val: Value): Value {
602
- if (!val.isString()) return Value.Null();
603
-
604
- const str = val.asString();
605
- const parts = str.split(':');
606
-
607
- if (parts.length < 2) return Value.Null();
608
-
609
- const hours = parseIntValue(parts[0]);
610
- const minutes = parseIntValue(parts[1]);
611
- const seconds = parts.length >= 3 ? parseIntValue(parts[2]) : 0;
612
-
613
- if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59) {
614
- return Value.Null();
615
- }
616
-
617
- const totalSeconds = hours * 3600 + minutes * 60 + seconds;
618
- return Value.Float(<f64>totalSeconds);
619
- }
620
-
621
- /**
622
- * Parse a duration string into a numeric value.
623
- *
624
- * Supports ISO 8601 duration format: P[n]Y[n]M[n]DT[n]H[n]M[n]S
625
- * Also supports simple formats like "30d", "24h", "60m", "3600s"
626
- *
627
- * @param val - A string value containing a duration
628
- * @returns A float Value representing duration in seconds
629
- */
630
- export function parseDuration(val: Value): Value {
631
- if (!val.isString()) return Value.Null();
632
-
633
- const str = val.asString();
634
-
635
- // Handle simple formats: "30d", "24h", "60m", "3600s"
636
- if (str.length > 1) {
637
- const unit = str.charAt(str.length - 1);
638
- const numStr = str.substring(0, str.length - 1);
639
- const num = parseFloatValue(numStr);
640
-
641
- if (num >= 0) {
642
- if (unit == 's') return Value.Float(num);
643
- if (unit == 'm') return Value.Float(num * 60);
644
- if (unit == 'h') return Value.Float(num * 3600);
645
- if (unit == 'd') return Value.Float(num * 86400);
646
- }
647
- }
648
-
649
- // Full ISO 8601 duration format (P[n]Y[n]M[n]DT[n]H[n]M[n]S) not yet supported
650
- return Value.Null();
651
- }
652
-
653
- /**
654
- * Parse a string to an integer value.
655
- */
656
- function parseIntValue(str: string): i32 {
657
- let result: i32 = 0;
658
- let negative = false;
659
- let i = 0;
660
-
661
- // Handle leading whitespace
662
- while (i < str.length && str.charCodeAt(i) == 32) {
663
- i++;
664
- }
665
-
666
- // Handle negative sign
667
- if (i < str.length && str.charCodeAt(i) == 45) {
668
- // '-'
669
- negative = true;
670
- i++;
671
- }
672
-
673
- // Parse digits
674
- let hasDigits = false;
675
- while (i < str.length) {
676
- const c = str.charCodeAt(i);
677
- if (c >= 48 && c <= 57) {
678
- // '0'-'9'
679
- result = result * 10 + (c - 48);
680
- hasDigits = true;
681
- i++;
682
- } else {
683
- break;
684
- }
685
- }
686
-
687
- // AssemblyScript doesn't have optional types or Result<T,E>, so we use -1 as sentinel
688
- // value for parse errors. Callers check for negative values to detect failures.
689
- if (!hasDigits) return -1;
690
-
691
- return negative ? -result : result;
692
- }
693
-
694
- /**
695
- * Parse a string to a float value.
696
- * Returns NaN for invalid input.
697
- */
698
- function parseFloatValue(str: string): f64 {
699
- return parseFloat(str);
700
- }
701
-
702
- /**
703
- * Calculate days since Unix epoch (1970-01-01).
704
- * Uses a simplified algorithm that handles leap years.
705
- */
706
- function calculateDaysSinceEpoch(year: i32, month: i32, day: i32): i32 {
707
- // Days in each month (non-leap year)
708
- const daysInMonth: i32[] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
709
-
710
- // Calculate days from years
711
- let days: i32 = 0;
712
-
713
- // Calculate years from 1970 to target year
714
- if (year >= 1970) {
715
- for (let y = 1970; y < year; y++) {
716
- days += isLeapYear(y) ? 366 : 365;
717
- }
718
- } else {
719
- for (let y = year; y < 1970; y++) {
720
- days -= isLeapYear(y) ? 366 : 365;
721
- }
722
- }
723
-
724
- // Add days from months
725
- for (let m = 1; m < month; m++) {
726
- days += unchecked(daysInMonth[m - 1]);
727
- // Add leap day for February in leap years
728
- if (m == 2 && isLeapYear(year)) {
729
- days += 1;
730
- }
731
- }
732
-
733
- // Add days
734
- days += day - 1; // day 1 is the first day, so subtract 1
735
-
736
- return days;
737
- }
738
-
739
- /**
740
- * Check if a year is a leap year.
741
- */
742
- function isLeapYear(year: i32): bool {
743
- return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
744
- }
745
-
746
- // === Math Functions ===
747
-
748
- export function absValue(val: Value): Value {
749
- if (val.isNumber()) {
750
- const n = val.asFloat();
751
- return Value.Float(n < 0 ? -n : n);
752
- }
753
- return Value.Null();
754
- }
755
-
756
- export function floorValue(val: Value): Value {
757
- if (val.isNumber()) {
758
- return Value.Float(Math.floor(val.asFloat()));
759
- }
760
- return Value.Null();
761
- }
762
-
763
- export function ceilValue(val: Value): Value {
764
- if (val.isNumber()) {
765
- return Value.Float(Math.ceil(val.asFloat()));
766
- }
767
- return Value.Null();
768
- }
769
-
770
- export function roundValue(val: Value): Value {
771
- if (val.isNumber()) {
772
- return Value.Float(Math.round(val.asFloat()));
773
- }
774
- return Value.Null();
775
- }
776
-
777
- // === String Functions ===
778
-
779
- /**
780
- * Convert a string to uppercase.
781
- *
782
- * Non-alphabetic characters (numbers, symbols, spaces) remain unchanged.
783
- * If the input is not a string, returns null.
784
- *
785
- * Examples:
786
- * - upper("hello") → "HELLO"
787
- * - upper("Hello World") → "HELLO WORLD"
788
- * - upper("123abc") → "123ABC"
789
- * - upper("!@#") → "!@#"
790
- * - upper(123) → null
791
- * - upper(null) → null
792
- *
793
- * @param val - The value to convert to uppercase
794
- * @returns The uppercase string, or null if input is not a string
795
- */
796
- export function upperString(val: Value): Value {
797
- if (!val.isString()) return Value.Null();
798
- const str = val.asString();
799
- return Value.String(str.toUpperCase());
800
- }
801
-
802
- /**
803
- * Convert a string to lowercase.
804
- *
805
- * Non-alphabetic characters (numbers, symbols, spaces) remain unchanged.
806
- * If the input is not a string, returns null.
807
- *
808
- * Examples:
809
- * - lower("HELLO") → "hello"
810
- * - lower("Hello World") → "hello world"
811
- * - lower("123ABC") → "123abc"
812
- * - lower("!@#") → "!@#"
813
- * - lower(123) → null
814
- * - lower(null) → null
815
- *
816
- * @param val - The value to convert to lowercase
817
- * @returns The lowercase string, or null if input is not a string
818
- */
819
- export function lowerString(val: Value): Value {
820
- if (!val.isString()) return Value.Null();
821
- const str = val.asString();
822
- return Value.String(str.toLowerCase());
823
- }
824
-
825
- /**
826
- * Remove leading and trailing whitespace from a string.
827
- *
828
- * Whitespace includes spaces, tabs, newlines, and other whitespace characters.
829
- * If the input is not a string, returns null.
830
- *
831
- * Examples:
832
- * - trim(" hello ") → "hello"
833
- * - trim("\t\nhello\n\t") → "hello"
834
- * - trim("hello") → "hello" (no change)
835
- * - trim(" hello world ") → "hello world"
836
- * - trim("") → "" (empty string)
837
- * - trim(" ") → "" (only whitespace)
838
- * - trim(123) → null
839
- * - trim(null) → null
840
- *
841
- * @param val - The value to trim whitespace from
842
- * @returns The trimmed string, or null if input is not a string
843
- */
844
- export function trimString(val: Value): Value {
845
- if (!val.isString()) return Value.Null();
846
- const str = val.asString();
847
- return Value.String(str.trim());
848
- }
849
-
850
- /**
851
- * Extract a portion of a string.
852
- *
853
- * Returns a substring starting at the specified index and optionally
854
- * ending at the specified index (or length).
855
- *
856
- * Parameters:
857
- * - str: The input string
858
- * - start: The starting index (0-based, inclusive)
859
- * - endOrLength: Optional end index (exclusive) or length. If omitted,
860
- * extracts from start to the end of the string.
861
- *
862
- * Behavior:
863
- * - If start is negative, it's treated as an offset from the end of the string
864
- * - If start < 0, it's clamped to 0 (empty prefix)
865
- * - If start >= str.length, returns empty string
866
- * - If endOrLength is omitted or null, extracts to end of string
867
- * - If endOrLength is negative, it's treated as an offset from the end
868
- * - If endOrLength > str.length, it's clamped to str.length
869
- * - If endOrLength <= start, returns empty string
870
- *
871
- * Examples:
872
- * - substring("hello", 0, 2) → "he"
873
- * - substring("hello", 1, 3) → "el"
874
- * - substring("hello", 2) → "llo"
875
- * - substring("hello", 0) → "hello"
876
- * - substring("hello", 0, 5) → "hello"
877
- * - substring("hello", 0, 10) → "hello" (clamped to end)
878
- * - substring("hello", 3, 5) → "lo"
879
- * - substring("hello", -3) → "llo" (from end)
880
- * - substring("hello", 0, -2) → "hel" (from end)
881
- * - substring("hello", 10) → "" (start beyond length)
882
- * - substring("", 0, 5) → "" (empty string)
883
- * - substring(123, 0, 2) → null (not a string)
884
- * - substring(null, 0, 2) → null (not a string)
885
- *
886
- * @param strVal - The string value to extract from
887
- * @param startVal - The starting index
888
- * @param endOrLengthVal - Optional end index (exclusive) or length
889
- * @returns The substring, or null if input is not a string
890
- */
891
- export function substringString(strVal: Value, startVal: Value, endOrLengthVal: Value): Value {
892
- if (!strVal.isString()) return Value.Null();
893
- const str = strVal.asString();
894
- const len = str.length;
895
-
896
- if (len == 0) return Value.String('');
897
-
898
- // Parse start index - use asFloat() to handle both Int and Float types
899
- let start: i32 = 0;
900
- if (startVal.isNumber()) {
901
- start = <i32>startVal.asFloat();
902
- }
903
-
904
- // Handle negative start (offset from end)
905
- if (start < 0) {
906
- start = len + start;
907
- if (start < 0) start = 0; // Clamp to 0
908
- }
909
-
910
- // Handle start beyond length
911
- if (start >= len) {
912
- return Value.String('');
913
- }
914
-
915
- // Parse end index if provided
916
- let hasEnd = !endOrLengthVal.isNull();
917
- let end: i32 = len; // Default to end of string
918
-
919
- if (hasEnd && endOrLengthVal.isNumber()) {
920
- end = <i32>endOrLengthVal.asFloat();
921
-
922
- // Handle negative end (offset from end)
923
- if (end < 0) {
924
- end = len + end;
925
- if (end < 0) end = 0;
926
- }
927
-
928
- // Clamp end to string length
929
- if (end > len) end = len;
930
-
931
- // Ensure end >= start
932
- if (end < start) end = start;
933
- }
934
-
935
- // Extract substring
936
- return Value.String(str.substring(start, end));
937
- }
938
-
939
- /**
940
- * Find the index of the first occurrence of a substring.
941
- *
942
- * Returns the index of the first occurrence of the search string,
943
- * starting from the specified position. Returns -1 if not found.
944
- *
945
- * Parameters:
946
- * - str: The string to search in
947
- * - searchVal: The substring to search for
948
- * - startVal: Optional starting position (default: 0)
949
- *
950
- * Behavior:
951
- * - If the source string is not a string, returns null
952
- * - If the search value is not a string, returns null
953
- * - If start is negative or omitted, defaults to 0
954
- * - If start > str.length, returns -1 (not found)
955
- * - If search string is empty, returns the start position
956
- * - Returns -1 if the substring is not found
957
- *
958
- * Examples:
959
- * - indexOf("hello", "l") → 2
960
- * - indexOf("hello", "x") → -1
961
- * - indexOf("hello", "l", 3) → 3
962
- * - indexOf("hello", "ll") → 2
963
- * - indexOf("hello", "lo", 0) → 3
964
- * - indexOf("hello", "") → 0 (empty search string)
965
- * - indexOf("hello", "o", 5) → -1 (start at end)
966
- * - indexOf("hello", "o", 10) → -1 (start beyond length)
967
- * - indexOf("", "x") → -1 (empty source)
968
- * - indexOf("", "") → 0 (both empty)
969
- * - indexOf(123, "1") → null (not a string)
970
- *
971
- * @param strVal - The string value to search in
972
- * @param searchVal - The substring value to search for
973
- * @param startVal - Optional starting position
974
- * @returns The index of the first occurrence, or -1 if not found, or null if inputs are not strings
975
- */
976
- export function indexOfString(strVal: Value, searchVal: Value, startVal: Value): Value {
977
- if (!strVal.isString() || !searchVal.isString()) return Value.Null();
978
- const str = strVal.asString();
979
- const search = searchVal.asString();
980
-
981
- // Parse start index
982
- let start: i32 = 0;
983
- if (!startVal.isNull() && startVal.isNumber()) {
984
- start = <i32>startVal.asFloat();
985
- }
986
-
987
- // Handle negative start
988
- if (start < 0) {
989
- start = 0;
990
- }
991
-
992
- // Special case: empty search string (matches JavaScript behavior)
993
- // When searching for empty string, return the start position clamped to string length
994
- if (search.length == 0) {
995
- const len = <i32>str.length;
996
- if (start >= len) {
997
- return Value.Int(<i64>len);
998
- }
999
- return Value.Int(<i64>start);
1000
- }
1001
-
1002
- // AssemblyScript's indexOf returns -1 for not found
1003
- const index = str.indexOf(search, start);
1004
- return Value.Int(<i64>index);
1005
- }
1006
-
1007
- /**
1008
- * Check if a string starts with a given prefix.
1009
- *
1010
- * Returns true if the string begins with the specified prefix, false otherwise.
1011
- *
1012
- * Parameters:
1013
- * - str: The string to check
1014
- * - prefix: The prefix to look for
1015
- *
1016
- * Behavior:
1017
- * - If either input is not a string, returns null
1018
- * - If the prefix is an empty string, returns true
1019
- * - Case-sensitive comparison
1020
- *
1021
- * Examples:
1022
- * - startsWith("hello", "he") → true
1023
- * - startsWith("hello", "lo") → false
1024
- * - startsWith("hello", "hello") → true
1025
- * - startsWith("hello", "hello world") → false (prefix longer than string)
1026
- * - startsWith("", "") → true (empty strings)
1027
- * - startsWith("hello", "") → true (empty prefix)
1028
- * - startsWith("", "he") → false (prefix longer than empty string)
1029
- * - startsWith("Hello", "he") → false (case-sensitive)
1030
- * - startsWith(123, "1") → null (not a string)
1031
- * - startsWith(null, "he") → null (not a string)
1032
- *
1033
- * @param strVal - The string value to check
1034
- * @param prefixVal - The prefix value to look for
1035
- * @returns true if the string starts with the prefix, false otherwise, or null if inputs are not strings
1036
- */
1037
- export function startsWithString(strVal: Value, prefixVal: Value): Value {
1038
- if (!strVal.isString() || !prefixVal.isString()) return Value.Null();
1039
- const str = strVal.asString();
1040
- const prefix = prefixVal.asString();
1041
-
1042
- // AssemblyScript's startsWith returns a bool
1043
- const result = str.startsWith(prefix);
1044
- return Value.Bool(result);
1045
- }
1046
-
1047
- /**
1048
- * Check if a string ends with a given suffix.
1049
- *
1050
- * Returns true if the string ends with the specified suffix, false otherwise.
1051
- *
1052
- * Parameters:
1053
- * - str: The string to check
1054
- * - suffix: The suffix to look for
1055
- *
1056
- * Behavior:
1057
- * - If either input is not a string, returns null
1058
- * - If the suffix is an empty string, returns true
1059
- * - Case-sensitive comparison
1060
- *
1061
- * Examples:
1062
- * - endsWith("hello", "lo") → true
1063
- * - endsWith("hello", "he") → false
1064
- * - endsWith("hello", "hello") → true
1065
- * - endsWith("hello", "hello world") → false (suffix longer than string)
1066
- * - endsWith("", "") → true (empty strings)
1067
- * - endsWith("hello", "") → true (empty suffix)
1068
- * - endsWith("", "lo") → false (suffix longer than empty string)
1069
- * - endsWith("Hello", "LO") → false (case-sensitive)
1070
- * - endsWith(123, "1") → null (not a string)
1071
- * - endsWith(null, "lo") → null (not a string)
1072
- *
1073
- * @param strVal - The string value to check
1074
- * @param suffixVal - The suffix value to look for
1075
- * @returns true if the string ends with the suffix, false otherwise, or null if inputs are not strings
1076
- */
1077
- export function endsWithString(strVal: Value, suffixVal: Value): Value {
1078
- if (!strVal.isString() || !suffixVal.isString()) return Value.Null();
1079
- const str = strVal.asString();
1080
- const suffix = suffixVal.asString();
1081
-
1082
- // AssemblyScript's endsWith returns a bool
1083
- const result = str.endsWith(suffix);
1084
- return Value.Bool(result);
1085
- }
1086
-
1087
- /**
1088
- * Split a string into an array based on a delimiter.
1089
- *
1090
- * Returns an array of substrings by dividing the input string at each
1091
- * occurrence of the specified delimiter.
1092
- *
1093
- * Parameters:
1094
- * - str: The string to split
1095
- * - delimiter: The delimiter to split on
1096
- *
1097
- * Behavior:
1098
- * - If the input string is not a string, returns null
1099
- * - If the delimiter is not a string, returns null
1100
- * - If the delimiter is an empty string, returns an array of individual characters
1101
- * - If the string and delimiter are both empty, returns an array with one empty string
1102
- * - If the delimiter is not found, returns an array with the original string
1103
- *
1104
- * Examples:
1105
- * - split("a,b,c", ",") → ["a", "b", "c"]
1106
- * - split("hello", "") → ["h", "e", "l", "l", "o"]
1107
- * - split("a-b-c", "-") → ["a", "b", "c"]
1108
- * - split("word", "x") → ["word"]
1109
- * - split("", ",") → [""]
1110
- * - split("", "") → [""] (special case: empty string splits into array with one empty string)
1111
- * - split("a,,b", ",") → ["a", "", "b"] (preserves empty segments)
1112
- * - split(null, ",") → null
1113
- * - split("hello", null) → null
1114
- * - split("hello", 123) → null
1115
- *
1116
- * @param strVal - The string value to split
1117
- * @param delimiterVal - The delimiter value to split on
1118
- * @returns An array of strings, or null if inputs are invalid
1119
- */
1120
- export function splitString(strVal: Value, delimiterVal: Value): Value {
1121
- if (!strVal.isString() || !delimiterVal.isString()) return Value.Null();
1122
- const str = strVal.asString();
1123
- const delimiter = delimiterVal.asString();
1124
-
1125
- const parts = str.split(delimiter);
1126
-
1127
- // Convert string array to Value array
1128
- const result = new Array<Value>(parts.length);
1129
- for (let i = 0; i < parts.length; i++) {
1130
- unchecked((result[i] = Value.String(unchecked(parts[i]))));
1131
- }
1132
-
1133
- return Value.Array(result);
1134
- }
1135
-
1136
- /**
1137
- * Join array elements into a string with a separator.
1138
- *
1139
- * Returns a string that concatenates all elements of the array,
1140
- * separated by the specified delimiter.
1141
- *
1142
- * Parameters:
1143
- * - arr: The array to join
1144
- * - delimiter: The delimiter to insert between elements
1145
- *
1146
- * Behavior:
1147
- * - If the input is not an array, returns null
1148
- * - If the delimiter is not a string, returns null
1149
- * - If the array is empty, returns an empty string
1150
- * - Each array element is converted to its string representation
1151
- * - The delimiter is placed between each adjacent element, not at the start or end
1152
- *
1153
- * Examples:
1154
- * - join(["a", "b", "c"], "-") → "a-b-c"
1155
- * - join(["hello", "world"], " ") → "hello world"
1156
- * - join(["a"], ",") → "a"
1157
- * - join([], ",") → ""
1158
- * - join([1, 2, 3], "-") → "1-2-3" (numbers converted to strings)
1159
- * - join([true, false], " ") → "true false" (booleans converted to strings)
1160
- * - join(["a", null, "b"], "-") → "a-null-b" (null converted to "null")
1161
- * - join(null, ",") → null
1162
- * - join(["a"], null) → null
1163
- * - join(["a"], 123) → null
1164
- *
1165
- * @param arrVal - The array value to join
1166
- * @param delimiterVal - The delimiter to insert between elements
1167
- * @returns The concatenated string, or null if inputs are invalid
1168
- */
1169
- export function joinArray(arrVal: Value, delimiterVal: Value): Value {
1170
- if (!arrVal.isArray() || !delimiterVal.isString()) return Value.Null();
1171
- const arr = arrVal.asArray();
1172
- const delimiter = delimiterVal.asString();
1173
-
1174
- // Handle empty array
1175
- if (arr.length == 0) {
1176
- return Value.String('');
1177
- }
1178
-
1179
- // Build the result string
1180
- let result: string = '';
1181
- for (let i = 0; i < arr.length; i++) {
1182
- // Append delimiter if not first element
1183
- if (i > 0) {
1184
- result += delimiter;
1185
- }
1186
- // Convert value to string and append
1187
- result += valueToString(unchecked(arr[i]));
1188
- }
1189
-
1190
- return Value.String(result);
1191
- }
1192
-
1193
- /**
1194
- * Convert a string value to a number.
1195
- *
1196
- * Parses a string containing a numeric value and returns it as a float.
1197
- * Supports decimal numbers and negative numbers.
1198
- *
1199
- * Parameters:
1200
- * - val: The value to convert to a number (must be a string)
1201
- *
1202
- * Behavior:
1203
- * - If input is not a string, returns null
1204
- * - If string cannot be parsed as a number, returns NaN
1205
- * - Returns f64 (float) for all numeric values
1206
- *
1207
- * Examples:
1208
- * - number("123") → 123.0
1209
- * - number("3.14") → 3.14
1210
- * - number("-42") → -42.0
1211
- * - number("0.5") → 0.5
1212
- * - number("not a number") → NaN
1213
- * - number(null) → null
1214
- * - number(123) → null (not a string)
1215
- *
1216
- * @param val - The value to convert to a number
1217
- * @returns The numeric value as a float, or null if input is not a string
1218
- */
1219
- export function numberValue(val: Value): Value {
1220
- if (val.isString()) {
1221
- const str = val.asString();
1222
- // Parse the string using the same logic as parseIntValue
1223
- let result: i64 = 0;
1224
- let negative = false;
1225
- let i = 0;
1226
-
1227
- // Handle leading whitespace
1228
- while (i < str.length && str.charCodeAt(i) == 32) {
1229
- i++;
1230
- }
1231
-
1232
- // Handle negative sign
1233
- if (i < str.length && str.charCodeAt(i) == 45) {
1234
- // '-'
1235
- negative = true;
1236
- i++;
1237
- }
1238
-
1239
- // Parse digits
1240
- let hasDigits = false;
1241
- while (i < str.length) {
1242
- const c = str.charCodeAt(i);
1243
- if (c >= 48 && c <= 57) {
1244
- // '0'-'9'
1245
- result = result * 10 + (c - 48);
1246
- hasDigits = true;
1247
- i++;
1248
- } else {
1249
- break;
1250
- }
1251
- }
1252
-
1253
- if (!hasDigits) return Value.Null();
1254
-
1255
- const finalResult = negative ? -result : result;
1256
- return Value.Float(<f64>finalResult);
1257
- } else if (val.isNumber()) {
1258
- return val;
1259
- } else if (val.isBool()) {
1260
- return val.asBool() ? Value.Float(1.0) : Value.Float(0.0);
1261
- }
1262
- return Value.Null();
1263
- }
1264
-
1265
- /**
1266
- * Convert any value to a string.
1267
- *
1268
- * Converts the input value to its string representation.
1269
- *
1270
- * Parameters:
1271
- * - val: Any value to convert to string
1272
- *
1273
- * Behavior:
1274
- * - Boolean: "true" or "false"
1275
- * - Number: numeric string representation
1276
- * - String: returned as-is
1277
- * - Null: null (returns null, not the string "null")
1278
- * - Array/Object: "[object]"
1279
- *
1280
- * Examples:
1281
- * - string(42) → "42"
1282
- * - string(3.14) → "3.14"
1283
- * - string(true) → "true"
1284
- * - string(false) → "false"
1285
- * - string("hello") → "hello"
1286
- * - string(null) → null
1287
- *
1288
- * @param val - The value to convert to a string
1289
- * @returns The string representation, or null if input is null
1290
- */
1291
- export function stringValue(val: Value): Value {
1292
- if (val.isNull()) {
1293
- return Value.Null();
1294
- }
1295
- if (val.isBool()) {
1296
- return Value.String(val.asBool() ? 'true' : 'false');
1297
- }
1298
- if (val.isNumber()) {
1299
- // Converting to string matches JavaScript String() coercion for consistency with JS engines.
1300
- // Integers print without decimals (3 not 3.0), floats use AS toString() formatting.
1301
- if (val.type == TYPE_INT) {
1302
- return Value.String(val.intVal.toString());
1303
- }
1304
- const f = val.asFloat();
1305
- // Check if it's a whole number by testing if remainder is 0
1306
- if (f % 1.0 == 0.0) {
1307
- return Value.String(i64(<i64>f).toString());
1308
- }
1309
- return Value.String(f.toString());
1310
- }
1311
- if (val.isString()) {
1312
- return val;
1313
- }
1314
- // Arrays and objects fall back to a placeholder
1315
- return Value.String('[object]');
1316
- }
1317
-
1318
- /**
1319
- * Replace the first occurrence of a substring with another string.
1320
- *
1321
- * Returns a new string where the first occurrence of the search string
1322
- * is replaced by the replacement string.
1323
- *
1324
- * Parameters:
1325
- * - str: The string to modify
1326
- * - search: The substring to search for
1327
- * - replacement: The string to replace with
1328
- *
1329
- * Behavior:
1330
- * - If the source string is not a string, returns null
1331
- * - If the search value is not a string, returns null
1332
- * - If the replacement value is not a string, returns null
1333
- * - If the search string is empty, the replacement is inserted at the beginning
1334
- * - If the search string is not found, returns the original string unchanged
1335
- * - Only the first occurrence is replaced
1336
- *
1337
- * Examples:
1338
- * - replace("hello", "l", "L") → "heLlo"
1339
- * - replace("hello", "lo", "LO") → "heLLO"
1340
- * - replace("hello", "x", "X") → "hello" (not found, unchanged)
1341
- * - replace("hello hello", "l", "L") → "heLlo hello" (only first occurrence)
1342
- * - replace("hello", "", "X") → "Xhello" (empty search, insert at start)
1343
- * - replace("", "x", "X") → "" (empty source, no match)
1344
- * - replace(null, "l", "L") → null (not a string)
1345
- * - replace("hello", null, "L") → null (not a string)
1346
- * - replace("hello", "l", null) → null (not a string)
1347
- *
1348
- * @param strVal - The string value to modify
1349
- * @param searchVal - The substring value to search for
1350
- * @param replacementVal - The replacement string value
1351
- * @returns The modified string with first occurrence replaced, or null if inputs are invalid
1352
- */
1353
- export function replaceString(strVal: Value, searchVal: Value, replacementVal: Value): Value {
1354
- if (!strVal.isString() || !searchVal.isString() || !replacementVal.isString())
1355
- return Value.Null();
1356
- const str = strVal.asString();
1357
- const search = searchVal.asString();
1358
- const replacement = replacementVal.asString();
1359
-
1360
- // AssemblyScript's replace() replaces all occurrences by default
1361
- // We need to replace only the first occurrence, so we use indexOf and string concatenation
1362
- const index = str.indexOf(search);
1363
-
1364
- // Search string not found - return original string
1365
- if (index < 0) {
1366
- return Value.String(str);
1367
- }
1368
-
1369
- // Build result: prefix + replacement + suffix
1370
- const prefix = str.substring(0, index);
1371
- const suffix = str.substring(index + search.length);
1372
- return Value.String(prefix + replacement + suffix);
1373
- }
1374
-
1375
- /**
1376
- * Replace all occurrences of a substring with another string.
1377
- *
1378
- * Returns a new string where all occurrences of the search string
1379
- * are replaced by the replacement string.
1380
- *
1381
- * Parameters:
1382
- * - str: The string to modify
1383
- * - search: The substring to search for
1384
- * - replacement: The string to replace with
1385
- *
1386
- * Behavior:
1387
- * - If the source string is not a string, returns null
1388
- * - If the search value is not a string, returns null
1389
- * - If the replacement value is not a string, returns null
1390
- * - If the search string is empty, the replacement is inserted between each character
1391
- * - If the search string is not found, returns the original string unchanged
1392
- * - All occurrences are replaced
1393
- *
1394
- * Examples:
1395
- * - replaceAll("hello", "l", "L") → "heLLo"
1396
- * - replaceAll("hello hello", "l", "L") → "heLLo heLLo"
1397
- * - replaceAll("hello", "lo", "LO") → "heLLO"
1398
- * - replaceAll("hello", "x", "X") → "hello" (not found, unchanged)
1399
- * - replaceAll("hello", "", "X") → "XhXeXlXlXoX" (empty search, insert between chars)
1400
- * - replaceAll("", "x", "X") → "" (empty source, no match)
1401
- * - replaceAll(null, "l", "L") → null (not a string)
1402
- * - replaceAll("hello", null, "L") → null (not a string)
1403
- * - replaceAll("hello", "l", null) → null (not a string)
1404
- *
1405
- * @param strVal - The string value to modify
1406
- * @param searchVal - The substring value to search for
1407
- * @param replacementVal - The replacement string value
1408
- * @returns The modified string with all occurrences replaced, or null if inputs are invalid
1409
- */
1410
- export function replaceAllString(strVal: Value, searchVal: Value, replacementVal: Value): Value {
1411
- if (!strVal.isString() || !searchVal.isString() || !replacementVal.isString())
1412
- return Value.Null();
1413
- let str = strVal.asString();
1414
- const search = searchVal.asString();
1415
- const replacement = replacementVal.asString();
1416
-
1417
- // Handle empty search string - insert replacement between each character
1418
- if (search.length == 0) {
1419
- let result = replacement;
1420
- for (let i = 0; i < str.length; i++) {
1421
- result += str.charAt(i) + replacement;
1422
- }
1423
- return Value.String(result);
1424
- }
1425
-
1426
- // Use split and join for O(n) complexity instead of quadratic substring approach
1427
- const parts = str.split(search);
1428
- return Value.String(parts.join(replacement));
1429
- }
1430
-
1431
- // === Value Type Conversion ===
1432
-
1433
- /**
1434
- * Convert a Value to its string representation.
1435
- *
1436
- * This follows JavaScript's String() conversion rules:
1437
- * - Number: converted using JS toString()
1438
- * - Boolean: "true" or "false"
1439
- * - String: returned as-is
1440
- * - Null: "null"
1441
- *
1442
- * @param val - The value to convert
1443
- * @returns The string representation
1444
- */
1445
- function valueToString(val: Value): string {
1446
- if (val.isNull()) {
1447
- return 'null';
1448
- }
1449
- if (val.isBool()) {
1450
- return val.asBool() ? 'true' : 'false';
1451
- }
1452
- if (val.isNumber()) {
1453
- // Converting to string matches JavaScript String() coercion for consistency with JS engines.
1454
- // Integers print without decimals (3 not 3.0), floats use AS toString() formatting.
1455
- if (val.type == TYPE_INT) {
1456
- return val.intVal.toString();
1457
- }
1458
- const f = val.asFloat();
1459
- // Check if it's a whole number by testing if remainder is 0
1460
- if (f % 1.0 == 0.0) {
1461
- return i64(<i64>f).toString();
1462
- }
1463
- return f.toString();
1464
- }
1465
- if (val.isString()) {
1466
- return val.asString();
1467
- }
1468
- // Arrays and objects fall back to a placeholder
1469
- return '[object]';
1470
- }
1471
-
1472
- // === Comparison Helpers ===
1473
-
1474
- export function valuesEqual(a: Value, b: Value): bool {
1475
- if (a.type != b.type) {
1476
- // Allow int/float comparison
1477
- if (a.isNumber() && b.isNumber()) {
1478
- return a.asFloat() == b.asFloat();
1479
- }
1480
- return false;
1481
- }
1482
- if (a.isBool()) return a.boolVal == b.boolVal;
1483
- if (a.type == TYPE_INT) return a.intVal == b.intVal;
1484
- if (a.type == TYPE_FLOAT) return a.floatVal == b.floatVal;
1485
- if (a.isString()) return a.asString() == b.asString();
1486
- if (a.isNull()) return true; // null == null
1487
- return false; // Arrays/objects: reference equality not supported
1488
- }
1489
-
1490
- /**
1491
- * Compare two values for ordering.
1492
- *
1493
- * Returns:
1494
- * - Negative number if a < b
1495
- * - Zero if a == b
1496
- * - Positive number if a > b
1497
- *
1498
- * Comparison rules:
1499
- * - Strings: Lexicographic comparison using UTF-16 code units (case-sensitive)
1500
- * - Numbers: Numeric comparison (int and float are comparable)
1501
- * - Booleans: false < true
1502
- * - Null: Treated as less than all other values
1503
- * - Mixed types: Comparisons between incompatible types return 0 (not recommended)
1504
- *
1505
- * Examples:
1506
- * - compareValues(String("apple"), String("banana")) → negative
1507
- * - compareValues(String("zebra"), String("apple")) → positive
1508
- * - compareValues(String("abc"), String("abc")) → 0
1509
- * - compareValues(Float(3.14), Float(2.71)) → positive
1510
- * - compareValues(Bool(false), Bool(true)) → negative
1511
- *
1512
- * @param a - The left operand to compare
1513
- * @param b - The right operand to compare
1514
- * @returns Comparison result (-1, 0, or 1)
1515
- */
1516
- export function compareValues(a: Value, b: Value): i32 {
1517
- // Handle null - null is less than all other values
1518
- if (a.isNull()) return b.isNull() ? 0 : -1;
1519
- if (b.isNull()) return 1;
1520
-
1521
- // Allow int/float comparison for numbers
1522
- if (a.isNumber() && b.isNumber()) {
1523
- const aNum = a.asFloat();
1524
- const bNum = b.asFloat();
1525
- if (aNum < bNum) return -1;
1526
- if (aNum > bNum) return 1;
1527
- return 0;
1528
- }
1529
-
1530
- // String comparison - lexicographic, case-sensitive
1531
- if (a.isString() && b.isString()) {
1532
- const aStr = a.asString();
1533
- const bStr = b.asString();
1534
- if (aStr < bStr) return -1;
1535
- if (aStr > bStr) return 1;
1536
- return 0;
1537
- }
1538
-
1539
- // Boolean comparison
1540
- if (a.isBool() && b.isBool()) {
1541
- const aBool = a.asBool();
1542
- const bBool = b.asBool();
1543
- if (!aBool && bBool) return -1;
1544
- if (aBool && !bBool) return 1;
1545
- return 0;
1546
- }
1547
-
1548
- // Mixed types or unsupported types - treat as equal (but probably shouldn't happen)
1549
- return 0;
1550
- }
1551
-
1552
- // === Arithmetic Operators ===
1553
-
1554
- /**
1555
- * Add two values, supporting both numeric addition and string concatenation.
1556
- *
1557
- * Type coercion rules:
1558
- * - If either operand is a string, both are converted to strings and concatenated
1559
- * - Otherwise, both operands are treated as numbers and added together
1560
- *
1561
- * Examples:
1562
- * - "hello" + " " + "world" → "hello world"
1563
- * - "count: " + 5 → "count: 5"
1564
- * - 1 + 2 → 3
1565
- * - true + false → 1 (true=1, false=0, numeric addition)
1566
- * - null + 5 → 5 (null=0, numeric addition)
1567
- * - "prefix" + null → "prefixnull" (string concatenation)
1568
- *
1569
- * @param a - The left operand
1570
- * @param b - The right operand
1571
- * @returns The sum or concatenation result
1572
- */
1573
- export function addValues(a: Value, b: Value): Value {
1574
- // String concatenation takes priority over numeric addition
1575
- // If either operand is a string, concatenate both as strings
1576
- if (a.isString() || b.isString()) {
1577
- const leftStr = a.isString() ? a.asString() : valueToString(a);
1578
- const rightStr = b.isString() ? b.asString() : valueToString(b);
1579
- return Value.String(leftStr + rightStr);
1580
- }
1581
-
1582
- // Numeric addition
1583
- const aNum = a.isNumber() ? a.asFloat() : a.isBool() ? (a.asBool() ? 1.0 : 0.0) : 0.0;
1584
- const bNum = b.isNumber() ? b.asFloat() : b.isBool() ? (b.asBool() ? 1.0 : 0.0) : 0.0;
1585
- return Value.Float(aNum + bNum);
1586
- }
1587
-
1588
- export function subtractValues(a: Value, b: Value): Value {
1589
- if (!a.isNumber() || !b.isNumber()) return Value.Null();
1590
- const aVal = a.asFloat();
1591
- const bVal = b.asFloat();
1592
- return Value.Float(aVal - bVal);
1593
- }
1594
-
1595
- export function multiplyValues(a: Value, b: Value): Value {
1596
- if (!a.isNumber() || !b.isNumber()) return Value.Null();
1597
- const aVal = a.asFloat();
1598
- const bVal = b.asFloat();
1599
- return Value.Float(aVal * bVal);
1600
- }
1601
-
1602
- export function divideValues(a: Value, b: Value): Value {
1603
- if (!a.isNumber() || !b.isNumber()) return Value.Null();
1604
- const aVal = a.asFloat();
1605
- const bVal = b.asFloat();
1606
- if (bVal == 0) return Value.Null(); // Division by zero
1607
- return Value.Float(aVal / bVal);
1608
- }
1609
-
1610
- export function moduloValues(a: Value, b: Value): Value {
1611
- if (!a.isNumber() || !b.isNumber()) return Value.Null();
1612
- const aVal = a.asFloat();
1613
- const bVal = b.asFloat();
1614
- if (bVal == 0) return Value.Null(); // Division by zero
1615
- // Use fmod for floating point modulo
1616
- const result = aVal - Math.trunc(aVal / bVal) * bVal;
1617
- return Value.Float(result);
1618
- }
1619
-
1620
- export function powerValues(a: Value, b: Value): Value {
1621
- if (!a.isNumber() || !b.isNumber()) return Value.Null();
1622
- const aVal = a.asFloat();
1623
- const bVal = b.asFloat();
1624
- const result = Math.pow(aVal, bVal);
1625
- return Value.Float(result);
1626
- }
1627
-
1628
- export function negateValue(a: Value): Value {
1629
- if (!a.isNumber()) return Value.Null();
1630
- const n = a.asFloat();
1631
- return Value.Float(-n);
1632
- }
1633
-
1634
- /**
1635
- * Get a property from an object Value.
1636
- *
1637
- * @param obj - The Value to get the property from (should be an object)
1638
- * @param property - The property name to get
1639
- * @returns The Value of the property, or Null if not found or obj is null/not an object
1640
- */
1641
- export function getProperty(obj: Value, property: string): Value {
1642
- if (obj.isNull()) return Value.Null();
1643
- if (!obj.isObject()) return Value.Null();
1644
-
1645
- const map = obj.asObject();
1646
- if (map.has(property)) {
1647
- return map.get(property);
1648
- }
1649
- return Value.Null();
1650
- }
1651
-
1652
- /**
1653
- * Get an element from an array at the specified index.
1654
- *
1655
- * @param arr - The Value to get the element from (should be an array)
1656
- * @param idx - The index to get (must be a number Value)
1657
- * @returns The Value at the index, or Null if out of bounds or arr is null/not an array
1658
- */
1659
- export function getIndex(arr: Value, idx: Value): Value {
1660
- if (arr.isNull()) return Value.Null();
1661
- if (idx.isNull()) return Value.Null();
1662
-
1663
- // Handle object access with string keys (e.g., baseCosts["FastTrack"])
1664
- if (arr.isObject()) {
1665
- if (idx.isString()) {
1666
- const obj = arr.asObject();
1667
- const key = idx.asString();
1668
- if (obj.has(key)) {
1669
- return obj.get(key);
1670
- }
1671
- return Value.Null();
1672
- }
1673
- // If idx is a number, convert to string for object key lookup
1674
- // This handles cases like obj[0] where obj is {"0": value}
1675
- const keyStr = idx.asString();
1676
- const obj = arr.asObject();
1677
- if (obj.has(keyStr)) {
1678
- return obj.get(keyStr);
1679
- }
1680
- return Value.Null();
1681
- }
1682
-
1683
- if (!arr.isArray()) return Value.Null();
1684
-
1685
- const array = arr.asArray();
1686
- const index = idx.asFloat();
1687
-
1688
- // Handle negative indices (from end of array)
1689
- let actualIndex = <i32>index;
1690
- if (actualIndex < 0) {
1691
- actualIndex = <i32>(array.length + actualIndex);
1692
- }
1693
-
1694
- // Check bounds
1695
- if (actualIndex < 0 || actualIndex >= array.length) {
1696
- return Value.Null();
1697
- }
1698
-
1699
- return array[actualIndex];
1700
- }
1701
-
1702
- /**
1703
- * Create an interval object for range checking.
1704
- *
1705
- * @param start - Start value of the interval
1706
- * @param end - End value of the interval
1707
- * @param startInclusive - Whether the start is inclusive (true) or exclusive (false)
1708
- * @param endInclusive - Whether the end is inclusive (true) or exclusive (false)
1709
- * @returns An object representing the interval
1710
- */
1711
- export function createInterval(
1712
- start: Value,
1713
- end: Value,
1714
- startInclusive: bool,
1715
- endInclusive: bool,
1716
- ): Value {
1717
- const intervalMap = new Map<string, Value>();
1718
- intervalMap.set('__isInterval', Value.Bool(true));
1719
- intervalMap.set('start', start);
1720
- intervalMap.set('end', end);
1721
- intervalMap.set('startInclusive', Value.Bool(startInclusive));
1722
- intervalMap.set('endInclusive', Value.Bool(endInclusive));
1723
- return Value.Object(intervalMap);
1724
- }
1725
-
1726
- /**
1727
- * Check if a value is within an interval.
1728
- *
1729
- * @param needle - The value to check
1730
- * @param interval - The interval object created by createInterval
1731
- * @returns true if the value is within the interval, false otherwise
1732
- */
1733
- function valueInInterval(needle: Value, interval: Value): bool {
1734
- if (!interval.isObject()) return false;
1735
-
1736
- const intervalObj = interval.asObject();
1737
-
1738
- // Check if this is actually an interval object
1739
- if (!intervalObj.has('__isInterval')) return false;
1740
-
1741
- const start = intervalObj.get('start');
1742
- const end = intervalObj.get('end');
1743
- const startInclusive = intervalObj.get('startInclusive').asBool();
1744
- const endInclusive = intervalObj.get('endInclusive').asBool();
1745
-
1746
- const cmpStart = compareValues(needle, start);
1747
- const cmpEnd = compareValues(needle, end);
1748
-
1749
- // Check start boundary
1750
- let passesStart: bool;
1751
- if (startInclusive) {
1752
- passesStart = cmpStart >= 0;
1753
- } else {
1754
- passesStart = cmpStart > 0;
1755
- }
1756
-
1757
- // Check end boundary
1758
- let passesEnd: bool;
1759
- if (endInclusive) {
1760
- passesEnd = cmpEnd <= 0;
1761
- } else {
1762
- passesEnd = cmpEnd < 0;
1763
- }
1764
-
1765
- return passesStart && passesEnd;
1766
- }
1767
-
1768
- /**
1769
- * Check if a value is in an array, a substring is in a string, or a value is in an interval.
1770
- * For arrays: checks if the exact value exists in the array
1771
- * For strings: checks if the needle string is a substring of the haystack
1772
- * For intervals: checks if the value is within the range
1773
- *
1774
- * @param needle - The Value or string to search for
1775
- * @param haystack - The Value to search in (should be an array, string, or interval)
1776
- * @returns true if found, false otherwise
1777
- */
1778
- export function valueIn(needle: Value, haystack: Value): Value {
1779
- if (needle.isNull() || haystack.isNull()) return Value.Bool(false);
1780
-
1781
- // Check if haystack is an interval
1782
- if (haystack.isObject()) {
1783
- const obj = haystack.asObject();
1784
- if (obj.has('__isInterval')) {
1785
- return Value.Bool(valueInInterval(needle, haystack));
1786
- }
1787
- }
1788
-
1789
- // Check if haystack is an array
1790
- if (haystack.isArray()) {
1791
- const array = haystack.asArray();
1792
- for (let i = 0; i < array.length; i++) {
1793
- if (valuesEqual(needle, unchecked(array[i]))) {
1794
- return Value.Bool(true);
1795
- }
1796
- }
1797
- return Value.Bool(false);
1798
- }
1799
-
1800
- // Check if haystack is a string
1801
- if (haystack.isString() && needle.isString()) {
1802
- const hayStr = haystack.asString();
1803
- const needStr = needle.asString();
1804
- if (hayStr.indexOf(needStr) >= 0) {
1805
- return Value.Bool(true);
1806
- }
1807
- return Value.Bool(false);
1808
- }
1809
-
1810
- return Value.Bool(false);
1811
- }