import { isSafeVersion } from "./safeVersion.js"; // Matches `hyperframes@` as a whole token inside a script string. The // version class mirrors isSafeVersion's semver shape; capturing group 1 is the // old version. `(?=\s|$)` keeps it from matching a longer package name. export const HYPERFRAMES_PIN_RE = /\bhyperframes@([0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)(?=\s|$)/g; export interface PinRewriteResult { changed: boolean; scripts: Record; fromVersions: string[]; } export function readPinnedHyperframesVersions(scripts: Record): string[] { const found = new Set(); for (const cmd of Object.values(scripts ?? {})) { for (const m of cmd.matchAll(HYPERFRAMES_PIN_RE)) if (m[1]) found.add(m[1]); } return [...found].sort(); } export function rewriteProjectPinnedScripts( scripts: Record, targetVersion: string, ): PinRewriteResult { // Never emit an unverified version into a script the user (or npx) will run. if (!isSafeVersion(targetVersion)) { return { changed: false, scripts: { ...scripts }, fromVersions: [] }; } const fromVersions = new Set(); const next: Record = {}; for (const [name, cmd] of Object.entries(scripts ?? {})) { next[name] = cmd.replace(HYPERFRAMES_PIN_RE, (_full, version: string) => { if (version !== targetVersion) fromVersions.add(version); return `hyperframes@${targetVersion}`; }); } return { changed: [...fromVersions].length > 0, scripts: next, fromVersions: [...fromVersions].sort(), }; }