@vibgrate/cli 1.0.66 → 1.0.67

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.
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  baselineCommand,
3
3
  runBaseline
4
- } from "./chunk-IHDUX5MC.js";
5
- import "./chunk-DMYMJUQP.js";
4
+ } from "./chunk-NASOHLRL.js";
5
+ import "./chunk-56UAFVE4.js";
6
6
  import "./chunk-TBE6NQ5Z.js";
7
7
  export {
8
8
  baselineCommand,
@@ -319,20 +319,6 @@ function formatText(artifact) {
319
319
  if (artifact.extended?.architecture) {
320
320
  lines.push(...formatArchitectureDiagram(artifact.extended.architecture));
321
321
  }
322
- if (artifact.relationshipDiagram?.mermaid) {
323
- lines.push(chalk.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
324
- lines.push(chalk.bold.cyan("\u2551 Project Relationship Diagram \u2551"));
325
- lines.push(chalk.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
326
- lines.push("");
327
- lines.push(chalk.bold(" Mermaid") + chalk.dim(" (copy into https://mermaid.live or a ```mermaid code block)"));
328
- lines.push("");
329
- lines.push(chalk.dim(" ```mermaid"));
330
- for (const mLine of artifact.relationshipDiagram.mermaid.split("\n")) {
331
- lines.push(chalk.dim(` ${mLine}`));
332
- }
333
- lines.push(chalk.dim(" ```"));
334
- lines.push("");
335
- }
336
322
  if (artifact.solutions && artifact.solutions.length > 0) {
337
323
  lines.push(chalk.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
338
324
  lines.push(chalk.bold.cyan("\u2551 Solution Drift Summary \u2551"));
@@ -1308,7 +1294,9 @@ function compactApiSurface(result) {
1308
1294
  parameters: [],
1309
1295
  // Don't include params
1310
1296
  configOptions: [],
1311
- authHints: []
1297
+ authHints: [],
1298
+ files: []
1299
+ // Don't include file paths
1312
1300
  }));
1313
1301
  return {
1314
1302
  integrations: uniqueIntegrations,
@@ -7726,6 +7714,17 @@ function extract(content, pattern, sourceFile) {
7726
7714
  }
7727
7715
  return out;
7728
7716
  }
7717
+ var LOCKFILE_NAMES = /* @__PURE__ */ new Set([
7718
+ "package-lock.json",
7719
+ "pnpm-lock.yaml",
7720
+ "yarn.lock",
7721
+ "composer.lock",
7722
+ "Gemfile.lock",
7723
+ "Cargo.lock",
7724
+ "poetry.lock",
7725
+ "Pipfile.lock",
7726
+ "packages.lock.json"
7727
+ ]);
7729
7728
  function detectDbBrand(raw) {
7730
7729
  const value = raw.toLowerCase();
7731
7730
  if (value.includes("postgres")) return { kind: "sql", brand: "PostgreSQL", version: null, evidence: raw };
@@ -7736,6 +7735,7 @@ function detectDbBrand(raw) {
7736
7735
  if (value.includes("mongodb")) return { kind: "nosql", brand: "MongoDB", version: null, evidence: raw };
7737
7736
  if (value.includes("redis")) return { kind: "nosql", brand: "Redis", version: null, evidence: raw };
7738
7737
  if (value.includes("cassandra")) return { kind: "nosql", brand: "Cassandra", version: null, evidence: raw };
7738
+ if (value.includes("cosmosdb") || value.includes("@azure/cosmos") || value.includes("cosmos")) return { kind: "nosql", brand: "CosmosDB", version: null, evidence: raw };
7739
7739
  if (value.includes("dynamodb")) return { kind: "nosql", brand: "DynamoDB", version: null, evidence: raw };
7740
7740
  if (value.includes("neo4j")) return { kind: "nosql", brand: "Neo4j", version: null, evidence: raw };
7741
7741
  return null;
@@ -7817,13 +7817,16 @@ async function scanDataStores(rootDir, fileCache) {
7817
7817
  for (const file of files) {
7818
7818
  const connStrings = extract(file.content, /\b(?:postgres(?:ql)?:\/\/[^\s'"`]+|mysql:\/\/[^\s'"`]+|mongodb(?:\+srv)?:\/\/[^\s'"`]+|redis:\/\/[^\s'"`]+)\b/gi, file.relPath);
7819
7819
  result.connectionStrings.push(...connStrings);
7820
- const dbEvidence = [
7821
- ...extract(file.content, /\b(?:postgres|postgresql|mysql|mariadb|mssql|sqlserver|oracle|sqlite|mongodb|redis|cassandra|dynamodb|neo4j)\b/gi, file.relPath),
7822
- ...connStrings
7823
- ];
7824
- for (const evidence of dbEvidence) {
7825
- const detected = detectDbBrand(evidence);
7826
- if (detected) dbTechnologies.push(detected);
7820
+ const isLockFile = LOCKFILE_NAMES.has(path23.basename(file.relPath));
7821
+ if (!isLockFile) {
7822
+ const dbEvidence = [
7823
+ ...extract(file.content, /\b(?:postgres|postgresql|mysql|mariadb|mssql|sqlserver|oracle|sqlite|mongodb|redis|cassandra|cosmosdb|cosmos|dynamodb|neo4j)\b/gi, file.relPath),
7824
+ ...connStrings
7825
+ ];
7826
+ for (const evidence of dbEvidence) {
7827
+ const detected = detectDbBrand(evidence);
7828
+ if (detected) dbTechnologies.push(detected);
7829
+ }
7827
7830
  }
7828
7831
  result.connectionPoolSettings.push(...extract(file.content, /\b(?:pool(?:Size|_size)?|maxPoolSize|minPoolSize|connectionLimit)\b[^\n]*/gi, file.relPath));
7829
7832
  result.replicationSettings.push(...extract(file.content, /\b(?:replication|cluster|replicaSet)\b[^\n]*/gi, file.relPath));
@@ -7840,7 +7843,10 @@ async function scanDataStores(rootDir, fileCache) {
7840
7843
  result.otherServices.push(...extract(file.content, /\b(?:redis:\/\/[^\s'"`]+|amqp:\/\/[^\s'"`]+|kafka:\/\/[^\s'"`]+|elasticsearch:\/\/[^\s'"`]+)\b/gi, file.relPath));
7841
7844
  }
7842
7845
  const dedupDb = /* @__PURE__ */ new Map();
7843
- for (const db of dbTechnologies) dedupDb.set(`${db.kind}:${db.brand}:${db.evidence}`, db);
7846
+ for (const db of dbTechnologies) {
7847
+ const key = `${db.kind}:${db.brand}`;
7848
+ if (!dedupDb.has(key)) dedupDb.set(key, db);
7849
+ }
7844
7850
  result.databaseTechnologies = [...dedupDb.values()].sort((a, b) => a.brand.localeCompare(b.brand));
7845
7851
  result.connectionStrings = uniq(result.connectionStrings);
7846
7852
  result.connectionPoolSettings = uniq(result.connectionPoolSettings);
@@ -7870,6 +7876,17 @@ function detectOpenApiSpecification(relPath, content) {
7870
7876
  const endpointCount = content.match(/(^|\n)\s*\/[^\n:]+:\s*(get|post|put|patch|delete|options|head)/gim)?.length ?? content.match(/"\/[^"]+"\s*:\s*\{/g)?.length ?? null;
7871
7877
  return { path: relPath, format, version, title, endpointCount };
7872
7878
  }
7879
+ function isInternalHost(hostname) {
7880
+ const h = hostname.toLowerCase();
7881
+ if (h === "localhost" || h === "127.0.0.1" || h === "::1" || h === "0.0.0.0") return true;
7882
+ if (/^192\.168\./.test(h) || /^10\./.test(h) || /^172\.(1[6-9]|2\d|3[01])\./.test(h)) return true;
7883
+ if (!h.includes(".")) return true;
7884
+ if (/^(example|test|localhost|local|internal)\b/.test(h)) return true;
7885
+ return false;
7886
+ }
7887
+ function normalizeUrl(raw) {
7888
+ return raw.replace(/[\u0000-\u001F\u007F-\u009F\u200B-\u200F\u202A-\u202E\u2066-\u2069\uFEFF]/g, "").trim();
7889
+ }
7873
7890
  async function scanApiSurface(rootDir, fileCache) {
7874
7891
  const files = await scanTextFiles(rootDir, fileCache);
7875
7892
  const integrations = [];
@@ -7889,18 +7906,29 @@ async function scanApiSurface(rootDir, fileCache) {
7889
7906
  for (const file of files) {
7890
7907
  const openApiSpec = detectOpenApiSpecification(file.relPath, file.content);
7891
7908
  if (openApiSpec) result.openApiSpecifications.push(openApiSpec);
7892
- const endpoints = extract(file.content, /\bhttps?:\/\/[^\s'"`]+/gi, file.relPath);
7893
- for (const endpoint of endpoints) {
7894
- const provider = endpoint.replace(/^https?:\/\//, "").split("/")[0] ?? "unknown";
7895
- const versionMatch = endpoint.match(/\/(v\d+(?:\.\d+)?)\b/i);
7896
- const params = endpoint.match(/[?&]([a-zA-Z0-9_.-]+)=/g)?.map((p) => p.replace(/[?&=]/g, "")) ?? [];
7909
+ const urlRegex = /\bhttps?:\/\/[^\s'"`]+/gi;
7910
+ let match;
7911
+ while ((match = urlRegex.exec(file.content)) !== null) {
7912
+ const rawUrl = match[0].replace(/[,;)\]}"'`]+$/, "");
7913
+ const cleanUrl = normalizeUrl(rawUrl);
7914
+ if (!cleanUrl.startsWith("http")) continue;
7915
+ let hostname;
7916
+ try {
7917
+ hostname = new URL(cleanUrl).hostname;
7918
+ } catch {
7919
+ continue;
7920
+ }
7921
+ if (isInternalHost(hostname)) continue;
7922
+ const versionMatch = cleanUrl.match(/\/(v\d+(?:\.\d+)?)\b/i);
7923
+ const params = cleanUrl.match(/[?&]([a-zA-Z0-9_.-]+)=/g)?.map((p) => p.replace(/[?&=]/g, "")) ?? [];
7897
7924
  integrations.push({
7898
- provider,
7899
- endpoint,
7925
+ provider: hostname,
7926
+ endpoint: cleanUrl,
7900
7927
  version: versionMatch ? versionMatch[1] : null,
7901
7928
  parameters: params,
7902
7929
  configOptions: extract(file.content, /\b(?:baseUrl|apiUrl|endpoint|timeout|retries|apiVersion)\b[^\n]*/gi, file.relPath),
7903
- authHints: extract(file.content, /\b(?:api[_-]?key|bearer\s+[a-z0-9_.-]+|client[_-]?secret|oauth)\b[^\n]*/gi, file.relPath)
7930
+ authHints: extract(file.content, /\b(?:api[_-]?key|bearer\s+[a-z0-9_.-]+|client[_-]?secret|oauth)\b[^\n]*/gi, file.relPath),
7931
+ files: [file.relPath]
7904
7932
  });
7905
7933
  }
7906
7934
  result.webhookUrls.push(...extract(file.content, /\bhttps?:\/\/[^\s'"`]*(?:webhook|hooks?)[^\s'"`]*/gi, file.relPath));
@@ -7910,18 +7938,27 @@ async function scanApiSurface(rootDir, fileCache) {
7910
7938
  result.rateLimitOverrides.push(...extract(file.content, /\b(?:rate[_-]?limit|max[_-]?requests|throttle)\b[^\n]*/gi, file.relPath));
7911
7939
  result.customHeaders.push(...extract(file.content, /\b(?:x-[a-z0-9-]+|authorization|user-agent)\b[^\n]*/gi, file.relPath));
7912
7940
  result.corsPolicies.push(...extract(file.content, /\b(?:cors|access-control-allow-origin|allowedOrigins)\b[^\n]*/gi, file.relPath));
7913
- result.oauthScopes.push(...extract(file.content, /\b(?:scope|scopes)\b[^\n]*(?:openid|profile|email|read|write)/gi, file.relPath));
7941
+ result.oauthScopes.push(...extract(file.content, /\bscopes?\s*[=:]\s*['"][^'"]*(?:openid|profile|email|offline_access|read(?::\w+)?|write(?::\w+)?)[^'"]*['"]/gi, file.relPath));
7914
7942
  result.apiTokens.push(...extract(file.content, /\b(?:api[_-]?token|access[_-]?token|bearer[_-]?token)\b[^\n]*/gi, file.relPath));
7915
7943
  }
7916
7944
  const integrationMap = /* @__PURE__ */ new Map();
7917
7945
  for (const integration of integrations) {
7918
- const key = `${integration.provider}:${integration.endpoint}`;
7919
- integrationMap.set(key, {
7920
- ...integration,
7921
- parameters: uniq(integration.parameters),
7922
- configOptions: uniq(integration.configOptions),
7923
- authHints: uniq(integration.authHints)
7924
- });
7946
+ const key = integration.endpoint;
7947
+ const existing = integrationMap.get(key);
7948
+ if (existing) {
7949
+ existing.files = uniq([...existing.files, ...integration.files]);
7950
+ existing.parameters = uniq([...existing.parameters, ...integration.parameters]);
7951
+ existing.configOptions = uniq([...existing.configOptions, ...integration.configOptions]);
7952
+ existing.authHints = uniq([...existing.authHints, ...integration.authHints]);
7953
+ } else {
7954
+ integrationMap.set(key, {
7955
+ ...integration,
7956
+ parameters: uniq(integration.parameters),
7957
+ configOptions: uniq(integration.configOptions),
7958
+ authHints: uniq(integration.authHints),
7959
+ files: [...integration.files]
7960
+ });
7961
+ }
7925
7962
  }
7926
7963
  result.integrations = [...integrationMap.values()].sort((a, b) => a.provider.localeCompare(b.provider));
7927
7964
  result.openApiSpecifications = [...new Map(result.openApiSpecifications.map((spec) => [spec.path, spec])).values()].sort((a, b) => a.path.localeCompare(b.path));
@@ -7936,8 +7973,27 @@ async function scanApiSurface(rootDir, fileCache) {
7936
7973
  result.apiTokens = uniq(result.apiTokens);
7937
7974
  return result;
7938
7975
  }
7976
+ var NON_CODE_EXTENSIONS = /* @__PURE__ */ new Set([
7977
+ ".md",
7978
+ ".mdx",
7979
+ ".markdown",
7980
+ ".txt",
7981
+ ".rst",
7982
+ ".adoc",
7983
+ ".asciidoc",
7984
+ ".html",
7985
+ ".htm",
7986
+ ".pdf",
7987
+ ".docx",
7988
+ ".doc",
7989
+ ".rtf"
7990
+ ]);
7939
7991
  async function scanOperationalResilience(rootDir, fileCache) {
7940
- const files = await scanTextFiles(rootDir, fileCache);
7992
+ const allFiles = await scanTextFiles(rootDir, fileCache);
7993
+ const files = allFiles.filter((f) => {
7994
+ const ext = path23.extname(f.relPath).toLowerCase();
7995
+ return !NON_CODE_EXTENSIONS.has(ext);
7996
+ });
7941
7997
  const result = {
7942
7998
  implicitTimeouts: [],
7943
7999
  defaultPaginationSize: [],
@@ -8912,12 +8968,10 @@ async function runScan(rootDir, opts) {
8912
8968
  steps: progress.getStepTimings()
8913
8969
  });
8914
8970
  if (!opts.noLocalArtifacts && !maxPrivacyMode) {
8971
+ const projectScores = {};
8915
8972
  for (const project of allProjects) {
8916
8973
  if (project.drift && project.path) {
8917
- const projectDir = path24.resolve(rootDir, project.path);
8918
- const projectVibgrateDir = path24.join(projectDir, ".vibgrate");
8919
- await ensureDir(projectVibgrateDir);
8920
- await writeJsonFile(path24.join(projectVibgrateDir, "project_score.json"), {
8974
+ projectScores[project.path] = {
8921
8975
  projectId: project.projectId,
8922
8976
  name: project.name,
8923
8977
  type: project.type,
@@ -8930,9 +8984,14 @@ async function runScan(rootDir, opts) {
8930
8984
  vibgrateVersion: VERSION,
8931
8985
  solutionId: project.solutionId,
8932
8986
  solutionName: project.solutionName
8933
- });
8987
+ };
8934
8988
  }
8935
8989
  }
8990
+ if (Object.keys(projectScores).length > 0) {
8991
+ const vibgrateDir = path24.join(rootDir, ".vibgrate");
8992
+ await ensureDir(vibgrateDir);
8993
+ await writeJsonFile(path24.join(vibgrateDir, "project_scores.json"), projectScores);
8994
+ }
8936
8995
  }
8937
8996
  if (opts.format === "json") {
8938
8997
  const jsonStr = JSON.stringify(artifact, null, 2);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runScan
3
- } from "./chunk-DMYMJUQP.js";
3
+ } from "./chunk-56UAFVE4.js";
4
4
  import {
5
5
  writeJsonFile
6
6
  } from "./chunk-TBE6NQ5Z.js";
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  baselineCommand
4
- } from "./chunk-IHDUX5MC.js";
4
+ } from "./chunk-NASOHLRL.js";
5
5
  import {
6
6
  VERSION,
7
7
  dsnCommand,
@@ -10,7 +10,7 @@ import {
10
10
  pushCommand,
11
11
  scanCommand,
12
12
  writeDefaultConfig
13
- } from "./chunk-DMYMJUQP.js";
13
+ } from "./chunk-56UAFVE4.js";
14
14
  import {
15
15
  ensureDir,
16
16
  pathExists,
@@ -39,7 +39,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
39
39
  console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
40
40
  }
41
41
  if (opts.baseline) {
42
- const { runBaseline } = await import("./baseline-IEG2JITU.js");
42
+ const { runBaseline } = await import("./baseline-YGEO4GK7.js");
43
43
  await runBaseline(rootDir);
44
44
  }
45
45
  console.log("");
package/dist/index.d.ts CHANGED
@@ -541,6 +541,7 @@ interface ApiIntegration {
541
541
  parameters: string[];
542
542
  configOptions: string[];
543
543
  authHints: string[];
544
+ files: string[];
544
545
  }
545
546
  interface ApiSurfaceResult {
546
547
  integrations: ApiIntegration[];
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  formatText,
6
6
  generateFindings,
7
7
  runScan
8
- } from "./chunk-DMYMJUQP.js";
8
+ } from "./chunk-56UAFVE4.js";
9
9
  import "./chunk-TBE6NQ5Z.js";
10
10
  export {
11
11
  computeDriftScore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibgrate/cli",
3
- "version": "1.0.66",
3
+ "version": "1.0.67",
4
4
  "description": "CLI for measuring upgrade drift across Node, .NET, Python & Java projects",
5
5
  "type": "module",
6
6
  "bin": {