Files
git.stella-ops.org/plugins/scanner/node/runtime-hooks/runtime-esm-loader.mjs
StellaOps Bot 909d9b6220
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
up
2025-12-01 21:16:22 +02:00

62 lines
1.5 KiB
JavaScript

// Runtime ESM loader for StellaOps Scanner runtime evidence
// Usage: node --experimental-loader=./runtime-esm-loader.mjs app.mjs
import fs from 'fs';
import path from 'path';
import crypto from 'crypto';
import { fileURLToPath, pathToFileURL } from 'url';
const outPath = process.env.SCANNER_NODE_RUNTIME_OUT || path.join(process.cwd(), 'node-runtime-evidence.ndjson');
const root = process.env.SCANNER_NODE_ROOT || process.cwd();
const loaderId = hashLoaderId(import.meta.url);
function hashLoaderId(value) {
return crypto.createHash('sha256').update(value || '').digest('hex');
}
function scrub(p) {
if (!p) return p;
try {
const absolute = p.startsWith('file:') ? fileURLToPath(p) : p;
const rel = path.relative(root, absolute);
return rel.startsWith('..') ? p : rel.split(path.sep).join('/');
} catch {
return p;
}
}
function emit(record) {
try {
fs.appendFileSync(outPath, JSON.stringify(record) + '\n');
} catch {
// best-effort: ignore write failures
}
}
export async function resolve(specifier, context, next) {
const parent = context.parentURL ? scrub(context.parentURL) : undefined;
const target = scrub(specifier);
emit({
type: 'edge',
from: parent,
to: target,
reason: 'runtime-import',
loaderId
});
return next(specifier, context, next);
}
export async function load(url, context, next) {
const pathOrUrl = scrub(url);
emit({
type: 'component',
path: pathOrUrl,
reason: 'runtime-load',
loaderId
});
return next(url, context, next);
}