@qlever-llc/trellis 0.8.3 → 0.8.4
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/bin/trellis-generate.js +132 -0
- package/esm/npm/src/generate.js +19 -2
- package/package.json +6 -7
- package/script/npm/src/generate.js +19 -2
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { createHash } = require("node:crypto");
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const https = require("node:https");
|
|
5
|
+
const os = require("node:os");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
const { spawnSync } = require("node:child_process");
|
|
8
|
+
|
|
9
|
+
const REPO_OWNER = "qlever-llc";
|
|
10
|
+
const REPO_NAME = "trellis";
|
|
11
|
+
const BIN_NAME = "trellis-generate";
|
|
12
|
+
const SUPPORTED_TARGETS = new Set([
|
|
13
|
+
"x86_64-unknown-linux-gnu",
|
|
14
|
+
"aarch64-unknown-linux-gnu",
|
|
15
|
+
"x86_64-apple-darwin",
|
|
16
|
+
"aarch64-apple-darwin",
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
main().catch((error) => {
|
|
20
|
+
console.error(error);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
async function main() {
|
|
25
|
+
const packageVersion = readPackageVersion();
|
|
26
|
+
const binary = (process.env.TRELLIS_GENERATE_BIN || "").trim() ||
|
|
27
|
+
await ensureCachedReleaseBinary(packageVersion);
|
|
28
|
+
verifyBinaryVersion(binary, packageVersion);
|
|
29
|
+
const status = spawnSync(binary, process.argv.slice(2), { stdio: "inherit" });
|
|
30
|
+
if (status.error) throw status.error;
|
|
31
|
+
process.exit(status.status ?? 1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function readPackageVersion() {
|
|
35
|
+
const manifestPath = path.resolve(__dirname, "../package.json");
|
|
36
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
37
|
+
if (typeof manifest.version !== "string" || !manifest.version.trim()) {
|
|
38
|
+
throw new Error("@qlever-llc/trellis package manifest does not declare a version");
|
|
39
|
+
}
|
|
40
|
+
return manifest.version.trim();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function ensureCachedReleaseBinary(version) {
|
|
44
|
+
const target = releaseTarget();
|
|
45
|
+
const cacheDir = path.join(cacheRoot(), version, target);
|
|
46
|
+
const binary = path.join(cacheDir, BIN_NAME);
|
|
47
|
+
if (fs.existsSync(binary)) return binary;
|
|
48
|
+
|
|
49
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
50
|
+
const tag = "v" + version;
|
|
51
|
+
const archiveName = BIN_NAME + "-" + tag + "-" + target + ".tar.gz";
|
|
52
|
+
const checksumName = "checksum-" + tag + "-" + target + "-" + BIN_NAME + ".sha256";
|
|
53
|
+
const releaseBase = "https://github.com/" + REPO_OWNER + "/" + REPO_NAME + "/releases/download/" + tag;
|
|
54
|
+
const [archive, checksumText] = await Promise.all([
|
|
55
|
+
downloadBytes(releaseBase + "/" + archiveName),
|
|
56
|
+
downloadText(releaseBase + "/" + checksumName),
|
|
57
|
+
]);
|
|
58
|
+
verifyChecksum(archive, checksumText, archiveName);
|
|
59
|
+
|
|
60
|
+
const archivePath = path.join(cacheDir, archiveName);
|
|
61
|
+
fs.writeFileSync(archivePath, archive);
|
|
62
|
+
const extract = spawnSync("tar", ["-xzf", archivePath, "-C", cacheDir], { stdio: "inherit" });
|
|
63
|
+
if (extract.error) throw extract.error;
|
|
64
|
+
if (extract.status !== 0) throw new Error("tar failed with exit code " + extract.status);
|
|
65
|
+
fs.chmodSync(binary, 0o755);
|
|
66
|
+
return binary;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function releaseTarget() {
|
|
70
|
+
const arch = os.arch() === "x64" ? "x86_64" : os.arch() === "arm64" ? "aarch64" : os.arch();
|
|
71
|
+
const platform = os.platform() === "darwin" ? "apple-darwin" : os.platform() === "linux" ? "unknown-linux-gnu" : undefined;
|
|
72
|
+
const target = platform ? arch + "-" + platform : undefined;
|
|
73
|
+
if (target && SUPPORTED_TARGETS.has(target)) return target;
|
|
74
|
+
throw new Error("no " + BIN_NAME + " release binary is available for " + os.platform() + " " + os.arch());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function cacheRoot() {
|
|
78
|
+
if ((process.env.TRELLIS_GENERATE_CACHE || "").trim()) return process.env.TRELLIS_GENERATE_CACHE.trim();
|
|
79
|
+
if ((process.env.XDG_CACHE_HOME || "").trim()) return path.join(process.env.XDG_CACHE_HOME.trim(), "trellis", BIN_NAME);
|
|
80
|
+
if ((process.env.LOCALAPPDATA || "").trim()) return path.join(process.env.LOCALAPPDATA.trim(), "trellis", BIN_NAME);
|
|
81
|
+
if ((process.env.HOME || "").trim()) return path.join(process.env.HOME.trim(), ".cache", "trellis", BIN_NAME);
|
|
82
|
+
throw new Error("HOME, LOCALAPPDATA, or TRELLIS_GENERATE_CACHE must be set to cache trellis-generate");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function downloadBytes(url) {
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
https.get(url, (response) => {
|
|
88
|
+
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
89
|
+
resolve(downloadBytes(response.headers.location));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (response.statusCode !== 200) {
|
|
93
|
+
reject(new Error("failed to download " + url + ": HTTP " + response.statusCode));
|
|
94
|
+
response.resume();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const chunks = [];
|
|
98
|
+
response.on("data", (chunk) => chunks.push(chunk));
|
|
99
|
+
response.on("end", () => resolve(Buffer.concat(chunks)));
|
|
100
|
+
}).on("error", reject);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function downloadText(url) {
|
|
105
|
+
return (await downloadBytes(url)).toString("utf8");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function verifyChecksum(bytes, checksumText, label) {
|
|
109
|
+
const expected = checksumText.trim().split(/\s+/)[0]?.toLowerCase();
|
|
110
|
+
if (!expected || !/^[0-9a-f]{64}$/.test(expected)) {
|
|
111
|
+
throw new Error("release checksum asset did not contain a SHA-256 digest");
|
|
112
|
+
}
|
|
113
|
+
const actual = createHash("sha256").update(bytes).digest("hex");
|
|
114
|
+
if (actual !== expected) {
|
|
115
|
+
throw new Error("checksum mismatch for " + label + ": expected " + expected + ", got " + actual);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function verifyBinaryVersion(binary, expectedVersion) {
|
|
120
|
+
const output = spawnSync(binary, ["--version"], { encoding: "utf8" });
|
|
121
|
+
if (output.error) throw output.error;
|
|
122
|
+
if (output.status !== 0) throw new Error("failed to run " + binary + " --version");
|
|
123
|
+
const text = (output.stdout || "").trim();
|
|
124
|
+
const actualVersion = text.split(/\s+/).find((part) => /^v?\d+\.\d+\.\d+/.test(part));
|
|
125
|
+
if (!actualVersion || normalizeVersion(actualVersion) !== normalizeVersion(expectedVersion)) {
|
|
126
|
+
throw new Error(binary + " is " + (text || "unknown version") + "; expected " + BIN_NAME + " " + expectedVersion);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function normalizeVersion(version) {
|
|
131
|
+
return version.trim().replace(/^v/, "").split("+")[0];
|
|
132
|
+
}
|
package/esm/npm/src/generate.js
CHANGED
|
@@ -44,13 +44,30 @@ async function runLocalGenerator(repoRoot, args) {
|
|
|
44
44
|
]);
|
|
45
45
|
}
|
|
46
46
|
async function readPackageVersion() {
|
|
47
|
-
const
|
|
48
|
-
|
|
47
|
+
const manifest = await readFirstManifest([
|
|
48
|
+
new URL("./deno.json", globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).url),
|
|
49
|
+
new URL("../package.json", globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).url),
|
|
50
|
+
new URL("../../../package.json", globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).url),
|
|
51
|
+
]);
|
|
49
52
|
if (typeof manifest.version !== "string" || !manifest.version.trim()) {
|
|
50
53
|
throw new Error("@qlever-llc/trellis package manifest does not declare a version");
|
|
51
54
|
}
|
|
52
55
|
return manifest.version.trim();
|
|
53
56
|
}
|
|
57
|
+
async function readFirstManifest(urls) {
|
|
58
|
+
for (const url of urls) {
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(await dntShim.Deno.readTextFile(url));
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (error instanceof dntShim.Deno.errors.NotFound) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
throw new Error("could not find @qlever-llc/trellis package manifest");
|
|
70
|
+
}
|
|
54
71
|
async function ensureCachedReleaseBinary(version) {
|
|
55
72
|
const target = releaseTarget();
|
|
56
73
|
const cacheDir = joinPath(cacheRoot(), version, target);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qlever-llc/trellis",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
4
4
|
"description": "Client-side Trellis runtime, models, and contract helpers for TypeScript applications.",
|
|
5
5
|
"homepage": "https://github.com/Qlever-LLC/trellis#readme",
|
|
6
6
|
"repository": {
|
|
@@ -42,10 +42,6 @@
|
|
|
42
42
|
"import": "./esm/npm/src/device/deno.js",
|
|
43
43
|
"require": "./script/npm/src/device/deno.js"
|
|
44
44
|
},
|
|
45
|
-
"./generate": {
|
|
46
|
-
"import": "./esm/npm/src/generate.js",
|
|
47
|
-
"require": "./script/npm/src/generate.js"
|
|
48
|
-
},
|
|
49
45
|
"./health": {
|
|
50
46
|
"import": "./esm/health.js",
|
|
51
47
|
"require": "./script/health.js"
|
|
@@ -123,10 +119,13 @@
|
|
|
123
119
|
"ts-deepmerge": "^7.0.3",
|
|
124
120
|
"typebox": "^1.0.15",
|
|
125
121
|
"ulid": "^3.0.1",
|
|
126
|
-
"@qlever-llc/result": "^0.8.
|
|
122
|
+
"@qlever-llc/result": "^0.8.4"
|
|
127
123
|
},
|
|
128
124
|
"devDependencies": {
|
|
129
125
|
"@types/node": "^20.9.0"
|
|
130
126
|
},
|
|
131
|
-
"_generatedBy": "dnt@dev"
|
|
127
|
+
"_generatedBy": "dnt@dev",
|
|
128
|
+
"bin": {
|
|
129
|
+
"trellis-generate": "./bin/trellis-generate.js"
|
|
130
|
+
}
|
|
132
131
|
}
|
|
@@ -79,13 +79,30 @@ async function runLocalGenerator(repoRoot, args) {
|
|
|
79
79
|
]);
|
|
80
80
|
}
|
|
81
81
|
async function readPackageVersion() {
|
|
82
|
-
const
|
|
83
|
-
|
|
82
|
+
const manifest = await readFirstManifest([
|
|
83
|
+
new URL("./deno.json", globalThis[Symbol.for("import-meta-ponyfill-commonjs")](require, module).url),
|
|
84
|
+
new URL("../package.json", globalThis[Symbol.for("import-meta-ponyfill-commonjs")](require, module).url),
|
|
85
|
+
new URL("../../../package.json", globalThis[Symbol.for("import-meta-ponyfill-commonjs")](require, module).url),
|
|
86
|
+
]);
|
|
84
87
|
if (typeof manifest.version !== "string" || !manifest.version.trim()) {
|
|
85
88
|
throw new Error("@qlever-llc/trellis package manifest does not declare a version");
|
|
86
89
|
}
|
|
87
90
|
return manifest.version.trim();
|
|
88
91
|
}
|
|
92
|
+
async function readFirstManifest(urls) {
|
|
93
|
+
for (const url of urls) {
|
|
94
|
+
try {
|
|
95
|
+
return JSON.parse(await dntShim.Deno.readTextFile(url));
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error instanceof dntShim.Deno.errors.NotFound) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
throw new Error("could not find @qlever-llc/trellis package manifest");
|
|
105
|
+
}
|
|
89
106
|
async function ensureCachedReleaseBinary(version) {
|
|
90
107
|
const target = releaseTarget();
|
|
91
108
|
const cacheDir = joinPath(cacheRoot(), version, target);
|