@cratestack/cli 0.4.16
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 +49 -0
- package/bin/cratestack.js +33 -0
- package/package.json +43 -0
- package/scripts/install.js +148 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stephane Segning
|
|
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,49 @@
|
|
|
1
|
+
# @cratestack/cli
|
|
2
|
+
|
|
3
|
+
npm wrapper around [`cratestack-cli`](https://crates.io/crates/cratestack-cli) — the CrateStack
|
|
4
|
+
`.cstack` schema validator and client/Studio code generator. Installing this package downloads the
|
|
5
|
+
prebuilt `cratestack` binary matching your platform from the matching
|
|
6
|
+
[GitHub Release](https://github.com/cratestack/cratestack/releases); no Rust toolchain required.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install --global @cratestack/cli
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Or run without installing:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @cratestack/cli --help
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Supported platforms
|
|
21
|
+
|
|
22
|
+
macOS (x64, arm64), Linux (x64, arm64), Windows (x64). See
|
|
23
|
+
[cratestack-cli](https://github.com/cratestack/cratestack/tree/main/crates/cratestack-cli) for the
|
|
24
|
+
full command reference (`check`, `generate-dart`, `generate-typescript`, `studio`, `print-ir`).
|
|
25
|
+
|
|
26
|
+
## How it works
|
|
27
|
+
|
|
28
|
+
The `postinstall` script (`scripts/install.js`) detects your OS/architecture, downloads the
|
|
29
|
+
matching release archive plus its `.sha256` checksum from GitHub Releases, verifies the checksum,
|
|
30
|
+
and extracts the `cratestack` binary into `bin/`. The `cratestack` command then execs that binary.
|
|
31
|
+
|
|
32
|
+
Environment variables:
|
|
33
|
+
|
|
34
|
+
- `CRATESTACK_CLI_SKIP_DOWNLOAD=1` — skip the postinstall download (e.g. offline/vendored installs
|
|
35
|
+
that provide the binary another way).
|
|
36
|
+
- `CRATESTACK_CLI_BINARY_PATH=/path/to/cratestack` — run a specific binary instead of the one
|
|
37
|
+
downloaded at install time.
|
|
38
|
+
|
|
39
|
+
If the download fails or your platform isn't yet supported, fall back to:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
cargo binstall cratestack-cli
|
|
43
|
+
# or
|
|
44
|
+
cargo install cratestack-cli
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
|
|
49
|
+
MIT
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Thin shim that execs the binary scripts/install.js downloaded into
|
|
3
|
+
// ./bin/ at install time. Set CRATESTACK_CLI_BINARY_PATH to point at a
|
|
4
|
+
// manually provided binary instead (e.g. a vendored/offline install).
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
const fs = require("node:fs");
|
|
9
|
+
const path = require("node:path");
|
|
10
|
+
const { spawnSync } = require("node:child_process");
|
|
11
|
+
|
|
12
|
+
const binName = process.platform === "win32" ? "cratestack.exe" : "cratestack";
|
|
13
|
+
const binaryPath =
|
|
14
|
+
process.env.CRATESTACK_CLI_BINARY_PATH || path.join(__dirname, binName);
|
|
15
|
+
|
|
16
|
+
if (!fs.existsSync(binaryPath)) {
|
|
17
|
+
console.error(
|
|
18
|
+
`cratestack binary not found at ${binaryPath}.\n` +
|
|
19
|
+
"The postinstall download may have failed or been skipped " +
|
|
20
|
+
"(CRATESTACK_CLI_SKIP_DOWNLOAD, --ignore-scripts). Reinstall with:\n" +
|
|
21
|
+
" npm install @cratestack/cli\n" +
|
|
22
|
+
"or fall back to:\n" +
|
|
23
|
+
" cargo binstall cratestack-cli",
|
|
24
|
+
);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const result = spawnSync(binaryPath, process.argv.slice(2), { stdio: "inherit" });
|
|
29
|
+
if (result.error) {
|
|
30
|
+
console.error(`failed to run ${binaryPath}: ${result.error.message}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
process.exit(result.status === null ? 1 : result.status);
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cratestack/cli",
|
|
3
|
+
"version": "0.4.16",
|
|
4
|
+
"description": "Prebuilt cratestack-cli binary, fetched from GitHub Releases on install. No Rust toolchain required.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/cratestack/cratestack.git",
|
|
9
|
+
"directory": "packages/cratestack-cli-npm"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://cratestack.dev",
|
|
12
|
+
"bugs": "https://github.com/cratestack/cratestack/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"cratestack",
|
|
15
|
+
"cstack",
|
|
16
|
+
"cli",
|
|
17
|
+
"codegen"
|
|
18
|
+
],
|
|
19
|
+
"bin": {
|
|
20
|
+
"cratestack": "bin/cratestack.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"bin",
|
|
24
|
+
"scripts",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"postinstall": "node ./scripts/install.js"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"os": [
|
|
35
|
+
"darwin",
|
|
36
|
+
"linux",
|
|
37
|
+
"win32"
|
|
38
|
+
],
|
|
39
|
+
"cpu": [
|
|
40
|
+
"x64",
|
|
41
|
+
"arm64"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Downloads the cratestack-cli binary matching this package's version and
|
|
3
|
+
// the host platform from the GitHub Release the release-cli.yml workflow
|
|
4
|
+
// publishes, verifies its checksum, and extracts it to ./bin/.
|
|
5
|
+
//
|
|
6
|
+
// Archive naming and layout here must stay in sync with:
|
|
7
|
+
// - .github/workflows/release-cli.yml ("Package archive" step)
|
|
8
|
+
// - crates/cratestack-cli/Cargo.toml ([package.metadata.binstall])
|
|
9
|
+
|
|
10
|
+
"use strict";
|
|
11
|
+
|
|
12
|
+
const fs = require("node:fs");
|
|
13
|
+
const https = require("node:https");
|
|
14
|
+
const os = require("node:os");
|
|
15
|
+
const path = require("node:path");
|
|
16
|
+
const crypto = require("node:crypto");
|
|
17
|
+
const { execFileSync } = require("node:child_process");
|
|
18
|
+
|
|
19
|
+
const pkg = require("../package.json");
|
|
20
|
+
|
|
21
|
+
const REPO = "https://github.com/cratestack/cratestack";
|
|
22
|
+
const BIN_DIR = path.join(__dirname, "..", "bin");
|
|
23
|
+
|
|
24
|
+
function resolveTarget() {
|
|
25
|
+
const platform = process.platform;
|
|
26
|
+
const arch = process.arch;
|
|
27
|
+
const targets = {
|
|
28
|
+
"darwin:x64": "x86_64-apple-darwin",
|
|
29
|
+
"darwin:arm64": "aarch64-apple-darwin",
|
|
30
|
+
"linux:x64": "x86_64-unknown-linux-gnu",
|
|
31
|
+
"linux:arm64": "aarch64-unknown-linux-gnu",
|
|
32
|
+
"win32:x64": "x86_64-pc-windows-msvc",
|
|
33
|
+
};
|
|
34
|
+
const target = targets[`${platform}:${arch}`];
|
|
35
|
+
if (!target) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`@cratestack/cli has no prebuilt binary for ${platform}/${arch}. ` +
|
|
38
|
+
`Supported: ${Object.keys(targets).join(", ")}. ` +
|
|
39
|
+
"Install from source instead: cargo install cratestack-cli",
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return target;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function fetchBuffer(url, redirectsLeft = 5) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
https
|
|
48
|
+
.get(url, { headers: { "User-Agent": "cratestack-cli-npm-installer" } }, (res) => {
|
|
49
|
+
if (
|
|
50
|
+
res.statusCode >= 300 &&
|
|
51
|
+
res.statusCode < 400 &&
|
|
52
|
+
res.headers.location &&
|
|
53
|
+
redirectsLeft > 0
|
|
54
|
+
) {
|
|
55
|
+
res.resume();
|
|
56
|
+
resolve(fetchBuffer(res.headers.location, redirectsLeft - 1));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (res.statusCode !== 200) {
|
|
60
|
+
reject(new Error(`GET ${url} failed: HTTP ${res.statusCode}`));
|
|
61
|
+
res.resume();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const chunks = [];
|
|
65
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
66
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
67
|
+
res.on("error", reject);
|
|
68
|
+
})
|
|
69
|
+
.on("error", reject);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function verifyChecksum(assetName, archiveBuf, sha256Buf) {
|
|
74
|
+
const expected = sha256Buf.toString("utf8").trim().split(/\s+/)[0];
|
|
75
|
+
const actual = crypto.createHash("sha256").update(archiveBuf).digest("hex");
|
|
76
|
+
if (expected !== actual) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`checksum mismatch for ${assetName}: expected ${expected}, got ${actual}`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function extract(archivePath, ext, destDir) {
|
|
84
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
85
|
+
if (ext === "zip") {
|
|
86
|
+
execFileSync(
|
|
87
|
+
"powershell",
|
|
88
|
+
[
|
|
89
|
+
"-NoProfile",
|
|
90
|
+
"-Command",
|
|
91
|
+
`Expand-Archive -Path '${archivePath}' -DestinationPath '${destDir}' -Force`,
|
|
92
|
+
],
|
|
93
|
+
{ stdio: "inherit" },
|
|
94
|
+
);
|
|
95
|
+
} else {
|
|
96
|
+
execFileSync("tar", ["-xzf", archivePath, "-C", destDir], { stdio: "inherit" });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function main() {
|
|
101
|
+
if (process.env.CRATESTACK_CLI_SKIP_DOWNLOAD) {
|
|
102
|
+
console.log("CRATESTACK_CLI_SKIP_DOWNLOAD set; skipping binary download.");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const target = resolveTarget();
|
|
107
|
+
const version = pkg.version;
|
|
108
|
+
const ext = process.platform === "win32" ? "zip" : "tar.gz";
|
|
109
|
+
const assetName = `cratestack-cli-${target}-v${version}.${ext}`;
|
|
110
|
+
const assetUrl = `${REPO}/releases/download/v${version}/${assetName}`;
|
|
111
|
+
|
|
112
|
+
console.log(`Downloading ${assetName} ...`);
|
|
113
|
+
const [archiveBuf, checksumBuf] = await Promise.all([
|
|
114
|
+
fetchBuffer(assetUrl),
|
|
115
|
+
fetchBuffer(`${assetUrl}.sha256`),
|
|
116
|
+
]);
|
|
117
|
+
verifyChecksum(assetName, archiveBuf, checksumBuf);
|
|
118
|
+
|
|
119
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "cratestack-cli-"));
|
|
120
|
+
const archivePath = path.join(tmpDir, assetName);
|
|
121
|
+
fs.writeFileSync(archivePath, archiveBuf);
|
|
122
|
+
|
|
123
|
+
extract(archivePath, ext, tmpDir);
|
|
124
|
+
|
|
125
|
+
const binName = process.platform === "win32" ? "cratestack.exe" : "cratestack";
|
|
126
|
+
const extracted = path.join(tmpDir, binName);
|
|
127
|
+
if (!fs.existsSync(extracted)) {
|
|
128
|
+
throw new Error(`expected ${binName} inside ${assetName}, but it was not found`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fs.mkdirSync(BIN_DIR, { recursive: true });
|
|
132
|
+
const installed = path.join(BIN_DIR, binName);
|
|
133
|
+
fs.copyFileSync(extracted, installed);
|
|
134
|
+
if (process.platform !== "win32") {
|
|
135
|
+
fs.chmodSync(installed, 0o755);
|
|
136
|
+
}
|
|
137
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
138
|
+
|
|
139
|
+
console.log(`cratestack-cli ${version} (${target}) installed to ${installed}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
main().catch((err) => {
|
|
143
|
+
console.error(`@cratestack/cli install failed: ${err.message}`);
|
|
144
|
+
console.error(
|
|
145
|
+
"Fall back to: cargo binstall cratestack-cli or cargo install cratestack-cli",
|
|
146
|
+
);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
});
|