@divmain/jdm-asm 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +53 -0
- package/.oxfmtrc.json +16 -0
- package/.oxlintrc.json +183 -0
- package/AGENTS.md +81 -0
- package/README.md +769 -0
- package/asconfig.json +23 -0
- package/benchmarks/fixtures.ts +111 -0
- package/benchmarks/input-fixtures.ts +80 -0
- package/benchmarks/run.ts +913 -0
- package/benchmarks/worker-pool.ts +223 -0
- package/benchmarks/worker.ts +374 -0
- package/dist/index.d.ts +996 -0
- package/dist/index.js +12239 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
- package/scripts/run-all-tests.ts +220 -0
- package/src/compiler/EXPRESSION_SUBSETS.md +228 -0
- package/src/compiler/asc-compiler.ts +315 -0
- package/src/compiler/ast-types.ts +215 -0
- package/src/compiler/build.ts +56 -0
- package/src/compiler/cache.ts +414 -0
- package/src/compiler/code-generators.ts +211 -0
- package/src/compiler/codegen/index.ts +15 -0
- package/src/compiler/codegen/js-marshal.ts +999 -0
- package/src/compiler/codegen/js-validation.ts +243 -0
- package/src/compiler/codegen.ts +19 -0
- package/src/compiler/compile-time-validation.ts +507 -0
- package/src/compiler/cst-visitor.ts +434 -0
- package/src/compiler/errors.ts +227 -0
- package/src/compiler/expression-parser.ts +536 -0
- package/src/compiler/graph.ts +197 -0
- package/src/compiler/index.ts +199 -0
- package/src/compiler/input-validation.ts +33 -0
- package/src/compiler/marshal-gen.ts +21 -0
- package/src/compiler/nodes/context-resolvers.ts +197 -0
- package/src/compiler/nodes/decision-table.ts +507 -0
- package/src/compiler/nodes/decision.ts +292 -0
- package/src/compiler/nodes/expression-compiler.ts +526 -0
- package/src/compiler/nodes/expression.ts +425 -0
- package/src/compiler/nodes/function.ts +316 -0
- package/src/compiler/nodes/input.ts +60 -0
- package/src/compiler/nodes/switch.ts +547 -0
- package/src/compiler/optimizer.ts +948 -0
- package/src/compiler/orchestrator.ts +352 -0
- package/src/compiler/parser.ts +115 -0
- package/src/compiler/result-selection.ts +161 -0
- package/src/compiler/runtime/index.ts +26 -0
- package/src/compiler/runtime-codegen.ts +211 -0
- package/src/compiler/runtime-validation-codegen.ts +294 -0
- package/src/compiler/runtime.ts +452 -0
- package/src/compiler/schema.ts +245 -0
- package/src/compiler/switch-branch-detection.ts +92 -0
- package/src/compiler/types.ts +136 -0
- package/src/compiler/unary-ast-transforms.ts +148 -0
- package/src/compiler/unary-parser.ts +301 -0
- package/src/compiler/unary-transform.ts +161 -0
- package/src/compiler/utils.ts +27 -0
- package/src/compiler/virtual-fs.ts +90 -0
- package/src/compiler/wasm-instantiate.ts +127 -0
- package/src/index.ts +1 -0
- package/src/runtime/arrays.ts +579 -0
- package/src/runtime/context.ts +189 -0
- package/src/runtime/expressions.ts +1811 -0
- package/src/runtime/index.ts +8 -0
- package/src/runtime/memory.ts +607 -0
- package/src/runtime/strings.ts +260 -0
- package/src/runtime/tables.ts +96 -0
- package/src/runtime/tsconfig.json +4 -0
- package/src/runtime/values.ts +209 -0
- package/test-data/README.md +83 -0
- package/test-data/decision-tables/basic/8k.json +87992 -0
- package/test-data/decision-tables/basic/affiliate-commission-calculator.json +228 -0
- package/test-data/decision-tables/basic/airline-loyalty-points-calculations.json +285 -0
- package/test-data/decision-tables/basic/airline-upgrade-eligibility.json +466 -0
- package/test-data/decision-tables/basic/auto-insurance-premium-calculator.json +412 -0
- package/test-data/decision-tables/basic/booking-personalization-system.json +553 -0
- package/test-data/decision-tables/basic/care-team-assignment-system.json +585 -0
- package/test-data/decision-tables/basic/claim-validation-system.json +307 -0
- package/test-data/decision-tables/basic/clinical-lab-result-interpreter.json +433 -0
- package/test-data/decision-tables/basic/clinical-treatment-protocol.json +474 -0
- package/test-data/decision-tables/basic/credit-limit-adjustment.json +479 -0
- package/test-data/decision-tables/basic/customer-eligibility-engine.json +551 -0
- package/test-data/decision-tables/basic/customer-lifetime-value.json +200 -0
- package/test-data/decision-tables/basic/customer-onboarding-kyc-verification.json +611 -0
- package/test-data/decision-tables/basic/customer-service-escalation.json +191 -0
- package/test-data/decision-tables/basic/decision-table-discounts.json +168 -0
- package/test-data/decision-tables/basic/decision-table-shipping.json +398 -0
- package/test-data/decision-tables/basic/delivery-route-optimizer.json +271 -0
- package/test-data/decision-tables/basic/device-compatibility-checker.json +303 -0
- package/test-data/decision-tables/basic/disaster-relief-fund-allocation.json +296 -0
- package/test-data/decision-tables/basic/dynamic-fx-rate-pricing-system.json +237 -0
- package/test-data/decision-tables/basic/dynamic-marketplace-comission-calculator.json +242 -0
- package/test-data/decision-tables/basic/dynamic-shipping-cost-calculator.json +378 -0
- package/test-data/decision-tables/basic/dynamic-tarrif-engine.json +289 -0
- package/test-data/decision-tables/basic/dynamic-ticket-pricing.json +325 -0
- package/test-data/decision-tables/basic/empty-column-with-space.json +100 -0
- package/test-data/decision-tables/basic/empty-column-without-space.json +100 -0
- package/test-data/decision-tables/basic/environment-compliance-assessment.json +386 -0
- package/test-data/decision-tables/basic/expression-table-map.json +313 -0
- package/test-data/decision-tables/basic/flash-sale-eligibility.json +366 -0
- package/test-data/decision-tables/basic/flight-dispatch-decision-system.json +455 -0
- package/test-data/decision-tables/basic/flight-rebooking-fee-calculator.json +406 -0
- package/test-data/decision-tables/basic/government-assistance.json +299 -0
- package/test-data/decision-tables/basic/grant-funding-distribution.json +307 -0
- package/test-data/decision-tables/basic/hazardous-materials-management-system.json +414 -0
- package/test-data/decision-tables/basic/immigration-eligibility-evaluator.json +765 -0
- package/test-data/decision-tables/basic/import-duties-calculator.json +318 -0
- package/test-data/decision-tables/basic/insurance-agent-commission.json +228 -0
- package/test-data/decision-tables/basic/insurance-coverage-calculator.json +362 -0
- package/test-data/decision-tables/basic/insurance-underwriting-risk.json +321 -0
- package/test-data/decision-tables/basic/international-roaming-policy-manager.json +199 -0
- package/test-data/decision-tables/basic/legacy-plan-management.json +434 -0
- package/test-data/decision-tables/basic/marketplace-listing-verification-system.json +334 -0
- package/test-data/decision-tables/basic/medication-dosage-calculator.json +318 -0
- package/test-data/decision-tables/basic/merch-bags.json +171 -0
- package/test-data/decision-tables/basic/municipal-permit-evaluation-system.json +364 -0
- package/test-data/decision-tables/basic/mvno-partner-enablement.json +313 -0
- package/test-data/decision-tables/basic/partner-revenue-sharing.json +244 -0
- package/test-data/decision-tables/basic/payment-routing-and-fee-calculator.json +475 -0
- package/test-data/decision-tables/basic/policy-discount-calculator.json +307 -0
- package/test-data/decision-tables/basic/policy-eligibility-analyzer.json +299 -0
- package/test-data/decision-tables/basic/product-listing-scoring.json +358 -0
- package/test-data/decision-tables/basic/realtime-fraud-detection.json +235 -0
- package/test-data/decision-tables/basic/regional-compliance-manager.json +278 -0
- package/test-data/decision-tables/basic/returns-and-refund-policy.json +366 -0
- package/test-data/decision-tables/basic/returns-processing-system.json +448 -0
- package/test-data/decision-tables/basic/school-district-resource-allocation.json +282 -0
- package/test-data/decision-tables/basic/seat-map-optimization.json +325 -0
- package/test-data/decision-tables/basic/seller-fee-calculator.json +307 -0
- package/test-data/decision-tables/basic/service-level-agreement-enforcement.json +575 -0
- package/test-data/decision-tables/basic/smart-financial-product-matcher.json +249 -0
- package/test-data/decision-tables/basic/supply-chain-risk.json +316 -0
- package/test-data/decision-tables/basic/table-loop.json +93 -0
- package/test-data/decision-tables/basic/table.json +76 -0
- package/test-data/decision-tables/basic/traffic-violation-penalty-calculator.json +436 -0
- package/test-data/decision-tables/basic/transaction-compliance-classifier.json +525 -0
- package/test-data/decision-tables/basic/vehicle-claims-resolution.json +310 -0
- package/test-data/decision-tables/basic/warehouse-storage-location.json +345 -0
- package/test-data/decision-tables/hit-policy-collect/collect-multiple-matches.json +127 -0
- package/test-data/decision-tables/hit-policy-collect/collect-no-match.json +95 -0
- package/test-data/decision-tables/hit-policy-first/first-match.json +103 -0
- package/test-data/decision-tables/hit-policy-first/no-match.json +95 -0
- package/test-data/decision-tables/hit-policy-output-order/output-order-respected.json +94 -0
- package/test-data/decision-tables/hit-policy-output-order/string-output-order.json +94 -0
- package/test-data/decision-tables/hit-policy-priority/priority-respected.json +86 -0
- package/test-data/decision-tables/hit-policy-rule-order/rule-order-respected.json +94 -0
- package/test-data/decision-tables/hit-policy-unique/all-match-error.json +89 -0
- package/test-data/decision-tables/hit-policy-unique/multiple-match-error.json +89 -0
- package/test-data/decision-tables/hit-policy-unique/no-match.json +88 -0
- package/test-data/decision-tables/hit-policy-unique/unique-match.json +99 -0
- package/test-data/expressions/arithmetic/error-cyclic.json +114 -0
- package/test-data/expressions/arithmetic/error-missing-input.json +54 -0
- package/test-data/expressions/arithmetic/error-missing-output.json +54 -0
- package/test-data/expressions/arithmetic/expression-default.json +93 -0
- package/test-data/expressions/arithmetic/expression-fields.json +94 -0
- package/test-data/expressions/arithmetic/expression-loop.json +94 -0
- package/test-data/expressions/arithmetic/expression-passthrough.json +108 -0
- package/test-data/expressions/arithmetic/expression.json +69 -0
- package/test-data/expressions/arithmetic/nested-request.json +125 -0
- package/test-data/expressions/arithmetic/number-function.json +58 -0
- package/test-data/expressions/arithmetic/test-number-functions.json +68 -0
- package/test-data/expressions/functions/all.json +149 -0
- package/test-data/expressions/functions/avg.json +89 -0
- package/test-data/expressions/functions/filter.json +109 -0
- package/test-data/expressions/functions/flat.json +167 -0
- package/test-data/expressions/functions/map-strings.json +65 -0
- package/test-data/expressions/functions/map.json +73 -0
- package/test-data/expressions/functions/reduce.json +49 -0
- package/test-data/expressions/functions/some.json +175 -0
- package/test-data/expressions/functions/sort-strings.json +97 -0
- package/test-data/expressions/functions/sort.json +97 -0
- package/test-data/expressions/logical/logical-and.json +116 -0
- package/test-data/expressions/logical/logical-complex.json +260 -0
- package/test-data/expressions/logical/logical-not.json +111 -0
- package/test-data/expressions/logical/logical-or.json +123 -0
- package/test-data/expressions/string/string-comparison.json +128 -0
- package/test-data/expressions/string/string-concat.json +106 -0
- package/test-data/expressions/string/string-contains.json +125 -0
- package/test-data/expressions/string/string-endsWith.json +113 -0
- package/test-data/expressions/string/string-indexOf.json +131 -0
- package/test-data/expressions/string/string-join.json +92 -0
- package/test-data/expressions/string/string-lower.json +94 -0
- package/test-data/expressions/string/string-replace.json +130 -0
- package/test-data/expressions/string/string-split.json +101 -0
- package/test-data/expressions/string/string-startsWith.json +113 -0
- package/test-data/expressions/string/string-substring.json +138 -0
- package/test-data/expressions/string/string-trim.json +100 -0
- package/test-data/expressions/string/string-upper.json +94 -0
- package/test-data/other/custom.json +51 -0
- package/test-data/other/customer-input-schema.json +34 -0
- package/test-data/other/customer-output-schema.json +34 -0
- package/test-data/other/passthrough.json +31 -0
- package/test-data/sub-decisions/basic/$nodes-child.json +31 -0
- package/test-data/sub-decisions/basic/$nodes-parent.json +49 -0
- package/test-data/sub-decisions/basic/recursive-table1.json +49 -0
- package/test-data/sub-decisions/basic/recursive-table2.json +49 -0
- package/test-data/sub-decisions/complex-multi/approval-decision.json +31 -0
- package/test-data/sub-decisions/complex-multi/complex-dag.json +175 -0
- package/test-data/sub-decisions/complex-multi/credit-check.json +31 -0
- package/test-data/sub-decisions/complex-multi/customer-segmentation.json +31 -0
- package/test-data/sub-decisions/complex-multi/discount-eligibility.json +31 -0
- package/test-data/sub-decisions/complex-multi/eligibility-check.json +31 -0
- package/test-data/sub-decisions/complex-multi/final-offer.json +31 -0
- package/test-data/sub-decisions/complex-multi/income-verification.json +31 -0
- package/test-data/sub-decisions/complex-multi/linear-chain.json +121 -0
- package/test-data/sub-decisions/complex-multi/pricing-calculation.json +31 -0
- package/test-data/sub-decisions/complex-multi/product-eligibility.json +31 -0
- package/test-data/sub-decisions/complex-multi/risk-assessment.json +31 -0
- package/test-data/sub-decisions/complex-multi/shared-validation.json +31 -0
- package/test-data/sub-decisions/complex-multi/validation.json +31 -0
- package/test-data/sub-decisions/diamond/decision-a.json +31 -0
- package/test-data/sub-decisions/diamond/decision-b.json +31 -0
- package/test-data/sub-decisions/diamond/decision-c.json +31 -0
- package/test-data/sub-decisions/diamond/decision-shared.json +31 -0
- package/test-data/sub-decisions/diamond/diamond-pattern.json +109 -0
- package/test-data/sub-decisions/error-propagation/parent-calls-error.json +44 -0
- package/test-data/sub-decisions/error-propagation/sub-decision-with-error.json +60 -0
- package/test-data/switch-nodes/basic/account-dormancy-management.json +245 -0
- package/test-data/switch-nodes/basic/application-risk-assessment.json +474 -0
- package/test-data/switch-nodes/basic/cellular-data-rollover-system.json +281 -0
- package/test-data/switch-nodes/basic/clinical-pathway-selection.json +454 -0
- package/test-data/switch-nodes/basic/insurance-prior-authorization.json +467 -0
- package/test-data/switch-nodes/basic/last-mile-delivery-assignment.json +373 -0
- package/test-data/switch-nodes/basic/loan-approval.json +469 -0
- package/test-data/switch-nodes/basic/multi-switch.json +498 -0
- package/test-data/switch-nodes/basic/online-checkin-eligibility.json +285 -0
- package/test-data/switch-nodes/basic/order-consolidation-system.json +493 -0
- package/test-data/switch-nodes/basic/seller-approval-workflow.json +383 -0
- package/test-data/switch-nodes/basic/set-fee.json +243 -0
- package/test-data/switch-nodes/basic/shipping-carrier-selector.json +379 -0
- package/test-data/switch-nodes/basic/switch-node.json +167 -0
- package/test-data/switch-nodes/basic/switch-performance-2.json +1307 -0
- package/test-data/switch-nodes/basic/switch-performance.json +691 -0
- package/test-data/switch-nodes/basic/tax-exemption.json +295 -0
- package/test-data/switch-nodes/basic/warehouse-cross-docking.json +313 -0
- package/test-data/switch-nodes/default-cases/switch-with-default.json +134 -0
- package/test-data/zen-reference/$nodes-child.json +69 -0
- package/test-data/zen-reference/$nodes-parent.json +34 -0
- package/test-data/zen-reference/8k.json +87992 -0
- package/test-data/zen-reference/credit-analysis.json +324 -0
- package/test-data/zen-reference/custom.json +51 -0
- package/test-data/zen-reference/customer-input-schema.json +34 -0
- package/test-data/zen-reference/customer-output-schema.json +34 -0
- package/test-data/zen-reference/error-cyclic.json +114 -0
- package/test-data/zen-reference/error-missing-input.json +54 -0
- package/test-data/zen-reference/error-missing-output.json +54 -0
- package/test-data/zen-reference/expression.json +69 -0
- package/test-data/zen-reference/function-v2.json +48 -0
- package/test-data/zen-reference/function.json +46 -0
- package/test-data/zen-reference/graphs/account-dormancy-management.json +245 -0
- package/test-data/zen-reference/graphs/affiliate-commission-calculator.json +228 -0
- package/test-data/zen-reference/graphs/airline-loyalty-points-calculations.json +285 -0
- package/test-data/zen-reference/graphs/airline-upgrade-eligibility.json +466 -0
- package/test-data/zen-reference/graphs/aml.json +537 -0
- package/test-data/zen-reference/graphs/application-risk-assessment.json +474 -0
- package/test-data/zen-reference/graphs/auto-insurance-premium-calculator.json +412 -0
- package/test-data/zen-reference/graphs/booking-personalization-system.json +553 -0
- package/test-data/zen-reference/graphs/care-team-assignment-system.json +585 -0
- package/test-data/zen-reference/graphs/cellular-data-rollover-system.json +281 -0
- package/test-data/zen-reference/graphs/claim-validation-system.json +307 -0
- package/test-data/zen-reference/graphs/clinical-lab-result-interpreter.json +433 -0
- package/test-data/zen-reference/graphs/clinical-pathway-selection.json +454 -0
- package/test-data/zen-reference/graphs/clinical-treatment-protocol.json +474 -0
- package/test-data/zen-reference/graphs/company-analysis.json +390 -0
- package/test-data/zen-reference/graphs/credit-limit-adjustment.json +479 -0
- package/test-data/zen-reference/graphs/customer-eligibility-engine.json +551 -0
- package/test-data/zen-reference/graphs/customer-lifetime-value.json +200 -0
- package/test-data/zen-reference/graphs/customer-onboarding-kyc-verification.json +611 -0
- package/test-data/zen-reference/graphs/customer-service-escalation.json +191 -0
- package/test-data/zen-reference/graphs/decision-table-discounts.json +168 -0
- package/test-data/zen-reference/graphs/decision-table-shipping.json +398 -0
- package/test-data/zen-reference/graphs/delivery-route-optimizer.json +271 -0
- package/test-data/zen-reference/graphs/device-compatibility-checker.json +303 -0
- package/test-data/zen-reference/graphs/disaster-relief-fund-allocation.json +296 -0
- package/test-data/zen-reference/graphs/dynamic-fx-rate-pricing-system.json +237 -0
- package/test-data/zen-reference/graphs/dynamic-marketplace-comission-calculator.json +242 -0
- package/test-data/zen-reference/graphs/dynamic-shipping-cost-calculator.json +378 -0
- package/test-data/zen-reference/graphs/dynamic-tarrif-engine.json +289 -0
- package/test-data/zen-reference/graphs/dynamic-ticket-pricing.json +325 -0
- package/test-data/zen-reference/graphs/empty-column-with-space.json +100 -0
- package/test-data/zen-reference/graphs/empty-column-without-space.json +100 -0
- package/test-data/zen-reference/graphs/environment-compliance-assessment.json +386 -0
- package/test-data/zen-reference/graphs/expression-default.json +93 -0
- package/test-data/zen-reference/graphs/expression-fields.json +94 -0
- package/test-data/zen-reference/graphs/expression-loop.json +94 -0
- package/test-data/zen-reference/graphs/expression-passthrough.json +108 -0
- package/test-data/zen-reference/graphs/expression-table-map.json +313 -0
- package/test-data/zen-reference/graphs/flash-sale-eligibility.json +366 -0
- package/test-data/zen-reference/graphs/flight-dispatch-decision-system.json +455 -0
- package/test-data/zen-reference/graphs/flight-rebooking-fee-calculator.json +406 -0
- package/test-data/zen-reference/graphs/government-assistance.json +299 -0
- package/test-data/zen-reference/graphs/grant-funding-distribution.json +307 -0
- package/test-data/zen-reference/graphs/hazardous-materials-management-system.json +414 -0
- package/test-data/zen-reference/graphs/immigration-eligibility-evaluator.json +765 -0
- package/test-data/zen-reference/graphs/import-duties-calculator.json +318 -0
- package/test-data/zen-reference/graphs/insurance-agent-commission.json +228 -0
- package/test-data/zen-reference/graphs/insurance-breakdown.json +421 -0
- package/test-data/zen-reference/graphs/insurance-coverage-calculator.json +362 -0
- package/test-data/zen-reference/graphs/insurance-prior-authorization.json +467 -0
- package/test-data/zen-reference/graphs/insurance-underwriting-risk.json +321 -0
- package/test-data/zen-reference/graphs/international-roaming-policy-manager.json +199 -0
- package/test-data/zen-reference/graphs/last-mile-delivery-assignment.json +373 -0
- package/test-data/zen-reference/graphs/legacy-plan-management.json +434 -0
- package/test-data/zen-reference/graphs/loan-approval.json +469 -0
- package/test-data/zen-reference/graphs/marketplace-listing-verification-system.json +334 -0
- package/test-data/zen-reference/graphs/medication-dosage-calculator.json +318 -0
- package/test-data/zen-reference/graphs/merch-bags.json +171 -0
- package/test-data/zen-reference/graphs/multi-switch.json +498 -0
- package/test-data/zen-reference/graphs/municipal-permit-evaluation-system.json +364 -0
- package/test-data/zen-reference/graphs/mvno-partner-enablement.json +313 -0
- package/test-data/zen-reference/graphs/nested-request.json +125 -0
- package/test-data/zen-reference/graphs/online-checkin-eligibility.json +285 -0
- package/test-data/zen-reference/graphs/order-consolidation-system.json +493 -0
- package/test-data/zen-reference/graphs/partner-revenue-sharing.json +244 -0
- package/test-data/zen-reference/graphs/payment-routing-and-fee-calculator.json +475 -0
- package/test-data/zen-reference/graphs/policy-discount-calculator.json +307 -0
- package/test-data/zen-reference/graphs/policy-eligibility-analyzer.json +299 -0
- package/test-data/zen-reference/graphs/product-listing-scoring.json +358 -0
- package/test-data/zen-reference/graphs/realtime-fraud-detection.json +235 -0
- package/test-data/zen-reference/graphs/regional-compliance-manager.json +278 -0
- package/test-data/zen-reference/graphs/returns-and-refund-policy.json +366 -0
- package/test-data/zen-reference/graphs/returns-processing-system.json +448 -0
- package/test-data/zen-reference/graphs/school-district-resource-allocation.json +282 -0
- package/test-data/zen-reference/graphs/seat-map-optimization.json +325 -0
- package/test-data/zen-reference/graphs/seller-approval-workflow.json +383 -0
- package/test-data/zen-reference/graphs/seller-fee-calculator.json +307 -0
- package/test-data/zen-reference/graphs/service-level-agreement-enforcement.json +575 -0
- package/test-data/zen-reference/graphs/set-fee.json +243 -0
- package/test-data/zen-reference/graphs/shipping-carrier-selector.json +379 -0
- package/test-data/zen-reference/graphs/smart-financial-product-matcher.json +249 -0
- package/test-data/zen-reference/graphs/supply-chain-risk.json +316 -0
- package/test-data/zen-reference/graphs/table-loop.json +93 -0
- package/test-data/zen-reference/graphs/tax-exemption.json +295 -0
- package/test-data/zen-reference/graphs/traffic-violation-penalty-calculator.json +436 -0
- package/test-data/zen-reference/graphs/transaction-compliance-classifier.json +525 -0
- package/test-data/zen-reference/graphs/vehicle-claims-resolution.json +310 -0
- package/test-data/zen-reference/graphs/warehouse-cross-docking.json +313 -0
- package/test-data/zen-reference/graphs/warehouse-storage-location.json +345 -0
- package/test-data/zen-reference/http-function.json +34 -0
- package/test-data/zen-reference/infinite-function.json +46 -0
- package/test-data/zen-reference/js/imports.js +25 -0
- package/test-data/zen-reference/passthrough.json +31 -0
- package/test-data/zen-reference/recursive-table1.json +49 -0
- package/test-data/zen-reference/recursive-table2.json +49 -0
- package/test-data/zen-reference/sleep-function.json +34 -0
- package/test-data/zen-reference/switch-node.json +167 -0
- package/test-data/zen-reference/switch-performance-2.json +1307 -0
- package/test-data/zen-reference/switch-performance.json +691 -0
- package/test-data/zen-reference/table.json +76 -0
- package/tests/helpers/index.ts +73 -0
- package/tests/helpers/mock-context.ts +231 -0
- package/tests/helpers/round-trip.ts +398 -0
- package/tests/helpers/test-harness-comparison.ts +325 -0
- package/tests/helpers/test-harness-wasm.ts +710 -0
- package/tests/helpers/test-harness.ts +28 -0
- package/tests/helpers/wasm-test.ts +659 -0
- package/tests/integration/compilation-errors.test.ts +864 -0
- package/tests/integration/decision-tables.test.ts +531 -0
- package/tests/integration/edge-cases.test.ts +787 -0
- package/tests/integration/expressions.test.ts +513 -0
- package/tests/integration/function-node-integration.test.ts +182 -0
- package/tests/integration/sub-decisions.test.ts +108 -0
- package/tests/integration/switch-nodes.test.ts +399 -0
- package/tests/integration/unary-or-matching.test.ts +53 -0
- package/tests/integration/wasm-data-types.test.ts +398 -0
- package/tests/integration/wasm-errors.test.ts +199 -0
- package/tests/integration/wasm-execution.test.ts +348 -0
- package/tests/integration/wasm-memory.test.ts +228 -0
- package/tests/scripts/analyze-coverage.ts +166 -0
- package/tests/scripts/categorize-tests.ts +396 -0
- package/tests/scripts/coverage-analysis.ts +836 -0
- package/tests/unit/compiler/cache.test.ts +238 -0
- package/tests/unit/compiler/errors.test.ts +316 -0
- package/tests/unit/compiler/graph-scalability.test.ts +510 -0
- package/tests/unit/compiler/graph.test.ts +878 -0
- package/tests/unit/compiler/input-validation.test.ts +447 -0
- package/tests/unit/compiler/logical-and-parser.test.ts +143 -0
- package/tests/unit/compiler/logical-not-parser.test.ts +107 -0
- package/tests/unit/compiler/logical-or-parser.test.ts +236 -0
- package/tests/unit/compiler/marshal-gen/marshal-gen.test.ts +97 -0
- package/tests/unit/compiler/nodes/decision-table.test.ts +103 -0
- package/tests/unit/compiler/nodes/decision.test.ts +182 -0
- package/tests/unit/compiler/nodes/function-compile.test.ts +204 -0
- package/tests/unit/compiler/nodes/function.test.ts +176 -0
- package/tests/unit/compiler/nodes/input.test.ts +30 -0
- package/tests/unit/compiler/nodes/switch.test.ts +127 -0
- package/tests/unit/compiler/optimizer-cache.test.ts +327 -0
- package/tests/unit/compiler/optimizer-implementation.test.ts +625 -0
- package/tests/unit/compiler/parser.test.ts +508 -0
- package/tests/unit/compiler/runtime-error-cleanup.test.ts +426 -0
- package/tests/unit/compiler/runtime-validation.test.ts +303 -0
- package/tests/unit/compiler/runtime.test.ts +221 -0
- package/tests/unit/compiler/schema/schema.test.ts +248 -0
- package/tests/unit/compiler/unary-ast-transforms.test.ts +245 -0
- package/tsconfig.json +27 -0
- package/tsup.config.ts +11 -0
- package/vitest.config.ts +12 -0
package/README.md
ADDED
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
# jdm-asm
|
|
2
|
+
|
|
3
|
+
A high-performance JDM (JSON Decision Model) compiler that transforms decision models into WebAssembly modules using AssemblyScript. Compatible with [GoRules zen-engine](https://github.com/gorules/zen) JDM format with **sub-10 microsecond** decision latency and **5-9x faster** execution than zen-engine.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Installation](#installation)
|
|
9
|
+
- [Quick Start](#quick-start)
|
|
10
|
+
- [Usage Guide](#usage-guide)
|
|
11
|
+
- [Defining Schemas](#defining-schemas)
|
|
12
|
+
- [Creating JDM Files](#creating-jdm-files)
|
|
13
|
+
- [Compiling Decisions](#compiling-decisions)
|
|
14
|
+
- [Executing Decisions](#executing-decisions)
|
|
15
|
+
- [Configuration Options](#configuration-options)
|
|
16
|
+
- [JDM Format Reference](#jdm-format-reference)
|
|
17
|
+
- [Node Types](#node-types)
|
|
18
|
+
- [Expression Language](#expression-language)
|
|
19
|
+
- [Built-in Functions](#built-in-functions)
|
|
20
|
+
- [Decision Table Hit Policies](#decision-table-hit-policies)
|
|
21
|
+
- [Architecture](#architecture)
|
|
22
|
+
- [Compile Phase vs Execution Phase](#compile-phase-vs-execution-phase)
|
|
23
|
+
- [Component Overview](#component-overview)
|
|
24
|
+
- [Data Marshaling](#data-marshaling)
|
|
25
|
+
- [Performance](#performance)
|
|
26
|
+
- [Benchmark Results](#benchmark-results)
|
|
27
|
+
- [When to Use jdm-asm](#when-to-use-jdm-asm)
|
|
28
|
+
- [Compatibility with zen-engine](#compatibility-with-zen-engine)
|
|
29
|
+
- [Development](#development)
|
|
30
|
+
- [License](#license)
|
|
31
|
+
|
|
32
|
+
## Overview
|
|
33
|
+
|
|
34
|
+
jdm-asm compiles JSON Decision Models (JDM) into optimized WebAssembly modules. This approach moves expensive parsing and interpretation to compile-time, resulting in significantly faster runtime execution compared to traditional JavaScript rule engines.
|
|
35
|
+
|
|
36
|
+
**Key Features:**
|
|
37
|
+
|
|
38
|
+
- **Ultra-Low Latency**: 3.5-7 microsecond execution for typical decisions in single-threaded mode
|
|
39
|
+
- **High Performance**: 5-9x faster than zen-engine across all scenarios
|
|
40
|
+
- **Type Safety**: TypeBox schemas for input/output validation
|
|
41
|
+
- **zen-engine Compatible**: Drop-in replacement for most JDM files
|
|
42
|
+
- **Extended Hit Policies**: Supports DMN-standard policies (unique, ruleOrder, outputOrder, priority)
|
|
43
|
+
- **Multi-threaded**: Worker pool support for high-throughput batch processing
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install jdm-asm
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Requirements:**
|
|
52
|
+
- Node.js 18+
|
|
53
|
+
- TypeScript 5.0+ (for TypeBox schemas)
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { Type } from '@sinclair/typebox';
|
|
59
|
+
import { compile, createCompiledDecision } from 'jdm-asm';
|
|
60
|
+
|
|
61
|
+
// 1. Define input/output schemas
|
|
62
|
+
const InputSchema = Type.Object({
|
|
63
|
+
customerType: Type.String(),
|
|
64
|
+
orderAmount: Type.Number(),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const OutputSchema = Type.Object({
|
|
68
|
+
discount: Type.Number(),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// 2. Define your decision model (or load from file)
|
|
72
|
+
const jdm = {
|
|
73
|
+
nodes: [
|
|
74
|
+
{ id: 'input', type: 'inputNode', name: 'Request', position: { x: 0, y: 0 } },
|
|
75
|
+
{
|
|
76
|
+
id: 'table',
|
|
77
|
+
type: 'decisionTableNode',
|
|
78
|
+
name: 'Discount Rules',
|
|
79
|
+
position: { x: 200, y: 0 },
|
|
80
|
+
content: {
|
|
81
|
+
hitPolicy: 'first',
|
|
82
|
+
inputs: [
|
|
83
|
+
{ id: 'in1', name: 'Customer', field: 'customerType' },
|
|
84
|
+
{ id: 'in2', name: 'Amount', field: 'orderAmount' },
|
|
85
|
+
],
|
|
86
|
+
outputs: [{ id: 'out1', name: 'Discount', field: 'discount' }],
|
|
87
|
+
rules: [
|
|
88
|
+
{ _id: 'r1', in1: '"premium"', in2: '> 100', out1: '0.15' },
|
|
89
|
+
{ _id: 'r2', in1: '"standard"', in2: '> 100', out1: '0.10' },
|
|
90
|
+
{ _id: 'r3', in1: '', in2: '', out1: '0' },
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
{ id: 'output', type: 'outputNode', name: 'Response', position: { x: 400, y: 0 } },
|
|
95
|
+
],
|
|
96
|
+
edges: [
|
|
97
|
+
{ id: 'e1', sourceId: 'input', targetId: 'table', type: 'edge' },
|
|
98
|
+
{ id: 'e2', sourceId: 'table', targetId: 'output', type: 'edge' },
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// 3. Compile to WebAssembly
|
|
103
|
+
const compiled = await compile({
|
|
104
|
+
jdm,
|
|
105
|
+
inputSchema: InputSchema,
|
|
106
|
+
outputSchema: OutputSchema,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// 4. Create decision instance
|
|
110
|
+
const decision = await createCompiledDecision(compiled);
|
|
111
|
+
|
|
112
|
+
// 5. Evaluate
|
|
113
|
+
const result = await decision.evaluate({
|
|
114
|
+
customerType: 'premium',
|
|
115
|
+
orderAmount: 150,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
console.log(result); // { discount: 0.15 }
|
|
119
|
+
|
|
120
|
+
// 6. Clean up when done
|
|
121
|
+
decision.dispose();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Usage Guide
|
|
125
|
+
|
|
126
|
+
### Defining Schemas
|
|
127
|
+
|
|
128
|
+
jdm-asm uses [TypeBox](https://github.com/sinclairzx81/typebox) schemas to define the structure of input and output data. Schemas enable:
|
|
129
|
+
|
|
130
|
+
- Compile-time type checking
|
|
131
|
+
- Runtime input validation
|
|
132
|
+
- Optimized memory layout for WASM
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { Type } from '@sinclair/typebox';
|
|
136
|
+
|
|
137
|
+
// Simple flat schema
|
|
138
|
+
const InputSchema = Type.Object({
|
|
139
|
+
age: Type.Number(),
|
|
140
|
+
name: Type.String(),
|
|
141
|
+
active: Type.Boolean(),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Nested schema
|
|
145
|
+
const InputSchema = Type.Object({
|
|
146
|
+
customer: Type.Object({
|
|
147
|
+
id: Type.String(),
|
|
148
|
+
tier: Type.String(),
|
|
149
|
+
loyaltyYears: Type.Number(),
|
|
150
|
+
}),
|
|
151
|
+
order: Type.Object({
|
|
152
|
+
total: Type.Number(),
|
|
153
|
+
items: Type.Array(Type.Object({
|
|
154
|
+
sku: Type.String(),
|
|
155
|
+
price: Type.Number(),
|
|
156
|
+
quantity: Type.Number(),
|
|
157
|
+
})),
|
|
158
|
+
}),
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Output schema
|
|
162
|
+
const OutputSchema = Type.Object({
|
|
163
|
+
eligible: Type.Boolean(),
|
|
164
|
+
discount: Type.Number(),
|
|
165
|
+
message: Type.String(),
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Creating JDM Files
|
|
170
|
+
|
|
171
|
+
JDM files are JSON documents containing nodes and edges that form a decision graph:
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"nodes": [
|
|
176
|
+
{
|
|
177
|
+
"id": "unique-node-id",
|
|
178
|
+
"type": "inputNode|outputNode|expressionNode|decisionTableNode|switchNode|decisionNode",
|
|
179
|
+
"name": "Human-readable name",
|
|
180
|
+
"position": { "x": 0, "y": 0 },
|
|
181
|
+
"content": { }
|
|
182
|
+
}
|
|
183
|
+
],
|
|
184
|
+
"edges": [
|
|
185
|
+
{
|
|
186
|
+
"id": "unique-edge-id",
|
|
187
|
+
"type": "edge",
|
|
188
|
+
"sourceId": "source-node-id",
|
|
189
|
+
"targetId": "target-node-id"
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Every JDM must have:
|
|
196
|
+
- Exactly one `inputNode` (entry point)
|
|
197
|
+
- At least one `outputNode` (exit point)
|
|
198
|
+
- A connected path from input to output
|
|
199
|
+
|
|
200
|
+
### Compiling Decisions
|
|
201
|
+
|
|
202
|
+
The `compile()` function transforms JDM into WebAssembly:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { compile } from 'jdm-asm';
|
|
206
|
+
|
|
207
|
+
const result = await compile({
|
|
208
|
+
// Required
|
|
209
|
+
jdm: jdmObject, // JDM as object or JSON string
|
|
210
|
+
inputSchema: InputSchema, // TypeBox schema
|
|
211
|
+
outputSchema: OutputSchema,
|
|
212
|
+
|
|
213
|
+
// Optional
|
|
214
|
+
optimize: true, // Enable WASM optimizations (default: true)
|
|
215
|
+
debug: false, // Include AS source and WAT in output
|
|
216
|
+
noMatchBehavior: { // When no rules match:
|
|
217
|
+
type: 'returnNull' // 'returnNull' | 'throwError' | 'returnDefault'
|
|
218
|
+
},
|
|
219
|
+
loadDecision: (key) => {}, // Custom loader for sub-decisions
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Compilation Result:**
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
interface CompilationResult {
|
|
227
|
+
wasm: Uint8Array; // Compiled WASM binary
|
|
228
|
+
schemaHash: bigint; // Hash for runtime validation
|
|
229
|
+
marshalCode: string; // JS code for data marshaling
|
|
230
|
+
validationCode: string; // JS code for input validation
|
|
231
|
+
wat?: string; // WAT text format (debug mode)
|
|
232
|
+
assemblyScript?: string; // Generated AS source (debug mode)
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Executing Decisions
|
|
237
|
+
|
|
238
|
+
Use `createCompiledDecision()` to instantiate and execute compiled decisions:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { createCompiledDecision } from 'jdm-asm';
|
|
242
|
+
|
|
243
|
+
// Create instance (instantiates WASM module)
|
|
244
|
+
const decision = await createCompiledDecision(compiledResult);
|
|
245
|
+
|
|
246
|
+
// Evaluate with input data
|
|
247
|
+
const output = await decision.evaluate({
|
|
248
|
+
customerType: 'premium',
|
|
249
|
+
orderAmount: 150,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Reuse for multiple evaluations (recommended for performance)
|
|
253
|
+
for (const input of inputs) {
|
|
254
|
+
const result = await decision.evaluate(input);
|
|
255
|
+
processResult(result);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Release resources when done
|
|
259
|
+
decision.dispose();
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**CompiledDecision API:**
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
class CompiledDecision {
|
|
266
|
+
// Evaluate with input
|
|
267
|
+
evaluate<I, O>(input: I): Promise<O>;
|
|
268
|
+
|
|
269
|
+
// Get WASM memory (debugging)
|
|
270
|
+
getMemory(): WebAssembly.Memory | null;
|
|
271
|
+
|
|
272
|
+
// Memory statistics
|
|
273
|
+
getMemoryStats(): { current: number; maximum: number; used: number };
|
|
274
|
+
|
|
275
|
+
// Release resources
|
|
276
|
+
dispose(): void;
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Configuration Options
|
|
281
|
+
|
|
282
|
+
#### No-Match Behavior
|
|
283
|
+
|
|
284
|
+
Configure what happens when no rules match:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// Return null (default)
|
|
288
|
+
{ type: 'returnNull' }
|
|
289
|
+
|
|
290
|
+
// Set error code and return null
|
|
291
|
+
{ type: 'throwError' }
|
|
292
|
+
|
|
293
|
+
// Return a specific default value
|
|
294
|
+
{ type: 'returnDefault', value: { discount: 0, message: 'No match' } }
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Can be set globally via compile options or per-node in the JDM content.
|
|
298
|
+
|
|
299
|
+
#### Compilation Cache
|
|
300
|
+
|
|
301
|
+
jdm-asm caches WASM compilations to disk for faster subsequent builds:
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
import {
|
|
305
|
+
clearCache,
|
|
306
|
+
getCacheStats,
|
|
307
|
+
pruneCache,
|
|
308
|
+
getCacheDirectory,
|
|
309
|
+
} from 'jdm-asm';
|
|
310
|
+
|
|
311
|
+
// Clear all cached compilations
|
|
312
|
+
clearCache();
|
|
313
|
+
|
|
314
|
+
// Get cache statistics
|
|
315
|
+
const stats = getCacheStats();
|
|
316
|
+
|
|
317
|
+
// Remove old entries
|
|
318
|
+
pruneCache();
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## JDM Format Reference
|
|
322
|
+
|
|
323
|
+
### Node Types
|
|
324
|
+
|
|
325
|
+
#### Input Node
|
|
326
|
+
|
|
327
|
+
Entry point for the decision graph.
|
|
328
|
+
|
|
329
|
+
```json
|
|
330
|
+
{
|
|
331
|
+
"id": "input-1",
|
|
332
|
+
"type": "inputNode",
|
|
333
|
+
"name": "Request"
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
#### Output Node
|
|
338
|
+
|
|
339
|
+
Exit point that returns accumulated context data.
|
|
340
|
+
|
|
341
|
+
```json
|
|
342
|
+
{
|
|
343
|
+
"id": "output-1",
|
|
344
|
+
"type": "outputNode",
|
|
345
|
+
"name": "Response"
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
#### Expression Node
|
|
350
|
+
|
|
351
|
+
Evaluates expressions and sets output fields.
|
|
352
|
+
|
|
353
|
+
```json
|
|
354
|
+
{
|
|
355
|
+
"id": "expr-1",
|
|
356
|
+
"type": "expressionNode",
|
|
357
|
+
"name": "Calculate",
|
|
358
|
+
"content": {
|
|
359
|
+
"expressions": [
|
|
360
|
+
{ "id": "e1", "key": "total", "value": "price * quantity" },
|
|
361
|
+
{ "id": "e2", "key": "tax", "value": "total * 0.08" },
|
|
362
|
+
{ "id": "e3", "key": "grandTotal", "value": "$.total + $.tax" }
|
|
363
|
+
],
|
|
364
|
+
"passThrough": true, // Include input fields in output
|
|
365
|
+
"inputField": "order", // Scope to nested field
|
|
366
|
+
"outputPath": "result" // Store results under this key
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
**Self-Referential Expressions:** Use `$` to reference earlier computed values within the same expression node (e.g., `$.total` refers to the `total` computed above).
|
|
372
|
+
|
|
373
|
+
#### Decision Table Node
|
|
374
|
+
|
|
375
|
+
Rule-based decision table with conditions and outputs.
|
|
376
|
+
|
|
377
|
+
```json
|
|
378
|
+
{
|
|
379
|
+
"id": "table-1",
|
|
380
|
+
"type": "decisionTableNode",
|
|
381
|
+
"name": "Discount Rules",
|
|
382
|
+
"content": {
|
|
383
|
+
"hitPolicy": "first",
|
|
384
|
+
"inputs": [
|
|
385
|
+
{ "id": "in1", "name": "Customer Type", "field": "customerType" },
|
|
386
|
+
{ "id": "in2", "name": "Order Amount", "field": "orderAmount" }
|
|
387
|
+
],
|
|
388
|
+
"outputs": [
|
|
389
|
+
{ "id": "out1", "name": "Discount", "field": "discount" }
|
|
390
|
+
],
|
|
391
|
+
"rules": [
|
|
392
|
+
{ "_id": "r1", "in1": "\"premium\"", "in2": "> 100", "out1": "0.15" },
|
|
393
|
+
{ "_id": "r2", "in1": "\"standard\"", "in2": "> 100", "out1": "0.10" },
|
|
394
|
+
{ "_id": "r3", "in1": "", "in2": "", "out1": "0" }
|
|
395
|
+
],
|
|
396
|
+
"passThrough": false
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**Unary Mode:** In decision table cells, expressions are implicitly compared against the column's input value (`$`):
|
|
402
|
+
|
|
403
|
+
| Cell Value | Interpreted As |
|
|
404
|
+
|------------|----------------|
|
|
405
|
+
| `"admin"` | `$ == "admin"` |
|
|
406
|
+
| `> 100` | `$ > 100` |
|
|
407
|
+
| `>= 18, < 65` | `$ >= 18 and $ < 65` |
|
|
408
|
+
| `[1..10]` | `$ >= 1 and $ <= 10` |
|
|
409
|
+
| `"a", "b", "c"` | `$ == "a" or $ == "b" or $ == "c"` |
|
|
410
|
+
| `["gold", "platinum"]` | `$ in ["gold", "platinum"]` |
|
|
411
|
+
| `` (empty) or `-` | `true` (always matches) |
|
|
412
|
+
|
|
413
|
+
#### Switch Node
|
|
414
|
+
|
|
415
|
+
Conditional branching based on expressions.
|
|
416
|
+
|
|
417
|
+
```json
|
|
418
|
+
{
|
|
419
|
+
"id": "switch-1",
|
|
420
|
+
"type": "switchNode",
|
|
421
|
+
"name": "Route",
|
|
422
|
+
"content": {
|
|
423
|
+
"statements": [
|
|
424
|
+
{ "id": "s1", "condition": "status == 'active'" },
|
|
425
|
+
{ "id": "s2", "condition": "status == 'pending'" },
|
|
426
|
+
{ "id": "s3", "condition": "" }
|
|
427
|
+
],
|
|
428
|
+
"hitPolicy": "first"
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Edges from switch nodes use `sourceHandle` to specify which branch they belong to (matching the statement `id`).
|
|
434
|
+
|
|
435
|
+
#### Decision Node
|
|
436
|
+
|
|
437
|
+
References external sub-decisions for modular composition.
|
|
438
|
+
|
|
439
|
+
```json
|
|
440
|
+
{
|
|
441
|
+
"id": "sub-1",
|
|
442
|
+
"type": "decisionNode",
|
|
443
|
+
"name": "Calculate Shipping",
|
|
444
|
+
"content": {
|
|
445
|
+
"key": "shipping-rules.json"
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
The `loadDecision` option in compile controls how sub-decisions are loaded.
|
|
451
|
+
|
|
452
|
+
### Expression Language
|
|
453
|
+
|
|
454
|
+
jdm-asm supports the ZEN expression language:
|
|
455
|
+
|
|
456
|
+
#### Literals
|
|
457
|
+
|
|
458
|
+
```
|
|
459
|
+
42, 3.14, -5 // Numbers
|
|
460
|
+
"hello", 'world' // Strings
|
|
461
|
+
true, false // Booleans
|
|
462
|
+
null // Null
|
|
463
|
+
[1, 2, 3] // Arrays
|
|
464
|
+
{key: value} // Objects
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
#### Operators
|
|
468
|
+
|
|
469
|
+
| Category | Operators |
|
|
470
|
+
|----------|-----------|
|
|
471
|
+
| Arithmetic | `+`, `-`, `*`, `/`, `%`, `^` (power) |
|
|
472
|
+
| Comparison | `==`, `!=`, `<`, `>`, `<=`, `>=` |
|
|
473
|
+
| Logical | `and`, `or`, `not` |
|
|
474
|
+
| Null coalescing | `??` |
|
|
475
|
+
| Membership | `in`, `not in` |
|
|
476
|
+
| Ternary | `condition ? then : else` |
|
|
477
|
+
|
|
478
|
+
#### Access Patterns
|
|
479
|
+
|
|
480
|
+
```
|
|
481
|
+
object.property // Member access
|
|
482
|
+
object["key"] // Bracket access
|
|
483
|
+
array[0] // Index access
|
|
484
|
+
nested.deeply.nested.value // Chained access
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
#### Template Literals
|
|
488
|
+
|
|
489
|
+
```
|
|
490
|
+
`Hello, ${name}!`
|
|
491
|
+
`Order ${id}: ${items.length} items totaling ${total}`
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
#### Interval Notation (DMN-style)
|
|
495
|
+
|
|
496
|
+
```
|
|
497
|
+
[1..10] // 1 <= x <= 10 (inclusive)
|
|
498
|
+
(1..10) // 1 < x < 10 (exclusive)
|
|
499
|
+
[1..10) // 1 <= x < 10
|
|
500
|
+
(1..10] // 1 < x <= 10
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Built-in Functions
|
|
504
|
+
|
|
505
|
+
#### Array Functions
|
|
506
|
+
|
|
507
|
+
| Function | Description | Example |
|
|
508
|
+
|----------|-------------|---------|
|
|
509
|
+
| `sum(array)` | Sum numeric values | `sum([1, 2, 3])` → `6` |
|
|
510
|
+
| `avg(array)` | Average of values | `avg([1, 2, 3])` → `2` |
|
|
511
|
+
| `min(array)` | Minimum value | `min([3, 1, 2])` → `1` |
|
|
512
|
+
| `max(array)` | Maximum value | `max([3, 1, 2])` → `3` |
|
|
513
|
+
| `count(array)` | Array length | `count([1, 2, 3])` → `3` |
|
|
514
|
+
| `sort(array)` | Sort ascending | `sort([3, 1, 2])` → `[1, 2, 3]` |
|
|
515
|
+
| `flat(array, depth?)` | Flatten nested arrays | `flat([[1], [2, 3]])` → `[1, 2, 3]` |
|
|
516
|
+
| `contains(array, val)` | Check membership | `contains([1, 2], 2)` → `true` |
|
|
517
|
+
|
|
518
|
+
#### Higher-Order Functions
|
|
519
|
+
|
|
520
|
+
| Function | Description | Example |
|
|
521
|
+
|----------|-------------|---------|
|
|
522
|
+
| `filter(array, predicate)` | Filter elements | `filter(items, # > 5)` |
|
|
523
|
+
| `map(array, transform)` | Transform elements | `map(items, # * 2)` |
|
|
524
|
+
| `reduce(array, expr, init)` | Reduce to value | `reduce(nums, acc + #, 0)` |
|
|
525
|
+
| `all(array, predicate)` | All match | `all(scores, # >= 60)` |
|
|
526
|
+
| `some(array, predicate)` | Any match | `some(items, # == "gold")` |
|
|
527
|
+
|
|
528
|
+
**Note:** Use `#` to reference the current array element in predicates.
|
|
529
|
+
|
|
530
|
+
#### String Functions
|
|
531
|
+
|
|
532
|
+
| Function | Description | Example |
|
|
533
|
+
|----------|-------------|---------|
|
|
534
|
+
| `upper(str)` | Uppercase | `upper("hello")` → `"HELLO"` |
|
|
535
|
+
| `lower(str)` | Lowercase | `lower("HELLO")` → `"hello"` |
|
|
536
|
+
| `trim(str)` | Remove whitespace | `trim(" hi ")` → `"hi"` |
|
|
537
|
+
| `substring(str, start, end?)` | Extract substring | `substring("hello", 0, 2)` → `"he"` |
|
|
538
|
+
| `indexOf(str, search)` | Find index | `indexOf("hello", "l")` → `2` |
|
|
539
|
+
| `startsWith(str, prefix)` | Check prefix | `startsWith("hello", "he")` → `true` |
|
|
540
|
+
| `endsWith(str, suffix)` | Check suffix | `endsWith("hello", "lo")` → `true` |
|
|
541
|
+
| `split(str, delim)` | Split to array | `split("a,b,c", ",")` → `["a", "b", "c"]` |
|
|
542
|
+
| `join(array, delim)` | Join to string | `join(["a", "b"], "-")` → `"a-b"` |
|
|
543
|
+
| `replace(str, find, repl)` | Replace first | `replace("hello", "l", "L")` → `"heLlo"` |
|
|
544
|
+
| `replaceAll(str, find, repl)` | Replace all | `replaceAll("hello", "l", "L")` → `"heLLo"` |
|
|
545
|
+
| `contains(str, substr)` | Check substring | `contains("hello", "ell")` → `true` |
|
|
546
|
+
|
|
547
|
+
#### Math Functions
|
|
548
|
+
|
|
549
|
+
| Function | Description | Example |
|
|
550
|
+
|----------|-------------|---------|
|
|
551
|
+
| `abs(num)` | Absolute value | `abs(-5)` → `5` |
|
|
552
|
+
| `floor(num)` | Round down | `floor(3.7)` → `3` |
|
|
553
|
+
| `ceil(num)` | Round up | `ceil(3.2)` → `4` |
|
|
554
|
+
| `round(num)` | Round nearest | `round(3.5)` → `4` |
|
|
555
|
+
|
|
556
|
+
#### Date/Time Functions
|
|
557
|
+
|
|
558
|
+
| Function | Description | Example |
|
|
559
|
+
|----------|-------------|---------|
|
|
560
|
+
| `date(str)` | Parse ISO date to timestamp (seconds) | `date("2025-03-20T10:30:00Z")` |
|
|
561
|
+
| `date("now")` | Current timestamp | `date("now")` |
|
|
562
|
+
| `time(str)` | Parse time to seconds since midnight | `time("10:30:00")` → `37800` |
|
|
563
|
+
| `duration(str)` | Parse duration string | `duration("24h")` → `86400` |
|
|
564
|
+
|
|
565
|
+
#### Type Functions
|
|
566
|
+
|
|
567
|
+
| Function | Description | Example |
|
|
568
|
+
|----------|-------------|---------|
|
|
569
|
+
| `number(str)` | Parse to number | `number("42")` → `42` |
|
|
570
|
+
| `string(val)` | Convert to string | `string(42)` → `"42"` |
|
|
571
|
+
| `keys(obj)` | Get object keys | `keys({a: 1})` → `["a"]` |
|
|
572
|
+
| `values(obj)` | Get object values | `values({a: 1})` → `[1]` |
|
|
573
|
+
|
|
574
|
+
### Decision Table Hit Policies
|
|
575
|
+
|
|
576
|
+
| Policy | Behavior | zen-engine |
|
|
577
|
+
|--------|----------|------------|
|
|
578
|
+
| `first` | Return first matching rule | Yes |
|
|
579
|
+
| `collect` | Return all matches as array | Yes |
|
|
580
|
+
| `unique` | Return single match (error if multiple) | **No** |
|
|
581
|
+
| `ruleOrder` | Return all matches in rule definition order | **No** |
|
|
582
|
+
| `outputOrder` | Return all matches sorted by output value | **No** |
|
|
583
|
+
| `priority` | Return highest priority match | **No** |
|
|
584
|
+
|
|
585
|
+
## Architecture
|
|
586
|
+
|
|
587
|
+
### Compile Phase vs Execution Phase
|
|
588
|
+
|
|
589
|
+
jdm-asm operates in two distinct phases:
|
|
590
|
+
|
|
591
|
+
**Compile Phase (~250ms-10s depending on complexity/hardware):**
|
|
592
|
+
1. Parse JDM JSON and validate structure
|
|
593
|
+
2. Build dependency graph from nodes/edges
|
|
594
|
+
3. Topologically sort for evaluation order
|
|
595
|
+
4. Generate AssemblyScript code for each node
|
|
596
|
+
5. Compile AssemblyScript to WebAssembly
|
|
597
|
+
6. Generate JavaScript marshaling code
|
|
598
|
+
|
|
599
|
+
**Execution Phase (~3-1000 microseconds):**
|
|
600
|
+
1. Validate input against schema
|
|
601
|
+
2. Marshal JavaScript object to WASM linear memory
|
|
602
|
+
3. Call WASM `evaluate()` function
|
|
603
|
+
4. Unmarshal result from WASM memory to JavaScript
|
|
604
|
+
|
|
605
|
+
This separation enables high performance: expensive compilation happens once, while fast execution occurs many times.
|
|
606
|
+
|
|
607
|
+
### Data Marshaling
|
|
608
|
+
|
|
609
|
+
Data flows between JavaScript and WASM via typed memory:
|
|
610
|
+
|
|
611
|
+
```
|
|
612
|
+
JavaScript WASM Linear Memory
|
|
613
|
+
────────── ──────────────────
|
|
614
|
+
{ name: "John", ──► [ValueMap]
|
|
615
|
+
age: 30, ├─ length: 2
|
|
616
|
+
tags: ["a","b"] } ├─ key0 → "name" (UTF-16)
|
|
617
|
+
├─ val0 → [STRING, ptr → "John"]
|
|
618
|
+
├─ key1 → "age"
|
|
619
|
+
├─ val1 → [FLOAT, 30.0]
|
|
620
|
+
├─ key2 → "tags"
|
|
621
|
+
└─ val2 → [ARRAY, ptr → [...]]
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
**Value Types (tagged union):**
|
|
625
|
+
|
|
626
|
+
| Tag | Type | Storage |
|
|
627
|
+
|-----|------|---------|
|
|
628
|
+
| 0 | Null | Tag only |
|
|
629
|
+
| 1 | Boolean | Tag + u8 |
|
|
630
|
+
| 2 | Integer | Tag + i64 |
|
|
631
|
+
| 3 | Float | Tag + f64 |
|
|
632
|
+
| 4 | String | Tag + pointer to UTF-16 data |
|
|
633
|
+
| 5 | Array | Tag + pointer to element array |
|
|
634
|
+
| 6 | Object | Tag + pointer to ValueMap |
|
|
635
|
+
|
|
636
|
+
## Performance
|
|
637
|
+
|
|
638
|
+
### Benchmark Results
|
|
639
|
+
|
|
640
|
+
Benchmarks run on Apple M3 Max, Node.js 24.7, comparing jdm-asm to @gorules/zen-engine:
|
|
641
|
+
|
|
642
|
+
#### Single-Threaded Latency (Best-Case)
|
|
643
|
+
|
|
644
|
+
In single-threaded mode, jdm-asm achieves **microsecond-level** latency:
|
|
645
|
+
|
|
646
|
+
| Scenario | jdm-asm | zen-engine | Speedup |
|
|
647
|
+
|----------|---------|------------|---------|
|
|
648
|
+
| Simple (5 rules) | **3.5 µs** | 32.4 µs | **9.2x faster** |
|
|
649
|
+
| Moderate (12 rules) | **7.2 µs** | 36.2 µs | **5.0x faster** |
|
|
650
|
+
| Complex (~8000 rules) | **492 µs** | 1.47 ms | **3x faster** |
|
|
651
|
+
|
|
652
|
+
For typical decision tables (5-50 rules), expect **sub-10 microsecond** execution times.
|
|
653
|
+
|
|
654
|
+
#### Single-Threaded Latency Percentiles
|
|
655
|
+
|
|
656
|
+
| Scenario | p50 | p95 | p99 |
|
|
657
|
+
|----------|-----|-----|-----|
|
|
658
|
+
| Simple | 3.5 µs | 4.2 µs | 4.7 µs |
|
|
659
|
+
| Moderate | 7.2 µs | 8.0 µs | 9.7 µs |
|
|
660
|
+
| Complex | 492 µs | 1.1 ms | 1.6 ms |
|
|
661
|
+
|
|
662
|
+
#### Throughput (decisions/second)
|
|
663
|
+
|
|
664
|
+
| Mode | Scenario | zen-engine | jdm-asm |
|
|
665
|
+
|------|----------|------------|---------|
|
|
666
|
+
| Single-threaded | Simple | 34.6K | **277.6K** |
|
|
667
|
+
| Single-threaded | Moderate | 27.6K | **139.1K** |
|
|
668
|
+
| Single-threaded | Complex | 682 | **892** |
|
|
669
|
+
| Multi-thread (16) | Simple | 119K | **180K** |
|
|
670
|
+
| Multi-thread (16) | Moderate | 98K | **178K** |
|
|
671
|
+
| Multi-thread (16) | Complex | 5.6K | **20.6K** |
|
|
672
|
+
|
|
673
|
+
#### Multi-Threaded Batch Latency
|
|
674
|
+
|
|
675
|
+
When using worker threads (includes IPC overhead):
|
|
676
|
+
|
|
677
|
+
| Scenario | jdm-asm (16 threads) | zen-engine |
|
|
678
|
+
|----------|----------------------|------------|
|
|
679
|
+
| Simple | 2.7ms / 4.9ms / 6.7ms | 4.4ms / 6.3ms / 7.5ms |
|
|
680
|
+
| Moderate | 2.7ms / 5.0ms / 5.9ms | 5.3ms / 8.2ms / 8.9ms |
|
|
681
|
+
| Complex | 20.9ms / 38.1ms / 43.9ms | 121.9ms / 159.9ms / 173.8ms |
|
|
682
|
+
|
|
683
|
+
#### Compilation Time
|
|
684
|
+
|
|
685
|
+
| Scenario | WASM Size | Compile Time |
|
|
686
|
+
|----------|-----------|--------------|
|
|
687
|
+
| Simple | 34 KB | 1.4s |
|
|
688
|
+
| Moderate | 55 KB | 0.8s |
|
|
689
|
+
| Complex | 6.1 MB | 9.5s |
|
|
690
|
+
|
|
691
|
+
### When to Use jdm-asm
|
|
692
|
+
|
|
693
|
+
**Best for:**
|
|
694
|
+
- Ultra-low latency requirements (sub-10 µs for typical decisions)
|
|
695
|
+
- High-throughput APIs evaluating hundreds of thousands of decisions per second
|
|
696
|
+
- Latency-sensitive applications where p99 matters
|
|
697
|
+
- Simple to moderate complexity rules (5-50 rules)
|
|
698
|
+
- Cases where rules change infrequently (amortize compilation cost)
|
|
699
|
+
|
|
700
|
+
**Consider zen-engine when:**
|
|
701
|
+
- Rules change frequently (compilation cost is high)
|
|
702
|
+
- Using function nodes with complex JavaScript logic
|
|
703
|
+
- Quick prototyping without schema definitions
|
|
704
|
+
|
|
705
|
+
## Compatibility with zen-engine
|
|
706
|
+
|
|
707
|
+
jdm-asm is designed as a drop-in replacement for zen-engine with some differences:
|
|
708
|
+
|
|
709
|
+
**Fully Supported:**
|
|
710
|
+
- All node types (input, output, expression, decision table, switch, decision)
|
|
711
|
+
- Expression language (operators, functions, access patterns)
|
|
712
|
+
- Decision table hit policies: `first`, `collect`
|
|
713
|
+
- Sub-decision composition
|
|
714
|
+
|
|
715
|
+
**Extended Features (not in zen-engine):**
|
|
716
|
+
- DMN-standard hit policies: `unique`, `ruleOrder`, `outputOrder`, `priority`
|
|
717
|
+
- Compile-time optimization
|
|
718
|
+
- Multi-threaded execution
|
|
719
|
+
|
|
720
|
+
**Limitations:**
|
|
721
|
+
- Function nodes: Parsed but not executed (returns placeholder)
|
|
722
|
+
- Division by zero: Returns `null` instead of `Infinity`
|
|
723
|
+
- Full ISO 8601 durations: Simple formats (`24h`, `30d`) work; complex format (`P1Y2M3D`) not supported
|
|
724
|
+
|
|
725
|
+
## Development
|
|
726
|
+
|
|
727
|
+
```bash
|
|
728
|
+
# Install dependencies
|
|
729
|
+
npm install
|
|
730
|
+
|
|
731
|
+
# Build TypeScript
|
|
732
|
+
npm run build
|
|
733
|
+
|
|
734
|
+
# Build AssemblyScript runtime
|
|
735
|
+
npm run asbuild
|
|
736
|
+
|
|
737
|
+
# Run tests
|
|
738
|
+
npm run test
|
|
739
|
+
|
|
740
|
+
# Run benchmarks
|
|
741
|
+
npm run bench
|
|
742
|
+
|
|
743
|
+
# Lint code
|
|
744
|
+
npm run lint
|
|
745
|
+
|
|
746
|
+
# Type check
|
|
747
|
+
npx tsc --noEmit
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
### Project Structure
|
|
751
|
+
|
|
752
|
+
```
|
|
753
|
+
jdm-asm/
|
|
754
|
+
├── src/
|
|
755
|
+
│ ├── compiler/ # TypeScript compiler code
|
|
756
|
+
│ └── runtime/ # AssemblyScript runtime
|
|
757
|
+
├── tests/
|
|
758
|
+
│ ├── unit/ # Unit tests
|
|
759
|
+
│ ├── integration/ # Integration tests
|
|
760
|
+
│ └── helpers/ # Test utilities
|
|
761
|
+
├── test-data/ # Ported zen-engine test fixtures
|
|
762
|
+
├── benchmarks/ # Performance benchmarks
|
|
763
|
+
├── build/ # Compiled WASM output
|
|
764
|
+
└── dist/ # Built npm package
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
## License
|
|
768
|
+
|
|
769
|
+
MIT License - see [LICENSE](LICENSE) for details.
|