@shukelabs/jq 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +53 -0
- package/bin/jq.mjs +90 -0
- package/package.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SHUKE LABS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# @shukelabs/jq
|
|
2
|
+
|
|
3
|
+
The `jq` command for machines where the only thing you can install is an npm package.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install -g @shukelabs/jq
|
|
7
|
+
jq --version # jq-1.8.2
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## What this is, and what it is not
|
|
11
|
+
|
|
12
|
+
This is **not** a replacement for [jq](https://github.com/jqlang/jq), and not a fork or a
|
|
13
|
+
reimplementation of it. If you can install jq the normal way (`apt`, `brew`, `winget`, `scoop`,
|
|
14
|
+
or the [official release binaries](https://github.com/jqlang/jq/releases)), do that. Native jq
|
|
15
|
+
is faster and has none of the limitations below.
|
|
16
|
+
|
|
17
|
+
It exists for one situation: a locked-down machine where native executables cannot be
|
|
18
|
+
downloaded or installed, but Node.js and an npm registry are available. There, tools and
|
|
19
|
+
scripts that call `jq` can still work.
|
|
20
|
+
|
|
21
|
+
The actual jq is the official jq source compiled to WebAssembly by
|
|
22
|
+
[jq-wasm](https://github.com/owenthereal/jq-wasm). This package adds a small command-line
|
|
23
|
+
wrapper (`bin/jq.mjs`) that turns `jq [options] filter [files...]` into a jq-wasm call. It ships
|
|
24
|
+
no native binary and runs no install scripts.
|
|
25
|
+
|
|
26
|
+
## Differences from native jq
|
|
27
|
+
|
|
28
|
+
jq runs inside a WebAssembly sandbox. The wrapper bridges what it can and refuses what it cannot,
|
|
29
|
+
rather than produce a silently different result:
|
|
30
|
+
|
|
31
|
+
| Feature | Behaviour |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| Input files, `-f`, `--slurpfile`, `--rawfile` | Supported. The wrapper reads the files and hands their contents to jq. |
|
|
34
|
+
| `--args`, `--jsonargs`, `-L` / modules | Rejected with exit code 2. |
|
|
35
|
+
| Streaming input | Not supported: nothing is printed until stdin reaches end of file, so `tail -f log \| jq` does not work. |
|
|
36
|
+
| `-n` | Input is read only when the filter mentions `input` or `inputs`; a stray `"input"` inside a string literal also counts. |
|
|
37
|
+
| `$ENV`, `env` | See the sandbox environment, not your shell's. |
|
|
38
|
+
| `localtime`, `strflocaltime` | Use UTC. |
|
|
39
|
+
| `input_filename` | Always `null`. |
|
|
40
|
+
| `-C` (color) | Ignored; output is never colored. |
|
|
41
|
+
| Line endings | Always LF, also on Windows (native Windows jq writes CRLF). |
|
|
42
|
+
| Startup | About 0.3 s per call (Node.js start-up), against about 0.1 s for native jq. |
|
|
43
|
+
|
|
44
|
+
Apart from these, the test suite checks that output and exit codes match native jq. CI runs that
|
|
45
|
+
comparison against the runner's own jq on every push.
|
|
46
|
+
|
|
47
|
+
## Requirements
|
|
48
|
+
|
|
49
|
+
Node.js 20 or later.
|
|
50
|
+
|
|
51
|
+
## Licence
|
|
52
|
+
|
|
53
|
+
MIT. jq and jq-wasm are also MIT-licensed; see their repositories.
|
package/bin/jq.mjs
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// jq command line over jq-wasm: the official jq compiled to WebAssembly.
|
|
3
|
+
// jq runs in a sandbox that cannot see host files, so this wrapper reads
|
|
4
|
+
// every file the command line names and hands jq the contents instead.
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
|
+
import { loadJq } from "jq-wasm";
|
|
7
|
+
|
|
8
|
+
const jq = await loadJq();
|
|
9
|
+
|
|
10
|
+
// jq-wasm trims leading and trailing whitespace from jq's output. The real
|
|
11
|
+
// jq CLI never does, and `-r`/`-j` output can legitimately start or end with
|
|
12
|
+
// spaces or blank lines, so neutralise the trim for the duration of the call.
|
|
13
|
+
function run(input, query, opts) {
|
|
14
|
+
const trim = String.prototype.trim;
|
|
15
|
+
String.prototype.trim = function () { return String(this); };
|
|
16
|
+
try {
|
|
17
|
+
return jq.raw(input, query, opts);
|
|
18
|
+
} finally {
|
|
19
|
+
String.prototype.trim = trim;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function usage(msg) {
|
|
24
|
+
process.stderr.write(`jq: error: ${msg}\nUse jq --help for help with command-line options.\n`);
|
|
25
|
+
process.exit(2);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function read(file) {
|
|
29
|
+
try {
|
|
30
|
+
return readFileSync(file === "-" ? 0 : file, "utf8");
|
|
31
|
+
} catch (e) {
|
|
32
|
+
process.stderr.write(`jq: error: Could not open ${file}: ${e.code === "ENOENT" ? "No such file or directory" : e.message}\n`);
|
|
33
|
+
process.exit(2);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Options that take values, by how many. Every other option is a switch.
|
|
38
|
+
const VALUES = { "--arg": 2, "--argjson": 2, "--slurpfile": 2, "--rawfile": 2, "--indent": 1 };
|
|
39
|
+
const UNSUPPORTED = new Set(["--args", "--jsonargs", "-L", "--library-path"]);
|
|
40
|
+
const NO_INPUT = new Set(["--version", "-V", "-h", "--help", "--build-configuration"]);
|
|
41
|
+
|
|
42
|
+
const argv = process.argv.slice(2);
|
|
43
|
+
const opts = [], positionals = [], switches = new Set();
|
|
44
|
+
for (let i = 0; i < argv.length; i++) {
|
|
45
|
+
const a = argv[i];
|
|
46
|
+
if (a === "--") { positionals.push(...argv.slice(i + 1)); break; }
|
|
47
|
+
if (a === "-" || !a.startsWith("-")) { positionals.push(a); continue; }
|
|
48
|
+
// A bundle (-nrf) is its short options, in order.
|
|
49
|
+
const names = /^-[a-zA-Z]{2,}$/.test(a) ? [...a.slice(1)].map((c) => "-" + c) : [a];
|
|
50
|
+
for (const name of names) {
|
|
51
|
+
if (UNSUPPORTED.has(name) || name.startsWith("--library-path=")) usage(`${name} is not supported by @shukelabs/jq`);
|
|
52
|
+
const n = VALUES[name] ?? 0;
|
|
53
|
+
if (i + n >= argv.length) usage(`${name} takes ${n} parameter${n > 1 ? "s" : ""}`);
|
|
54
|
+
const v = argv.slice(i + 1, i + 1 + n);
|
|
55
|
+
i += n;
|
|
56
|
+
if (name === "-f" || name === "--from-file") switches.add("-f");
|
|
57
|
+
else if (name === "--rawfile") opts.push("--argjson", v[0], JSON.stringify(read(v[1])));
|
|
58
|
+
else if (name === "--slurpfile") {
|
|
59
|
+
const r = run(read(v[1]), ".", ["-c", "-s"]);
|
|
60
|
+
if (r.exitCode !== 0) { process.stderr.write(r.stderr); process.exit(2); }
|
|
61
|
+
opts.push("--argjson", v[0], r.stdout);
|
|
62
|
+
} else {
|
|
63
|
+
opts.push(name, ...v);
|
|
64
|
+
if (n === 0) switches.add(name);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The first positional is the filter, or with -f the file holding it.
|
|
70
|
+
let filter = positionals.shift();
|
|
71
|
+
if (switches.has("-f") && filter !== undefined) filter = read(filter);
|
|
72
|
+
const files = positionals;
|
|
73
|
+
const noInput = [...NO_INPUT].some((s) => switches.has(s));
|
|
74
|
+
if (filter === undefined && !noInput) {
|
|
75
|
+
// Like jq: default to "." unless both ends are a terminal.
|
|
76
|
+
if (process.stdin.isTTY && process.stdout.isTTY) usage("no filter given");
|
|
77
|
+
filter = ".";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Under -n jq reads input only when the filter calls input/inputs. Skipping
|
|
81
|
+
// the read otherwise matters: jq never opens the files then, and reading an
|
|
82
|
+
// open-but-idle stdin would hang.
|
|
83
|
+
const nullInput = switches.has("-n") || switches.has("--null-input");
|
|
84
|
+
const wantsInput = !noInput && (!nullInput || /(?<![.$\w])inputs?\b(?!\s*:)/.test(filter ?? ""));
|
|
85
|
+
// jq reads its input files as one continuous stream, so join them as-is.
|
|
86
|
+
const input = !wantsInput ? "" : files.length ? files.map(read).join("") : read("-");
|
|
87
|
+
const r = run(input, filter ?? ".", opts);
|
|
88
|
+
process.stdout.write(r.stdout);
|
|
89
|
+
process.stderr.write(r.stderr);
|
|
90
|
+
process.exitCode = r.exitCode;
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shukelabs/jq",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "The jq command for machines that can only install npm packages: official jq compiled to WebAssembly (via jq-wasm), no native binary.",
|
|
5
|
+
"keywords": ["jq", "json", "cli", "wasm", "webassembly"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "shukebeta <weizhong2004@gmail.com>",
|
|
8
|
+
"homepage": "https://github.com/SHUKE-LABS/jq#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/SHUKE-LABS/jq.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": "https://github.com/SHUKE-LABS/jq/issues",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"bin": {
|
|
16
|
+
"jq": "bin/jq.mjs"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"bin"
|
|
20
|
+
],
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "node --test"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"jq-wasm": "3.0.0-jq-1.8.2"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
}
|
|
33
|
+
}
|