@mesh-tech/mesh-cli
Version:
CLI for Mesh platform development utilities
104 lines (103 loc) • 4.75 kB
JavaScript
import { execFileSync } from "node:child_process";
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { homeNpmrcPath, parseNpmrcRegistryAuth } from "../utils/auth-preflight.js";
export const DOCS_PACKAGE = "@mesh-tech/docs";
export function readDocsRegistryAuth(npmrcPath = homeNpmrcPath()) {
if (!existsSync(npmrcPath))
return null;
return parseNpmrcRegistryAuth(readFileSync(npmrcPath, "utf-8"));
}
function packumentUrl(endpoint, pkg) {
return `${endpoint.replace(/\/+$/, "")}/${pkg.replace(/\//g, "%2f")}`;
}
async function registryFetch(auth, url, fetchImpl, accept = "application/json") {
return fetchImpl(url, {
headers: {
authorization: `Bearer ${auth.token}`,
accept,
},
});
}
export function compareVersions(a, b) {
const parse = (v) => {
const parts = v.split(".").map((n) => Number.parseInt(n, 10) || 0);
while (parts.length < 3)
parts.push(0);
return parts;
};
const pa = parse(a);
const pb = parse(b);
for (let i = 0; i < 3; i++) {
if (pa[i] !== pb[i])
return pa[i] - pb[i];
}
return 0;
}
export async function listDocsVersions(auth, fetchImpl = fetch) {
const response = await registryFetch(auth, packumentUrl(auth.endpoint, DOCS_PACKAGE), fetchImpl).catch((error) => {
throw new Error(`could not reach the package registry: ${error instanceof Error ? error.message : String(error)}`);
});
if (response.status === 401 || response.status === 403) {
throw new Error(`registry auth rejected the token (${response.status}). Refresh it: mesh registry login`);
}
if (response.status === 404) {
throw new Error(`${DOCS_PACKAGE} is not published yet — no release has shipped the docs artifact. In a mesh-platform checkout, use \`mesh docs start\` against the working tree instead.`);
}
if (!response.ok) {
throw new Error(`registry returned ${response.status} for ${DOCS_PACKAGE}`);
}
const packument = (await response.json());
const versions = Object.keys(packument.versions ?? {}).sort(compareVersions);
return { versions, latest: packument["dist-tags"]?.latest };
}
export function docsCacheRoot() {
const base = process.env.XDG_CACHE_HOME ?? path.join(homeDir(), ".cache");
return path.join(base, "mesh", "docs");
}
function homeDir() {
return process.env.HOME ?? tmpdir();
}
export async function fetchDocsArtifact(auth, version, cacheRoot = docsCacheRoot(), fetchImpl = fetch) {
const targetDir = path.join(cacheRoot, version);
if (existsSync(path.join(targetDir, "dist", "index.html")))
return targetDir;
const listResponse = await registryFetch(auth, packumentUrl(auth.endpoint, DOCS_PACKAGE), fetchImpl);
if (!listResponse.ok) {
throw new Error(`registry returned ${listResponse.status} for ${DOCS_PACKAGE}`);
}
const packument = (await listResponse.json());
const tarball = packument.versions?.[version]?.dist?.tarball;
if (!tarball) {
throw new Error(`${DOCS_PACKAGE}@${version} is not in the registry. Run \`mesh docs list\` to see published versions.`);
}
if (new URL(tarball).origin !== new URL(auth.endpoint).origin) {
throw new Error(`${DOCS_PACKAGE}@${version} points its tarball at ${new URL(tarball).origin}, not the registry (${new URL(auth.endpoint).origin}) — refusing to send the registry token off-origin`);
}
const tgzResponse = await registryFetch(auth, tarball, fetchImpl, "application/octet-stream");
if (!tgzResponse.ok) {
throw new Error(`downloading ${DOCS_PACKAGE}@${version} failed: ${tgzResponse.status}`);
}
mkdirSync(cacheRoot, { recursive: true });
const tgzPath = path.join(cacheRoot, `.${version}.tgz`);
writeFileSync(tgzPath, Buffer.from(await tgzResponse.arrayBuffer()));
const staging = path.join(cacheRoot, `.staging-${version}`);
rmSync(staging, { recursive: true, force: true });
mkdirSync(staging, { recursive: true });
try {
execFileSync("tar", ["-xzf", tgzPath, "-C", staging, "--strip-components", "1"], {
stdio: ["pipe", "pipe", "pipe"],
});
}
finally {
rmSync(tgzPath, { force: true });
}
if (!existsSync(path.join(staging, "dist", "index.html"))) {
rmSync(staging, { recursive: true, force: true });
throw new Error(`${DOCS_PACKAGE}@${version} unpacked without a dist/index.html — the artifact is malformed`);
}
rmSync(targetDir, { recursive: true, force: true });
renameSync(staging, targetDir);
return targetDir;
}