UNPKG

@mesh-tech/mesh-cli

Version:

CLI for Mesh platform development utilities

104 lines (103 loc) 4.75 kB
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; }