style: apply repo formatter to the security-fix files (no behavior change)

This commit is contained in:
Affaan Mustafa 2026-06-18 20:03:24 -04:00
parent bd9083ca1e
commit ed251f958a
3 changed files with 209 additions and 295 deletions

View File

@ -25,7 +25,7 @@ function parseArgs(argv) {
repoRoot: null, repoRoot: null,
dryRun: false, dryRun: false,
json: false, json: false,
help: false, help: false
}; };
for (let index = 0; index < args.length; index += 1) { for (let index = 0; index < args.length; index += 1) {
@ -63,9 +63,7 @@ function deriveRepoRootFromState(state) {
continue; continue;
} }
const relativeParts = operation.sourceRelativePath const relativeParts = operation.sourceRelativePath.split(/[\\/]+/).filter(Boolean);
.split(/[\\/]+/)
.filter(Boolean);
if (relativeParts.length === 0) { if (relativeParts.length === 0) {
continue; continue;
@ -149,9 +147,7 @@ function validateRepoRoot(repoRoot) {
throw new Error(`Invalid ECC repo root: unreadable package.json at ${packageJsonPath}`); throw new Error(`Invalid ECC repo root: unreadable package.json at ${packageJsonPath}`);
} }
if (!ECC_PACKAGE_NAMES.has(pkgName)) { if (!ECC_PACKAGE_NAMES.has(pkgName)) {
throw new Error( throw new Error(`Refusing to run install from untrusted repo root ${normalized}: package.json name '${pkgName}' is not an official ECC package.`);
`Refusing to run install from untrusted repo root ${normalized}: package.json name '${pkgName}' is not an official ECC package.`
);
} }
return normalized; return normalized;
@ -162,7 +158,7 @@ function runExternalCommand(command, args, options = {}) {
cwd: options.cwd, cwd: options.cwd,
env: options.env || process.env, env: options.env || process.env,
encoding: 'utf8', encoding: 'utf8',
maxBuffer: 10 * 1024 * 1024, maxBuffer: 10 * 1024 * 1024
}); });
if (result.error) { if (result.error) {
@ -186,7 +182,7 @@ function runAutoUpdate(options = {}, dependencies = {}) {
const records = discover({ const records = discover({
homeDir, homeDir,
projectRoot, projectRoot,
targets: options.targets, targets: options.targets
}).filter(record => record.exists); }).filter(record => record.exists);
const results = []; const results = [];
@ -198,8 +194,8 @@ function runAutoUpdate(options = {}, dependencies = {}) {
summary: { summary: {
checkedCount: 0, checkedCount: 0,
updatedCount: 0, updatedCount: 0,
errorCount: 0, errorCount: 0
}, }
}; };
} }
@ -211,7 +207,7 @@ function runAutoUpdate(options = {}, dependencies = {}) {
adapter: record.adapter, adapter: record.adapter,
installStatePath: record.installStatePath, installStatePath: record.installStatePath,
status: 'error', status: 'error',
error: record.error || 'No valid install-state available', error: record.error || 'No valid install-state available'
}); });
continue; continue;
} }
@ -220,7 +216,7 @@ function runAutoUpdate(options = {}, dependencies = {}) {
inferredRepoRoots.push(recordRepoRoot); inferredRepoRoots.push(recordRepoRoot);
validRecords.push({ validRecords.push({
record, record,
repoRoot: recordRepoRoot, repoRoot: recordRepoRoot
}); });
} }
@ -240,15 +236,15 @@ function runAutoUpdate(options = {}, dependencies = {}) {
summary: { summary: {
checkedCount: results.length, checkedCount: results.length,
updatedCount: 0, updatedCount: 0,
errorCount: results.length, errorCount: results.length
}, }
}; };
} }
const env = { const env = {
...process.env, ...process.env,
HOME: homeDir, HOME: homeDir,
USERPROFILE: homeDir, USERPROFILE: homeDir
}; };
if (!options.dryRun) { if (!options.dryRun) {
@ -258,11 +254,7 @@ function runAutoUpdate(options = {}, dependencies = {}) {
for (const entry of validRecords) { for (const entry of validRecords) {
const installArgs = buildInstallApplyArgs(entry.record); const installArgs = buildInstallApplyArgs(entry.record);
const args = [ const args = [path.join(repoRoot, 'scripts', 'install-apply.js'), ...installArgs, '--json'];
path.join(repoRoot, 'scripts', 'install-apply.js'),
...installArgs,
'--json',
];
if (options.dryRun) { if (options.dryRun) {
args.push('--dry-run'); args.push('--dry-run');
@ -271,7 +263,7 @@ function runAutoUpdate(options = {}, dependencies = {}) {
try { try {
const commandResult = execute(process.execPath, args, { const commandResult = execute(process.execPath, args, {
cwd: determineInstallCwd(entry.record, repoRoot), cwd: determineInstallCwd(entry.record, repoRoot),
env, env
}); });
let payload = null; let payload = null;
@ -286,7 +278,7 @@ function runAutoUpdate(options = {}, dependencies = {}) {
cwd: determineInstallCwd(entry.record, repoRoot), cwd: determineInstallCwd(entry.record, repoRoot),
installArgs, installArgs,
status: options.dryRun ? 'planned' : 'updated', status: options.dryRun ? 'planned' : 'updated',
payload, payload
}); });
} catch (error) { } catch (error) {
results.push({ results.push({
@ -295,7 +287,7 @@ function runAutoUpdate(options = {}, dependencies = {}) {
repoRoot, repoRoot,
installArgs, installArgs,
status: 'error', status: 'error',
error: error.message, error: error.message
}); });
} }
} }
@ -307,8 +299,8 @@ function runAutoUpdate(options = {}, dependencies = {}) {
summary: { summary: {
checkedCount: results.length, checkedCount: results.length,
updatedCount: results.filter(result => result.status === 'updated' || result.status === 'planned').length, updatedCount: results.filter(result => result.status === 'updated' || result.status === 'planned').length,
errorCount: results.filter(result => result.status === 'error').length, errorCount: results.filter(result => result.status === 'error').length
}, }
}; };
} }
@ -350,7 +342,7 @@ function main() {
projectRoot: process.cwd(), projectRoot: process.cwd(),
targets: options.targets, targets: options.targets,
repoRoot: options.repoRoot, repoRoot: options.repoRoot,
dryRun: options.dryRun, dryRun: options.dryRun
}); });
if (options.json) { if (options.json) {
@ -375,5 +367,5 @@ module.exports = {
deriveRepoRootFromState, deriveRepoRootFromState,
buildInstallApplyArgs, buildInstallApplyArgs,
determineInstallCwd, determineInstallCwd,
runAutoUpdate, runAutoUpdate
}; };

View File

@ -5,13 +5,8 @@ const path = require('path');
const { resolveInstallPlan, loadInstallManifests } = require('./install-manifests'); const { resolveInstallPlan, loadInstallManifests } = require('./install-manifests');
const { readInstallState, writeInstallState } = require('./install-state'); const { readInstallState, writeInstallState } = require('./install-state');
const { assertWithinTrustedRoot } = require('./path-safety'); const { assertWithinTrustedRoot } = require('./path-safety');
const { const { createManifestInstallPlan } = require('./install-executor');
createManifestInstallPlan, const { getInstallTargetAdapter, listInstallTargetAdapters } = require('./install-targets/registry');
} = require('./install-executor');
const {
getInstallTargetAdapter,
listInstallTargetAdapters,
} = require('./install-targets/registry');
const DEFAULT_REPO_ROOT = path.join(__dirname, '../..'); const DEFAULT_REPO_ROOT = path.join(__dirname, '../..');
@ -52,9 +47,7 @@ function compareStringArrays(left, right) {
} }
function getManagedOperations(state) { function getManagedOperations(state) {
return Array.isArray(state && state.operations) return Array.isArray(state && state.operations) ? state.operations.filter(operation => operation.ownership === 'managed') : [];
? state.operations.filter(operation => operation.ownership === 'managed')
: [];
} }
function resolveOperationSourcePath(repoRoot, operation) { function resolveOperationSourcePath(repoRoot, operation) {
@ -116,13 +109,7 @@ function parseJsonLikeValue(value, label) {
} }
function getOperationTextContent(operation) { function getOperationTextContent(operation) {
const candidateKeys = [ const candidateKeys = ['renderedContent', 'content', 'managedContent', 'expectedContent', 'templateOutput'];
'renderedContent',
'content',
'managedContent',
'expectedContent',
'templateOutput',
];
for (const key of candidateKeys) { for (const key of candidateKeys) {
if (typeof operation[key] === 'string') { if (typeof operation[key] === 'string') {
@ -134,13 +121,7 @@ function getOperationTextContent(operation) {
} }
function getOperationJsonPayload(operation) { function getOperationJsonPayload(operation) {
const candidateKeys = [ const candidateKeys = ['mergePayload', 'managedPayload', 'payload', 'value', 'expectedValue'];
'mergePayload',
'managedPayload',
'payload',
'value',
'expectedValue',
];
for (const key of candidateKeys) { for (const key of candidateKeys) {
if (operation[key] !== undefined) { if (operation[key] !== undefined) {
@ -152,11 +133,7 @@ function getOperationJsonPayload(operation) {
} }
function getOperationPreviousContent(operation) { function getOperationPreviousContent(operation) {
const candidateKeys = [ const candidateKeys = ['previousContent', 'originalContent', 'backupContent'];
'previousContent',
'originalContent',
'backupContent',
];
for (const key of candidateKeys) { for (const key of candidateKeys) {
if (typeof operation[key] === 'string') { if (typeof operation[key] === 'string') {
@ -168,11 +145,7 @@ function getOperationPreviousContent(operation) {
} }
function getOperationPreviousJson(operation) { function getOperationPreviousJson(operation) {
const candidateKeys = [ const candidateKeys = ['previousValue', 'previousJson', 'originalValue'];
'previousValue',
'previousJson',
'originalValue',
];
for (const key of candidateKeys) { for (const key of candidateKeys) {
if (operation[key] !== undefined) { if (operation[key] !== undefined) {
@ -217,10 +190,7 @@ function jsonContainsSubset(actualValue, expectedValue) {
return false; return false;
} }
return Object.entries(expectedValue).every(([key, value]) => ( return Object.entries(expectedValue).every(([key, value]) => Object.prototype.hasOwnProperty.call(actualValue, key) && jsonContainsSubset(actualValue[key], value));
Object.prototype.hasOwnProperty.call(actualValue, key)
&& jsonContainsSubset(actualValue[key], value)
));
} }
if (Array.isArray(expectedValue)) { if (Array.isArray(expectedValue)) {
@ -288,7 +258,7 @@ function hydrateRecordedOperations(repoRoot, operations) {
return { return {
...operation, ...operation,
sourcePath: resolveOperationSourcePath(repoRoot, operation), sourcePath: resolveOperationSourcePath(repoRoot, operation)
}; };
}); });
} }
@ -300,9 +270,9 @@ function buildRecordedStatePreview(state, context, operations) {
source: { source: {
...state.source, ...state.source,
repoVersion: context.packageVersion, repoVersion: context.packageVersion,
manifestVersion: context.manifestVersion, manifestVersion: context.manifestVersion
}, },
lastValidatedAt: new Date().toISOString(), lastValidatedAt: new Date().toISOString()
}; };
} }
@ -344,9 +314,7 @@ function executeRepairOperation(repoRoot, operation, trustedRoot) {
throw new Error(`Missing merge payload for repair: ${operation.destinationPath}`); throw new Error(`Missing merge payload for repair: ${operation.destinationPath}`);
} }
const currentValue = fs.existsSync(operation.destinationPath) const currentValue = fs.existsSync(operation.destinationPath) ? readJsonFile(operation.destinationPath) : {};
? readJsonFile(operation.destinationPath)
: {};
const mergedValue = deepMergeJson(currentValue, payload); const mergedValue = deepMergeJson(currentValue, payload);
ensureParentDir(operation.destinationPath); ensureParentDir(operation.destinationPath);
@ -374,14 +342,14 @@ function executeUninstallOperation(operation, trustedRoot) {
if (!fs.existsSync(operation.destinationPath)) { if (!fs.existsSync(operation.destinationPath)) {
return { return {
removedPaths: [], removedPaths: [],
cleanupTargets: [], cleanupTargets: []
}; };
} }
fs.rmSync(operation.destinationPath, { force: true }); fs.rmSync(operation.destinationPath, { force: true });
return { return {
removedPaths: [operation.destinationPath], removedPaths: [operation.destinationPath],
cleanupTargets: [operation.destinationPath], cleanupTargets: [operation.destinationPath]
}; };
} }
@ -392,7 +360,7 @@ function executeUninstallOperation(operation, trustedRoot) {
fs.writeFileSync(operation.destinationPath, previousContent); fs.writeFileSync(operation.destinationPath, previousContent);
return { return {
removedPaths: [], removedPaths: [],
cleanupTargets: [], cleanupTargets: []
}; };
} }
@ -402,21 +370,21 @@ function executeUninstallOperation(operation, trustedRoot) {
fs.writeFileSync(operation.destinationPath, formatJson(previousJson)); fs.writeFileSync(operation.destinationPath, formatJson(previousJson));
return { return {
removedPaths: [], removedPaths: [],
cleanupTargets: [], cleanupTargets: []
}; };
} }
if (!fs.existsSync(operation.destinationPath)) { if (!fs.existsSync(operation.destinationPath)) {
return { return {
removedPaths: [], removedPaths: [],
cleanupTargets: [], cleanupTargets: []
}; };
} }
fs.rmSync(operation.destinationPath, { force: true }); fs.rmSync(operation.destinationPath, { force: true });
return { return {
removedPaths: [operation.destinationPath], removedPaths: [operation.destinationPath],
cleanupTargets: [operation.destinationPath], cleanupTargets: [operation.destinationPath]
}; };
} }
@ -427,7 +395,7 @@ function executeUninstallOperation(operation, trustedRoot) {
fs.writeFileSync(operation.destinationPath, previousContent); fs.writeFileSync(operation.destinationPath, previousContent);
return { return {
removedPaths: [], removedPaths: [],
cleanupTargets: [], cleanupTargets: []
}; };
} }
@ -437,14 +405,14 @@ function executeUninstallOperation(operation, trustedRoot) {
fs.writeFileSync(operation.destinationPath, formatJson(previousJson)); fs.writeFileSync(operation.destinationPath, formatJson(previousJson));
return { return {
removedPaths: [], removedPaths: [],
cleanupTargets: [], cleanupTargets: []
}; };
} }
if (!fs.existsSync(operation.destinationPath)) { if (!fs.existsSync(operation.destinationPath)) {
return { return {
removedPaths: [], removedPaths: [],
cleanupTargets: [], cleanupTargets: []
}; };
} }
@ -459,7 +427,7 @@ function executeUninstallOperation(operation, trustedRoot) {
fs.rmSync(operation.destinationPath, { force: true }); fs.rmSync(operation.destinationPath, { force: true });
return { return {
removedPaths: [operation.destinationPath], removedPaths: [operation.destinationPath],
cleanupTargets: [operation.destinationPath], cleanupTargets: [operation.destinationPath]
}; };
} }
@ -467,7 +435,7 @@ function executeUninstallOperation(operation, trustedRoot) {
fs.writeFileSync(operation.destinationPath, formatJson(nextValue)); fs.writeFileSync(operation.destinationPath, formatJson(nextValue));
return { return {
removedPaths: [], removedPaths: [],
cleanupTargets: [], cleanupTargets: []
}; };
} }
@ -478,7 +446,7 @@ function executeUninstallOperation(operation, trustedRoot) {
fs.writeFileSync(operation.destinationPath, previousContent); fs.writeFileSync(operation.destinationPath, previousContent);
return { return {
removedPaths: [], removedPaths: [],
cleanupTargets: [], cleanupTargets: []
}; };
} }
@ -488,13 +456,13 @@ function executeUninstallOperation(operation, trustedRoot) {
fs.writeFileSync(operation.destinationPath, formatJson(previousJson)); fs.writeFileSync(operation.destinationPath, formatJson(previousJson));
return { return {
removedPaths: [], removedPaths: [],
cleanupTargets: [], cleanupTargets: []
}; };
} }
return { return {
removedPaths: [], removedPaths: [],
cleanupTargets: [], cleanupTargets: []
}; };
} }
@ -506,7 +474,7 @@ function inspectManagedOperation(repoRoot, operation) {
if (!destinationPath) { if (!destinationPath) {
return { return {
status: 'invalid-destination', status: 'invalid-destination',
operation, operation
}; };
} }
@ -515,14 +483,14 @@ function inspectManagedOperation(repoRoot, operation) {
return { return {
status: 'drifted', status: 'drifted',
operation, operation,
destinationPath, destinationPath
}; };
} }
return { return {
status: 'ok', status: 'ok',
operation, operation,
destinationPath, destinationPath
}; };
} }
@ -530,7 +498,7 @@ function inspectManagedOperation(repoRoot, operation) {
return { return {
status: 'missing', status: 'missing',
operation, operation,
destinationPath, destinationPath
}; };
} }
@ -541,7 +509,7 @@ function inspectManagedOperation(repoRoot, operation) {
status: 'missing-source', status: 'missing-source',
operation, operation,
destinationPath, destinationPath,
sourcePath, sourcePath
}; };
} }
@ -550,7 +518,7 @@ function inspectManagedOperation(repoRoot, operation) {
status: 'drifted', status: 'drifted',
operation, operation,
destinationPath, destinationPath,
sourcePath, sourcePath
}; };
} }
@ -558,7 +526,7 @@ function inspectManagedOperation(repoRoot, operation) {
status: 'ok', status: 'ok',
operation, operation,
destinationPath, destinationPath,
sourcePath, sourcePath
}; };
} }
@ -568,7 +536,7 @@ function inspectManagedOperation(repoRoot, operation) {
return { return {
status: 'unverified', status: 'unverified',
operation, operation,
destinationPath, destinationPath
}; };
} }
@ -576,14 +544,14 @@ function inspectManagedOperation(repoRoot, operation) {
return { return {
status: 'drifted', status: 'drifted',
operation, operation,
destinationPath, destinationPath
}; };
} }
return { return {
status: 'ok', status: 'ok',
operation, operation,
destinationPath, destinationPath
}; };
} }
@ -593,7 +561,7 @@ function inspectManagedOperation(repoRoot, operation) {
return { return {
status: 'unverified', status: 'unverified',
operation, operation,
destinationPath, destinationPath
}; };
} }
@ -603,57 +571,60 @@ function inspectManagedOperation(repoRoot, operation) {
return { return {
status: 'drifted', status: 'drifted',
operation, operation,
destinationPath, destinationPath
}; };
} }
} catch (_error) { } catch (_error) {
return { return {
status: 'drifted', status: 'drifted',
operation, operation,
destinationPath, destinationPath
}; };
} }
return { return {
status: 'ok', status: 'ok',
operation, operation,
destinationPath, destinationPath
}; };
} }
return { return {
status: 'unverified', status: 'unverified',
operation, operation,
destinationPath, destinationPath
}; };
} }
function summarizeManagedOperationHealth(repoRoot, operations) { function summarizeManagedOperationHealth(repoRoot, operations) {
return operations.reduce((summary, operation) => { return operations.reduce(
const inspection = inspectManagedOperation(repoRoot, operation); (summary, operation) => {
if (inspection.status === 'missing') { const inspection = inspectManagedOperation(repoRoot, operation);
summary.missing.push(inspection); if (inspection.status === 'missing') {
} else if (inspection.status === 'drifted') { summary.missing.push(inspection);
summary.drifted.push(inspection); } else if (inspection.status === 'drifted') {
} else if (inspection.status === 'missing-source') { summary.drifted.push(inspection);
summary.missingSource.push(inspection); } else if (inspection.status === 'missing-source') {
} else if (inspection.status === 'unverified' || inspection.status === 'invalid-destination') { summary.missingSource.push(inspection);
summary.unverified.push(inspection); } else if (inspection.status === 'unverified' || inspection.status === 'invalid-destination') {
summary.unverified.push(inspection);
}
return summary;
},
{
missing: [],
drifted: [],
missingSource: [],
unverified: []
} }
return summary; );
}, {
missing: [],
drifted: [],
missingSource: [],
unverified: [],
});
} }
function buildDiscoveryRecord(adapter, context) { function buildDiscoveryRecord(adapter, context) {
const installTargetInput = { const installTargetInput = {
homeDir: context.homeDir, homeDir: context.homeDir,
projectRoot: context.projectRoot, projectRoot: context.projectRoot,
repoRoot: context.projectRoot, repoRoot: context.projectRoot
}; };
const targetRoot = adapter.resolveRoot(installTargetInput); const targetRoot = adapter.resolveRoot(installTargetInput);
const installStatePath = adapter.getInstallStatePath(installTargetInput); const installStatePath = adapter.getInstallStatePath(installTargetInput);
@ -664,13 +635,13 @@ function buildDiscoveryRecord(adapter, context) {
adapter: { adapter: {
id: adapter.id, id: adapter.id,
target: adapter.target, target: adapter.target,
kind: adapter.kind, kind: adapter.kind
}, },
targetRoot, targetRoot,
installStatePath, installStatePath,
exists: false, exists: false,
state: null, state: null,
error: null, error: null
}; };
} }
@ -680,26 +651,26 @@ function buildDiscoveryRecord(adapter, context) {
adapter: { adapter: {
id: adapter.id, id: adapter.id,
target: adapter.target, target: adapter.target,
kind: adapter.kind, kind: adapter.kind
}, },
targetRoot, targetRoot,
installStatePath, installStatePath,
exists: true, exists: true,
state, state,
error: null, error: null
}; };
} catch (error) { } catch (error) {
return { return {
adapter: { adapter: {
id: adapter.id, id: adapter.id,
target: adapter.target, target: adapter.target,
kind: adapter.kind, kind: adapter.kind
}, },
targetRoot, targetRoot,
installStatePath, installStatePath,
exists: true, exists: true,
state: null, state: null,
error: error.message, error: error.message
}; };
} }
} }
@ -707,7 +678,7 @@ function buildDiscoveryRecord(adapter, context) {
function discoverInstalledStates(options = {}) { function discoverInstalledStates(options = {}) {
const context = { const context = {
homeDir: options.homeDir || process.env.HOME || os.homedir(), homeDir: options.homeDir || process.env.HOME || os.homedir(),
projectRoot: options.projectRoot || process.cwd(), projectRoot: options.projectRoot || process.cwd()
}; };
const targets = normalizeTargets(options.targets); const targets = normalizeTargets(options.targets);
@ -722,7 +693,7 @@ function buildIssue(severity, code, message, extra = {}) {
severity, severity,
code, code,
message, message,
...extra, ...extra
}; };
} }
@ -746,7 +717,7 @@ function analyzeRecord(record, context) {
return { return {
...record, ...record,
status: determineStatus(issues), status: determineStatus(issues),
issues, issues
}; };
} }
@ -755,40 +726,30 @@ function analyzeRecord(record, context) {
return { return {
...record, ...record,
status: 'missing', status: 'missing',
issues, issues
}; };
} }
if (!fs.existsSync(state.target.root)) { if (!fs.existsSync(state.target.root)) {
issues.push(buildIssue( issues.push(buildIssue('error', 'missing-target-root', `Target root does not exist: ${state.target.root}`));
'error',
'missing-target-root',
`Target root does not exist: ${state.target.root}`
));
} }
if (state.target.root !== record.targetRoot) { if (state.target.root !== record.targetRoot) {
issues.push(buildIssue( issues.push(
'warning', buildIssue('warning', 'target-root-mismatch', `Recorded target root differs from current target root (${record.targetRoot})`, {
'target-root-mismatch',
`Recorded target root differs from current target root (${record.targetRoot})`,
{
recordedTargetRoot: state.target.root, recordedTargetRoot: state.target.root,
currentTargetRoot: record.targetRoot, currentTargetRoot: record.targetRoot
} })
)); );
} }
if (state.target.installStatePath !== record.installStatePath) { if (state.target.installStatePath !== record.installStatePath) {
issues.push(buildIssue( issues.push(
'warning', buildIssue('warning', 'install-state-path-mismatch', `Recorded install-state path differs from current path (${record.installStatePath})`, {
'install-state-path-mismatch',
`Recorded install-state path differs from current path (${record.installStatePath})`,
{
recordedInstallStatePath: state.target.installStatePath, recordedInstallStatePath: state.target.installStatePath,
currentInstallStatePath: record.installStatePath, currentInstallStatePath: record.installStatePath
} })
)); );
} }
const managedOperations = getManagedOperations(state); const managedOperations = getManagedOperations(state);
@ -796,67 +757,43 @@ function analyzeRecord(record, context) {
const missingManagedOperations = operationHealth.missing; const missingManagedOperations = operationHealth.missing;
if (missingManagedOperations.length > 0) { if (missingManagedOperations.length > 0) {
issues.push(buildIssue( issues.push(
'error', buildIssue('error', 'missing-managed-files', `${missingManagedOperations.length} managed file(s) are missing`, {
'missing-managed-files', paths: missingManagedOperations.map(entry => entry.destinationPath)
`${missingManagedOperations.length} managed file(s) are missing`, })
{ );
paths: missingManagedOperations.map(entry => entry.destinationPath),
}
));
} }
if (operationHealth.drifted.length > 0) { if (operationHealth.drifted.length > 0) {
issues.push(buildIssue( issues.push(
'warning', buildIssue('warning', 'drifted-managed-files', `${operationHealth.drifted.length} managed file(s) differ from the source repo`, {
'drifted-managed-files', paths: operationHealth.drifted.map(entry => entry.destinationPath)
`${operationHealth.drifted.length} managed file(s) differ from the source repo`, })
{ );
paths: operationHealth.drifted.map(entry => entry.destinationPath),
}
));
} }
if (operationHealth.missingSource.length > 0) { if (operationHealth.missingSource.length > 0) {
issues.push(buildIssue( issues.push(
'error', buildIssue('error', 'missing-source-files', `${operationHealth.missingSource.length} source file(s) referenced by install-state are missing`, {
'missing-source-files', paths: operationHealth.missingSource.map(entry => entry.sourcePath).filter(Boolean)
`${operationHealth.missingSource.length} source file(s) referenced by install-state are missing`, })
{ );
paths: operationHealth.missingSource.map(entry => entry.sourcePath).filter(Boolean),
}
));
} }
if (operationHealth.unverified.length > 0) { if (operationHealth.unverified.length > 0) {
issues.push(buildIssue( issues.push(
'warning', buildIssue('warning', 'unverified-managed-operations', `${operationHealth.unverified.length} managed operation(s) could not be content-verified`, {
'unverified-managed-operations', paths: operationHealth.unverified.map(entry => entry.destinationPath).filter(Boolean)
`${operationHealth.unverified.length} managed operation(s) could not be content-verified`, })
{ );
paths: operationHealth.unverified.map(entry => entry.destinationPath).filter(Boolean),
}
));
} }
if (state.source.manifestVersion !== context.manifestVersion) { if (state.source.manifestVersion !== context.manifestVersion) {
issues.push(buildIssue( issues.push(buildIssue('warning', 'manifest-version-mismatch', `Recorded manifest version ${state.source.manifestVersion} differs from current manifest version ${context.manifestVersion}`));
'warning',
'manifest-version-mismatch',
`Recorded manifest version ${state.source.manifestVersion} differs from current manifest version ${context.manifestVersion}`
));
} }
if ( if (context.packageVersion && state.source.repoVersion && state.source.repoVersion !== context.packageVersion) {
context.packageVersion issues.push(buildIssue('warning', 'repo-version-mismatch', `Recorded repo version ${state.source.repoVersion} differs from current repo version ${context.packageVersion}`));
&& state.source.repoVersion
&& state.source.repoVersion !== context.packageVersion
) {
issues.push(buildIssue(
'warning',
'repo-version-mismatch',
`Recorded repo version ${state.source.repoVersion} differs from current repo version ${context.packageVersion}`
));
} }
if (!state.request.legacyMode) { if (!state.request.legacyMode) {
@ -869,38 +806,28 @@ function analyzeRecord(record, context) {
profileId: state.request.profile || null, profileId: state.request.profile || null,
moduleIds: state.request.modules || [], moduleIds: state.request.modules || [],
includeComponentIds: state.request.includeComponents || [], includeComponentIds: state.request.includeComponents || [],
excludeComponentIds: state.request.excludeComponents || [], excludeComponentIds: state.request.excludeComponents || []
}); });
if ( if (!compareStringArrays(desiredPlan.selectedModuleIds, state.resolution.selectedModules) || !compareStringArrays(desiredPlan.skippedModuleIds, state.resolution.skippedModules)) {
!compareStringArrays(desiredPlan.selectedModuleIds, state.resolution.selectedModules) issues.push(
|| !compareStringArrays(desiredPlan.skippedModuleIds, state.resolution.skippedModules) buildIssue('warning', 'resolution-drift', 'Current manifest resolution differs from recorded install-state', {
) {
issues.push(buildIssue(
'warning',
'resolution-drift',
'Current manifest resolution differs from recorded install-state',
{
expectedSelectedModules: desiredPlan.selectedModuleIds, expectedSelectedModules: desiredPlan.selectedModuleIds,
recordedSelectedModules: state.resolution.selectedModules, recordedSelectedModules: state.resolution.selectedModules,
expectedSkippedModules: desiredPlan.skippedModuleIds, expectedSkippedModules: desiredPlan.skippedModuleIds,
recordedSkippedModules: state.resolution.skippedModules, recordedSkippedModules: state.resolution.skippedModules
} })
)); );
} }
} catch (error) { } catch (error) {
issues.push(buildIssue( issues.push(buildIssue('error', 'resolution-unavailable', error.message));
'error',
'resolution-unavailable',
error.message
));
} }
} }
return { return {
...record, ...record,
status: determineStatus(issues), status: determineStatus(issues),
issues, issues
}; };
} }
@ -910,39 +837,42 @@ function buildDoctorReport(options = {}) {
const records = discoverInstalledStates({ const records = discoverInstalledStates({
homeDir: options.homeDir, homeDir: options.homeDir,
projectRoot: options.projectRoot, projectRoot: options.projectRoot,
targets: options.targets, targets: options.targets
}).filter(record => record.exists); }).filter(record => record.exists);
const context = { const context = {
repoRoot, repoRoot,
homeDir: options.homeDir || process.env.HOME || os.homedir(), homeDir: options.homeDir || process.env.HOME || os.homedir(),
projectRoot: options.projectRoot || process.cwd(), projectRoot: options.projectRoot || process.cwd(),
manifestVersion: manifests.modulesVersion, manifestVersion: manifests.modulesVersion,
packageVersion: readPackageVersion(repoRoot), packageVersion: readPackageVersion(repoRoot)
}; };
const results = records.map(record => analyzeRecord(record, context)); const results = records.map(record => analyzeRecord(record, context));
const summary = results.reduce((accumulator, result) => { const summary = results.reduce(
const errorCount = result.issues.filter(issue => issue.severity === 'error').length; (accumulator, result) => {
const warningCount = result.issues.filter(issue => issue.severity === 'warning').length; const errorCount = result.issues.filter(issue => issue.severity === 'error').length;
const warningCount = result.issues.filter(issue => issue.severity === 'warning').length;
return { return {
checkedCount: accumulator.checkedCount + 1, checkedCount: accumulator.checkedCount + 1,
okCount: accumulator.okCount + (result.status === 'ok' ? 1 : 0), okCount: accumulator.okCount + (result.status === 'ok' ? 1 : 0),
errorCount: accumulator.errorCount + errorCount, errorCount: accumulator.errorCount + errorCount,
warningCount: accumulator.warningCount + warningCount, warningCount: accumulator.warningCount + warningCount
}; };
}, { },
checkedCount: 0, {
okCount: 0, checkedCount: 0,
errorCount: 0, okCount: 0,
warningCount: 0, errorCount: 0,
}); warningCount: 0
}
);
return { return {
generatedAt: new Date().toISOString(), generatedAt: new Date().toISOString(),
packageVersion: context.packageVersion, packageVersion: context.packageVersion,
manifestVersion: context.manifestVersion, manifestVersion: context.manifestVersion,
results, results,
summary, summary
}; };
} }
@ -964,11 +894,9 @@ function createRepairPlanFromRecord(record, context) {
installRoot: state.target.root, installRoot: state.target.root,
installStatePath: state.target.installStatePath, installStatePath: state.target.installStatePath,
warnings: [], warnings: [],
languages: Array.isArray(state.request.legacyLanguages) languages: Array.isArray(state.request.legacyLanguages) ? [...state.request.legacyLanguages] : [],
? [...state.request.legacyLanguages]
: [],
operations, operations,
statePreview, statePreview
}; };
} }
@ -980,7 +908,7 @@ function createRepairPlanFromRecord(record, context) {
includeComponentIds: state.request.includeComponents || [], includeComponentIds: state.request.includeComponents || [],
excludeComponentIds: state.request.excludeComponents || [], excludeComponentIds: state.request.excludeComponents || [],
projectRoot: context.projectRoot, projectRoot: context.projectRoot,
homeDir: context.homeDir, homeDir: context.homeDir
}); });
return { return {
@ -988,8 +916,8 @@ function createRepairPlanFromRecord(record, context) {
statePreview: { statePreview: {
...desiredPlan.statePreview, ...desiredPlan.statePreview,
installedAt: state.installedAt, installedAt: state.installedAt,
lastValidatedAt: new Date().toISOString(), lastValidatedAt: new Date().toISOString()
}, }
}; };
} }
@ -1001,12 +929,12 @@ function repairInstalledStates(options = {}) {
homeDir: options.homeDir || process.env.HOME || os.homedir(), homeDir: options.homeDir || process.env.HOME || os.homedir(),
projectRoot: options.projectRoot || process.cwd(), projectRoot: options.projectRoot || process.cwd(),
manifestVersion: manifests.modulesVersion, manifestVersion: manifests.modulesVersion,
packageVersion: readPackageVersion(repoRoot), packageVersion: readPackageVersion(repoRoot)
}; };
const records = discoverInstalledStates({ const records = discoverInstalledStates({
homeDir: context.homeDir, homeDir: context.homeDir,
projectRoot: context.projectRoot, projectRoot: context.projectRoot,
targets: options.targets, targets: options.targets
}).filter(record => record.exists); }).filter(record => record.exists);
const results = records.map(record => { const results = records.map(record => {
@ -1017,7 +945,7 @@ function repairInstalledStates(options = {}) {
installStatePath: record.installStatePath, installStatePath: record.installStatePath,
repairedPaths: [], repairedPaths: [],
plannedRepairs: [], plannedRepairs: [],
error: record.error, error: record.error
}; };
} }
@ -1032,14 +960,11 @@ function repairInstalledStates(options = {}) {
installStatePath: record.installStatePath, installStatePath: record.installStatePath,
repairedPaths: [], repairedPaths: [],
plannedRepairs: [], plannedRepairs: [],
error: `Missing source file(s): ${operationHealth.missingSource.map(entry => entry.sourcePath).join(', ')}`, error: `Missing source file(s): ${operationHealth.missingSource.map(entry => entry.sourcePath).join(', ')}`
}; };
} }
const repairOperations = [ const repairOperations = [...operationHealth.missing.map(entry => ({ ...entry.operation })), ...operationHealth.drifted.map(entry => ({ ...entry.operation }))];
...operationHealth.missing.map(entry => ({ ...entry.operation })),
...operationHealth.drifted.map(entry => ({ ...entry.operation })),
];
const plannedRepairs = repairOperations.map(operation => operation.destinationPath); const plannedRepairs = repairOperations.map(operation => operation.destinationPath);
if (options.dryRun) { if (options.dryRun) {
@ -1050,7 +975,7 @@ function repairInstalledStates(options = {}) {
repairedPaths: [], repairedPaths: [],
plannedRepairs, plannedRepairs,
stateRefreshed: plannedRepairs.length === 0, stateRefreshed: plannedRepairs.length === 0,
error: null, error: null
}; };
} }
@ -1070,7 +995,7 @@ function repairInstalledStates(options = {}) {
repairedPaths: plannedRepairs, repairedPaths: plannedRepairs,
plannedRepairs: [], plannedRepairs: [],
stateRefreshed: true, stateRefreshed: true,
error: null, error: null
}; };
} catch (error) { } catch (error) {
return { return {
@ -1079,28 +1004,31 @@ function repairInstalledStates(options = {}) {
installStatePath: record.installStatePath, installStatePath: record.installStatePath,
repairedPaths: [], repairedPaths: [],
plannedRepairs: [], plannedRepairs: [],
error: error.message, error: error.message
}; };
} }
}); });
const summary = results.reduce((accumulator, result) => ({ const summary = results.reduce(
checkedCount: accumulator.checkedCount + 1, (accumulator, result) => ({
repairedCount: accumulator.repairedCount + (result.status === 'repaired' ? 1 : 0), checkedCount: accumulator.checkedCount + 1,
plannedRepairCount: accumulator.plannedRepairCount + (result.status === 'planned' ? 1 : 0), repairedCount: accumulator.repairedCount + (result.status === 'repaired' ? 1 : 0),
errorCount: accumulator.errorCount + (result.status === 'error' ? 1 : 0), plannedRepairCount: accumulator.plannedRepairCount + (result.status === 'planned' ? 1 : 0),
}), { errorCount: accumulator.errorCount + (result.status === 'error' ? 1 : 0)
checkedCount: 0, }),
repairedCount: 0, {
plannedRepairCount: 0, checkedCount: 0,
errorCount: 0, repairedCount: 0,
}); plannedRepairCount: 0,
errorCount: 0
}
);
return { return {
dryRun: Boolean(options.dryRun), dryRun: Boolean(options.dryRun),
generatedAt: new Date().toISOString(), generatedAt: new Date().toISOString(),
results, results,
summary, summary
}; };
} }
@ -1108,11 +1036,7 @@ function cleanupEmptyParentDirs(filePath, stopAt) {
let currentPath = path.dirname(filePath); let currentPath = path.dirname(filePath);
const normalizedStopAt = path.resolve(stopAt); const normalizedStopAt = path.resolve(stopAt);
while ( while (currentPath && path.resolve(currentPath).startsWith(normalizedStopAt) && path.resolve(currentPath) !== normalizedStopAt) {
currentPath
&& path.resolve(currentPath).startsWith(normalizedStopAt)
&& path.resolve(currentPath) !== normalizedStopAt
) {
if (!fs.existsSync(currentPath)) { if (!fs.existsSync(currentPath)) {
currentPath = path.dirname(currentPath); currentPath = path.dirname(currentPath);
continue; continue;
@ -1132,7 +1056,7 @@ function uninstallInstalledStates(options = {}) {
const records = discoverInstalledStates({ const records = discoverInstalledStates({
homeDir: options.homeDir, homeDir: options.homeDir,
projectRoot: options.projectRoot, projectRoot: options.projectRoot,
targets: options.targets, targets: options.targets
}).filter(record => record.exists); }).filter(record => record.exists);
const results = records.map(record => { const results = records.map(record => {
@ -1143,15 +1067,12 @@ function uninstallInstalledStates(options = {}) {
installStatePath: record.installStatePath, installStatePath: record.installStatePath,
removedPaths: [], removedPaths: [],
plannedRemovals: [], plannedRemovals: [],
error: record.error || 'No valid install-state available', error: record.error || 'No valid install-state available'
}; };
} }
const state = record.state; const state = record.state;
const plannedRemovals = Array.from(new Set([ const plannedRemovals = Array.from(new Set([...getManagedOperations(state).map(operation => operation.destinationPath), state.target.installStatePath]));
...getManagedOperations(state).map(operation => operation.destinationPath),
state.target.installStatePath,
]));
if (options.dryRun) { if (options.dryRun) {
return { return {
@ -1160,7 +1081,7 @@ function uninstallInstalledStates(options = {}) {
installStatePath: record.installStatePath, installStatePath: record.installStatePath,
removedPaths: [], removedPaths: [],
plannedRemovals, plannedRemovals,
error: null, error: null
}; };
} }
@ -1192,7 +1113,7 @@ function uninstallInstalledStates(options = {}) {
installStatePath: record.installStatePath, installStatePath: record.installStatePath,
removedPaths, removedPaths,
plannedRemovals: [], plannedRemovals: [],
error: null, error: null
}; };
} catch (error) { } catch (error) {
return { return {
@ -1201,28 +1122,31 @@ function uninstallInstalledStates(options = {}) {
installStatePath: record.installStatePath, installStatePath: record.installStatePath,
removedPaths: [], removedPaths: [],
plannedRemovals, plannedRemovals,
error: error.message, error: error.message
}; };
} }
}); });
const summary = results.reduce((accumulator, result) => ({ const summary = results.reduce(
checkedCount: accumulator.checkedCount + 1, (accumulator, result) => ({
uninstalledCount: accumulator.uninstalledCount + (result.status === 'uninstalled' ? 1 : 0), checkedCount: accumulator.checkedCount + 1,
plannedRemovalCount: accumulator.plannedRemovalCount + (result.status === 'planned' ? 1 : 0), uninstalledCount: accumulator.uninstalledCount + (result.status === 'uninstalled' ? 1 : 0),
errorCount: accumulator.errorCount + (result.status === 'error' ? 1 : 0), plannedRemovalCount: accumulator.plannedRemovalCount + (result.status === 'planned' ? 1 : 0),
}), { errorCount: accumulator.errorCount + (result.status === 'error' ? 1 : 0)
checkedCount: 0, }),
uninstalledCount: 0, {
plannedRemovalCount: 0, checkedCount: 0,
errorCount: 0, uninstalledCount: 0,
}); plannedRemovalCount: 0,
errorCount: 0
}
);
return { return {
dryRun: Boolean(options.dryRun), dryRun: Boolean(options.dryRun),
generatedAt: new Date().toISOString(), generatedAt: new Date().toISOString(),
results, results,
summary, summary
}; };
} }
@ -1232,5 +1156,5 @@ module.exports = {
discoverInstalledStates, discoverInstalledStates,
normalizeTargets, normalizeTargets,
repairInstalledStates, repairInstalledStates,
uninstallInstalledStates, uninstallInstalledStates
}; };

View File

@ -70,9 +70,7 @@ function assertWithinTrustedRoot(target, root, action = 'write') {
throw new Error(`Refusing to ${action} '${target}': no trusted install root resolved.`); throw new Error(`Refusing to ${action} '${target}': no trusted install root resolved.`);
} }
if (!isWithinRoot(target, root)) { if (!isWithinRoot(target, root)) {
throw new Error( throw new Error(`Refusing to ${action} outside the install root: '${target}' is not within '${root}'.`);
`Refusing to ${action} outside the install root: '${target}' is not within '${root}'.`
);
} }
return realpathNearestExisting(target); return realpathNearestExisting(target);
} }
@ -80,5 +78,5 @@ function assertWithinTrustedRoot(target, root, action = 'write') {
module.exports = { module.exports = {
realpathNearestExisting, realpathNearestExisting,
isWithinRoot, isWithinRoot,
assertWithinTrustedRoot, assertWithinTrustedRoot
}; };