seaworthycode 1.2.8 → 1.2.10

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/index.js CHANGED
@@ -782,6 +782,7 @@ var copy = {
782
782
  "report.bySeverity": "By severity",
783
783
  "report.tierWatermark": (licensedTo) => `\u2693 Pro \xB7 ${licensedTo}`,
784
784
  "report.scanProgress.starting": (targetDir) => `\u2693 Scanning ${targetDir}...`,
785
+ "report.scanProgress.checking": (completed, total, checkName) => `\u2693 Running checks... [${completed}/${total}] ${checkName}`,
785
786
  "report.scanProgress.category": (label, count) => count > 0 ? ` \u2693 ${label} \u2014 ${count} issue(s)` : ` \u2693 ${label} \u2713`,
786
787
  "report.licenseNotice": (licensedTo) => `Licensed to ${licensedTo}`,
787
788
  "report.cta.clean": `\u2713 All clear \u2014 your project is shipshape!`,
@@ -1621,6 +1622,12 @@ var ScanRunner = class {
1621
1622
  };
1622
1623
  const results = [];
1623
1624
  const checkResults = /* @__PURE__ */ new Map();
1625
+ const categoryTotals = /* @__PURE__ */ new Map();
1626
+ for (const check2 of checks) {
1627
+ categoryTotals.set(check2.category, (categoryTotals.get(check2.category) ?? 0) + 1);
1628
+ }
1629
+ const categoryCompleted = /* @__PURE__ */ new Map();
1630
+ let completedCount = 0;
1624
1631
  await runWithConcurrency(
1625
1632
  checks,
1626
1633
  async (check2) => {
@@ -1630,26 +1637,26 @@ var ScanRunner = class {
1630
1637
  } catch (err) {
1631
1638
  console.error(`Check "${check2.id}" failed: ${String(err)}`);
1632
1639
  }
1640
+ options.onCheckComplete?.(++completedCount, checks.length, check2.name);
1641
+ if (options.onCategoryComplete) {
1642
+ const catDone = (categoryCompleted.get(check2.category) ?? 0) + 1;
1643
+ categoryCompleted.set(check2.category, catDone);
1644
+ if (catDone === categoryTotals.get(check2.category)) {
1645
+ let catFindingsCount = 0;
1646
+ for (const c of checks) {
1647
+ if (c.category === check2.category) {
1648
+ catFindingsCount += (checkResults.get(c.id) ?? []).length;
1649
+ }
1650
+ }
1651
+ options.onCategoryComplete(check2.category, catFindingsCount);
1652
+ }
1653
+ }
1633
1654
  },
1634
1655
  concurrency
1635
1656
  );
1636
1657
  for (const check2 of checks) {
1637
1658
  results.push(...checkResults.get(check2.id) ?? []);
1638
1659
  }
1639
- if (options.onCategoryComplete) {
1640
- const findingsPerCategory = /* @__PURE__ */ new Map();
1641
- for (const check2 of checks) {
1642
- const count = (checkResults.get(check2.id) ?? []).length;
1643
- findingsPerCategory.set(check2.category, (findingsPerCategory.get(check2.category) ?? 0) + count);
1644
- }
1645
- const seen = /* @__PURE__ */ new Set();
1646
- for (const check2 of checks) {
1647
- if (!seen.has(check2.category)) {
1648
- seen.add(check2.category);
1649
- options.onCategoryComplete(check2.category, findingsPerCategory.get(check2.category) ?? 0);
1650
- }
1651
- }
1652
- }
1653
1660
  ctx.astCache?.clear();
1654
1661
  for (const [, promise] of ctx.bashAstCache ?? []) {
1655
1662
  try {
@@ -15996,9 +16003,9 @@ function createCommand(definition, options) {
15996
16003
  }
15997
16004
 
15998
16005
  // src/commands/scan.ts
15999
- var FREE_SCAN_TIMEOUT_MS = 6e4;
16000
- var PRO_SCAN_TIMEOUT_MS = 6e5;
16001
- function resolveScanTimeoutMs(tier) {
16006
+ var FREE_CHECK_IDLE_MS = 6e4;
16007
+ var PRO_CHECK_IDLE_MS = 3e5;
16008
+ function resolveCheckIdleMs(tier) {
16002
16009
  const env2 = process.env.SEAWORTHY_SCAN_TIMEOUT_MS;
16003
16010
  if (env2) {
16004
16011
  const parsed = Number(env2);
@@ -16006,7 +16013,7 @@ function resolveScanTimeoutMs(tier) {
16006
16013
  return parsed;
16007
16014
  }
16008
16015
  }
16009
- return tier === "free" ? FREE_SCAN_TIMEOUT_MS : PRO_SCAN_TIMEOUT_MS;
16016
+ return tier === "free" ? FREE_CHECK_IDLE_MS : PRO_CHECK_IDLE_MS;
16010
16017
  }
16011
16018
  var CATEGORY_LABELS = {
16012
16019
  security: getCopy2("cli.category.security"),
@@ -16016,17 +16023,29 @@ var CATEGORY_LABELS = {
16016
16023
  configuration: getCopy2("cli.category.configuration"),
16017
16024
  "data-exposure": getCopy2("cli.category.data-exposure")
16018
16025
  };
16019
- function withTimeout(promise, ms, message) {
16020
- return new Promise((resolve5, reject) => {
16021
- const timer = setTimeout(() => reject(new Error(message)), ms);
16022
- promise.then((value) => {
16023
- clearTimeout(timer);
16024
- resolve5(value);
16025
- }).catch((err) => {
16026
- clearTimeout(timer);
16027
- reject(err);
16028
- });
16026
+ function withIdleTimeout(promise, idleMs, message) {
16027
+ let timerId;
16028
+ let rejectFn = () => {
16029
+ };
16030
+ const heartbeat = () => {
16031
+ clearTimeout(timerId);
16032
+ timerId = setTimeout(() => rejectFn(new Error(message)), idleMs);
16033
+ };
16034
+ const wrappedPromise = new Promise((resolve5, reject) => {
16035
+ rejectFn = reject;
16036
+ heartbeat();
16037
+ promise.then(
16038
+ (value) => {
16039
+ clearTimeout(timerId);
16040
+ resolve5(value);
16041
+ },
16042
+ (err) => {
16043
+ clearTimeout(timerId);
16044
+ reject(err);
16045
+ }
16046
+ );
16029
16047
  });
16048
+ return { promise: wrappedPromise, heartbeat };
16030
16049
  }
16031
16050
  function isInsideDirectory(outputPath, targetDir) {
16032
16051
  const resolvedOutput = resolve2(outputPath);
@@ -16058,24 +16077,36 @@ async function executeScan(args, ctx) {
16058
16077
  process.stderr.write(getCopy("report.scanProgress.starting", args.targetDir) + "\n");
16059
16078
  }
16060
16079
  const runner = new ScanRunner();
16061
- const scanTimeoutMs = resolveScanTimeoutMs(ctx.tier);
16062
- const result = await withTimeout(
16063
- runner.run({
16064
- targetDir: args.targetDir,
16065
- tier: ctx.tier,
16066
- llm,
16067
- maxFileSize: args.maxFileSize,
16068
- debug: args.debug,
16069
- baseline: args.baseline,
16070
- minConfidence: args.minConfidence,
16071
- onCategoryComplete: showProgress ? (category, count) => {
16072
- const label = CATEGORY_LABELS[category] ?? category;
16073
- process.stderr.write(getCopy("report.scanProgress.category", label, count) + "\n");
16074
- } : void 0
16075
- }),
16076
- scanTimeoutMs,
16077
- getCopy("errors.scan.timed_out", Math.round(scanTimeoutMs / 1e3))
16080
+ const checkIdleMs = resolveCheckIdleMs(ctx.tier);
16081
+ let heartbeat = () => {
16082
+ };
16083
+ const scanPromise = runner.run({
16084
+ targetDir: args.targetDir,
16085
+ tier: ctx.tier,
16086
+ llm,
16087
+ maxFileSize: args.maxFileSize,
16088
+ debug: args.debug,
16089
+ baseline: args.baseline,
16090
+ minConfidence: args.minConfidence,
16091
+ onCheckComplete: (completed, total, checkName) => {
16092
+ heartbeat();
16093
+ if (showProgress) {
16094
+ process.stderr.write(`\r${getCopy("report.scanProgress.checking", completed, total, checkName)}\x1B[K`);
16095
+ }
16096
+ },
16097
+ onCategoryComplete: showProgress ? (category, count) => {
16098
+ const label = CATEGORY_LABELS[category] ?? category;
16099
+ process.stderr.write(`\r\x1B[K${getCopy("report.scanProgress.category", label, count)}
16100
+ `);
16101
+ } : void 0
16102
+ });
16103
+ const { promise: timedScanPromise, heartbeat: hb } = withIdleTimeout(
16104
+ scanPromise,
16105
+ checkIdleMs,
16106
+ getCopy("errors.scan.timed_out", Math.round(checkIdleMs / 1e3))
16078
16107
  );
16108
+ heartbeat = hb;
16109
+ const result = await timedScanPromise;
16079
16110
  const reportInput = {
16080
16111
  findings: result.findings,
16081
16112
  targetDir: args.targetDir,