fyn 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fyn.js +1219 -269
- package/package.json +1 -1
package/dist/fyn.js
CHANGED
|
@@ -132,6 +132,7 @@ const PromiseQueue = __webpack_require__("./lib/util/promise-queue.ts");
|
|
|
132
132
|
const sortObjKeys = __webpack_require__("./lib/util/sort-obj-keys.ts");
|
|
133
133
|
const fyntil = __webpack_require__("./lib/util/fyntil.ts");
|
|
134
134
|
const showStat = __webpack_require__("./cli/show-stat.ts");
|
|
135
|
+
const showAudit = __webpack_require__("./cli/show-audit.ts");
|
|
135
136
|
const { runNpmScript, addNpmLifecycle } = __webpack_require__("./lib/util/run-npm-script.ts");
|
|
136
137
|
const { initEnv, makeNpmEnv } = __webpack_require__("./lib/util/make-npm-env.ts");
|
|
137
138
|
const runScript = __webpack_require__("./node_modules/.f/_/@npmcli/run-script/10.0.2/@npmcli/run-script/lib/run-script.js");
|
|
@@ -437,10 +438,11 @@ class FynCli {
|
|
|
437
438
|
* 3. postinstall
|
|
438
439
|
* 4. prepare
|
|
439
440
|
*/
|
|
440
|
-
install() {
|
|
441
|
+
install(argv = {}) {
|
|
441
442
|
let failure;
|
|
442
443
|
let installLocked;
|
|
443
444
|
const start = Date.now();
|
|
445
|
+
const runAudit = argv.opts?.audit !== false;
|
|
444
446
|
return Promise.try(() => this.fyn._initializePkg()).then(async () => {
|
|
445
447
|
checkNewVersion(this.fyn._options);
|
|
446
448
|
if (!this.fyn._changeProdMode && !this.fyn._options.forceInstall && this.fyn._installConfig.time) {
|
|
@@ -528,6 +530,17 @@ class FynCli {
|
|
|
528
530
|
chalk.green("complete in total"),
|
|
529
531
|
chalk.magenta(`${(end - start) / 1e3}`) + "secs"
|
|
530
532
|
);
|
|
533
|
+
if (runAudit) {
|
|
534
|
+
try {
|
|
535
|
+
await showAudit(this.fyn, {
|
|
536
|
+
colors: this.fyn._options.colors !== false,
|
|
537
|
+
summary: true
|
|
538
|
+
// Show brief summary, not full report
|
|
539
|
+
});
|
|
540
|
+
} catch (err) {
|
|
541
|
+
logger.warn("Security audit failed:", err.message);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
531
544
|
}).catch((err) => {
|
|
532
545
|
if (err.message === "No Change") {
|
|
533
546
|
logger.info(`No changes detected since last fyn install - nothing to be done.
|
|
@@ -555,6 +568,17 @@ class FynCli {
|
|
|
555
568
|
return this._opts.saveLogs && this.saveLogs(this._opts.saveLogs);
|
|
556
569
|
});
|
|
557
570
|
}
|
|
571
|
+
audit(argv) {
|
|
572
|
+
const opts = {
|
|
573
|
+
json: argv.opts?.json || false,
|
|
574
|
+
omit: argv.opts?.omit || [],
|
|
575
|
+
auditLevel: argv.opts?.auditLevel || "info",
|
|
576
|
+
noCache: argv.opts?.noCache || false
|
|
577
|
+
};
|
|
578
|
+
return showAudit(this.fyn, opts).finally(() => {
|
|
579
|
+
return this._opts.saveLogs && this.saveLogs(this._opts.saveLogs);
|
|
580
|
+
});
|
|
581
|
+
}
|
|
558
582
|
async runScript(pkg, script, env, scriptArgs = []) {
|
|
559
583
|
const config = (x) => this.fyn.allrc[x];
|
|
560
584
|
const options = {
|
|
@@ -1005,7 +1029,7 @@ const commands = {
|
|
|
1005
1029
|
desc: "Install modules",
|
|
1006
1030
|
async exec(cmd) {
|
|
1007
1031
|
const cli = new FynCli(await pickOptions(cmd));
|
|
1008
|
-
return cli.install();
|
|
1032
|
+
return cli.install(cmd.jsonMeta);
|
|
1009
1033
|
},
|
|
1010
1034
|
options: {
|
|
1011
1035
|
"run-npm": {
|
|
@@ -1016,6 +1040,11 @@ const commands = {
|
|
|
1016
1040
|
alias: "fi",
|
|
1017
1041
|
desc: "force install even if no files changed since last install",
|
|
1018
1042
|
args: "<flag boolean>"
|
|
1043
|
+
},
|
|
1044
|
+
audit: {
|
|
1045
|
+
desc: "run security audit after install (use --no-audit to skip)",
|
|
1046
|
+
args: "<flag boolean>",
|
|
1047
|
+
default: true
|
|
1019
1048
|
}
|
|
1020
1049
|
}
|
|
1021
1050
|
},
|
|
@@ -1040,7 +1069,7 @@ const commands = {
|
|
|
1040
1069
|
config.noStartupInfo = true;
|
|
1041
1070
|
logger.info("installing...");
|
|
1042
1071
|
fynTil.resetFynpo();
|
|
1043
|
-
return new FynCli(config).install();
|
|
1072
|
+
return new FynCli(config).install({ opts: { audit: meta.opts.audit } });
|
|
1044
1073
|
});
|
|
1045
1074
|
},
|
|
1046
1075
|
options: {
|
|
@@ -1110,6 +1139,32 @@ const commands = {
|
|
|
1110
1139
|
return new FynCli(await pickOptions(cmd)).stat(cmd.jsonMeta);
|
|
1111
1140
|
}
|
|
1112
1141
|
},
|
|
1142
|
+
audit: {
|
|
1143
|
+
desc: "Check for known security vulnerabilities",
|
|
1144
|
+
exec: async (cmd) => {
|
|
1145
|
+
return new FynCli(await pickOptions(cmd)).audit(cmd.jsonMeta);
|
|
1146
|
+
},
|
|
1147
|
+
options: {
|
|
1148
|
+
json: {
|
|
1149
|
+
alias: "j",
|
|
1150
|
+
args: "<flag boolean>",
|
|
1151
|
+
desc: "Output audit report as JSON"
|
|
1152
|
+
},
|
|
1153
|
+
omit: {
|
|
1154
|
+
args: "[types string..]",
|
|
1155
|
+
desc: "Dependency types to omit (dev, optional, peer)"
|
|
1156
|
+
},
|
|
1157
|
+
"audit-level": {
|
|
1158
|
+
args: "<level string>",
|
|
1159
|
+
desc: "Minimum severity level to report (info, low, moderate, high, critical)",
|
|
1160
|
+
default: "info"
|
|
1161
|
+
},
|
|
1162
|
+
"no-cache": {
|
|
1163
|
+
args: "<flag boolean>",
|
|
1164
|
+
desc: "Bypass the audit cache and fetch fresh data"
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
},
|
|
1113
1168
|
run: {
|
|
1114
1169
|
desc: "Run a npm script",
|
|
1115
1170
|
args: "[script string] [args...]",
|
|
@@ -1437,6 +1492,95 @@ const myPkg = JSON.parse(Fs.readFileSync(Path.join(__dirname, "../package.json")
|
|
|
1437
1492
|
module.exports = myPkg;
|
|
1438
1493
|
|
|
1439
1494
|
|
|
1495
|
+
/***/ }),
|
|
1496
|
+
|
|
1497
|
+
/***/ "./cli/show-audit.ts":
|
|
1498
|
+
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
1499
|
+
|
|
1500
|
+
"use strict";
|
|
1501
|
+
|
|
1502
|
+
const Promise = __webpack_require__("./node_modules/.f/_/aveazul/1.1.0/aveazul/cjs-entry.cjs");
|
|
1503
|
+
const CliLogger = __webpack_require__("./lib/cli-logger.ts");
|
|
1504
|
+
const logger = __webpack_require__("./lib/logger.ts");
|
|
1505
|
+
const AuditReport = __webpack_require__("./lib/audit/audit-report.ts");
|
|
1506
|
+
const AuditFormatter = __webpack_require__("./lib/audit/audit-formatter.ts");
|
|
1507
|
+
const PkgStatProvider = __webpack_require__("./lib/pkg-stat-provider.ts");
|
|
1508
|
+
const { FETCH_META } = __webpack_require__("./lib/log-items.ts");
|
|
1509
|
+
class ShowAudit {
|
|
1510
|
+
/**
|
|
1511
|
+
* @param {Object} options
|
|
1512
|
+
* @param {Object} options.fyn - Fyn instance
|
|
1513
|
+
* @param {Object} options.opts - CLI options
|
|
1514
|
+
*/
|
|
1515
|
+
constructor({ fyn, opts }) {
|
|
1516
|
+
this._fyn = fyn;
|
|
1517
|
+
this._opts = opts || {};
|
|
1518
|
+
this._fyn._options.buildLocal = false;
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* Run the audit workflow.
|
|
1522
|
+
*
|
|
1523
|
+
* @returns {Promise<void>}
|
|
1524
|
+
*/
|
|
1525
|
+
async runAudit() {
|
|
1526
|
+
const spinner = CliLogger.spinners[1];
|
|
1527
|
+
try {
|
|
1528
|
+
let depData = this._fyn._data;
|
|
1529
|
+
if (!depData || Object.keys(depData.pkgs || {}).length === 0) {
|
|
1530
|
+
logger.addItem({ name: FETCH_META, color: "green", spinner });
|
|
1531
|
+
logger.updateItem(FETCH_META, "resolving dependencies...");
|
|
1532
|
+
await this._fyn.resolveDependencies();
|
|
1533
|
+
logger.removeItem(FETCH_META);
|
|
1534
|
+
depData = this._fyn._data;
|
|
1535
|
+
}
|
|
1536
|
+
const pkgCount = Object.keys(depData.pkgs).length;
|
|
1537
|
+
if (pkgCount === 0) {
|
|
1538
|
+
logger.info("No dependencies to audit");
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
logger.addItem({ name: FETCH_META, color: "green", spinner });
|
|
1542
|
+
logger.updateItem(FETCH_META, "fetching security advisories...");
|
|
1543
|
+
const auditReport = new AuditReport({
|
|
1544
|
+
fyn: this._fyn,
|
|
1545
|
+
depData,
|
|
1546
|
+
noCache: this._opts.noCache || false,
|
|
1547
|
+
omit: this._opts.omit || []
|
|
1548
|
+
});
|
|
1549
|
+
const auditResult = await auditReport.fetchAdvisories();
|
|
1550
|
+
logger.removeItem(FETCH_META);
|
|
1551
|
+
const vulnerabilities = auditReport.matchVulnerabilities(auditResult);
|
|
1552
|
+
let statProvider = null;
|
|
1553
|
+
if (!this._opts.summary && !this._opts.json) {
|
|
1554
|
+
statProvider = new PkgStatProvider({ fyn: this._fyn });
|
|
1555
|
+
}
|
|
1556
|
+
const formatter = new AuditFormatter({
|
|
1557
|
+
json: this._opts.json || false,
|
|
1558
|
+
colors: this._fyn._options.colors !== false,
|
|
1559
|
+
auditLevel: this._opts.auditLevel || "info",
|
|
1560
|
+
summary: this._opts.summary || false,
|
|
1561
|
+
statProvider
|
|
1562
|
+
});
|
|
1563
|
+
if (statProvider) {
|
|
1564
|
+
await formatter.precomputeStats(vulnerabilities);
|
|
1565
|
+
}
|
|
1566
|
+
const { output } = formatter.format(auditResult, vulnerabilities);
|
|
1567
|
+
if (output) {
|
|
1568
|
+
console.log(output);
|
|
1569
|
+
}
|
|
1570
|
+
} catch (err) {
|
|
1571
|
+
logger.removeItem(FETCH_META);
|
|
1572
|
+
logger.error("Audit failed:", err.message);
|
|
1573
|
+
if (process.env.FYN_DEBUG) {
|
|
1574
|
+
logger.error(err.stack);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
module.exports = (fyn, opts) => {
|
|
1580
|
+
return new ShowAudit({ fyn, opts }).runAudit();
|
|
1581
|
+
};
|
|
1582
|
+
|
|
1583
|
+
|
|
1440
1584
|
/***/ }),
|
|
1441
1585
|
|
|
1442
1586
|
/***/ "./cli/show-stat.ts":
|
|
@@ -1447,13 +1591,10 @@ module.exports = myPkg;
|
|
|
1447
1591
|
const chalk = __webpack_require__("./node_modules/.f/_/chalk/4.1.2/chalk/source/index.js");
|
|
1448
1592
|
const CliLogger = __webpack_require__("./lib/cli-logger.ts");
|
|
1449
1593
|
const logger = __webpack_require__("./lib/logger.ts");
|
|
1450
|
-
const _ = __webpack_require__("./node_modules/.f/_/lodash/4.17.21/lodash/lodash.min.js");
|
|
1451
|
-
const semverUtil = __webpack_require__("./lib/util/semver.ts");
|
|
1452
1594
|
const Promise = __webpack_require__("./node_modules/.f/_/aveazul/1.1.0/aveazul/cjs-entry.cjs");
|
|
1453
1595
|
const logFormat = __webpack_require__("./lib/util/log-format.ts");
|
|
1454
|
-
const
|
|
1596
|
+
const PkgStatProvider = __webpack_require__("./lib/pkg-stat-provider.ts");
|
|
1455
1597
|
const { FETCH_META } = __webpack_require__("./lib/log-items.ts");
|
|
1456
|
-
const { SEMVER } = __webpack_require__("./lib/symbols.ts");
|
|
1457
1598
|
const PACKAGE_JSON = "~package.json";
|
|
1458
1599
|
const formatPkgId = (pkg) => {
|
|
1459
1600
|
if (pkg.name === PACKAGE_JSON) {
|
|
@@ -1462,220 +1603,52 @@ const formatPkgId = (pkg) => {
|
|
|
1462
1603
|
const top = pkg.promoted ? "" : "\u2B07";
|
|
1463
1604
|
return `${logFormat.pkgId(pkg)}${top}`;
|
|
1464
1605
|
};
|
|
1465
|
-
const getPkgId = (pkg) => {
|
|
1466
|
-
if (pkg.name === PACKAGE_JSON) {
|
|
1467
|
-
return pkg.name;
|
|
1468
|
-
}
|
|
1469
|
-
return `${pkg.name}@${pkg.version}`;
|
|
1470
|
-
};
|
|
1471
1606
|
class ShowStat {
|
|
1472
1607
|
constructor({ fyn }) {
|
|
1473
1608
|
this._fyn = fyn;
|
|
1474
1609
|
this._fyn._options.buildLocal = false;
|
|
1475
|
-
|
|
1476
|
-
// returns array of packages match id
|
|
1477
|
-
findPkgsById(pkgs, id) {
|
|
1478
|
-
const ix = id.indexOf("@", 1);
|
|
1479
|
-
const sx = ix > 0 ? ix : id.length;
|
|
1480
|
-
const name = id.substr(0, sx);
|
|
1481
|
-
const semver = id.substr(sx + 1);
|
|
1482
|
-
return _(pkgs[name]).map((vpkg, version) => {
|
|
1483
|
-
if (!semver || semverUtil.satisfies(version, semver)) {
|
|
1484
|
-
return vpkg;
|
|
1485
|
-
}
|
|
1486
|
-
}).filter((x) => x).value();
|
|
1487
|
-
}
|
|
1488
|
-
findDependents(pkgs, ask) {
|
|
1489
|
-
const dependents = [];
|
|
1490
|
-
if (!this._fynRes) {
|
|
1491
|
-
const depLinker = new PkgDepLinker({ fyn: this._fyn });
|
|
1492
|
-
this._fynRes = depLinker.makeAppFynRes(this._fyn._data.res, {});
|
|
1493
|
-
}
|
|
1494
|
-
const check = (res, vpkg) => {
|
|
1495
|
-
const semv = ask.local ? semverUtil.unlocalify(ask.version) : ask.version;
|
|
1496
|
-
if (res && semverUtil.satisfies(res.resolved, semv)) {
|
|
1497
|
-
dependents.push(vpkg);
|
|
1498
|
-
}
|
|
1499
|
-
};
|
|
1500
|
-
for (const name in pkgs) {
|
|
1501
|
-
const pkg = pkgs[name];
|
|
1502
|
-
for (const version in pkg) {
|
|
1503
|
-
const vpkg = pkg[version];
|
|
1504
|
-
["dep", "opt", "per"].forEach((s) => {
|
|
1505
|
-
const x = vpkg.res[s];
|
|
1506
|
-
check(x && x[ask.name], vpkg);
|
|
1507
|
-
});
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
check(this._fynRes[ask.name], { name: PACKAGE_JSON, promoted: true });
|
|
1511
|
-
return dependents;
|
|
1512
|
-
}
|
|
1513
|
-
showPkgDependents(pkgs, ask) {
|
|
1514
|
-
const dependents = this.findDependents(pkgs, ask).sort((a, b) => {
|
|
1515
|
-
if (a.name === b.name) {
|
|
1516
|
-
return semverUtil.simpleCompare(a.version, b.version);
|
|
1517
|
-
}
|
|
1518
|
-
return a.name > b.name ? 1 : -1;
|
|
1519
|
-
});
|
|
1520
|
-
if (dependents.length > 0) {
|
|
1521
|
-
logger.prefix("").info(
|
|
1522
|
-
"=>",
|
|
1523
|
-
logFormat.pkgId(ask),
|
|
1524
|
-
`has ${dependents.length} dependents:`,
|
|
1525
|
-
dependents.map(formatPkgId).join(" ")
|
|
1526
|
-
);
|
|
1527
|
-
}
|
|
1528
|
-
return dependents;
|
|
1610
|
+
this._statProvider = new PkgStatProvider({ fyn });
|
|
1529
1611
|
}
|
|
1530
1612
|
_show(pkgIds) {
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
return Promise.each(pkgIds, (pkgId) => {
|
|
1535
|
-
const askPkgs = this.findPkgsById(data.pkgs, pkgId).sort(
|
|
1536
|
-
(a, b) => semverUtil.simpleCompare(a.version, b.version)
|
|
1537
|
-
);
|
|
1538
|
-
if (askPkgs.length === 0) {
|
|
1613
|
+
return Promise.each(pkgIds, async (pkgId) => {
|
|
1614
|
+
const matches = this._statProvider.findMatchingVersions(pkgId);
|
|
1615
|
+
if (matches.versions.length === 0) {
|
|
1539
1616
|
logger.prefix("").info(chalk.yellow(pkgId), "is not installed");
|
|
1540
1617
|
} else {
|
|
1541
1618
|
logger.prefix("").info(
|
|
1542
1619
|
chalk.green.bgRed(pkgId),
|
|
1543
1620
|
"matched these installed versions",
|
|
1544
|
-
|
|
1621
|
+
matches.versions.map(formatPkgId).join(" ")
|
|
1545
1622
|
);
|
|
1546
|
-
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
this._displayPaths(specificId, paths);
|
|
1556
|
-
}
|
|
1557
|
-
});
|
|
1558
|
-
});
|
|
1559
|
-
}
|
|
1560
|
-
}).then(() => {
|
|
1561
|
-
logger.info(chalk.green(`stat completed for ${pkgIds.join(" ")}`));
|
|
1562
|
-
});
|
|
1563
|
-
}
|
|
1564
|
-
_findDepPaths(pkgIds, output = [], askName = "") {
|
|
1565
|
-
const data = this._fyn._data;
|
|
1566
|
-
return Promise.each(pkgIds, (pkgId) => {
|
|
1567
|
-
const askPkgs = this.findPkgsById(data.pkgs, pkgId).sort(
|
|
1568
|
-
(a, b) => semverUtil.simpleCompare(a.version, b.version)
|
|
1569
|
-
);
|
|
1570
|
-
if (askPkgs.length < 1) {
|
|
1571
|
-
if (pkgId === PACKAGE_JSON) {
|
|
1572
|
-
const newOutput = [].concat(output);
|
|
1573
|
-
["dependencies", "optionalDependencies", "peerDependencies", "devDependencies"].find(
|
|
1574
|
-
(s) => {
|
|
1575
|
-
const semver = _.get(this._fyn, ["_pkg", s, askName]);
|
|
1576
|
-
if (semver) {
|
|
1577
|
-
newOutput[SEMVER] = semver;
|
|
1578
|
-
}
|
|
1579
|
-
return semver;
|
|
1580
|
-
}
|
|
1581
|
-
);
|
|
1582
|
-
this._allPaths.push(newOutput);
|
|
1583
|
-
} else {
|
|
1584
|
-
this._allPaths.push([pkgId]);
|
|
1585
|
-
}
|
|
1586
|
-
return void 0;
|
|
1587
|
-
}
|
|
1588
|
-
return Promise.map(
|
|
1589
|
-
askPkgs,
|
|
1590
|
-
(pkg) => {
|
|
1591
|
-
const pkgId2 = getPkgId(pkg);
|
|
1592
|
-
if (output.indexOf(pkgId2) >= 0) {
|
|
1593
|
-
logger.debug("stat detected circular dependency:", pkgId2, output.join(" "));
|
|
1594
|
-
return;
|
|
1595
|
-
}
|
|
1596
|
-
let dependents = this._dependentsCache[pkgId2];
|
|
1597
|
-
if (!dependents) {
|
|
1598
|
-
this._dependentsCache[pkgId2] = dependents = this.findDependents(data.pkgs, pkg).sort(
|
|
1599
|
-
(a, b) => {
|
|
1600
|
-
if (a.name === b.name) {
|
|
1601
|
-
return semverUtil.simpleCompare(a.version, b.version);
|
|
1602
|
-
}
|
|
1603
|
-
return a.name > b.name ? 1 : -1;
|
|
1604
|
-
}
|
|
1623
|
+
for (const match of matches.versions) {
|
|
1624
|
+
const stat = await this._statProvider.getPackageStat(match.name, match.version);
|
|
1625
|
+
if (!stat) continue;
|
|
1626
|
+
if (stat.dependents.length > 0) {
|
|
1627
|
+
logger.prefix("").info(
|
|
1628
|
+
"=>",
|
|
1629
|
+
logFormat.pkgId({ name: stat.name, version: stat.version }),
|
|
1630
|
+
`has ${stat.dependents.length} dependents:`,
|
|
1631
|
+
stat.dependents.map(formatPkgId).join(" ")
|
|
1605
1632
|
);
|
|
1606
1633
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
if (output.length === 1) {
|
|
1611
|
-
["dep", "opt", "per", "dev"].find((s) => {
|
|
1612
|
-
const sv = _.get(pkg, ["res", s, askName]);
|
|
1613
|
-
if (sv) {
|
|
1614
|
-
newOutput[SEMVER] = sv.semver;
|
|
1615
|
-
}
|
|
1616
|
-
return sv;
|
|
1617
|
-
});
|
|
1618
|
-
} else {
|
|
1619
|
-
newOutput[SEMVER] = output[SEMVER];
|
|
1620
|
-
}
|
|
1621
|
-
if (followIds.length > 0) {
|
|
1622
|
-
return this._findDepPaths(followIds, newOutput, askName);
|
|
1623
|
-
} else if (output) {
|
|
1624
|
-
this._allPaths.push(newOutput);
|
|
1634
|
+
if (stat.circularDeps && stat.circularDeps.length > 0) {
|
|
1635
|
+
for (const circular of stat.circularDeps) {
|
|
1636
|
+
logger.prefix("").info("stat detected circular dependency:", circular.join(" "));
|
|
1625
1637
|
}
|
|
1626
|
-
} else {
|
|
1627
|
-
logger.prefix("").info("no dependents for", pkgId2);
|
|
1628
1638
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
for (let ixA = 0; ixA < a.length; ixA++) {
|
|
1637
|
-
if (b.length <= ixA) {
|
|
1638
|
-
return 1;
|
|
1639
|
-
}
|
|
1640
|
-
const aId = a[ixA];
|
|
1641
|
-
const bId = b[ixA];
|
|
1642
|
-
if (aId !== bId) {
|
|
1643
|
-
return aId > bId ? 1 : -1;
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
return 0;
|
|
1647
|
-
};
|
|
1648
|
-
paths = paths.sort((a, b) => a.length - b.length);
|
|
1649
|
-
let minDetails = 5;
|
|
1650
|
-
let briefPaths = paths;
|
|
1651
|
-
while (briefPaths.length > 10 && minDetails > 0) {
|
|
1652
|
-
const occurLevels = {};
|
|
1653
|
-
briefPaths = paths.filter((dp) => {
|
|
1654
|
-
const last = dp.length - 1;
|
|
1655
|
-
for (let ix = 0; ix < last; ix++) {
|
|
1656
|
-
const pkgId2 = dp[ix];
|
|
1657
|
-
const occur = occurLevels[pkgId2];
|
|
1658
|
-
if (occur && occur.level < ix && occur.leaf === dp[last] && dp.length - ix > minDetails) {
|
|
1659
|
-
return false;
|
|
1639
|
+
const paths = stat.allPaths;
|
|
1640
|
+
const briefPaths = stat.significantPaths;
|
|
1641
|
+
if (paths.length > 0) {
|
|
1642
|
+
const formattedPaths = this._statProvider.formatPaths(briefPaths);
|
|
1643
|
+
const msg = paths.length === briefPaths.length ? `these dependency paths:` : `${paths.length} dependency paths, showing the ${briefPaths.length} most significant ones below:`;
|
|
1644
|
+
logger.prefix("").info(`=> ${stat.name}@${stat.version} has ${msg}`);
|
|
1645
|
+
logger.prefix("").info(formattedPaths.map((p) => ` > ${p}`).join("\n"));
|
|
1660
1646
|
}
|
|
1661
|
-
occurLevels[pkgId2] = {
|
|
1662
|
-
level: ix,
|
|
1663
|
-
leaf: dp[last]
|
|
1664
|
-
};
|
|
1665
1647
|
}
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
}
|
|
1670
|
-
briefPaths = briefPaths.sort(cmpDepPath);
|
|
1671
|
-
const msg = paths.length === briefPaths.length ? `these dependency paths:` : `${paths.length} dependency paths, showing the ${briefPaths.length} most significant ones below:`;
|
|
1672
|
-
logger.prefix("").info(`=> ${pkgId} has ${msg}`);
|
|
1673
|
-
logger.prefix("").info(
|
|
1674
|
-
briefPaths.map((x) => {
|
|
1675
|
-
const semver = x[SEMVER];
|
|
1676
|
-
return ` > ` + x.join(" > ") + (semver ? ` (${semver})` : "");
|
|
1677
|
-
}).join("\n")
|
|
1678
|
-
);
|
|
1648
|
+
}
|
|
1649
|
+
}).then(() => {
|
|
1650
|
+
logger.info(chalk.green(`stat completed for ${pkgIds.join(" ")}`));
|
|
1651
|
+
});
|
|
1679
1652
|
}
|
|
1680
1653
|
showStat(pkgIds) {
|
|
1681
1654
|
const spinner = CliLogger.spinners[1];
|
|
@@ -1972,6 +1945,474 @@ module.exports.__TEST__ = {
|
|
|
1972
1945
|
};
|
|
1973
1946
|
|
|
1974
1947
|
|
|
1948
|
+
/***/ }),
|
|
1949
|
+
|
|
1950
|
+
/***/ "./lib/audit/audit-cache.ts":
|
|
1951
|
+
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
1952
|
+
|
|
1953
|
+
"use strict";
|
|
1954
|
+
|
|
1955
|
+
const cacache = __webpack_require__("./node_modules/.f/_/cacache/20.0.1/cacache/lib/index.js");
|
|
1956
|
+
const crypto = __webpack_require__("crypto");
|
|
1957
|
+
const Path = __webpack_require__("path");
|
|
1958
|
+
const AUDIT_CACHE_PREFIX = "fyn-audit-";
|
|
1959
|
+
function generateCacheKey(payload) {
|
|
1960
|
+
const sorted = Object.keys(payload).sort().reduce((acc, name) => {
|
|
1961
|
+
acc[name] = [...payload[name]].sort();
|
|
1962
|
+
return acc;
|
|
1963
|
+
}, {});
|
|
1964
|
+
const hash = crypto.createHash("sha256").update(JSON.stringify(sorted)).digest("hex");
|
|
1965
|
+
return `${AUDIT_CACHE_PREFIX}${hash}`;
|
|
1966
|
+
}
|
|
1967
|
+
async function cacheAuditResult(cacheDir, key, result) {
|
|
1968
|
+
const auditCacheDir = Path.join(cacheDir, "audit");
|
|
1969
|
+
const data = JSON.stringify(result);
|
|
1970
|
+
await cacache.put(auditCacheDir, key, data);
|
|
1971
|
+
}
|
|
1972
|
+
async function getCachedAuditResult(cacheDir, key) {
|
|
1973
|
+
const auditCacheDir = Path.join(cacheDir, "audit");
|
|
1974
|
+
try {
|
|
1975
|
+
const { data } = await cacache.get(auditCacheDir, key);
|
|
1976
|
+
return JSON.parse(data.toString());
|
|
1977
|
+
} catch (err) {
|
|
1978
|
+
if (err.code === "ENOENT" || err.code === "ENOTCACHED") {
|
|
1979
|
+
return null;
|
|
1980
|
+
}
|
|
1981
|
+
throw err;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
async function hasAuditCache(cacheDir, key) {
|
|
1985
|
+
const auditCacheDir = Path.join(cacheDir, "audit");
|
|
1986
|
+
try {
|
|
1987
|
+
const info = await cacache.get.info(auditCacheDir, key);
|
|
1988
|
+
return info !== null;
|
|
1989
|
+
} catch (err) {
|
|
1990
|
+
return false;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
module.exports = {
|
|
1994
|
+
AUDIT_CACHE_PREFIX,
|
|
1995
|
+
generateCacheKey,
|
|
1996
|
+
cacheAuditResult,
|
|
1997
|
+
getCachedAuditResult,
|
|
1998
|
+
hasAuditCache
|
|
1999
|
+
};
|
|
2000
|
+
|
|
2001
|
+
|
|
2002
|
+
/***/ }),
|
|
2003
|
+
|
|
2004
|
+
/***/ "./lib/audit/audit-formatter.ts":
|
|
2005
|
+
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
2006
|
+
|
|
2007
|
+
"use strict";
|
|
2008
|
+
|
|
2009
|
+
const chalk = __webpack_require__("./node_modules/.f/_/chalk/4.1.2/chalk/source/index.js");
|
|
2010
|
+
const { SEMVER } = __webpack_require__("./lib/symbols.ts");
|
|
2011
|
+
const SEVERITY_COLORS = {
|
|
2012
|
+
critical: chalk.red.bold,
|
|
2013
|
+
high: chalk.red,
|
|
2014
|
+
moderate: chalk.yellow,
|
|
2015
|
+
low: chalk.cyan,
|
|
2016
|
+
info: chalk.blue
|
|
2017
|
+
};
|
|
2018
|
+
const SEVERITY_ORDER = ["critical", "high", "moderate", "low", "info"];
|
|
2019
|
+
class AuditFormatter {
|
|
2020
|
+
/**
|
|
2021
|
+
* @param {Object} options
|
|
2022
|
+
* @param {boolean} options.json - Output as JSON
|
|
2023
|
+
* @param {boolean} options.colors - Use colors in output
|
|
2024
|
+
* @param {string} options.auditLevel - Minimum severity to report
|
|
2025
|
+
* @param {boolean} options.summary - Output brief summary only (like npm after install)
|
|
2026
|
+
* @param {Object} options.statProvider - PkgStatProvider instance for dependency path info
|
|
2027
|
+
* @param {Object} options.statCache - Pre-computed stat results keyed by "name@version"
|
|
2028
|
+
*/
|
|
2029
|
+
constructor(options = {}) {
|
|
2030
|
+
this._json = options.json || false;
|
|
2031
|
+
this._colors = options.colors !== false;
|
|
2032
|
+
this._auditLevel = options.auditLevel || "info";
|
|
2033
|
+
this._summary = options.summary || false;
|
|
2034
|
+
this._statProvider = options.statProvider || null;
|
|
2035
|
+
this._statCache = options.statCache || {};
|
|
2036
|
+
}
|
|
2037
|
+
/**
|
|
2038
|
+
* Get severity level as number for comparison.
|
|
2039
|
+
*/
|
|
2040
|
+
getSeverityLevel(severity) {
|
|
2041
|
+
const levels = { critical: 5, high: 4, moderate: 3, low: 2, info: 1 };
|
|
2042
|
+
return levels[severity] || 0;
|
|
2043
|
+
}
|
|
2044
|
+
/**
|
|
2045
|
+
* Check if severity meets minimum audit level.
|
|
2046
|
+
*/
|
|
2047
|
+
meetsAuditLevel(severity) {
|
|
2048
|
+
return this.getSeverityLevel(severity) >= this.getSeverityLevel(this._auditLevel);
|
|
2049
|
+
}
|
|
2050
|
+
/**
|
|
2051
|
+
* Colorize text based on severity.
|
|
2052
|
+
*/
|
|
2053
|
+
colorize(text, severity) {
|
|
2054
|
+
if (!this._colors) return text;
|
|
2055
|
+
const colorFn = SEVERITY_COLORS[severity] || ((t) => t);
|
|
2056
|
+
return colorFn(text);
|
|
2057
|
+
}
|
|
2058
|
+
/**
|
|
2059
|
+
* Format audit result for output.
|
|
2060
|
+
*
|
|
2061
|
+
* @param {Object} auditResult - Result from AuditReport.fetchAdvisories()
|
|
2062
|
+
* @param {Array} vulnerabilities - Matched vulnerabilities from matchVulnerabilities()
|
|
2063
|
+
* @returns {Object} { output: string, exitCode: number }
|
|
2064
|
+
*/
|
|
2065
|
+
format(auditResult, vulnerabilities) {
|
|
2066
|
+
if (this._json) {
|
|
2067
|
+
return this.formatJson(auditResult, vulnerabilities);
|
|
2068
|
+
}
|
|
2069
|
+
if (this._summary) {
|
|
2070
|
+
return this.formatSummary(auditResult, vulnerabilities);
|
|
2071
|
+
}
|
|
2072
|
+
return this.formatHuman(auditResult, vulnerabilities);
|
|
2073
|
+
}
|
|
2074
|
+
/**
|
|
2075
|
+
* Format as brief summary (like npm shows after install).
|
|
2076
|
+
* Example: "audited 745 packages\n3 vulnerabilities (1 moderate, 1 high, 1 critical)"
|
|
2077
|
+
*/
|
|
2078
|
+
formatSummary(auditResult, vulnerabilities) {
|
|
2079
|
+
const filtered = vulnerabilities.filter((v) => this.meetsAuditLevel(v.advisory.severity));
|
|
2080
|
+
const pkgCount = auditResult.metadata?.totalDependencies || 0;
|
|
2081
|
+
if (filtered.length === 0) {
|
|
2082
|
+
return { output: `audited ${pkgCount} packages`, exitCode: 0 };
|
|
2083
|
+
}
|
|
2084
|
+
const counts = {};
|
|
2085
|
+
filtered.forEach((v) => {
|
|
2086
|
+
const sev = v.advisory.severity;
|
|
2087
|
+
counts[sev] = (counts[sev] || 0) + 1;
|
|
2088
|
+
});
|
|
2089
|
+
const parts = SEVERITY_ORDER.filter((sev) => counts[sev]).map((sev) => {
|
|
2090
|
+
const count = counts[sev];
|
|
2091
|
+
return this.colorize(`${count} ${sev}`, sev);
|
|
2092
|
+
});
|
|
2093
|
+
const total = filtered.length;
|
|
2094
|
+
const output = `audited ${pkgCount} packages
|
|
2095
|
+
|
|
2096
|
+
${total} ${total === 1 ? "vulnerability" : "vulnerabilities"} (${parts.join(", ")})
|
|
2097
|
+
|
|
2098
|
+
Run \`fyn audit\` for details.`;
|
|
2099
|
+
return { output, exitCode: 0 };
|
|
2100
|
+
}
|
|
2101
|
+
/**
|
|
2102
|
+
* Format as JSON.
|
|
2103
|
+
*/
|
|
2104
|
+
formatJson(auditResult, vulnerabilities) {
|
|
2105
|
+
const filtered = vulnerabilities.filter((v) => this.meetsAuditLevel(v.advisory.severity));
|
|
2106
|
+
const output = JSON.stringify(
|
|
2107
|
+
{
|
|
2108
|
+
vulnerabilities: filtered,
|
|
2109
|
+
metadata: auditResult.metadata,
|
|
2110
|
+
advisories: auditResult.advisories
|
|
2111
|
+
},
|
|
2112
|
+
null,
|
|
2113
|
+
2
|
|
2114
|
+
);
|
|
2115
|
+
return { output, exitCode: 0 };
|
|
2116
|
+
}
|
|
2117
|
+
/**
|
|
2118
|
+
* Format as human-readable table.
|
|
2119
|
+
*/
|
|
2120
|
+
formatHuman(auditResult, vulnerabilities) {
|
|
2121
|
+
const lines = [];
|
|
2122
|
+
const { metadata } = auditResult;
|
|
2123
|
+
const filtered = vulnerabilities.filter((v) => this.meetsAuditLevel(v.advisory.severity));
|
|
2124
|
+
const bySeverity = {};
|
|
2125
|
+
SEVERITY_ORDER.forEach((s) => bySeverity[s] = []);
|
|
2126
|
+
filtered.forEach((vuln) => {
|
|
2127
|
+
const sev = vuln.advisory.severity;
|
|
2128
|
+
if (bySeverity[sev]) {
|
|
2129
|
+
bySeverity[sev].push(vuln);
|
|
2130
|
+
}
|
|
2131
|
+
});
|
|
2132
|
+
lines.push("");
|
|
2133
|
+
if (filtered.length === 0) {
|
|
2134
|
+
lines.push(this._colors ? chalk.green("No vulnerabilities found!") : "No vulnerabilities found!");
|
|
2135
|
+
lines.push("");
|
|
2136
|
+
lines.push(`Scanned ${metadata.totalDependencies} packages`);
|
|
2137
|
+
} else {
|
|
2138
|
+
const counts = SEVERITY_ORDER.map((sev) => {
|
|
2139
|
+
const count = bySeverity[sev].length;
|
|
2140
|
+
if (count === 0) return null;
|
|
2141
|
+
return this.colorize(`${count} ${sev}`, sev);
|
|
2142
|
+
}).filter(Boolean);
|
|
2143
|
+
lines.push(`Found ${filtered.length} vulnerabilities (${counts.join(", ")})`);
|
|
2144
|
+
lines.push("");
|
|
2145
|
+
SEVERITY_ORDER.forEach((severity) => {
|
|
2146
|
+
const vulns = bySeverity[severity];
|
|
2147
|
+
if (vulns.length === 0) return;
|
|
2148
|
+
vulns.forEach((vuln) => {
|
|
2149
|
+
const { advisory } = vuln;
|
|
2150
|
+
lines.push(this.formatVulnerability(vuln));
|
|
2151
|
+
lines.push("");
|
|
2152
|
+
});
|
|
2153
|
+
});
|
|
2154
|
+
lines.push(`Scanned ${metadata.totalDependencies} packages`);
|
|
2155
|
+
}
|
|
2156
|
+
lines.push("");
|
|
2157
|
+
return { output: lines.join("\n"), exitCode: 0 };
|
|
2158
|
+
}
|
|
2159
|
+
/**
|
|
2160
|
+
* Format a single vulnerability entry.
|
|
2161
|
+
*/
|
|
2162
|
+
formatVulnerability(vuln) {
|
|
2163
|
+
const { advisory, name, version } = vuln;
|
|
2164
|
+
const lines = [];
|
|
2165
|
+
const sevLabel = this.colorize(advisory.severity.toUpperCase().padEnd(10), advisory.severity);
|
|
2166
|
+
lines.push(`${sevLabel} ${advisory.title}`);
|
|
2167
|
+
const pkgId = this._colors ? chalk.yellow(`${name}@${version}`) : `${name}@${version}`;
|
|
2168
|
+
const vulnRange = this._colors ? chalk.red(`Vulnerable: ${advisory.vulnerable_versions}`) : `Vulnerable: ${advisory.vulnerable_versions}`;
|
|
2169
|
+
const infoUrl = advisory.url ? this._colors ? chalk.cyan(`info: ${advisory.url}`) : `info: ${advisory.url}` : "";
|
|
2170
|
+
let details = ` ${pkgId} - ${vulnRange}`;
|
|
2171
|
+
if (infoUrl) {
|
|
2172
|
+
details += ` - ${infoUrl}`;
|
|
2173
|
+
}
|
|
2174
|
+
lines.push(details);
|
|
2175
|
+
const cacheKey = `${name}@${version}`;
|
|
2176
|
+
const stat = this._statCache[cacheKey];
|
|
2177
|
+
if (stat && stat.significantPaths && stat.significantPaths.length > 0) {
|
|
2178
|
+
const seenFirstLegs = /* @__PURE__ */ new Set();
|
|
2179
|
+
const uniquePaths = [];
|
|
2180
|
+
for (const path of stat.significantPaths) {
|
|
2181
|
+
const firstLeg = path[0];
|
|
2182
|
+
if (!seenFirstLegs.has(firstLeg)) {
|
|
2183
|
+
seenFirstLegs.add(firstLeg);
|
|
2184
|
+
uniquePaths.push(path);
|
|
2185
|
+
if (uniquePaths.length >= 5) break;
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
for (const path of uniquePaths) {
|
|
2189
|
+
const semver = path[SEMVER];
|
|
2190
|
+
const pathStr = path.join(" > ");
|
|
2191
|
+
const fullPath = semver ? `${pathStr} (${semver})` : pathStr;
|
|
2192
|
+
lines.push(` > ${fullPath}`);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
return lines.join("\n");
|
|
2196
|
+
}
|
|
2197
|
+
/**
|
|
2198
|
+
* Pre-compute stat info for all vulnerabilities.
|
|
2199
|
+
* Call this before format() when using a stat provider.
|
|
2200
|
+
*
|
|
2201
|
+
* @param {Array} vulnerabilities - List of vulnerabilities from matchVulnerabilities()
|
|
2202
|
+
* @returns {Promise<void>}
|
|
2203
|
+
*/
|
|
2204
|
+
async precomputeStats(vulnerabilities) {
|
|
2205
|
+
if (!this._statProvider) return;
|
|
2206
|
+
const filtered = vulnerabilities.filter((v) => this.meetsAuditLevel(v.advisory.severity));
|
|
2207
|
+
for (const vuln of filtered) {
|
|
2208
|
+
const cacheKey = `${vuln.name}@${vuln.version}`;
|
|
2209
|
+
if (!this._statCache[cacheKey]) {
|
|
2210
|
+
const stat = await this._statProvider.getPackageStat(vuln.name, vuln.version);
|
|
2211
|
+
if (stat) {
|
|
2212
|
+
this._statCache[cacheKey] = stat;
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
module.exports = AuditFormatter;
|
|
2219
|
+
|
|
2220
|
+
|
|
2221
|
+
/***/ }),
|
|
2222
|
+
|
|
2223
|
+
/***/ "./lib/audit/audit-report.ts":
|
|
2224
|
+
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
2225
|
+
|
|
2226
|
+
"use strict";
|
|
2227
|
+
|
|
2228
|
+
const npmFetch = __webpack_require__("./node_modules/.f/_/npm-registry-fetch/19.1.0/npm-registry-fetch/lib/index.js");
|
|
2229
|
+
const logger = __webpack_require__("./lib/logger.ts");
|
|
2230
|
+
const {
|
|
2231
|
+
generateCacheKey,
|
|
2232
|
+
cacheAuditResult,
|
|
2233
|
+
getCachedAuditResult
|
|
2234
|
+
} = __webpack_require__("./lib/audit/audit-cache.ts");
|
|
2235
|
+
class AuditReport {
|
|
2236
|
+
/**
|
|
2237
|
+
* @param {Object} options
|
|
2238
|
+
* @param {Object} options.fyn - Fyn instance with resolved data
|
|
2239
|
+
* @param {Object} options.depData - DepData with resolved packages
|
|
2240
|
+
* @param {boolean} options.noCache - Skip cache lookup
|
|
2241
|
+
* @param {string[]} options.omit - Dependency types to omit (dev, optional, peer)
|
|
2242
|
+
*/
|
|
2243
|
+
constructor(options) {
|
|
2244
|
+
this._fyn = options.fyn;
|
|
2245
|
+
this._depData = options.depData;
|
|
2246
|
+
this._noCache = options.noCache || false;
|
|
2247
|
+
this._omit = options.omit || [];
|
|
2248
|
+
this._cacheDir = options.fyn.fynDir;
|
|
2249
|
+
}
|
|
2250
|
+
/**
|
|
2251
|
+
* Build bulk request payload from resolved packages.
|
|
2252
|
+
* Format: { "packageName": ["version1", "version2"], ... }
|
|
2253
|
+
*
|
|
2254
|
+
* @returns {Object} Bulk payload for npm security API
|
|
2255
|
+
*/
|
|
2256
|
+
buildBulkPayload() {
|
|
2257
|
+
const payload = {};
|
|
2258
|
+
const pkgs = this._depData.pkgs;
|
|
2259
|
+
const omitDev = this._omit.includes("dev");
|
|
2260
|
+
const omitOptional = this._omit.includes("optional");
|
|
2261
|
+
const omitPeer = this._omit.includes("peer");
|
|
2262
|
+
Object.keys(pkgs).forEach((name) => {
|
|
2263
|
+
const versions = pkgs[name];
|
|
2264
|
+
Object.keys(versions).forEach((version) => {
|
|
2265
|
+
const pkgInfo = versions[version];
|
|
2266
|
+
if (omitDev && pkgInfo.src === "dev") return;
|
|
2267
|
+
if (omitOptional && pkgInfo.src === "opt") return;
|
|
2268
|
+
if (omitPeer && pkgInfo.src === "per") return;
|
|
2269
|
+
if (pkgInfo.local || pkgInfo.localType) return;
|
|
2270
|
+
if (!payload[name]) {
|
|
2271
|
+
payload[name] = [];
|
|
2272
|
+
}
|
|
2273
|
+
if (!payload[name].includes(version)) {
|
|
2274
|
+
payload[name].push(version);
|
|
2275
|
+
}
|
|
2276
|
+
});
|
|
2277
|
+
});
|
|
2278
|
+
return payload;
|
|
2279
|
+
}
|
|
2280
|
+
/**
|
|
2281
|
+
* Get registry URL for audit API.
|
|
2282
|
+
* Uses the main registry (not scoped registries since audit is global).
|
|
2283
|
+
*
|
|
2284
|
+
* @returns {string} Registry URL with trailing slash
|
|
2285
|
+
*/
|
|
2286
|
+
getAuditRegistryUrl() {
|
|
2287
|
+
const registry = this._fyn._options.registry || "https://registry.npmjs.org";
|
|
2288
|
+
return registry.endsWith("/") ? registry : `${registry}/`;
|
|
2289
|
+
}
|
|
2290
|
+
/**
|
|
2291
|
+
* Get npm-registry-fetch options with auth.
|
|
2292
|
+
*
|
|
2293
|
+
* @returns {Object} Options for npm-registry-fetch
|
|
2294
|
+
*/
|
|
2295
|
+
getFetchOptions() {
|
|
2296
|
+
const opts = this._fyn._options;
|
|
2297
|
+
const fetchOpts = {
|
|
2298
|
+
registry: this.getAuditRegistryUrl()
|
|
2299
|
+
};
|
|
2300
|
+
if (opts.username) fetchOpts.username = opts.username;
|
|
2301
|
+
if (opts.password) fetchOpts.password = opts.password;
|
|
2302
|
+
if (opts.email) fetchOpts.email = opts.email;
|
|
2303
|
+
if (opts["always-auth"]) fetchOpts.alwaysAuth = opts["always-auth"];
|
|
2304
|
+
Object.keys(opts).forEach((key) => {
|
|
2305
|
+
if (key.endsWith(":_authToken")) {
|
|
2306
|
+
fetchOpts[key] = opts[key];
|
|
2307
|
+
}
|
|
2308
|
+
});
|
|
2309
|
+
return fetchOpts;
|
|
2310
|
+
}
|
|
2311
|
+
/**
|
|
2312
|
+
* Fetch advisories from npm security API.
|
|
2313
|
+
* Uses cache if available and noCache is false.
|
|
2314
|
+
*
|
|
2315
|
+
* @returns {Promise<Object>} Audit result with advisories
|
|
2316
|
+
*/
|
|
2317
|
+
async fetchAdvisories() {
|
|
2318
|
+
const payload = this.buildBulkPayload();
|
|
2319
|
+
const packageCount = Object.keys(payload).length;
|
|
2320
|
+
if (packageCount === 0) {
|
|
2321
|
+
logger.info("No packages to audit");
|
|
2322
|
+
return { advisories: {}, metadata: { totalDependencies: 0 } };
|
|
2323
|
+
}
|
|
2324
|
+
const cacheKey = generateCacheKey(payload);
|
|
2325
|
+
if (!this._noCache) {
|
|
2326
|
+
const cached = await getCachedAuditResult(this._cacheDir, cacheKey);
|
|
2327
|
+
if (cached) {
|
|
2328
|
+
logger.debug("Using cached audit result");
|
|
2329
|
+
return cached;
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
const registryUrl = this.getAuditRegistryUrl();
|
|
2333
|
+
const auditUrl = `${registryUrl}-/npm/v1/security/advisories/bulk`;
|
|
2334
|
+
logger.info(`Auditing ${packageCount} packages...`);
|
|
2335
|
+
try {
|
|
2336
|
+
const fetchOpts = this.getFetchOptions();
|
|
2337
|
+
const response = await npmFetch(auditUrl, {
|
|
2338
|
+
...fetchOpts,
|
|
2339
|
+
method: "POST",
|
|
2340
|
+
body: payload,
|
|
2341
|
+
gzip: true
|
|
2342
|
+
});
|
|
2343
|
+
const advisories = await response.json();
|
|
2344
|
+
const result = {
|
|
2345
|
+
advisories,
|
|
2346
|
+
metadata: {
|
|
2347
|
+
totalDependencies: packageCount,
|
|
2348
|
+
vulnerabilities: Object.keys(advisories).length
|
|
2349
|
+
}
|
|
2350
|
+
};
|
|
2351
|
+
await cacheAuditResult(this._cacheDir, cacheKey, result);
|
|
2352
|
+
return result;
|
|
2353
|
+
} catch (err) {
|
|
2354
|
+
if (!this._noCache) {
|
|
2355
|
+
const cached = await getCachedAuditResult(this._cacheDir, cacheKey);
|
|
2356
|
+
if (cached) {
|
|
2357
|
+
logger.warn("Network error, using cached audit data");
|
|
2358
|
+
return cached;
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
if (err.code === "ENOTFOUND" || err.code === "ECONNREFUSED") {
|
|
2362
|
+
throw new Error(`Unable to reach npm registry for audit: ${err.message}`);
|
|
2363
|
+
}
|
|
2364
|
+
if (err.statusCode === 401 || err.statusCode === 403) {
|
|
2365
|
+
throw new Error("Authentication required for security audit");
|
|
2366
|
+
}
|
|
2367
|
+
throw err;
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
/**
|
|
2371
|
+
* Match advisories to resolved packages.
|
|
2372
|
+
* Returns list of affected packages with advisory details.
|
|
2373
|
+
*
|
|
2374
|
+
* The bulk API returns advisories grouped by package name:
|
|
2375
|
+
* { "package-name": [{ id, vulnerable_versions, ... }, ...] }
|
|
2376
|
+
*
|
|
2377
|
+
* @param {Object} auditResult - Result from fetchAdvisories()
|
|
2378
|
+
* @returns {Array} List of vulnerabilities with package info
|
|
2379
|
+
*/
|
|
2380
|
+
matchVulnerabilities(auditResult) {
|
|
2381
|
+
const vulnerabilities = [];
|
|
2382
|
+
const { advisories } = auditResult;
|
|
2383
|
+
const pkgs = this._depData.pkgs;
|
|
2384
|
+
const semver = __webpack_require__("./node_modules/.f/_/semver/7.7.3/semver/index.js");
|
|
2385
|
+
Object.keys(advisories).forEach((pkgName) => {
|
|
2386
|
+
const pkgAdvisories = advisories[pkgName];
|
|
2387
|
+
if (!pkgs[pkgName]) return;
|
|
2388
|
+
pkgAdvisories.forEach((advisory) => {
|
|
2389
|
+
const vulnVersions = advisory.vulnerable_versions;
|
|
2390
|
+
Object.keys(pkgs[pkgName]).forEach((version) => {
|
|
2391
|
+
if (semver.satisfies(version, vulnVersions)) {
|
|
2392
|
+
vulnerabilities.push({
|
|
2393
|
+
name: pkgName,
|
|
2394
|
+
version,
|
|
2395
|
+
advisory: {
|
|
2396
|
+
id: advisory.id,
|
|
2397
|
+
title: advisory.title,
|
|
2398
|
+
severity: advisory.severity,
|
|
2399
|
+
url: advisory.url,
|
|
2400
|
+
vulnerable_versions: vulnVersions,
|
|
2401
|
+
patched_versions: advisory.patched_versions,
|
|
2402
|
+
recommendation: advisory.recommendation
|
|
2403
|
+
},
|
|
2404
|
+
paths: pkgs[pkgName][version].requests || []
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
});
|
|
2408
|
+
});
|
|
2409
|
+
});
|
|
2410
|
+
return vulnerabilities;
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
module.exports = AuditReport;
|
|
2414
|
+
|
|
2415
|
+
|
|
1975
2416
|
/***/ }),
|
|
1976
2417
|
|
|
1977
2418
|
/***/ "./lib/cacache-util.ts":
|
|
@@ -2747,9 +3188,12 @@ class FynGlobal {
|
|
|
2747
3188
|
*/
|
|
2748
3189
|
constructor(options = {}) {
|
|
2749
3190
|
this.options = options;
|
|
2750
|
-
|
|
3191
|
+
const isBun = typeof process.versions.bun === "string";
|
|
3192
|
+
const runtimeVersion = options.nodeVersion || process.version.match(/^v?(\d+)/)[1];
|
|
3193
|
+
this.runtimePrefix = isBun ? "bun" : "v";
|
|
3194
|
+
this.nodeVersion = runtimeVersion;
|
|
2751
3195
|
this.globalRoot = options.globalDir || Path.join(Os.homedir(), ".fyn", "global");
|
|
2752
|
-
this.versionDir = Path.join(this.globalRoot,
|
|
3196
|
+
this.versionDir = Path.join(this.globalRoot, `${this.runtimePrefix}${runtimeVersion}`);
|
|
2753
3197
|
this.packagesDir = Path.join(this.versionDir, "packages");
|
|
2754
3198
|
this.globalBinDir = Path.join(this.versionDir, "bin");
|
|
2755
3199
|
this.installedJsonPath = Path.join(this.versionDir, "installed.json");
|
|
@@ -3053,7 +3497,7 @@ class FynGlobal {
|
|
|
3053
3497
|
*/
|
|
3054
3498
|
async ensureBinSymlink() {
|
|
3055
3499
|
const binSymlink = Path.join(this.globalRoot, "bin");
|
|
3056
|
-
const targetDir =
|
|
3500
|
+
const targetDir = `${this.runtimePrefix}${this.nodeVersion}/bin`;
|
|
3057
3501
|
try {
|
|
3058
3502
|
const currentTarget = await Fs.readlink(binSymlink);
|
|
3059
3503
|
if (currentTarget === targetDir) {
|
|
@@ -3625,7 +4069,7 @@ Use: fyn global link ${packageName}@<version>`);
|
|
|
3625
4069
|
} else {
|
|
3626
4070
|
const pkgJsonPath = Path.join(pkgDir, "package.json");
|
|
3627
4071
|
const pkgJson = JSON.parse(await Fs.readFile(pkgJsonPath));
|
|
3628
|
-
pkgJson.dependencies[targetPackageName] = "latest";
|
|
4072
|
+
pkgJson.dependencies[targetPackageName] = targetVersion.semver || "latest";
|
|
3629
4073
|
await Fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + "\n");
|
|
3630
4074
|
}
|
|
3631
4075
|
const lockPath = Path.join(pkgDir, "fyn-lock.yaml");
|
|
@@ -4021,7 +4465,82 @@ class Fyn {
|
|
|
4021
4465
|
return { mm: new mm.Minimatch(finalPath), res: resData[depPath] };
|
|
4022
4466
|
});
|
|
4023
4467
|
}
|
|
4468
|
+
const overridesData = {
|
|
4469
|
+
...this._pkg.overrides,
|
|
4470
|
+
..._.get(this._fynpo, ["config", "overrides"])
|
|
4471
|
+
};
|
|
4472
|
+
if (!_.isEmpty(overridesData)) {
|
|
4473
|
+
this._overrides = overridesData;
|
|
4474
|
+
this._overridesMatchers = this._processOverrides(overridesData);
|
|
4475
|
+
}
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4478
|
+
/**
|
|
4479
|
+
* Process npm-style overrides into matchers
|
|
4480
|
+
*
|
|
4481
|
+
* npm overrides support:
|
|
4482
|
+
* 1. Simple: "package-name": "version"
|
|
4483
|
+
* 2. Nested: "parent-pkg": { "child-pkg": "version" }
|
|
4484
|
+
* 3. Version-conditional: "package@^1.0.0": "1.0.5"
|
|
4485
|
+
* 4. Reference: "$package-name" to reference a direct dependency version
|
|
4486
|
+
*
|
|
4487
|
+
* @param {object} overrides - The overrides object from package.json
|
|
4488
|
+
* @param {string} parentPath - The parent path for nested overrides
|
|
4489
|
+
* @returns {Array} Array of override matcher objects
|
|
4490
|
+
*/
|
|
4491
|
+
_processOverrides(overrides, parentPath = "") {
|
|
4492
|
+
const matchers = [];
|
|
4493
|
+
for (const key of Object.keys(overrides)) {
|
|
4494
|
+
const value = overrides[key];
|
|
4495
|
+
let pkgName = key;
|
|
4496
|
+
let versionConstraint = null;
|
|
4497
|
+
const atIdx = key.lastIndexOf("@");
|
|
4498
|
+
if (atIdx > 0) {
|
|
4499
|
+
pkgName = key.substring(0, atIdx);
|
|
4500
|
+
versionConstraint = key.substring(atIdx + 1);
|
|
4501
|
+
}
|
|
4502
|
+
if (typeof value === "string") {
|
|
4503
|
+
let resolvedValue = value;
|
|
4504
|
+
if (value.startsWith("$")) {
|
|
4505
|
+
const refPkgName = value.substring(1);
|
|
4506
|
+
const directDepVersion = this._getDirectDependencyVersion(refPkgName);
|
|
4507
|
+
if (directDepVersion) {
|
|
4508
|
+
resolvedValue = directDepVersion;
|
|
4509
|
+
} else {
|
|
4510
|
+
logger.warn(
|
|
4511
|
+
`Override reference $${refPkgName} not found in direct dependencies, using as-is`
|
|
4512
|
+
);
|
|
4513
|
+
resolvedValue = value;
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
4516
|
+
matchers.push({
|
|
4517
|
+
pkgName,
|
|
4518
|
+
versionConstraint,
|
|
4519
|
+
parentPath,
|
|
4520
|
+
replacement: resolvedValue
|
|
4521
|
+
});
|
|
4522
|
+
} else if (typeof value === "object" && value !== null) {
|
|
4523
|
+
const newParentPath = parentPath ? `${parentPath}/${pkgName}` : pkgName;
|
|
4524
|
+
const nestedMatchers = this._processOverrides(value, newParentPath);
|
|
4525
|
+
matchers.push(...nestedMatchers);
|
|
4526
|
+
}
|
|
4527
|
+
}
|
|
4528
|
+
return matchers;
|
|
4529
|
+
}
|
|
4530
|
+
/**
|
|
4531
|
+
* Get the version of a direct dependency from package.json
|
|
4532
|
+
* @param {string} pkgName - Package name to look up
|
|
4533
|
+
* @returns {string|null} The version or null if not found
|
|
4534
|
+
*/
|
|
4535
|
+
_getDirectDependencyVersion(pkgName) {
|
|
4536
|
+
const sections = ["dependencies", "devDependencies", "optionalDependencies"];
|
|
4537
|
+
for (const section of sections) {
|
|
4538
|
+
const version = _.get(this._pkg, [section, pkgName]);
|
|
4539
|
+
if (version) {
|
|
4540
|
+
return version;
|
|
4541
|
+
}
|
|
4024
4542
|
}
|
|
4543
|
+
return null;
|
|
4025
4544
|
}
|
|
4026
4545
|
async _startInstall() {
|
|
4027
4546
|
if (!this._distFetcher) {
|
|
@@ -4616,7 +5135,7 @@ const chalk = __webpack_require__("./node_modules/.f/_/chalk/4.1.2/chalk/source/
|
|
|
4616
5135
|
const _ = __webpack_require__("./node_modules/.f/_/lodash/4.17.21/lodash/lodash.min.js");
|
|
4617
5136
|
const logger = __webpack_require__("./lib/logger.ts");
|
|
4618
5137
|
const logFormat = __webpack_require__("./lib/util/log-format.ts");
|
|
4619
|
-
const VisualExec = __webpack_require__("./node_modules/.f/_/visual-exec/0.
|
|
5138
|
+
const { VisualExec } = __webpack_require__("./node_modules/.f/_/visual-exec/0.2.0-fynlocal_h/visual-exec/dist/index.js");
|
|
4620
5139
|
const fyntil = __webpack_require__("./lib/util/fyntil.ts");
|
|
4621
5140
|
const requireAt = __webpack_require__("./node_modules/.f/_/require-at/1.0.6/require-at/require-at.js");
|
|
4622
5141
|
const { setupNodeGypEnv } = __webpack_require__("./lib/util/setup-node-gyp.ts");
|
|
@@ -4737,7 +5256,7 @@ module.exports = LifecycleScripts;
|
|
|
4737
5256
|
const Path = __webpack_require__("path");
|
|
4738
5257
|
const logger = __webpack_require__("./lib/logger.ts");
|
|
4739
5258
|
const PromiseQueue = __webpack_require__("./lib/util/promise-queue.ts");
|
|
4740
|
-
const VisualExec = __webpack_require__("./node_modules/.f/_/visual-exec/0.
|
|
5259
|
+
const VisualExec = __webpack_require__("./node_modules/.f/_/visual-exec/0.2.0-fynlocal_h/visual-exec/dist/index.js");
|
|
4741
5260
|
const xaa = __webpack_require__("./node_modules/.f/_/xaa/2.0.0/xaa/dist-cjs/index.cjs");
|
|
4742
5261
|
const Fs = __webpack_require__("./lib/util/file-ops.ts");
|
|
4743
5262
|
const _ = __webpack_require__("./node_modules/.f/_/lodash/4.17.21/lodash/lodash.min.js");
|
|
@@ -4768,12 +5287,15 @@ class LocalPkgBuilder {
|
|
|
4768
5287
|
this._waitItems[data.item.fullPath].resolve({});
|
|
4769
5288
|
});
|
|
4770
5289
|
this._promiseQ.on("failItem", (data) => {
|
|
5290
|
+
const debugLog = Path.join(data.item.fullPath, "fyn-debug.log");
|
|
4771
5291
|
const itemRes = {
|
|
4772
5292
|
error: new AggregateError(
|
|
4773
5293
|
[data.error],
|
|
4774
|
-
`failed build local package at ${data.item.fullPath}`
|
|
5294
|
+
`failed build local package at ${data.item.fullPath} - check ${debugLog} for details`
|
|
4775
5295
|
)
|
|
4776
5296
|
};
|
|
5297
|
+
logger.error(`Failed to build local package at ${data.item.fullPath}`);
|
|
5298
|
+
logger.error(`Check debug log for details: ${debugLog}`);
|
|
4777
5299
|
this._waitItems[data.item.fullPath].resolve(itemRes);
|
|
4778
5300
|
this._failedItems[data.item.fullPath] = itemRes;
|
|
4779
5301
|
});
|
|
@@ -4857,8 +5379,9 @@ class LocalPkgBuilder {
|
|
|
4857
5379
|
process.argv[0],
|
|
4858
5380
|
this._fynJs,
|
|
4859
5381
|
this._fyn._options.registry && `--reg=${this._fyn._options.registry}`,
|
|
4860
|
-
"-q=d --pg=simple --no-build-local",
|
|
4861
|
-
!this._fyn._options.sourceMaps && "--no-source-maps"
|
|
5382
|
+
"-q=d --pg=simple --no-build-local --sl=fyn-debug.log",
|
|
5383
|
+
!this._fyn._options.sourceMaps && "--no-source-maps",
|
|
5384
|
+
"install --no-audit"
|
|
4862
5385
|
].filter((x) => x).join(" ");
|
|
4863
5386
|
const displayTitle = `building local pkg at ${dispPath}`;
|
|
4864
5387
|
logger.verbose(displayTitle);
|
|
@@ -6775,6 +7298,107 @@ ${item.depPath.join(" > ")}`
|
|
|
6775
7298
|
}
|
|
6776
7299
|
return void 0;
|
|
6777
7300
|
}
|
|
7301
|
+
/**
|
|
7302
|
+
* Apply npm-style overrides to a dependency item
|
|
7303
|
+
*
|
|
7304
|
+
* npm overrides differ from yarn resolutions:
|
|
7305
|
+
* - They apply to ALL instances of a package by default (not path-based)
|
|
7306
|
+
* - They can be scoped to specific parent packages
|
|
7307
|
+
* - They support version constraints on the source package
|
|
7308
|
+
*
|
|
7309
|
+
* @param {*} item - The dependency item to check for overrides
|
|
7310
|
+
* @returns {undefined}
|
|
7311
|
+
*/
|
|
7312
|
+
_applyOverrides(item) {
|
|
7313
|
+
if (!this._fyn._overridesMatchers || item._semver.$$) {
|
|
7314
|
+
return void 0;
|
|
7315
|
+
}
|
|
7316
|
+
const matchers = this._fyn._overridesMatchers;
|
|
7317
|
+
for (const matcher of matchers) {
|
|
7318
|
+
const { pkgName, versionConstraint, parentPath, replacement } = matcher;
|
|
7319
|
+
if (pkgName !== item.name) {
|
|
7320
|
+
continue;
|
|
7321
|
+
}
|
|
7322
|
+
if (versionConstraint) {
|
|
7323
|
+
if (!this._matchesVersionConstraint(item.semver, versionConstraint)) {
|
|
7324
|
+
continue;
|
|
7325
|
+
}
|
|
7326
|
+
}
|
|
7327
|
+
if (parentPath) {
|
|
7328
|
+
if (!this._matchesParentPath(item, parentPath)) {
|
|
7329
|
+
continue;
|
|
7330
|
+
}
|
|
7331
|
+
}
|
|
7332
|
+
if (replacement !== item.semver) {
|
|
7333
|
+
const parentInfo = parentPath ? ` (under ${parentPath})` : "";
|
|
7334
|
+
const constraintInfo = versionConstraint ? `@${versionConstraint}` : "";
|
|
7335
|
+
logger.info(
|
|
7336
|
+
`Override: ${item.name}${constraintInfo}${parentInfo} changed from ${item.semver} to ${replacement}`
|
|
7337
|
+
);
|
|
7338
|
+
semverUtil.replace(item._semver, replacement);
|
|
7339
|
+
return void 0;
|
|
7340
|
+
}
|
|
7341
|
+
}
|
|
7342
|
+
return void 0;
|
|
7343
|
+
}
|
|
7344
|
+
/**
|
|
7345
|
+
* Check if the item's semver matches the version constraint specified in the override key
|
|
7346
|
+
*
|
|
7347
|
+
* @param {string} itemSemver - The semver from the dependency
|
|
7348
|
+
* @param {string} constraint - The version constraint from override key (e.g., "^4.0.0", ">=1.0.0")
|
|
7349
|
+
* @returns {boolean}
|
|
7350
|
+
*/
|
|
7351
|
+
_matchesVersionConstraint(itemSemver, constraint) {
|
|
7352
|
+
if (Semver.valid(constraint)) {
|
|
7353
|
+
return Semver.satisfies(constraint, itemSemver);
|
|
7354
|
+
}
|
|
7355
|
+
try {
|
|
7356
|
+
return Semver.intersects(itemSemver, constraint);
|
|
7357
|
+
} catch {
|
|
7358
|
+
return itemSemver === constraint;
|
|
7359
|
+
}
|
|
7360
|
+
}
|
|
7361
|
+
/**
|
|
7362
|
+
* Check if the item's parent path matches the override's parent path constraint
|
|
7363
|
+
*
|
|
7364
|
+
* For example, if override is { "foo": { "bar": "1.0.0" } },
|
|
7365
|
+
* parentPath would be "foo" and we check if item's parent chain includes "foo"
|
|
7366
|
+
*
|
|
7367
|
+
* @param {*} item - The dependency item
|
|
7368
|
+
* @param {string} parentPath - The parent path from the override (e.g., "foo" or "foo/baz")
|
|
7369
|
+
* @returns {boolean}
|
|
7370
|
+
*/
|
|
7371
|
+
_matchesParentPath(item, parentPath) {
|
|
7372
|
+
if (!item.parent || item.parent.depth === 0) {
|
|
7373
|
+
return false;
|
|
7374
|
+
}
|
|
7375
|
+
const parentChain = [];
|
|
7376
|
+
let current = item.parent;
|
|
7377
|
+
while (current && current.depth > 0) {
|
|
7378
|
+
parentChain.unshift(current.name);
|
|
7379
|
+
current = current.parent;
|
|
7380
|
+
}
|
|
7381
|
+
const pathParts = parentPath.split("/").filter((p) => p);
|
|
7382
|
+
const normalizedParts = [];
|
|
7383
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
7384
|
+
if (pathParts[i].startsWith("@") && i + 1 < pathParts.length) {
|
|
7385
|
+
normalizedParts.push(`${pathParts[i]}/${pathParts[i + 1]}`);
|
|
7386
|
+
i++;
|
|
7387
|
+
} else {
|
|
7388
|
+
normalizedParts.push(pathParts[i]);
|
|
7389
|
+
}
|
|
7390
|
+
}
|
|
7391
|
+
if (normalizedParts.length > parentChain.length) {
|
|
7392
|
+
return false;
|
|
7393
|
+
}
|
|
7394
|
+
const startIdx = parentChain.length - normalizedParts.length;
|
|
7395
|
+
for (let i = 0; i < normalizedParts.length; i++) {
|
|
7396
|
+
if (parentChain[startIdx + i] !== normalizedParts[i]) {
|
|
7397
|
+
return false;
|
|
7398
|
+
}
|
|
7399
|
+
}
|
|
7400
|
+
return true;
|
|
7401
|
+
}
|
|
6778
7402
|
_resolveWithLockData(item) {
|
|
6779
7403
|
const isOpt = item.dsrc && item.dsrc.includes("opt");
|
|
6780
7404
|
if (isOpt && this._fyn.refreshOptionals) {
|
|
@@ -6843,6 +7467,7 @@ ${item.depPath.join(" > ")}`
|
|
|
6843
7467
|
return r;
|
|
6844
7468
|
});
|
|
6845
7469
|
};
|
|
7470
|
+
this._applyOverrides(item);
|
|
6846
7471
|
this._replaceWithResolutionsData(item);
|
|
6847
7472
|
const promise = !item.semverPath || this._fyn.preferLock ? tryLock().then((r) => r || item.semverPath && tryLocal()) : tryLocal().then((r) => r || tryLock());
|
|
6848
7473
|
return promise.then((r) => {
|
|
@@ -8063,7 +8688,7 @@ const longPending = __webpack_require__("./lib/long-pending.ts");
|
|
|
8063
8688
|
const { LOCAL_VERSION_MAPS, PACKAGE_RAW_INFO, DEP_ITEM } = __webpack_require__("./lib/symbols.ts");
|
|
8064
8689
|
const { LONG_WAIT_META, FETCH_META, FETCH_PACKAGE } = __webpack_require__("./lib/log-items.ts");
|
|
8065
8690
|
const PkgPreper = __webpack_require__("./node_modules/.f/_/pkg-preper/0.1.6-fynlocal_h/pkg-preper/lib/pkg-preper.js");
|
|
8066
|
-
const VisualExec = __webpack_require__("./node_modules/.f/_/visual-exec/0.
|
|
8691
|
+
const VisualExec = __webpack_require__("./node_modules/.f/_/visual-exec/0.2.0-fynlocal_h/visual-exec/dist/index.js");
|
|
8067
8692
|
const { readPkgJson, missPipe } = __webpack_require__("./lib/util/fyntil.ts");
|
|
8068
8693
|
const { MARK_URL_SPEC } = __webpack_require__("./lib/constants.ts");
|
|
8069
8694
|
const nodeFetch = __webpack_require__("./node_modules/.f/_/node-fetch-npm/2.0.4/node-fetch-npm/src/index.js");
|
|
@@ -8837,6 +9462,303 @@ module.exports = PkgSrcManager;
|
|
|
8837
9462
|
module.exports.META_CACHE_STALE_TIME = META_CACHE_STALE_TIME;
|
|
8838
9463
|
|
|
8839
9464
|
|
|
9465
|
+
/***/ }),
|
|
9466
|
+
|
|
9467
|
+
/***/ "./lib/pkg-stat-provider.ts":
|
|
9468
|
+
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
9469
|
+
|
|
9470
|
+
"use strict";
|
|
9471
|
+
|
|
9472
|
+
const _ = __webpack_require__("./node_modules/.f/_/lodash/4.17.21/lodash/lodash.min.js");
|
|
9473
|
+
const semverUtil = __webpack_require__("./lib/util/semver.ts");
|
|
9474
|
+
const PkgDepLinker = __webpack_require__("./lib/pkg-dep-linker.ts");
|
|
9475
|
+
const { SEMVER } = __webpack_require__("./lib/symbols.ts");
|
|
9476
|
+
const PACKAGE_JSON = "~package.json";
|
|
9477
|
+
class PkgStatProvider {
|
|
9478
|
+
_fyn;
|
|
9479
|
+
_fynRes;
|
|
9480
|
+
_dependentsCache;
|
|
9481
|
+
_allPaths;
|
|
9482
|
+
_circularDeps;
|
|
9483
|
+
constructor({ fyn }) {
|
|
9484
|
+
this._fyn = fyn;
|
|
9485
|
+
this._fynRes = null;
|
|
9486
|
+
this._dependentsCache = {};
|
|
9487
|
+
this._allPaths = [];
|
|
9488
|
+
this._circularDeps = [];
|
|
9489
|
+
}
|
|
9490
|
+
/**
|
|
9491
|
+
* Find packages matching the given ID pattern.
|
|
9492
|
+
*
|
|
9493
|
+
* @param pkgId - Package ID (name or name@semver)
|
|
9494
|
+
* @returns Array of matching package info
|
|
9495
|
+
*/
|
|
9496
|
+
findPkgsById(pkgId) {
|
|
9497
|
+
const pkgs = this._fyn._data?.pkgs;
|
|
9498
|
+
if (!pkgs) return [];
|
|
9499
|
+
const ix = pkgId.indexOf("@", 1);
|
|
9500
|
+
const sx = ix > 0 ? ix : pkgId.length;
|
|
9501
|
+
const name = pkgId.substr(0, sx);
|
|
9502
|
+
const semver = pkgId.substr(sx + 1);
|
|
9503
|
+
return _(pkgs[name]).map((vpkg, version) => {
|
|
9504
|
+
if (!semver || semverUtil.satisfies(version, semver)) {
|
|
9505
|
+
return vpkg;
|
|
9506
|
+
}
|
|
9507
|
+
}).filter((x) => x).value();
|
|
9508
|
+
}
|
|
9509
|
+
/**
|
|
9510
|
+
* Find all packages that depend on the given package.
|
|
9511
|
+
*
|
|
9512
|
+
* @param ask - Package info { name, version, local? }
|
|
9513
|
+
* @returns Array of dependent packages
|
|
9514
|
+
*/
|
|
9515
|
+
findDependents(ask) {
|
|
9516
|
+
const pkgs = this._fyn._data?.pkgs;
|
|
9517
|
+
if (!pkgs) return [];
|
|
9518
|
+
const dependents = [];
|
|
9519
|
+
if (!this._fynRes) {
|
|
9520
|
+
const depLinker = new PkgDepLinker({ fyn: this._fyn });
|
|
9521
|
+
this._fynRes = depLinker.makeAppFynRes(this._fyn._data.res, {});
|
|
9522
|
+
}
|
|
9523
|
+
const check = (res, vpkg) => {
|
|
9524
|
+
const semv = ask.local ? semverUtil.unlocalify(ask.version) : ask.version;
|
|
9525
|
+
if (res && semverUtil.satisfies(res.resolved, semv)) {
|
|
9526
|
+
dependents.push(vpkg);
|
|
9527
|
+
}
|
|
9528
|
+
};
|
|
9529
|
+
for (const name in pkgs) {
|
|
9530
|
+
const pkg = pkgs[name];
|
|
9531
|
+
for (const version in pkg) {
|
|
9532
|
+
const vpkg = pkg[version];
|
|
9533
|
+
["dep", "opt", "per"].forEach((s) => {
|
|
9534
|
+
const x = vpkg.res[s];
|
|
9535
|
+
check(x && x[ask.name], vpkg);
|
|
9536
|
+
});
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9539
|
+
check(this._fynRes[ask.name], { name: PACKAGE_JSON, promoted: true });
|
|
9540
|
+
return dependents;
|
|
9541
|
+
}
|
|
9542
|
+
/**
|
|
9543
|
+
* Get the package ID string.
|
|
9544
|
+
*/
|
|
9545
|
+
_getPkgId(pkg) {
|
|
9546
|
+
if (pkg.name === PACKAGE_JSON) {
|
|
9547
|
+
return pkg.name;
|
|
9548
|
+
}
|
|
9549
|
+
return `${pkg.name}@${pkg.version}`;
|
|
9550
|
+
}
|
|
9551
|
+
/**
|
|
9552
|
+
* Find dependency paths from root to the given package.
|
|
9553
|
+
*
|
|
9554
|
+
* @param pkgIds - Package IDs to trace
|
|
9555
|
+
* @param output - Current path being built
|
|
9556
|
+
* @param askName - Original package name being traced
|
|
9557
|
+
*/
|
|
9558
|
+
async _findDepPaths(pkgIds, output = [], askName = "") {
|
|
9559
|
+
const data = this._fyn._data;
|
|
9560
|
+
const pkgs = data?.pkgs;
|
|
9561
|
+
if (!pkgs) return;
|
|
9562
|
+
for (const pkgId of pkgIds) {
|
|
9563
|
+
const askPkgs = this.findPkgsById(pkgId).sort(
|
|
9564
|
+
(a, b) => semverUtil.simpleCompare(a.version, b.version)
|
|
9565
|
+
);
|
|
9566
|
+
if (askPkgs.length < 1) {
|
|
9567
|
+
if (pkgId === PACKAGE_JSON) {
|
|
9568
|
+
const newOutput = [].concat(output);
|
|
9569
|
+
["dependencies", "optionalDependencies", "peerDependencies", "devDependencies"].find((s) => {
|
|
9570
|
+
const semver = _.get(this._fyn, ["_pkg", s, askName]);
|
|
9571
|
+
if (semver) {
|
|
9572
|
+
newOutput[SEMVER] = semver;
|
|
9573
|
+
}
|
|
9574
|
+
return semver;
|
|
9575
|
+
});
|
|
9576
|
+
this._allPaths.push(newOutput);
|
|
9577
|
+
} else {
|
|
9578
|
+
this._allPaths.push([pkgId]);
|
|
9579
|
+
}
|
|
9580
|
+
continue;
|
|
9581
|
+
}
|
|
9582
|
+
for (const pkg of askPkgs) {
|
|
9583
|
+
const id = this._getPkgId(pkg);
|
|
9584
|
+
if (output.indexOf(id) >= 0) {
|
|
9585
|
+
this._circularDeps.push([id].concat(output));
|
|
9586
|
+
continue;
|
|
9587
|
+
}
|
|
9588
|
+
let dependents = this._dependentsCache[id];
|
|
9589
|
+
if (!dependents) {
|
|
9590
|
+
this._dependentsCache[id] = dependents = this.findDependents(pkg).sort((a, b) => {
|
|
9591
|
+
if (a.name === b.name) {
|
|
9592
|
+
return semverUtil.simpleCompare(a.version, b.version);
|
|
9593
|
+
}
|
|
9594
|
+
return a.name > b.name ? 1 : -1;
|
|
9595
|
+
});
|
|
9596
|
+
}
|
|
9597
|
+
const followIds = dependents.filter((x) => x.name !== PACKAGE_JSON).map((x) => `${x.name}@${x.version.replace("-fynlocal_h", "")}`);
|
|
9598
|
+
if (dependents.length > 0) {
|
|
9599
|
+
const newOutput = [id].concat(output);
|
|
9600
|
+
if (output.length === 1) {
|
|
9601
|
+
["dep", "opt", "per", "dev"].find((s) => {
|
|
9602
|
+
const sv = _.get(pkg, ["res", s, askName]);
|
|
9603
|
+
if (sv) {
|
|
9604
|
+
newOutput[SEMVER] = sv.semver;
|
|
9605
|
+
}
|
|
9606
|
+
return sv;
|
|
9607
|
+
});
|
|
9608
|
+
} else {
|
|
9609
|
+
newOutput[SEMVER] = output[SEMVER];
|
|
9610
|
+
}
|
|
9611
|
+
if (followIds.length > 0) {
|
|
9612
|
+
await this._findDepPaths(followIds, newOutput, askName);
|
|
9613
|
+
} else if (output) {
|
|
9614
|
+
this._allPaths.push(newOutput);
|
|
9615
|
+
}
|
|
9616
|
+
}
|
|
9617
|
+
}
|
|
9618
|
+
}
|
|
9619
|
+
}
|
|
9620
|
+
/**
|
|
9621
|
+
* Filter paths to show only the most significant ones.
|
|
9622
|
+
*
|
|
9623
|
+
* @param paths - All dependency paths
|
|
9624
|
+
* @param maxPaths - Maximum number of paths to return
|
|
9625
|
+
* @returns Filtered significant paths
|
|
9626
|
+
*/
|
|
9627
|
+
_filterSignificantPaths(paths, maxPaths = 5) {
|
|
9628
|
+
const cmpDepPath = (a, b) => {
|
|
9629
|
+
for (let ixA = 0; ixA < a.length; ixA++) {
|
|
9630
|
+
if (b.length <= ixA) return 1;
|
|
9631
|
+
const aId = a[ixA];
|
|
9632
|
+
const bId = b[ixA];
|
|
9633
|
+
if (aId !== bId) return aId > bId ? 1 : -1;
|
|
9634
|
+
}
|
|
9635
|
+
return 0;
|
|
9636
|
+
};
|
|
9637
|
+
paths = paths.sort((a, b) => a.length - b.length);
|
|
9638
|
+
let minDetails = 5;
|
|
9639
|
+
let briefPaths = paths;
|
|
9640
|
+
while (briefPaths.length > maxPaths && minDetails > 0) {
|
|
9641
|
+
const occurLevels = {};
|
|
9642
|
+
briefPaths = paths.filter((dp) => {
|
|
9643
|
+
const last = dp.length - 1;
|
|
9644
|
+
for (let ix = 0; ix < last; ix++) {
|
|
9645
|
+
const pkgId = dp[ix];
|
|
9646
|
+
const occur = occurLevels[pkgId];
|
|
9647
|
+
if (occur && occur.level < ix && occur.leaf === dp[last] && dp.length - ix > minDetails) {
|
|
9648
|
+
return false;
|
|
9649
|
+
}
|
|
9650
|
+
occurLevels[pkgId] = { level: ix, leaf: dp[last] };
|
|
9651
|
+
}
|
|
9652
|
+
return true;
|
|
9653
|
+
});
|
|
9654
|
+
minDetails--;
|
|
9655
|
+
}
|
|
9656
|
+
return briefPaths.sort(cmpDepPath);
|
|
9657
|
+
}
|
|
9658
|
+
/**
|
|
9659
|
+
* Get detailed stat information for a specific package version.
|
|
9660
|
+
*
|
|
9661
|
+
* @param name - Package name
|
|
9662
|
+
* @param version - Package version
|
|
9663
|
+
* @returns Stat result with dependents and paths
|
|
9664
|
+
*/
|
|
9665
|
+
async getPackageStat(name, version) {
|
|
9666
|
+
const pkgs = this._fyn._data?.pkgs;
|
|
9667
|
+
if (!pkgs || !pkgs[name] || !pkgs[name][version]) {
|
|
9668
|
+
return null;
|
|
9669
|
+
}
|
|
9670
|
+
const pkg = pkgs[name][version];
|
|
9671
|
+
const pkgId = `${name}@${version}`;
|
|
9672
|
+
const dependents = this.findDependents({ name, version, local: pkg.local }).sort((a, b) => {
|
|
9673
|
+
if (a.name === b.name) {
|
|
9674
|
+
return semverUtil.simpleCompare(a.version, b.version);
|
|
9675
|
+
}
|
|
9676
|
+
return a.name > b.name ? 1 : -1;
|
|
9677
|
+
});
|
|
9678
|
+
this._allPaths = [];
|
|
9679
|
+
this._circularDeps = [];
|
|
9680
|
+
const depIds = dependents.map((d) => this._getPkgId(d));
|
|
9681
|
+
await this._findDepPaths(depIds, [pkgId], name);
|
|
9682
|
+
const paths = this._allPaths.filter((p) => p[0] === pkgId || p.includes(pkgId));
|
|
9683
|
+
return {
|
|
9684
|
+
name,
|
|
9685
|
+
version,
|
|
9686
|
+
promoted: pkg.promoted || false,
|
|
9687
|
+
dependents: dependents.map((d) => ({
|
|
9688
|
+
name: d.name,
|
|
9689
|
+
version: d.version,
|
|
9690
|
+
promoted: d.promoted || false
|
|
9691
|
+
})),
|
|
9692
|
+
allPaths: paths,
|
|
9693
|
+
significantPaths: this._filterSignificantPaths(paths),
|
|
9694
|
+
circularDeps: this._circularDeps
|
|
9695
|
+
};
|
|
9696
|
+
}
|
|
9697
|
+
/**
|
|
9698
|
+
* Find all installed versions matching a package ID pattern.
|
|
9699
|
+
*
|
|
9700
|
+
* @param pkgId - Package ID (name or name@semver)
|
|
9701
|
+
* @returns Matching versions result
|
|
9702
|
+
*/
|
|
9703
|
+
findMatchingVersions(pkgId) {
|
|
9704
|
+
const matches = this.findPkgsById(pkgId).sort(
|
|
9705
|
+
(a, b) => semverUtil.simpleCompare(a.version, b.version)
|
|
9706
|
+
);
|
|
9707
|
+
return {
|
|
9708
|
+
searchId: pkgId,
|
|
9709
|
+
versions: matches.map((m) => ({
|
|
9710
|
+
name: m.name,
|
|
9711
|
+
version: m.version,
|
|
9712
|
+
promoted: m.promoted || false
|
|
9713
|
+
}))
|
|
9714
|
+
};
|
|
9715
|
+
}
|
|
9716
|
+
/**
|
|
9717
|
+
* Get stat for all versions of a package.
|
|
9718
|
+
* This is the main entry point for the audit formatter.
|
|
9719
|
+
*
|
|
9720
|
+
* @param name - Package name
|
|
9721
|
+
* @param version - Optional specific version
|
|
9722
|
+
* @returns Array of stat results for all matching versions
|
|
9723
|
+
*/
|
|
9724
|
+
async getPackageStats(name, version) {
|
|
9725
|
+
const pkgId = version ? `${name}@${version}` : name;
|
|
9726
|
+
const matches = this.findMatchingVersions(pkgId);
|
|
9727
|
+
const results = [];
|
|
9728
|
+
for (const match of matches.versions) {
|
|
9729
|
+
const stat = await this.getPackageStat(match.name, match.version);
|
|
9730
|
+
if (stat) {
|
|
9731
|
+
results.push(stat);
|
|
9732
|
+
}
|
|
9733
|
+
}
|
|
9734
|
+
return results;
|
|
9735
|
+
}
|
|
9736
|
+
/**
|
|
9737
|
+
* Format dependency paths with semver info.
|
|
9738
|
+
*
|
|
9739
|
+
* @param paths - Array of paths
|
|
9740
|
+
* @returns Formatted path strings
|
|
9741
|
+
*/
|
|
9742
|
+
formatPaths(paths) {
|
|
9743
|
+
return paths.map((p) => {
|
|
9744
|
+
const semver = p[SEMVER];
|
|
9745
|
+
const pathStr = p.join(" > ");
|
|
9746
|
+
return semver ? `${pathStr} (${semver})` : pathStr;
|
|
9747
|
+
});
|
|
9748
|
+
}
|
|
9749
|
+
/**
|
|
9750
|
+
* Reset caches. Call this if fyn data changes.
|
|
9751
|
+
*/
|
|
9752
|
+
reset() {
|
|
9753
|
+
this._fynRes = null;
|
|
9754
|
+
this._dependentsCache = {};
|
|
9755
|
+
this._allPaths = [];
|
|
9756
|
+
this._circularDeps = [];
|
|
9757
|
+
}
|
|
9758
|
+
}
|
|
9759
|
+
module.exports = PkgStatProvider;
|
|
9760
|
+
|
|
9761
|
+
|
|
8840
9762
|
/***/ }),
|
|
8841
9763
|
|
|
8842
9764
|
/***/ "./lib/symbols.ts":
|
|
@@ -102709,63 +103631,85 @@ module.exports = validate;
|
|
|
102709
103631
|
|
|
102710
103632
|
/***/ }),
|
|
102711
103633
|
|
|
102712
|
-
/***/ "./node_modules/.f/_/visual-exec/0.
|
|
102713
|
-
/***/ ((
|
|
103634
|
+
/***/ "./node_modules/.f/_/visual-exec/0.2.0-fynlocal_h/visual-exec/dist/get-default-logger.js":
|
|
103635
|
+
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
102714
103636
|
|
|
102715
103637
|
"use strict";
|
|
102716
103638
|
|
|
102717
|
-
|
|
102718
|
-
|
|
102719
|
-
|
|
102720
|
-
|
|
103639
|
+
var __importDefault = this && this.__importDefault || function(mod) {
|
|
103640
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
103641
|
+
};
|
|
103642
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
103643
|
+
exports.getDefaultLogger = getDefaultLogger;
|
|
103644
|
+
const visual_logger_1 = __importDefault(__webpack_require__("./node_modules/.f/_/visual-logger/1.1.3/visual-logger/lib/visual-logger.js"));
|
|
103645
|
+
const ci_info_1 = __webpack_require__("./node_modules/.f/_/ci-info/3.9.0/ci-info/index.js");
|
|
103646
|
+
let logger = null;
|
|
103647
|
+
function getDefaultLogger() {
|
|
102721
103648
|
if (!logger) {
|
|
102722
|
-
logger = new
|
|
102723
|
-
if (isCI) {
|
|
103649
|
+
logger = new visual_logger_1.default();
|
|
103650
|
+
if (ci_info_1.isCI) {
|
|
102724
103651
|
logger.info("visual-exec: CI env detected");
|
|
102725
103652
|
logger.setItemType("none");
|
|
102726
103653
|
}
|
|
102727
103654
|
}
|
|
102728
103655
|
return logger;
|
|
102729
|
-
}
|
|
102730
|
-
|
|
103656
|
+
}
|
|
103657
|
+
exports["default"] = getDefaultLogger;
|
|
102731
103658
|
|
|
102732
103659
|
|
|
102733
103660
|
/***/ }),
|
|
102734
103661
|
|
|
102735
|
-
/***/ "./node_modules/.f/_/visual-exec/0.
|
|
102736
|
-
/***/ ((
|
|
103662
|
+
/***/ "./node_modules/.f/_/visual-exec/0.2.0-fynlocal_h/visual-exec/dist/index.js":
|
|
103663
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
102737
103664
|
|
|
102738
103665
|
"use strict";
|
|
102739
103666
|
|
|
102740
|
-
|
|
102741
|
-
|
|
102742
|
-
|
|
102743
|
-
|
|
102744
|
-
|
|
102745
|
-
|
|
102746
|
-
|
|
103667
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
103668
|
+
exports["default"] = exports.getDefaultLogger = exports.VisualExec = void 0;
|
|
103669
|
+
var visual_exec_1 = __webpack_require__("./node_modules/.f/_/visual-exec/0.2.0-fynlocal_h/visual-exec/dist/visual-exec.js");
|
|
103670
|
+
Object.defineProperty(exports, "VisualExec", ({ enumerable: true, get: function() {
|
|
103671
|
+
return visual_exec_1.VisualExec;
|
|
103672
|
+
} }));
|
|
103673
|
+
var get_default_logger_1 = __webpack_require__("./node_modules/.f/_/visual-exec/0.2.0-fynlocal_h/visual-exec/dist/get-default-logger.js");
|
|
103674
|
+
Object.defineProperty(exports, "getDefaultLogger", ({ enumerable: true, get: function() {
|
|
103675
|
+
return get_default_logger_1.getDefaultLogger;
|
|
103676
|
+
} }));
|
|
103677
|
+
var visual_exec_2 = __webpack_require__("./node_modules/.f/_/visual-exec/0.2.0-fynlocal_h/visual-exec/dist/visual-exec.js");
|
|
103678
|
+
Object.defineProperty(exports, "default", ({ enumerable: true, get: function() {
|
|
103679
|
+
return visual_exec_2.VisualExec;
|
|
103680
|
+
} }));
|
|
103681
|
+
|
|
103682
|
+
|
|
103683
|
+
/***/ }),
|
|
103684
|
+
|
|
103685
|
+
/***/ "./node_modules/.f/_/visual-exec/0.2.0-fynlocal_h/visual-exec/dist/visual-exec.js":
|
|
103686
|
+
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
103687
|
+
|
|
103688
|
+
"use strict";
|
|
103689
|
+
|
|
103690
|
+
var __importDefault = this && this.__importDefault || function(mod) {
|
|
103691
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
103692
|
+
};
|
|
103693
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
103694
|
+
exports.VisualExec = void 0;
|
|
103695
|
+
const xsh_1 = __importDefault(__webpack_require__("./node_modules/.f/_/xsh/0.4.6-fynlocal_h/xsh/lib/index.js"));
|
|
103696
|
+
const chalk_1 = __importDefault(__webpack_require__("./node_modules/.f/_/chalk/4.1.2/chalk/source/index.js"));
|
|
103697
|
+
const get_default_logger_1 = __webpack_require__("./node_modules/.f/_/visual-exec/0.2.0-fynlocal_h/visual-exec/dist/get-default-logger.js");
|
|
103698
|
+
const visual_logger_1 = __importDefault(__webpack_require__("./node_modules/.f/_/visual-logger/1.1.3/visual-logger/lib/visual-logger.js"));
|
|
103699
|
+
const has_ansi_1 = __importDefault(__webpack_require__("./node_modules/.f/_/has-ansi/4.0.1/has-ansi/index.js"));
|
|
103700
|
+
const strip_ansi_1 = __importDefault(__webpack_require__("./node_modules/.f/_/strip-ansi/6.0.1/strip-ansi/index.js"));
|
|
103701
|
+
xsh_1.default.Promise = Promise;
|
|
102747
103702
|
const ONE_MB = 1024 * 1024;
|
|
102748
103703
|
const TEN_MB = 10 * ONE_MB;
|
|
102749
103704
|
class VisualExec {
|
|
102750
|
-
constructor({
|
|
102751
|
-
command,
|
|
102752
|
-
cwd = process.cwd(),
|
|
102753
|
-
visualLogger,
|
|
102754
|
-
spinner = VisualLogger.spinners[1],
|
|
102755
|
-
displayTitle = void 0,
|
|
102756
|
-
logLabel = void 0,
|
|
102757
|
-
outputLabel = void 0,
|
|
102758
|
-
outputLevel = "verbose",
|
|
102759
|
-
maxBuffer = TEN_MB,
|
|
102760
|
-
forceStderr = true,
|
|
102761
|
-
checkStdoutError = true
|
|
102762
|
-
}) {
|
|
103705
|
+
constructor(options) {
|
|
103706
|
+
const { command, cwd = process.cwd(), visualLogger, spinner = visual_logger_1.default.spinners[1], displayTitle, logLabel, outputLabel, outputLevel = "verbose", maxBuffer = TEN_MB, forceStderr = true, checkStdoutError = true } = options;
|
|
102763
103707
|
this._title = displayTitle || this._makeTitle(command);
|
|
102764
103708
|
this._logLabel = logLabel || this._title;
|
|
102765
103709
|
this._outputLabel = outputLabel || this._title;
|
|
102766
103710
|
this._command = command;
|
|
102767
103711
|
this._cwd = cwd || process.cwd();
|
|
102768
|
-
this._logger = visualLogger || getDefaultLogger();
|
|
103712
|
+
this._logger = visualLogger || (0, get_default_logger_1.getDefaultLogger)();
|
|
102769
103713
|
this._outputLevel = outputLevel;
|
|
102770
103714
|
this._spinner = spinner;
|
|
102771
103715
|
this._maxBuffer = maxBuffer;
|
|
@@ -102779,11 +103723,10 @@ class VisualExec {
|
|
|
102779
103723
|
}
|
|
102780
103724
|
return `Running ${command}`;
|
|
102781
103725
|
}
|
|
102782
|
-
/* eslint-disable max-statements */
|
|
102783
103726
|
_updateDigest(item, buf) {
|
|
102784
103727
|
const newBuf = item.buf + buf;
|
|
102785
103728
|
const lines = newBuf.split("\n").map((x) => x && x.trim()).filter((x) => x);
|
|
102786
|
-
const stripLines = lines.map((x) =>
|
|
103729
|
+
const stripLines = lines.map((x) => (0, has_ansi_1.default)(x) ? (0, strip_ansi_1.default)(x) : x);
|
|
102787
103730
|
let length = 0;
|
|
102788
103731
|
let ix = stripLines.length - 1;
|
|
102789
103732
|
for (; ix >= 0; ix--) {
|
|
@@ -102810,7 +103753,7 @@ class VisualExec {
|
|
|
102810
103753
|
item.buf += "\n";
|
|
102811
103754
|
}
|
|
102812
103755
|
this._logger.updateItem(item.name, {
|
|
102813
|
-
msg: msgs.join(
|
|
103756
|
+
msg: msgs.join(chalk_1.default.blue.inverse("\\n")),
|
|
102814
103757
|
_save: false,
|
|
102815
103758
|
_render: false
|
|
102816
103759
|
});
|
|
@@ -102852,51 +103795,58 @@ stdout`,
|
|
|
102852
103795
|
child.stdout.removeListener("data", this._updateStdout);
|
|
102853
103796
|
child.stderr.removeListener("data", this._updateStderr);
|
|
102854
103797
|
if (err) {
|
|
102855
|
-
this._logger.error(`${
|
|
103798
|
+
this._logger.error(`${chalk_1.default.red("Failed")} ${this._logLabel} - ${chalk_1.default.red(err.message)}`);
|
|
102856
103799
|
output = err.output;
|
|
102857
103800
|
} else {
|
|
102858
103801
|
const time = ((Date.now() - this._startTime) / 1e3).toFixed(2);
|
|
102859
|
-
const dispTime = `${
|
|
102860
|
-
this._logger.info(`Done ${this._logLabel} ${dispTime} ${
|
|
103802
|
+
const dispTime = `${chalk_1.default.magenta(time)}secs`;
|
|
103803
|
+
this._logger.info(`Done ${this._logLabel} ${dispTime} ${chalk_1.default.green("exit code 0")}`);
|
|
102861
103804
|
}
|
|
102862
103805
|
this.logFinalOutput(err, output);
|
|
102863
103806
|
}
|
|
102864
103807
|
checkForErrors(text) {
|
|
102865
|
-
|
|
103808
|
+
if (!this._checkStdoutError || !text)
|
|
103809
|
+
return null;
|
|
103810
|
+
if (this._checkStdoutError instanceof RegExp) {
|
|
103811
|
+
return text.match(this._checkStdoutError);
|
|
103812
|
+
}
|
|
103813
|
+
return null;
|
|
102866
103814
|
}
|
|
103815
|
+
/**
|
|
103816
|
+
* Log the final output. Can be overridden to customize output handling.
|
|
103817
|
+
* Set to a no-op function to suppress output logging.
|
|
103818
|
+
*/
|
|
102867
103819
|
logFinalOutput(err, output) {
|
|
102868
|
-
const level = err || this._forceStderr && output
|
|
103820
|
+
const level = err || this._forceStderr && output?.stderr || this.checkForErrors(output?.stdout || "") ? "error" : this._outputLevel;
|
|
102869
103821
|
if (!output || !output.stdout && !output.stderr) {
|
|
102870
|
-
this._logger[level](`${
|
|
103822
|
+
this._logger[level](`${chalk_1.default.green("No output")} from ${this._outputLabel}`);
|
|
102871
103823
|
return;
|
|
102872
103824
|
}
|
|
102873
|
-
const colorize = (t) => t.replace(/ERR!/g,
|
|
102874
|
-
const logs = [
|
|
103825
|
+
const colorize = (t) => t.replace(/ERR!/g, chalk_1.default.red("ERR!"));
|
|
103826
|
+
const logs = [chalk_1.default.green(">>>"), `Start of output from ${this._outputLabel} ===`];
|
|
102875
103827
|
if (output.stdout) {
|
|
102876
103828
|
logs.push(`
|
|
102877
103829
|
${colorize(output.stdout)}`);
|
|
102878
103830
|
}
|
|
102879
103831
|
if (output.stderr) {
|
|
102880
|
-
logs.push(
|
|
103832
|
+
logs.push(chalk_1.default.red("\n=== stderr ===\n") + colorize(output.stderr));
|
|
102881
103833
|
}
|
|
102882
|
-
logs.push(
|
|
103834
|
+
logs.push(chalk_1.default.blue("\n<<<"), `End of output from ${this._outputLabel} ---`);
|
|
102883
103835
|
this._logger.prefix(false)[level](...logs);
|
|
102884
103836
|
}
|
|
102885
103837
|
execute(command) {
|
|
102886
103838
|
this._startTime = Date.now();
|
|
102887
|
-
const child =
|
|
102888
|
-
|
|
102889
|
-
|
|
102890
|
-
|
|
102891
|
-
|
|
102892
|
-
|
|
102893
|
-
},
|
|
102894
|
-
command || this._command
|
|
102895
|
-
);
|
|
103839
|
+
const child = xsh_1.default.exec({
|
|
103840
|
+
silent: true,
|
|
103841
|
+
cwd: this._cwd,
|
|
103842
|
+
env: Object.assign({}, process.env, { PWD: this._cwd }),
|
|
103843
|
+
maxBuffer: this._maxBuffer
|
|
103844
|
+
}, command || this._command);
|
|
102896
103845
|
return this.show(child);
|
|
102897
103846
|
}
|
|
102898
103847
|
}
|
|
102899
|
-
|
|
103848
|
+
exports.VisualExec = VisualExec;
|
|
103849
|
+
exports["default"] = VisualExec;
|
|
102900
103850
|
|
|
102901
103851
|
|
|
102902
103852
|
/***/ }),
|