77 lines
3.0 KiB
JavaScript
77 lines
3.0 KiB
JavaScript
import { INSTALLER_TECHNOLOGIES } from '../data/installerCatalog.js';
|
|
|
|
// Installer intelligence: infer the installer technology from a file name and
|
|
// return recommended silent install/uninstall commands plus a detection
|
|
// suggestion. Pure + table-driven, so it is fully unit-tested. Binary signature
|
|
// sniffing (reading the file) is Phase 5; this works from the name/extension.
|
|
|
|
function extensionOf(fileName = '') {
|
|
const match = String(fileName).toLowerCase().match(/(\.[a-z0-9]+)$/);
|
|
return match ? match[1] : '';
|
|
}
|
|
|
|
function render(template = '', { file = '', productCode = '' } = {}) {
|
|
return String(template)
|
|
.split('{file}').join(file)
|
|
.split('{productCode}').join(productCode || '{productCode}');
|
|
}
|
|
|
|
function shape(tech, ctx) {
|
|
return {
|
|
id: tech.id,
|
|
label: tech.label,
|
|
install: render(tech.install, ctx),
|
|
uninstall: render(tech.uninstall, ctx),
|
|
psadtInstall: render(tech.psadtInstall, ctx),
|
|
psadtUninstall: tech.psadtUninstall ? render(tech.psadtUninstall, ctx) : '',
|
|
detection: tech.detection,
|
|
notes: tech.notes
|
|
};
|
|
}
|
|
|
|
export function listInstallerTechnologies() {
|
|
return INSTALLER_TECHNOLOGIES.map(({ id, label, extensions, detection, notes }) => ({ id, label, extensions, detection, notes }));
|
|
}
|
|
|
|
// Analyze a file name. Returns the best-guess technology, a confidence level,
|
|
// and the full set of candidate technologies for that extension so the operator
|
|
// can pick when the name alone is ambiguous (common for .exe).
|
|
export function analyzeInstaller({ fileName = '', productCode = '' } = {}) {
|
|
const ext = extensionOf(fileName);
|
|
const lower = String(fileName).toLowerCase();
|
|
const ctx = { file: fileName, productCode };
|
|
|
|
const byExt = INSTALLER_TECHNOLOGIES.filter((tech) => tech.extensions.includes(ext));
|
|
if (!byExt.length) {
|
|
return {
|
|
fileName,
|
|
extension: ext,
|
|
confidence: 'unknown',
|
|
primary: null,
|
|
candidates: [],
|
|
message: ext ? `No installer technology is known for "${ext}".` : 'Provide an installer file name with an extension.'
|
|
};
|
|
}
|
|
|
|
// Unique extension (.msi/.msp/.msix...) → high confidence, single technology.
|
|
if (byExt.length === 1) {
|
|
return { fileName, extension: ext, confidence: 'high', primary: shape(byExt[0], ctx), candidates: [shape(byExt[0], ctx)] };
|
|
}
|
|
|
|
// Ambiguous (.exe): try a name hint, otherwise fall back to generic EXE.
|
|
const hinted = byExt.find((tech) => (tech.hints || []).some((hint) => lower.includes(hint)) && tech.id !== 'exe');
|
|
const generic = byExt.find((tech) => tech.id === 'exe') || byExt[0];
|
|
const primary = hinted || generic;
|
|
return {
|
|
fileName,
|
|
extension: ext,
|
|
confidence: hinted ? 'medium' : 'low',
|
|
primary: shape(primary, ctx),
|
|
// Offer all candidates (generic last) so the UI can present a picker.
|
|
candidates: [...byExt.filter((t) => t.id !== 'exe'), ...byExt.filter((t) => t.id === 'exe')].map((t) => shape(t, ctx)),
|
|
message: hinted
|
|
? `Matched "${hinted.label}" from the file name.`
|
|
: 'Ambiguous EXE — pick the matching technology, or confirm the silent switch with the vendor.'
|
|
};
|
|
}
|