Numeric literals above i64::MAX panic the parser instead of erroring

Summary

A numeric literal above i64::MAX panics the parser instead of returning a compile error. It reproduces on any numeric filter field, in either mode:

type = Issue and weight > 9999999999999999999999999
# thread panicked at src/parser/literals.rs:29:44:
# called `Result::unwrap()` on an `Err` value: ParseIntError { kind: PosOverflow }

This breaks the compile contract rather than just producing a bad message. compile() returns {success: false, output: "Error: ..."} for every other kind of invalid input, but a panic bypasses the unwrap_or_else in compile_glql entirely, so nothing serialises an error. In WASM it surfaces as an unhandled runtime error instead of a structured failure the frontend can render.

Exactly i64::MAX (19 digits) parses fine - it is only the digit runs past it that panic.

Cause

src/parser/literals.rs:29:

// Parser for numbers
pub fn number(input: &str) -> IResult<&str, Value, GlqlError> {
    map(digit1, |s: &str| Number(s.parse().unwrap()))(input)
}

digit1 happily matches an unbounded run of digits, then s.parse::<i64>() returns Err(ParseIntError { kind: PosOverflow }) and .unwrap() panics. Value::Number is an i64 (src/types/value.rs:11), so there is no representation for the value either - this is a genuine domain limit, not just a missing conversion.

Options

  1. Return a parse error. Map the Err into a GlqlError so an out-of-range literal flows through the normal error contract like any other invalid token. This is the fix I would expect - it is local to number() and does not change any valid query's behaviour.
  2. Saturate to i64::MAX. Keeps the query compiling, but silently rewrites the user's predicate into one they did not ask for. Worse than erroring.

Option 1. Whichever way, it needs a test - the parser tests currently only cover Number(123) (src/parser/literals.rs:108).

Also worth noting

digit1 also means negative literals never parse at all (weight > -5 fails as an unexpected token). That is a separate question and may well be intended, but it is the same line of code, so worth deciding at the same time.

Separately, there is a second, narrower boundary below this one: GraphQL's Int is 32-bit, so even an in-i64 literal above i32::MAX cannot reach the wire as a valid Int. Sources that filter on Int columns handle that themselves today (ai_usage_events clamps its featuresCount range args for exactly this reason, which is correct for its purpose). Not asking for that to change - just noting that if a range check ever lands at parse or analysis time, it would subsume the per-source handling.

How it was found

Probing boundary values while reviewing !450 (merged), which clamps featuresCount range args to Int bounds. The panic is unrelated to that MR - it sits one boundary above the clamp, at the i64 limit, and reproduces on weight, which long predates it.

Reproduction as a test:

#[test]
fn probe() {
    // panics instead of returning an error
    println!("{}", compile("weight > 9999999999999999999999999", r#"{"project":"g/p"}"#));
}