Skip to content

Bug: @gorules/zen-engine-wasm truncates all decimal results to integers #236

@lfevrier-eudonet

Description

@lfevrier-eudonet

Bug: @gorules/zen-engine-wasm truncates all decimal results to integers

Description

evaluateExpression in @gorules/zen-engine-wasm truncates all decimal (non-integer) results to integers. The same expressions evaluated via @gorules/zen-engine (Node.js native) return correct decimal values.

Minimal reproduction

<script type="module">
  import init, { evaluateExpression } from '@gorules/zen-engine-wasm';
  await init();

  // All decimals are truncated to integers
  console.log(evaluateExpression('3.7', {}));           // Returns 3, expected 3.7
  console.log(evaluateExpression('1.5 + 0.1', {}));     // Returns 1, expected 1.6
  console.log(evaluateExpression('2 * 0.3', {}));       // Returns 0, expected 0.6
  console.log(evaluateExpression('10 / 3', {}));        // Returns 3, expected 3.333...

  // Integer results work correctly
  console.log(evaluateExpression('1 + 2', {}));         // Returns 3, correct
  console.log(evaluateExpression('4 * 5', {}));         // Returns 20, correct
</script>

Expected behavior

evaluateExpression('3.7', {}) should return 3.7.

Actual behavior

evaluateExpression('3.7', {}) returns 3.

Every expression that produces a non-integer Decimal value has its fractional part silently dropped.

Environment

  • @gorules/zen-engine-wasm: 0.23.1
  • Browser: Chrome 134, Firefox 136 (same behavior)
  • OS: Linux x86_64

Root cause analysis

The WASM package in gorules/jdm-editor (packages/zen-engine-wasm/src/expression/evaluate.rs) returns results via:

Ok(JsValue::from_serde(&result)?)

The Variable serializer (in gorules/zen at core/types/src/variable/ser.rs) without the arbitrary_precision feature tries:

if let Some(u) = v.to_u64() { return serializer.serialize_u64(u); }

rust_decimal::Decimal::to_u64() calls self.trunc() first, so Decimal(1.6).to_u64() returns Some(1) — the fractional part is silently dropped.

The Node.js native package (@gorules/zen-engine) enables arbitrary_precision in its Cargo.toml, which uses a string-based serialization path that preserves decimal precision. The WASM package does not enable this feature.

Suggested fix

In gorules/jdm-editor, file packages/zen-engine-wasm/Cargo.toml, add the arbitrary_precision feature:

# Current:
zen-expression = { version = "0.55.0", default-features = false, features = ["regex-lite"] }

# Fix:
zen-expression = { version = "0.55.0", default-features = false, features = ["regex-lite", "arbitrary_precision"] }

And add arbitrary_precision to serde_json:

serde_json = { version = "1", features = ["arbitrary_precision"] }

This aligns the WASM package with the Node.js, Python, C, and UniFFI bindings which all enable arbitrary_precision (as done in PR #433 for the other bindings).

Validated locally

I cloned gorules/jdm-editor, applied the above change to packages/zen-engine-wasm/Cargo.toml, and rebuilt with wasm-pack build --target web --release. After replacing the .wasm and .js files in node_modules/@gorules/zen-engine-wasm/dist/, all tests pass:

OK | 3.7 → 3.7 (expected 3.7)
OK | 1.5 + 0.1 → 1.6 (expected 1.6)
OK | 2 * 0.3 → 0.6 (expected 0.6)
OK | 1 + 2 → 3 (expected 3)
OK | 1 + (x ? 0.5 : 0) + y * 0.3 → 1.6 (expected 1.6)  // x=false, y=2
OK | 1 + (x ? 0.5 : 0) + y * 0.3 → 2.1 (expected 2.1)  // x=true, y=2

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions