# JSON output and CI integration

> Machine-readable --json stdout schema for decompile and unpack, warning kinds and is_error flags, elapsed_ms timing, and piping patterns for automation pipelines.

- Repository: pionxzh/wakaru
- GitHub: https://github.com/pionxzh/wakaru
- Human docs: https://grok-wiki.com/public/docs/pionxzh-wakaru-77a438a6cc6b
- Complete Markdown: https://grok-wiki.com/public/docs/pionxzh-wakaru-77a438a6cc6b/llms-full.txt

## Source Files

- `crates/cli/src/json_output.rs`
- `crates/cli/src/main.rs`
- `README.md`
- `crates/core/src/driver/types.rs`

---

---
title: "JSON output and CI integration"
description: "Machine-readable --json stdout schema for decompile and unpack, warning kinds and is_error flags, elapsed_ms timing, and piping patterns for automation pipelines."
---

The `wakaru` CLI exposes a `--json` flag that serializes operation results to **stdout** as compact JSON (single line, no pretty-printing). Warnings, timing, and metadata live in the JSON object; decompiled or unpacked **code** is written to `-o` when provided, or embedded in JSON for single-file decompile without an output path. Human-readable summaries and styled warning lines are suppressed on stderr when `--json` is active.

<ParamField body="--json" type="boolean">
Emit machine-readable JSON to stdout instead of human-readable summaries. Warnings and errors are included in the JSON object. Compatible with both single-file decompile and `--unpack` modes. Does not affect fatal pre-run errors (missing input, overwrite protection, invalid flag combinations), which still print to stderr and exit before JSON is written.
</ParamField>

## Stdout and stderr contract

| Stream | `--json` off | `--json` on |
|--------|--------------|-------------|
| **stdout** | Decompiled code (no `-o`), or nothing (with `-o` / unpack) | Single JSON object |
| **stderr** | Warnings, scan stats, format summaries | Mostly quiet; `--formatter` failures still print to stderr |

<Note>
JSON is emitted **before** a non-zero exit when non-fatal module errors exist. Pipelines should parse stdout first, then check the process exit code.
</Note>

```mermaid
sequenceDiagram
    participant CI as CI job
    participant Wakaru as wakaru --json
    participant FS as Filesystem (-o)

    CI->>Wakaru: input file or stdin
    Wakaru->>FS: write code / modules (if -o set)
    Wakaru-->>CI: JSON on stdout
    alt warnings with is_error true
        Wakaru-->>CI: exit code 1
    else success or diagnostics only
        Wakaru-->>CI: exit code 0
    end
```

## Decompile schema

Triggered when `--unpack` is absent. Applies to a single file or stdin (`-` or piped input).

<RequestExample>
```bash title="Decompile stdin to JSON"
echo 'var a=1;' | wakaru --json
```
</RequestExample>

<ResponseExample>
```json title="JsonDecompileOutput (stdout, no -o)"
{"code":"const a = 1;\n","warnings":[],"elapsed_ms":9}
```
</ResponseExample>

<ResponseField name="code" type="string | omitted">
Decompiled JavaScript. Present only when **no** `-o` / `--output` is set. When `-o` is set, code is written to the output file and this field is omitted from JSON.
</ResponseField>

<ResponseField name="source_map" type="string | omitted">
v3 source map JSON string. Present when `--emit-source-map` produced a map. Included in JSON regardless of whether `-o` is set; when `-o` is set, the `.map` file is also written alongside the output file.
</ResponseField>

