jqx

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

jq
json
query
filter
moonbit
moon add shina1024/jqx@0.4.1
Download zip
Author
Version
0.4.1
License
Apache-2.0
Last updated
16 days ago
Downloads
56
README

#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)

#
CompiledJsonTextRunError

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

#
JsonParseError

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

#
JsonTextRunError

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

#
RunError

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

impl Show for RunError

#
RuntimeError

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

#
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

#
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

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io