Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 36 additions & 6 deletions src/downloader.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { writeFile, mkdir, stat } from "fs/promises";
import { writeFile, mkdir, stat, readdir } from "fs/promises";
import { fetch } from "undici";
import { extract } from "tar";
import { join } from "path";
import { getModulePath, ensureCacheDir } from "./cache";
import { getCacheDir, getModulePath, ensureCacheDir } from "./cache";

async function downloadAndExtract(pkg: string, version: string) {
const modulePath = getModulePath(pkg, version);
Expand All @@ -24,18 +24,48 @@ async function downloadAndExtract(pkg: string, version: string) {
}

async function getLatestVersion(pkg: string): Promise<string> {
// console.log(`Fetching latest version of ${pkg}...`);
const res = await fetch(`https://registry.npmjs.org/${pkg}`);
if (!res.ok) throw new Error(`Failed to fetch package info: ${res.statusText}`);

const data = await res.json();
const ver = data["dist-tags"]?.latest || "";
// console.log(`Latest version is ${ver}`);
return ver;
}

async function findCachedVersion(pkg: string): Promise<string | null> {
const cacheDir = getCacheDir();
const prefix = `${pkg}@`;
try {
const entries = await readdir(cacheDir);
const matches = entries
.filter(e => e.startsWith(prefix))
.map(e => e.slice(prefix.length))
.sort()
.reverse();
return matches.length > 0 ? matches[0] : null;
} catch {
return null;
}
}

export async function ensureModule(pkg: string, version?: string) {
if (!version) version = await getLatestVersion(pkg);
// Allow pinning the version via environment variable to skip npm registry calls entirely
if (!version) version = process.env.HOLISTICS_CLI_CORE_VERSION;

if (!version) {
try {
version = await getLatestVersion(pkg);
} catch (err) {
// Offline fallback: if npm is unreachable, try to use a cached version
const cached = await findCachedVersion(pkg);
if (cached) {
console.log(`Network unavailable, using cached version ${pkg}@${cached}`);
return getModulePath(pkg, cached);
}
throw err;
}
}

const modulePath = getModulePath(pkg, version);

try {
Expand Down