<ResponseField name="warnings" type="JsonWarning[]">
Array of warning objects. See [Warning objects](#warning-objects).
</ResponseField>

<ResponseField name="elapsed_ms" type="number">
Wall-clock milliseconds for the decompile operation (`u64`).
</ResponseField>

<CodeGroup>
```bash title="Write code to file, JSON metadata only"
wakaru input.js --json -o output.js
```

```json title="Stdout when -o is set"
{"warnings":[],"elapsed_ms":10}
```
</CodeGroup>

## Unpack schema

Requires `-o` / `--output` (a directory). Module **code** is written under that directory; JSON lists filenames and aggregate stats only.

<RequestExample>
```bash title="Unpack bundle with JSON summary"
wakaru bundle.js --unpack --json -o unpacked/
```
</RequestExample>

<ResponseExample>
```json title="JsonUnpackOutput (stdout)"
{"detected_formats":["webpack4"],"modules":[{"filename":"module-0.js"},{"filename":"entry.js"}],"warnings":[],"total":52,"failed":0,"elapsed_ms":1768}
```
</ResponseExample>

<ResponseField name="detected_formats" type="string[]">
Bundle format identifiers detected during unpack. Values come from `BundleFormat::as_str()`: `webpack5`, `webpack4`, `browserify`, `systemjs`, `esbuild`, `amd`, `scope-hoisted`.
</ResponseField>

<ResponseField name="modules" type="JsonModule[]">
One entry per extracted module. Each object has only `filename` (logical module name, not the on-disk path under `-o`).
</ResponseField>

<ResponseField name="warnings" type="JsonWarning[]">
Per-module warnings from the unpack pipeline.
</ResponseField>

<ResponseField name="total" type="number">
Count of modules written to the output directory.
</ResponseField>

<ResponseField name="failed" type="number">
Count of **distinct module filenames** that have at least one warning with `is_error: true`. Matches the human-mode `"(N failed)"` suffix.
</ResponseField>

<ResponseField name="elapsed_ms" type="number">
Wall-clock milliseconds for the full unpack operation.
</ResponseField>

## Warning objects

Every warning in CLI JSON uses the same shape:

| Field | Type | Description |
|-------|------|-------------|
| `filename` | string | Module or input filename the warning applies to |
| `kind` | string | Machine-readable warning code (see table below) |
| `is_error` | boolean | `true` when the warning is a hard failure; `false` for diagnostics |
| `message` | string | Human-readable detail |

`is_error` is derived from `UnpackWarningKind::is_error()` in wakaru-core: diagnostic kinds return `false`; all others return `true`.

### Warning kinds

| `kind` | `is_error` | Meaning |
|--------|------------|---------|
| `raw_normalization_failed` | `true` | Raw extraction normalization failed |
| `fact_collection_parse_failed` | `true` | Phase 1 fact-collection parse failed |
| `decompile_failed` | `true` | Per-module decompile failed (fallback to raw code) |
| `input_parse_recovered` | `false` | Input parsed with error recovery |
| `tdz_violation` | `false` | Temporal dead zone detected after transform (`--diagnostics`) |
| `duplicate_declaration` | `true` | Duplicate binding in same scope (`--diagnostics`) |
| `import_cycle` | `false` | Import cycle detected |
| `output_parse_recovered` | `true` | Output parse required recovery |
| `output_parse_failed` | `true` | Output could not be parsed |

<Warning>
`--formatter` failures are **not** included in JSON warnings. When `--formatter` is enabled, formatter failures print to stderr and the unformatted code is preserved. For programmatic formatter-error handling, use the [WASM API](/wasm-api-reference) (`formatter_failed` kind) or parse stderr.
</Warning>

### Exit code behavior

| Condition | Exit code |
|-----------|-----------|
| Success, or only diagnostic warnings (`is_error: false`) | `0` |
| Any warning with `is_error: true` | `1` (after JSON is printed) |
| Fatal CLI error (missing input, overwrite without `--force`, invalid flags) | `1` (no JSON) |

Diagnostic warnings (`input_parse_recovered`, `tdz_violation`, `import_cycle`) do **not** cause a non-zero exit on their own.

## CI integration patterns

<Steps>
<Step title="Gate on exit code and errors">
Run wakaru with `--json`, capture stdout, then check `$?`. For stricter gates, also assert `failed == 0` (unpack) or no warnings with `is_error: true` (decompile).
</Step>

<Step title="Extract fields with jq">
Parse the JSON object from stdout. Compact output is valid `jq` input without preprocessing.
</Step>

<Step title="Separate code from metadata">
For unpack, read module bodies from the `-o` directory. For decompile without `-o`, read `code` from JSON; with `-o`, read the output file.
</Step>
</Steps>

<CodeGroup>
```bash title="Fail CI on decompile errors"
result=$(wakaru input.js --json)
echo "$result" | jq -e '.warnings | all(.is_error == false)' > /dev/null
```

```bash title="Unpack and assert zero failures"
wakaru bundle.js --unpack --json -o out/ | jq -e '.failed == 0'
```

```bash title="List error warnings"
wakaru bundle.js --unpack --json -o out/ | jq '.warnings[] | select(.is_error)'
```

```bash title="Capture timing for benchmarks"
wakaru input.js --json | jq '.elapsed_ms'
```

```bash title="Stdin pipeline"
echo "$SOURCE" | wakaru --json | jq -r '.code'
```
</CodeGroup>

<Tip>
Redirect stderr separately in CI (`2>wakaru.log`) so `--formatter` or linker warnings do not contaminate JSON parsing on stdout.
</Tip>

### Baseline regression stats

The repository's reproduction matrices (`scripts/repro/*-matrix/matrix.mjs`) support their own `--json` flag with a **different schema** (`summary`, `rows`, etc.). `scripts/repro/collect-stats.mjs` runs all matrices with `--json`, aggregates pass rates into `scripts/repro/stats.json`, and supports `--check` to fail CI when the baseline is stale:

```bash
node scripts/repro/collect-stats.mjs          # refresh stats.json
node scripts/repro/collect-stats.mjs --check  # exit non-zero if stale
```

This is separate from `wakaru --json` but follows the same automation pattern: machine-readable stdout, exit-code gating.

## CLI JSON vs WASM API

The browser/WASM bindings return JSON-shaped objects via `serde_wasm_bindgen` with different field sets:

| Aspect | CLI `--json` | WASM `decompile` / `unpack` |
|--------|--------------|----------------------------|
| Unpack module code | On disk only (`-o`) | Inline in `modules[].code` |
| `detected_formats` | Yes (unpack) | No |
| `total` / `failed` | Yes (unpack) | No |
| `elapsed_ms` | Yes | No |
| `is_error` on warnings | Yes | No (check `kind` manually) |
| `formatter_failed` kind | No (stderr only) | Yes |

See [WASM API reference](/wasm-api-reference) for `WakaruDecompileResult` and `WakaruUnpackResult` TypeScript shapes.

## Common pitfalls

<AccordionGroup>
<Accordion title="Empty stdout on fatal errors">
Overwrite protection, missing `-o` for unpack, and directory-as-input for decompile fail before JSON is emitted. Check stderr for the error message.
</Accordion>

<Accordion title="Expecting code in unpack JSON">
Unpack JSON lists `filename` only. Module source lives under the `-o` directory.
</Accordion>

<Accordion title="Treating diagnostics as CI failures">
`tdz_violation` and `import_cycle` set `is_error: false` and do not change the exit code. Gate on `is_error` or `failed`, not warning count alone.
</Accordion>

<Accordion title="Parsing stderr as JSON">
Only stdout is JSON when `--json` is set. Human scan stats (`scanned:`, `detected:`, `total:`) are suppressed in JSON mode but formatter warnings are not.
</Accordion>
</AccordionGroup>

## Related pages

<CardGroup>
<Card title="CLI reference" href="/cli-reference">
Complete flag surface including `--json`, stdin behavior, `--force`, and profiling options.
</Card>
<Card title="Unpack bundles" href="/unpack-bundles">
Operational unpack guide: `--unpack` modes, `-o` directory layout, and `--raw` extraction.
</Card>
<Card title="Quickstart" href="/quickstart">
First successful decompile and unpack runs with expected success signals.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
UnpackWarningKind codes, parse-recovery warnings, and bug-report fields.
</Card>
<Card title="Core API reference" href="/core-api-reference">
`UnpackOutput`, `DecompileOutput`, and `has_errors()` semantics in wakaru-core.
</Card>
<Card title="WASM API reference" href="/wasm-api-reference">
In-process JSON result types for browser and Node WASM consumers.
</Card>
</CardGroup>
