@pulseengine/synth 0.39.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/README.md +59 -0
- package/index.js +180 -0
- package/install.js +170 -0
- package/package.json +60 -0
- package/run.js +45 -0
- package/uninstall.js +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @pulseengine/synth
|
|
2
|
+
|
|
3
|
+
`synth` — a WebAssembly-to-native compiler with mechanized correctness proofs
|
|
4
|
+
in Rocq. It compiles WebAssembly to bare-metal ELF binaries for ARM Cortex-M
|
|
5
|
+
(Thumb-2), Cortex-R5 (A32), RISC-V (RV32IMAC), and host-native AArch64.
|
|
6
|
+
|
|
7
|
+
This npm package distributes the `synth` CLI binary so it can be installed and
|
|
8
|
+
invoked from any Node.js environment. It complements the crates.io
|
|
9
|
+
distribution (`cargo install synth-cli`).
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# One-shot (no install)
|
|
15
|
+
npx @pulseengine/synth --version
|
|
16
|
+
|
|
17
|
+
# Global install
|
|
18
|
+
npm install -g @pulseengine/synth
|
|
19
|
+
synth --version
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## How it works
|
|
23
|
+
|
|
24
|
+
There are no third-party dependencies. On install, the `postinstall` hook:
|
|
25
|
+
|
|
26
|
+
1. Detects your platform and architecture.
|
|
27
|
+
2. Downloads the matching `synth-v<version>-<triple>.tar.gz` from the GitHub
|
|
28
|
+
Release whose tag equals this package's version.
|
|
29
|
+
3. **Verifies** the tarball's SHA-256 against the release's signed
|
|
30
|
+
`SHA256SUMS.txt` manifest — a mismatch aborts the install.
|
|
31
|
+
4. Extracts the `synth` binary into the package's `bin/` directory.
|
|
32
|
+
|
|
33
|
+
The package version tracks the synth release version, so
|
|
34
|
+
`npm install @pulseengine/synth@0.38.0` fetches the `v0.38.0` binaries.
|
|
35
|
+
|
|
36
|
+
Set `SYNTH_NPM_SKIP_DOWNLOAD=1` to skip the download (e.g. air-gapped CI where
|
|
37
|
+
the binary is provided out of band).
|
|
38
|
+
|
|
39
|
+
## Supported platforms
|
|
40
|
+
|
|
41
|
+
Mirrors the release build matrix (no Windows build):
|
|
42
|
+
|
|
43
|
+
- `darwin-arm64` — `aarch64-apple-darwin`
|
|
44
|
+
- `darwin-x64` — `x86_64-apple-darwin`
|
|
45
|
+
- `linux-arm64` — `aarch64-unknown-linux-gnu`
|
|
46
|
+
- `linux-x64` — `x86_64-unknown-linux-gnu`
|
|
47
|
+
|
|
48
|
+
Binaries are pre-built and published alongside each GitHub release at
|
|
49
|
+
<https://github.com/pulseengine/synth/releases>. On an unsupported platform,
|
|
50
|
+
install from source:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
cargo install --git https://github.com/pulseengine/synth synth-cli
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
Apache-2.0. See the [repository](https://github.com/pulseengine/synth) for
|
|
59
|
+
source, documentation, and issues.
|
package/index.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Platform detection and release-asset naming for @pulseengine/synth.
|
|
4
|
+
//
|
|
5
|
+
// synth is distributed as a single npm package: install.js (postinstall)
|
|
6
|
+
// downloads the matching pre-built binary from the GitHub Release whose tag
|
|
7
|
+
// equals this package's version, verifies it against the release's signed
|
|
8
|
+
// SHA256SUMS manifest, and extracts it into ./bin. This module owns the
|
|
9
|
+
// platform -> Rust-target-triple mapping shared by install.js and run.js and
|
|
10
|
+
// is the single source of truth for asset names.
|
|
11
|
+
//
|
|
12
|
+
// Supported targets mirror .github/workflows/release.yml exactly — four
|
|
13
|
+
// tarballs, no Windows build:
|
|
14
|
+
// - aarch64-apple-darwin (macOS arm64)
|
|
15
|
+
// - x86_64-apple-darwin (macOS x64)
|
|
16
|
+
// - aarch64-unknown-linux-gnu (Linux arm64)
|
|
17
|
+
// - x86_64-unknown-linux-gnu (Linux x64)
|
|
18
|
+
|
|
19
|
+
const os = require("os");
|
|
20
|
+
const path = require("path");
|
|
21
|
+
|
|
22
|
+
const REPO = "pulseengine/synth";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Map (process.platform, process.arch) to the Rust target triple that
|
|
26
|
+
* release.yml builds and uploads. Throws with a clear message for any
|
|
27
|
+
* combination synth does not ship a binary for (notably Windows, which has
|
|
28
|
+
* no release build — do NOT synthesize a 404 URL for it).
|
|
29
|
+
*
|
|
30
|
+
* @param {string} [platform=os.platform()] node platform id
|
|
31
|
+
* @param {string} [arch=os.arch()] node arch id
|
|
32
|
+
* @returns {string} e.g. "aarch64-apple-darwin"
|
|
33
|
+
*/
|
|
34
|
+
function getRustTarget(platform = os.platform(), arch = os.arch()) {
|
|
35
|
+
let osTriple;
|
|
36
|
+
switch (platform) {
|
|
37
|
+
case "darwin":
|
|
38
|
+
osTriple = "apple-darwin";
|
|
39
|
+
break;
|
|
40
|
+
case "linux":
|
|
41
|
+
osTriple = "unknown-linux-gnu";
|
|
42
|
+
break;
|
|
43
|
+
default:
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Unsupported platform: ${platform}. synth ships binaries for ` +
|
|
46
|
+
"macOS (darwin) and Linux only. Build from source with " +
|
|
47
|
+
"`cargo install --git https://github.com/pulseengine/synth synth-cli`.",
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let archTriple;
|
|
52
|
+
switch (arch) {
|
|
53
|
+
case "x64":
|
|
54
|
+
archTriple = "x86_64";
|
|
55
|
+
break;
|
|
56
|
+
case "arm64":
|
|
57
|
+
archTriple = "aarch64";
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Unsupported architecture: ${arch}. synth ships x86_64 (x64) and ` +
|
|
62
|
+
"aarch64 (arm64) binaries only.",
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return `${archTriple}-${osTriple}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The release-asset tarball name for a given version + platform. Matches the
|
|
71
|
+
* `synth-${VERSION}-${TARGET}.tar.gz` name produced by release.yml, where
|
|
72
|
+
* VERSION is the tag *with* its leading `v` (e.g. v0.38.0).
|
|
73
|
+
*
|
|
74
|
+
* @param {string} version bare semver, e.g. "0.38.0"
|
|
75
|
+
* @param {string} [platform]
|
|
76
|
+
* @param {string} [arch]
|
|
77
|
+
* @returns {string} e.g. "synth-v0.38.0-aarch64-apple-darwin.tar.gz"
|
|
78
|
+
*/
|
|
79
|
+
function getTarballName(version, platform = os.platform(), arch = os.arch()) {
|
|
80
|
+
const target = getRustTarget(platform, arch);
|
|
81
|
+
return `synth-v${version}-${target}.tar.gz`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The binary name inside the tarball / on disk. synth has no Windows build,
|
|
85
|
+
* so there is never a `.exe`. */
|
|
86
|
+
function getBinaryName() {
|
|
87
|
+
return "synth";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Absolute path to the extracted synth binary (populated by install.js).
|
|
92
|
+
*/
|
|
93
|
+
function getBinaryPath() {
|
|
94
|
+
return path.join(__dirname, "bin", getBinaryName());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* GitHub Release download URL for the platform tarball at this version.
|
|
99
|
+
*/
|
|
100
|
+
function getTarballUrl(version, platform = os.platform(), arch = os.arch()) {
|
|
101
|
+
const name = getTarballName(version, platform, arch);
|
|
102
|
+
return `https://github.com/${REPO}/releases/download/v${version}/${name}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* GitHub Release download URL for the signed SHA256SUMS manifest.
|
|
107
|
+
*/
|
|
108
|
+
function getChecksumsUrl(version) {
|
|
109
|
+
return `https://github.com/${REPO}/releases/download/v${version}/SHA256SUMS.txt`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Parse a SHA256SUMS.txt manifest (as produced by `sha256sum ./*`) into a
|
|
114
|
+
* { basename: hash } map. Each line is `<64-hex-hash> ./<name>` — note the
|
|
115
|
+
* two-space separator and the `./` path prefix; we key by basename so a
|
|
116
|
+
* caller can look up `synth-v0.38.0-<triple>.tar.gz` directly.
|
|
117
|
+
*
|
|
118
|
+
* @param {string} text raw manifest contents
|
|
119
|
+
* @returns {Map<string,string>}
|
|
120
|
+
*/
|
|
121
|
+
function parseChecksums(text) {
|
|
122
|
+
const map = new Map();
|
|
123
|
+
for (const rawLine of text.split("\n")) {
|
|
124
|
+
const line = rawLine.trim();
|
|
125
|
+
if (!line) continue;
|
|
126
|
+
const m = line.match(/^([0-9a-fA-F]{64})\s+(.+)$/);
|
|
127
|
+
if (!m) continue;
|
|
128
|
+
const hash = m[1].toLowerCase();
|
|
129
|
+
const name = path.basename(m[2].trim());
|
|
130
|
+
map.set(name, hash);
|
|
131
|
+
}
|
|
132
|
+
return map;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Look up the expected sha256 for a tarball in a parsed manifest.
|
|
137
|
+
* @returns {string} lowercase hex hash
|
|
138
|
+
* @throws if the tarball is absent from the manifest
|
|
139
|
+
*/
|
|
140
|
+
function expectedHashFor(tarballName, checksumsText) {
|
|
141
|
+
const map =
|
|
142
|
+
checksumsText instanceof Map ? checksumsText : parseChecksums(checksumsText);
|
|
143
|
+
const hash = map.get(tarballName);
|
|
144
|
+
if (!hash) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`SHA256SUMS.txt has no entry for ${tarballName} — refusing to install ` +
|
|
147
|
+
"an unverifiable binary.",
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return hash;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = {
|
|
154
|
+
REPO,
|
|
155
|
+
getRustTarget,
|
|
156
|
+
getTarballName,
|
|
157
|
+
getBinaryName,
|
|
158
|
+
getBinaryPath,
|
|
159
|
+
getTarballUrl,
|
|
160
|
+
getChecksumsUrl,
|
|
161
|
+
parseChecksums,
|
|
162
|
+
expectedHashFor,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// When invoked directly, print platform info (useful for debugging installs).
|
|
166
|
+
if (require.main === module) {
|
|
167
|
+
try {
|
|
168
|
+
const version = require("./package.json").version;
|
|
169
|
+
console.log("synth npm — platform information:");
|
|
170
|
+
console.log(` Platform: ${os.platform()}`);
|
|
171
|
+
console.log(` Architecture: ${os.arch()}`);
|
|
172
|
+
console.log(` Rust target: ${getRustTarget()}`);
|
|
173
|
+
console.log(` Tarball: ${getTarballName(version)}`);
|
|
174
|
+
console.log(` Download URL: ${getTarballUrl(version)}`);
|
|
175
|
+
console.log(` Binary path: ${getBinaryPath()}`);
|
|
176
|
+
} catch (err) {
|
|
177
|
+
console.error("Error:", err.message);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
}
|
package/install.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Post-install hook for @pulseengine/synth.
|
|
4
|
+
//
|
|
5
|
+
// Downloads the pre-built `synth` binary for the current platform from the
|
|
6
|
+
// GitHub Release whose tag matches this package's version, VERIFIES the
|
|
7
|
+
// tarball against the release's SHA256SUMS.txt manifest, then extracts the
|
|
8
|
+
// binary into ./bin so run.js can spawn it.
|
|
9
|
+
//
|
|
10
|
+
// The checksum step is mandatory: an install that cannot verify the archive
|
|
11
|
+
// against the published manifest aborts rather than run an unverified binary.
|
|
12
|
+
//
|
|
13
|
+
// Uses only Node built-ins (https, node:crypto, fs, node:child_process) plus
|
|
14
|
+
// the system `tar` — no third-party dependencies.
|
|
15
|
+
|
|
16
|
+
const os = require("os");
|
|
17
|
+
const path = require("path");
|
|
18
|
+
const fs = require("fs");
|
|
19
|
+
const https = require("https");
|
|
20
|
+
const crypto = require("crypto");
|
|
21
|
+
const { execFileSync } = require("child_process");
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
getRustTarget,
|
|
25
|
+
getBinaryName,
|
|
26
|
+
getBinaryPath,
|
|
27
|
+
getTarballName,
|
|
28
|
+
getTarballUrl,
|
|
29
|
+
getChecksumsUrl,
|
|
30
|
+
expectedHashFor,
|
|
31
|
+
} = require("./index.js");
|
|
32
|
+
|
|
33
|
+
const VERSION = require("./package.json").version;
|
|
34
|
+
|
|
35
|
+
// Allow air-gapped / offline installs to skip the network fetch (e.g. when a
|
|
36
|
+
// binary was pre-placed or is provided by another mechanism).
|
|
37
|
+
if (process.env.SYNTH_NPM_SKIP_DOWNLOAD === "1") {
|
|
38
|
+
console.log(
|
|
39
|
+
"SYNTH_NPM_SKIP_DOWNLOAD=1 set — skipping synth binary download.",
|
|
40
|
+
);
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function download(url, { asBuffer = false, dest = null } = {}, redirects = 0) {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
if (redirects > 5) {
|
|
47
|
+
return reject(new Error(`Too many redirects fetching ${url}`));
|
|
48
|
+
}
|
|
49
|
+
https
|
|
50
|
+
.get(url, { headers: { "User-Agent": "pulseengine-synth-npm" } }, (res) => {
|
|
51
|
+
if (
|
|
52
|
+
res.statusCode >= 300 &&
|
|
53
|
+
res.statusCode < 400 &&
|
|
54
|
+
res.headers.location
|
|
55
|
+
) {
|
|
56
|
+
res.resume();
|
|
57
|
+
return download(res.headers.location, { asBuffer, dest }, redirects + 1)
|
|
58
|
+
.then(resolve)
|
|
59
|
+
.catch(reject);
|
|
60
|
+
}
|
|
61
|
+
if (res.statusCode !== 200) {
|
|
62
|
+
res.resume();
|
|
63
|
+
return reject(
|
|
64
|
+
new Error(`HTTP ${res.statusCode} ${res.statusMessage} for ${url}`),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (asBuffer) {
|
|
68
|
+
const chunks = [];
|
|
69
|
+
res.on("data", (c) => chunks.push(c));
|
|
70
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
71
|
+
res.on("error", reject);
|
|
72
|
+
} else {
|
|
73
|
+
const file = fs.createWriteStream(dest);
|
|
74
|
+
res.pipe(file);
|
|
75
|
+
file.on("finish", () => file.close(() => resolve(dest)));
|
|
76
|
+
file.on("error", (err) => {
|
|
77
|
+
fs.unlink(dest, () => {});
|
|
78
|
+
reject(err);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
.on("error", reject);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function sha256(buffer) {
|
|
87
|
+
return crypto.createHash("sha256").update(buffer).digest("hex");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Extract via execFile (no shell) — inputs are paths we constructed, but the
|
|
91
|
+
// argv form is the correct idiom regardless.
|
|
92
|
+
function extractTarball(archivePath, destDir) {
|
|
93
|
+
execFileSync("tar", ["-xzf", archivePath, "-C", destDir], {
|
|
94
|
+
stdio: "inherit",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function main() {
|
|
99
|
+
const target = getRustTarget();
|
|
100
|
+
const tarballName = getTarballName(VERSION);
|
|
101
|
+
const tarballUrl = getTarballUrl(VERSION);
|
|
102
|
+
const checksumsUrl = getChecksumsUrl(VERSION);
|
|
103
|
+
const binaryName = getBinaryName();
|
|
104
|
+
|
|
105
|
+
console.log(`synth npm installer — v${VERSION}`);
|
|
106
|
+
console.log(` Platform: ${os.type()} ${os.arch()} (${target})`);
|
|
107
|
+
console.log(` Tarball: ${tarballName}`);
|
|
108
|
+
|
|
109
|
+
const binDir = path.join(__dirname, "bin");
|
|
110
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
111
|
+
const archivePath = path.join(binDir, tarballName);
|
|
112
|
+
|
|
113
|
+
// 1. Fetch the signed checksum manifest first, so we know what to expect.
|
|
114
|
+
console.log(" Fetching SHA256SUMS.txt ...");
|
|
115
|
+
const checksumsText = (await download(checksumsUrl, { asBuffer: true })).toString(
|
|
116
|
+
"utf8",
|
|
117
|
+
);
|
|
118
|
+
const expected = expectedHashFor(tarballName, checksumsText);
|
|
119
|
+
|
|
120
|
+
// 2. Download the tarball.
|
|
121
|
+
console.log(` Downloading ${tarballUrl} ...`);
|
|
122
|
+
await download(tarballUrl, { dest: archivePath });
|
|
123
|
+
|
|
124
|
+
// 3. Verify BEFORE extracting.
|
|
125
|
+
const actual = sha256(fs.readFileSync(archivePath));
|
|
126
|
+
if (actual !== expected) {
|
|
127
|
+
fs.unlinkSync(archivePath);
|
|
128
|
+
throw new Error(
|
|
129
|
+
`Checksum mismatch for ${tarballName}\n` +
|
|
130
|
+
` expected: ${expected}\n` +
|
|
131
|
+
` actual: ${actual}\n` +
|
|
132
|
+
"The download may be corrupted or tampered with. Aborting.",
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
console.log(` Checksum OK (sha256 ${actual}).`);
|
|
136
|
+
|
|
137
|
+
// 4. Extract and expose the binary.
|
|
138
|
+
console.log(" Extracting ...");
|
|
139
|
+
extractTarball(archivePath, binDir);
|
|
140
|
+
fs.unlinkSync(archivePath);
|
|
141
|
+
|
|
142
|
+
const binaryPath = getBinaryPath();
|
|
143
|
+
if (!fs.existsSync(binaryPath)) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Extraction succeeded but ${binaryName} was not found at ${binaryPath}.`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
149
|
+
console.log(` Installed synth -> ${binaryPath}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
main().catch((err) => {
|
|
153
|
+
console.error("");
|
|
154
|
+
console.error("Failed to install the synth binary:", err.message);
|
|
155
|
+
console.error("");
|
|
156
|
+
console.error("Alternatives:");
|
|
157
|
+
console.error(
|
|
158
|
+
" 1. Build from source: cargo install --git " +
|
|
159
|
+
"https://github.com/pulseengine/synth synth-cli",
|
|
160
|
+
);
|
|
161
|
+
console.error(
|
|
162
|
+
" 2. Download a release manually: " +
|
|
163
|
+
"https://github.com/pulseengine/synth/releases",
|
|
164
|
+
);
|
|
165
|
+
// A failed binary install is a hard error: unlike rivet (which has a
|
|
166
|
+
// platform-package primary path and treats the download as a fallback),
|
|
167
|
+
// this package's ONLY delivery mechanism is the verified download. If it
|
|
168
|
+
// fails, `synth` would not exist — surface that loudly.
|
|
169
|
+
process.exit(1);
|
|
170
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pulseengine/synth",
|
|
3
|
+
"version": "0.39.0",
|
|
4
|
+
"description": "synth — a WebAssembly-to-ARM/RISC-V/AArch64 compiler with mechanized correctness proofs. Produces bare-metal ELF binaries for embedded targets.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"synth": "./run.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"postinstall": "node install.js",
|
|
10
|
+
"preuninstall": "node uninstall.js",
|
|
11
|
+
"test": "node --test"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"webassembly",
|
|
15
|
+
"wasm",
|
|
16
|
+
"compiler",
|
|
17
|
+
"arm",
|
|
18
|
+
"cortex-m",
|
|
19
|
+
"riscv",
|
|
20
|
+
"aarch64",
|
|
21
|
+
"embedded",
|
|
22
|
+
"bare-metal",
|
|
23
|
+
"verified",
|
|
24
|
+
"rocq",
|
|
25
|
+
"coq",
|
|
26
|
+
"rust",
|
|
27
|
+
"cli"
|
|
28
|
+
],
|
|
29
|
+
"author": "PulseEngine <https://github.com/pulseengine>",
|
|
30
|
+
"license": "Apache-2.0",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/pulseengine/synth.git"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/pulseengine/synth#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/pulseengine/synth/issues"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
},
|
|
42
|
+
"os": [
|
|
43
|
+
"darwin",
|
|
44
|
+
"linux"
|
|
45
|
+
],
|
|
46
|
+
"cpu": [
|
|
47
|
+
"x64",
|
|
48
|
+
"arm64"
|
|
49
|
+
],
|
|
50
|
+
"files": [
|
|
51
|
+
"index.js",
|
|
52
|
+
"install.js",
|
|
53
|
+
"run.js",
|
|
54
|
+
"uninstall.js",
|
|
55
|
+
"README.md"
|
|
56
|
+
],
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/run.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Thin launcher that spawns the synth binary extracted by install.js.
|
|
4
|
+
// Forwards argv, stdio, and exit code; propagates signals to the child.
|
|
5
|
+
|
|
6
|
+
const { spawn } = require("child_process");
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const { getBinaryPath } = require("./index.js");
|
|
9
|
+
|
|
10
|
+
const binaryPath = getBinaryPath();
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(binaryPath)) {
|
|
13
|
+
console.error("synth binary not found at:", binaryPath);
|
|
14
|
+
console.error("");
|
|
15
|
+
console.error("This usually means the postinstall download did not run or");
|
|
16
|
+
console.error("failed. Try reinstalling:");
|
|
17
|
+
console.error(" npm install --force @pulseengine/synth");
|
|
18
|
+
console.error("");
|
|
19
|
+
console.error(
|
|
20
|
+
"Or build from source: cargo install --git " +
|
|
21
|
+
"https://github.com/pulseengine/synth synth-cli",
|
|
22
|
+
);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
27
|
+
stdio: "inherit",
|
|
28
|
+
env: process.env,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
child.on("error", (err) => {
|
|
32
|
+
console.error("Failed to start synth:", err.message);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
child.on("exit", (code, signal) => {
|
|
37
|
+
if (signal) {
|
|
38
|
+
process.kill(process.pid, signal);
|
|
39
|
+
} else {
|
|
40
|
+
process.exit(code == null ? 0 : code);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
process.on("SIGINT", () => child.kill("SIGINT"));
|
|
45
|
+
process.on("SIGTERM", () => child.kill("SIGTERM"));
|
package/uninstall.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Pre-uninstall hook: remove the binary downloaded into ./bin by install.js.
|
|
4
|
+
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
|
|
8
|
+
const binDir = path.join(__dirname, "bin");
|
|
9
|
+
|
|
10
|
+
if (fs.existsSync(binDir)) {
|
|
11
|
+
try {
|
|
12
|
+
fs.rmSync(binDir, { recursive: true, force: true });
|
|
13
|
+
} catch (err) {
|
|
14
|
+
console.error("Failed to clean synth bin/:", err.message);
|
|
15
|
+
}
|
|
16
|
+
}
|