jqx

    jq-compatible JSON processor written in MoonBit with a CLI and TypeScript bindings

    jq
    json
    query
    filter
    moonbit
    Download zip
    Author
    Version
    0.4.3
    License
    Apache-2.0
    Last updated
    4 days ago
    Downloads
    113

    No dependencies

    #jqx

    jq-compatible JSON processor written in MoonBit with a CLI and TypeScript bindings

    #Install

    CLI:
    • Download the jqx executable from GitHub Releases.

    MoonBit:

    moon add shina1024/jqx

    JS/TS runtime:

    npm install @shina1024/jqx

    Optional standalone adapters:

    npm install @shina1024/jqx-zod-adapter zod npm install @shina1024/jqx-yup-adapter yup npm install @shina1024/jqx-valibot-adapter valibot

    #CLI Quick Start

    Use the jqx executable once it is available from GitHub Releases:

    # stdin input echo '{"foo": 1}' | jqx ".foo" # direct input argument jqx ".foo" '{"foo": 1}'

    Common jq-compatible flags:

    # Raw string output (no JSON quotes) jqx -r ".foo" '{"foo":"bar"}' # Raw input mode (line-based strings) jqx -R "." "a\nb" # Raw input slurp jqx -R -s "." "a\nb\n" # Null input jqx -n "." # Slurp inputs into one array jqx -s "." "1" # jq -e style exit status jqx -e ".ok" '{"ok": false}'

    The CLI stays on the shared jq-compatible core. Use it when you want the release artifact surface rather than an embedded library API.

    #MoonBit Quick Start

    MoonBit users should use the top-level shina1024/jqx package API. The normal path is standard Json via run(filter, input). Reach for compile(...) when you want to reuse a filter, and use run_json_text(...) when jq-style text fidelity matters.

    Add the package to your moon.pkg imports with an alias:

    ///|
    import {
    "moonbitlang/core/json",
    "shina1024/jqx",
    }

    Value lane example:

    ///|
    test "moonbit run on standard Json" {
    let input : Json = { "foo": 41.0 }
    let outputs = @jqx.run(".foo + 1", input) catch {
    err => fail(err.to_string())
    }
    assert_eq(outputs.length(), 1)
    assert_eq(outputs[0].stringify(), "42")
    }

    Compiled execution:

    ///|
    test "moonbit compile and run through a compiled filter" {
    let filter = @jqx.compile(".items[]") catch { err => fail(err.to_string()) }
    let input : Json = { "items": [1.0, 2.0, 3.0] }
    let outputs = filter.run(input) catch { err => fail(err.to_string()) }
    assert_eq(outputs.map(v => v.stringify()), ["1", "2", "3"])
    }

    Compiled filters expose run(...) for the value lane and run_json_text(...) for the compatibility lane.

    Compatibility lane:

    ///|
    test "moonbit run_json_text preserves output text" {
    let outputs = @jqx.run_json_text(".", "9007199254740993") catch {
    err => fail(err.to_string())
    }
    assert_eq(outputs, ["9007199254740993"])
    }

    Boundary helpers:
    • @jqx.is_valid_json(...) and @jqx.parse_json(...) are input-boundary helpers, not the main happy path.
    • When jq-style numeric or output fidelity matters, use @jqx.run_json_text(...) or CompiledFilter::run_json_text(...) before reaching for any advanced helper.
    • Normal MoonBit usage should stay on shina1024/jqx; you should not need shina1024/jqx/core, @core.Value, or @core.Filter.

    #JS/TS Quick Start

    Start with the direct runtime from @shina1024/jqx:

    import { run, runJsonText } from "@shina1024/jqx"; const result = run(".foo", { foo: 1 }); // { ok: true, value: [1] } const compat = runJsonText(".", "9007199254740993"); // { ok: true, value: ["9007199254740993"] }

    In JS/TS, the value lane is intentionally stricter than jq itself. run(...), parseJson(...), and isValidJson(...) only accept values that remain representable as plain JS JSON values, so non-finite numbers such as Infinity, -Infinity, and NaN are rejected. Native objects also follow ECMAScript key enumeration rules, including ascending order for integer-like keys. When jq-compatible numeric or object-order fidelity matters, stay on runJsonText(...).

    Reuse a compiled filter when you expect to run the same jq program repeatedly:

    import { compile } from "@shina1024/jqx"; const compiled = compile(".items[]"); if (compiled.ok) { const valueLane = compiled.value.run({ items: [1, 2, 3] }); const textLane = compiled.value.runJsonText('{"items":[1,2,3]}'); }

    @shina1024/jqx/bind is the backend-integration lane for custom JSON-text runtimes:

    import { bindRuntime, type JqxJsonTextRuntime } from "@shina1024/jqx/bind"; const backend: JqxJsonTextRuntime = { async runJsonText(filter, input) { return { ok: true as const, value: [input] }; }, }; const jqx = bindRuntime(backend); const result = await jqx.run(".", { x: 1 });

    The detailed runtime, query, and binding contracts live in ts/jqx/README.md.

    #Schema Adapter Example

    import { runtime } from "@shina1024/jqx"; import { createAdapter } from "@shina1024/jqx-zod-adapter"; import { z } from "zod"; const adapter = createAdapter(runtime); const result = await adapter.filter({ filter: ".users[].name", input: { users: [{ name: "alice" }, { name: "bob" }] }, inputSchema: z.object({ users: z.array(z.object({ name: z.string() })), }), outputSchema: z.string(), });

    Standalone adapter packages are:

    • @shina1024/jqx-zod-adapter
    • @shina1024/jqx-yup-adapter
    • @shina1024/jqx-valibot-adapter

    Each package keeps createAdapter(runtime).filter(...) as the primary on-ramp and owns its validator-specific details in its own README.

    CompileError

    pub suberror CompileError {
    InvalidChar(Position, Char)
    InvalidEof
    InvalidNumber(Position, String)
    Diagnostics(Array[String])
    } derive(Eq)

    CompileError::equal

    CompileError::not_equal

    fn CompileError::not_equal(x : CompileError, y : CompileError) -> Bool

    CompileError::output

    fn CompileError::output(self : CompileError, logger : &Logger) -> Unit

    CompileError::to_string

    fn CompileError::to_string(self : CompileError) -> String

    CompiledJsonTextRunError

    pub suberror CompiledJsonTextRunError {
    JsonParse(JsonParseError)
    Runtime(RuntimeError)
    } derive(Eq)

    CompiledJsonTextRunError::equal

    CompiledJsonTextRunError::not_equal

    CompiledJsonTextRunError::output

    fn CompiledJsonTextRunError::output(self : CompiledJsonTextRunError, logger : &Logger) -> Unit

    CompiledJsonTextRunError::to_string

    fn CompiledJsonTextRunError::to_string(self : CompiledJsonTextRunError) -> String

    JsonParseError

    pub suberror JsonParseError {
    InvalidChar(Position, Char)
    InvalidEof
    DepthLimitExceeded
    InvalidNumericLiteral(Position)
    InvalidNumericLiteralAtEof(Position)
    InvalidStringLiteral(Position)
    InvalidStringLiteralAtEof(Position)
    } derive(Eq)

    JsonParseError::equal

    JsonParseError::not_equal

    fn JsonParseError::not_equal(x : JsonParseError, y : JsonParseError) -> Bool

    JsonParseError::output

    fn JsonParseError::output(self : JsonParseError, logger : &Logger) -> Unit

    JsonParseError::to_string

    fn JsonParseError::to_string(self : JsonParseError) -> String

    JsonTextRunError

    pub suberror JsonTextRunError {
    JsonParse(JsonParseError)
    Compile(CompileError)
    Runtime(RuntimeError)
    } derive(Eq)

    JsonTextRunError::equal

    JsonTextRunError::not_equal

    fn JsonTextRunError::not_equal(x : JsonTextRunError, y : JsonTextRunError) -> Bool

    JsonTextRunError::output

    fn JsonTextRunError::output(self : JsonTextRunError, logger : &Logger) -> Unit

    JsonTextRunError::to_string

    fn JsonTextRunError::to_string(self : JsonTextRunError) -> String

    RunError

    pub suberror RunError {
    Compile(CompileError)
    Runtime(RuntimeError)
    } derive(Eq)

    impl Show for RunError

    RunError::equal

    fn RunError::equal(RunError, RunError) -> Bool

    RunError::not_equal

    fn RunError::not_equal(x : RunError, y : RunError) -> Bool

    RunError::output

    fn RunError::output(self : RunError, logger : &Logger) -> Unit

    RunError::to_string

    fn RunError::to_string(self : RunError) -> String

    RuntimeError

    pub suberror RuntimeError {
    TypeError(String)
    Thrown(Json)
    UnknownFunction(String)
    UnknownVariable(String)
    BreakSignal(String, Array[Json])
    Partial(Array[Json], Json, String)
    } derive(Eq)

    RuntimeError::equal

    RuntimeError::not_equal

    fn RuntimeError::not_equal(x : RuntimeError, y : RuntimeError) -> Bool

    RuntimeError::output

    fn RuntimeError::output(self : RuntimeError, logger : &Logger) -> Unit

    RuntimeError::to_string

    fn RuntimeError::to_string(self : RuntimeError) -> String

    CompiledFilter

    pub struct CompiledFilter {
    // private fields
    }

    CompiledFilter::run

    fn CompiledFilter::run(self : CompiledFilter, input : Json) -> Array[Json] raise RuntimeError

    CompiledFilter::run_json_text

    fn CompiledFilter::run_json_text(self : CompiledFilter, input : StringView, max_nesting_depth? : Int) -> Array[String] raise CompiledJsonTextRunError

    Position

    pub struct Position {
    line : Int
    column : Int
    } derive(Eq)

    impl Show for Position

    Position::equal

    fn Position::equal(Position, Position) -> Bool

    Position::not_equal

    fn Position::not_equal(x : Position, y : Position) -> Bool

    Position::output

    fn Position::output(self : Position, logger : &Logger) -> Unit

    Position::to_string

    fn Position::to_string(self : Position) -> String

    compile

    fn compile(input : StringView) -> CompiledFilter raise CompileError

    is_valid_json

    fn is_valid_json(input : StringView) -> Bool

    parse_json

    fn parse_json(input : StringView, max_nesting_depth? : Int) -> Json raise JsonParseError

    run

    fn run(filter : StringView, input : Json) -> Array[Json] raise RunError

    run_json_text

    fn run_json_text(filter : StringView, input : StringView, max_nesting_depth? : Int) -> Array[String] raise JsonTextRunError