import type { NpmIntegrityDrift, NpmSpecResolution } from "./install-source-utils.js"; export type NpmIntegrityDriftPayload = { spec: string; expectedIntegrity: string; actualIntegrity: string; resolution: NpmSpecResolution; }; type ResolveNpmIntegrityDriftParams = { spec: string; expectedIntegrity?: string; resolution: NpmSpecResolution; createPayload: (params: { spec: string; expectedIntegrity: string; actualIntegrity: string; resolution: NpmSpecResolution; }) => TPayload; onIntegrityDrift?: (payload: TPayload) => boolean | Promise; warn?: (payload: TPayload) => void; }; export type ResolveNpmIntegrityDriftResult = { integrityDrift?: NpmIntegrityDrift; proceed: boolean; payload?: TPayload; }; export async function resolveNpmIntegrityDrift( params: ResolveNpmIntegrityDriftParams, ): Promise> { if (!params.expectedIntegrity || !params.resolution.integrity) { return { proceed: true }; } if (params.expectedIntegrity === params.resolution.integrity) { return { proceed: true }; } const integrityDrift: NpmIntegrityDrift = { expectedIntegrity: params.expectedIntegrity, actualIntegrity: params.resolution.integrity, }; const payload = params.createPayload({ spec: params.spec, expectedIntegrity: integrityDrift.expectedIntegrity, actualIntegrity: integrityDrift.actualIntegrity, resolution: params.resolution, }); let proceed = true; if (params.onIntegrityDrift) { proceed = await params.onIntegrityDrift(payload); } else { params.warn?.(payload); } return { integrityDrift, proceed, payload }; } type ResolveNpmIntegrityDriftWithDefaultMessageParams = { spec: string; expectedIntegrity?: string; resolution: NpmSpecResolution; onIntegrityDrift?: (payload: NpmIntegrityDriftPayload) => boolean | Promise; warn?: (message: string) => void; }; export async function resolveNpmIntegrityDriftWithDefaultMessage( params: ResolveNpmIntegrityDriftWithDefaultMessageParams, ): Promise<{ integrityDrift?: NpmIntegrityDrift; error?: string }> { const driftResult = await resolveNpmIntegrityDrift({ spec: params.spec, expectedIntegrity: params.expectedIntegrity, resolution: params.resolution, createPayload: (drift) => ({ ...drift }), onIntegrityDrift: params.onIntegrityDrift, warn: (driftPayload) => { params.warn?.( `Integrity drift detected for ${driftPayload.resolution.resolvedSpec ?? driftPayload.spec}: expected ${driftPayload.expectedIntegrity}, got ${driftPayload.actualIntegrity}`, ); }, }); if (!driftResult.proceed && driftResult.payload) { return { integrityDrift: driftResult.integrityDrift, error: `aborted: npm package integrity drift detected for ${driftResult.payload.resolution.resolvedSpec ?? driftResult.payload.spec}`, }; } return { integrityDrift: driftResult.integrityDrift }; }