#!/usr/bin/env node import { access, readFile, stat } from "node:fs/promises"; import { dirname, isAbsolute, join, resolve } from "node:path"; const manifestPath = resolve(process.argv[2] || ""); if (!process.argv[2]) { console.error("Usage: npm run audit:engine-cache -- /absolute/path/to/cesium/manifest.json"); process.exitCode = 1; } else { await audit(manifestPath); } async function audit(path) { let manifest; try { manifest = JSON.parse(await readFile(path, "utf8")); } catch (error) { throw new Error(`Cannot read legacy cache manifest: ${error instanceof Error ? error.message : "unknown error"}`); } const root = dirname(path); const entries = Object.entries(manifest?.entries || {}); const providers = new Map(); let bytesFromManifest = 0; let presentFiles = 0; let missingFiles = 0; let bytesOnDisk = 0; for (const [rawUrl, entry] of entries) { let hostname = "invalid-url"; try { hostname = new URL(rawUrl).hostname.toLowerCase() || "invalid-url"; } catch {} const provider = providers.get(hostname) || { entries: 0, bytes: 0, presentFiles: 0, missingFiles: 0 }; const entryBytes = Math.max(0, Number(entry?.size || 0)); provider.entries += 1; provider.bytes += entryBytes; bytesFromManifest += entryBytes; const relativeFile = String(entry?.path || ""); const candidate = resolve(root, relativeFile); if (!relativeFile || !candidate.startsWith(`${root}/`) || isAbsolute(relativeFile) || !(await fileExists(candidate))) { provider.missingFiles += 1; missingFiles += 1; providers.set(hostname, provider); continue; } const fileInfo = await stat(candidate); presentFiles += 1; provider.presentFiles += 1; bytesOnDisk += fileInfo.size; providers.set(hostname, provider); } const report = { kind: "nodedc-legacy-cesium-cache-audit", generatedAt: new Date().toISOString(), manifestPath: path, manifestVersion: manifest?.version ?? null, entries: entries.length, declaredBytes: bytesFromManifest, declaredMegabytes: toMegabytes(bytesFromManifest), presentFiles, missingFiles, bytesOnDisk, megabytesOnDisk: toMegabytes(bytesOnDisk), providers: [...providers.entries()] .map(([hostname, value]) => ({ hostname, ...value, megabytes: toMegabytes(value.bytes) })) .sort((left, right) => right.bytes - left.bytes), migrationPolicy: { automaticImport: false, reason: "Audit only. Import requires an explicit licence/provider decision for every source host and must preserve required attribution.", target: "Map Gateway persistent volume or approved object storage; never Git history or a Docker image layer." } }; process.stdout.write(`${JSON.stringify(report, null, 2)}\n`); } async function fileExists(path) { try { await access(path); return true; } catch { return false; } } function toMegabytes(bytes) { return Number((bytes / 1024 / 1024).toFixed(2)); }