@vibgrate/cli 2026.606.2 → 2026.609.1

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-734OP6JR.js";
5
- import "./chunk-K2D6JXLA.js";
4
+ } from "./chunk-7CIAP6ZD.js";
5
+ import "./chunk-7J3LXQEA.js";
6
6
  import "./chunk-74ZJFYEM.js";
7
7
  import "./chunk-HAT4W7NZ.js";
8
8
  import "./chunk-JSBRDJBE.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runScan
3
- } from "./chunk-K2D6JXLA.js";
3
+ } from "./chunk-7J3LXQEA.js";
4
4
 
5
5
  // src/commands/baseline.ts
6
6
  import * as path3 from "path";
@@ -1995,6 +1995,44 @@ async function appendExcludePatterns(rootDir, newPatterns) {
1995
1995
  return false;
1996
1996
  }
1997
1997
  }
1998
+ var MS_PER_DAY = 864e5;
1999
+ var DAYS_PER_YEAR = 365.25;
2000
+ function ageDaysBetween(resolvedVersion, latestStable, releaseDates) {
2001
+ if (!resolvedVersion || !latestStable || !releaseDates) return null;
2002
+ const resolvedIso = releaseDates[resolvedVersion];
2003
+ const latestIso = releaseDates[latestStable];
2004
+ if (!resolvedIso || !latestIso) return null;
2005
+ const resolvedMs = Date.parse(resolvedIso);
2006
+ const latestMs = Date.parse(latestIso);
2007
+ if (Number.isNaN(resolvedMs) || Number.isNaN(latestMs)) return null;
2008
+ const days = (latestMs - resolvedMs) / MS_PER_DAY;
2009
+ return days > 0 ? days : 0;
2010
+ }
2011
+ function daysToLibyears(days) {
2012
+ if (days === null) return null;
2013
+ return days / DAYS_PER_YEAR;
2014
+ }
2015
+ function aggregateLibyears(values) {
2016
+ let total = 0;
2017
+ let max = 0;
2018
+ let measured = 0;
2019
+ for (const v of values) {
2020
+ if (v === null || v === void 0 || Number.isNaN(v)) continue;
2021
+ total += v;
2022
+ if (v > max) max = v;
2023
+ measured++;
2024
+ }
2025
+ if (measured === 0) return null;
2026
+ return { total, max, measured };
2027
+ }
2028
+ function freshnessScoreFromLibyears(agg) {
2029
+ if (!agg || agg.measured === 0) return null;
2030
+ const avg = agg.total / agg.measured;
2031
+ const avgPenalty = Math.min(avg * 20, 100);
2032
+ const maxPenalty = Math.min(agg.max * 10, 100);
2033
+ const score = 100 - (avgPenalty * 0.7 + maxPenalty * 0.3);
2034
+ return Math.max(0, Math.min(100, Math.round(score)));
2035
+ }
1998
2036
  var DEFAULT_THRESHOLDS = {
1999
2037
  failOnError: {
2000
2038
  eolDays: 180,
@@ -2077,28 +2115,57 @@ function eolScore(projects) {
2077
2115
  }
2078
2116
  return score;
2079
2117
  }
2118
+ function freshnessScore(projects) {
2119
+ const aggs = projects.map((p) => p.libyears).filter((a) => !!a);
2120
+ if (aggs.length === 0) return null;
2121
+ const total = aggs.reduce((s, a) => s + a.total, 0);
2122
+ const measured = aggs.reduce((s, a) => s + a.measured, 0);
2123
+ const max = aggs.reduce((m, a) => Math.max(m, a.max), 0);
2124
+ if (measured === 0) return null;
2125
+ return freshnessScoreFromLibyears({ total, max, measured });
2126
+ }
2127
+ function coverageConfidence(projects) {
2128
+ let known = 0;
2129
+ let unknown = 0;
2130
+ for (const p of projects) {
2131
+ known += p.dependencyAgeBuckets.current + p.dependencyAgeBuckets.oneBehind + p.dependencyAgeBuckets.twoPlusBehind;
2132
+ unknown += p.dependencyAgeBuckets.unknown;
2133
+ }
2134
+ const total = known + unknown;
2135
+ if (total === 0) return null;
2136
+ return Math.round(known / total * 100) / 100;
2137
+ }
2080
2138
  function computeDriftScore(projects) {
2081
2139
  const rs = runtimeScore(projects);
2082
2140
  const fs8 = frameworkScore(projects);
2083
2141
  const ds = dependencyScore(projects);
2084
2142
  const es = eolScore(projects);
2143
+ const frs = freshnessScore(projects);
2085
2144
  const components = [
2086
2145
  { score: rs, weight: 0.25 },
2087
2146
  { score: fs8, weight: 0.25 },
2088
2147
  { score: ds, weight: 0.3 },
2089
- { score: es, weight: 0.2 }
2148
+ { score: es, weight: 0.2 },
2149
+ { score: frs, weight: 0.15 }
2090
2150
  ];
2151
+ const confidence = coverageConfidence(projects) ?? void 0;
2152
+ const buildComponents = () => {
2153
+ const c = {
2154
+ runtimeScore: Math.round(rs ?? 100),
2155
+ frameworkScore: Math.round(fs8 ?? 100),
2156
+ dependencyScore: Math.round(ds ?? 100),
2157
+ eolScore: Math.round(es ?? 100)
2158
+ };
2159
+ if (frs !== null) c.freshnessScore = Math.round(frs);
2160
+ return c;
2161
+ };
2091
2162
  const active = components.filter((c) => c.score !== null);
2092
2163
  if (active.length === 0) {
2093
2164
  return {
2094
2165
  score: 100,
2095
2166
  riskLevel: "low",
2096
- components: {
2097
- runtimeScore: Math.round(rs ?? 100),
2098
- frameworkScore: Math.round(fs8 ?? 100),
2099
- dependencyScore: Math.round(ds ?? 100),
2100
- eolScore: Math.round(es ?? 100)
2101
- }
2167
+ components: buildComponents(),
2168
+ ...confidence !== void 0 ? { confidence } : {}
2102
2169
  };
2103
2170
  }
2104
2171
  const totalActiveWeight = active.reduce((sum, c) => sum + c.weight, 0);
@@ -2116,16 +2183,13 @@ function computeDriftScore(projects) {
2116
2183
  if (fs8 !== null) measured.push("framework");
2117
2184
  if (ds !== null) measured.push("dependency");
2118
2185
  if (es !== null) measured.push("eol");
2186
+ if (frs !== null) measured.push("freshness");
2119
2187
  return {
2120
2188
  score,
2121
2189
  riskLevel,
2122
- components: {
2123
- runtimeScore: Math.round(rs ?? 100),
2124
- frameworkScore: Math.round(fs8 ?? 100),
2125
- dependencyScore: Math.round(ds ?? 100),
2126
- eolScore: Math.round(es ?? 100)
2127
- },
2128
- measured
2190
+ components: buildComponents(),
2191
+ measured,
2192
+ ...confidence !== void 0 ? { confidence } : {}
2129
2193
  };
2130
2194
  }
2131
2195
  function generateFindings(projects, config) {
@@ -3059,25 +3123,53 @@ function maxStable(versions) {
3059
3123
  if (stable.length === 0) return null;
3060
3124
  return stable.sort(semver.rcompare)[0] ?? null;
3061
3125
  }
3126
+ function parseReleaseDates(time) {
3127
+ if (!time || typeof time !== "object") return void 0;
3128
+ const out = {};
3129
+ for (const [key, value] of Object.entries(time)) {
3130
+ if (key === "created" || key === "modified") continue;
3131
+ if (typeof value === "string") out[key] = value;
3132
+ }
3133
+ return Object.keys(out).length > 0 ? out : void 0;
3134
+ }
3062
3135
  function parseNpmMetaPayload(data) {
3063
3136
  let latest = null;
3064
3137
  let versions = [];
3138
+ let license = null;
3139
+ let releaseDates;
3065
3140
  if (data && typeof data === "object") {
3066
- const record = data;
3067
- const dtLatest = record["dist-tags.latest"];
3141
+ const record2 = data;
3142
+ const dtLatest = record2["dist-tags.latest"];
3068
3143
  if (typeof dtLatest === "string") latest = dtLatest;
3069
- const distTags = record["dist-tags"];
3144
+ const distTags = record2["dist-tags"];
3070
3145
  if (!latest && distTags && typeof distTags === "object" && typeof distTags.latest === "string") {
3071
3146
  latest = distTags.latest;
3072
3147
  }
3073
- const v = record.versions;
3148
+ const v = record2.versions;
3074
3149
  if (Array.isArray(v)) versions = v.map(String);
3075
3150
  else if (typeof v === "string") versions = [v];
3151
+ license = parseLicenseField(record2.license ?? record2.licenses);
3152
+ releaseDates = parseReleaseDates(record2.time);
3076
3153
  }
3077
3154
  const stable = stableOnly(versions);
3078
3155
  const latestStableOverall = maxStable(stable);
3079
3156
  if (!latest && latestStableOverall) latest = latestStableOverall;
3080
- return { latest, stableVersions: stable, latestStableOverall };
3157
+ return { latest, stableVersions: stable, latestStableOverall, license, ...releaseDates ? { releaseDates } : {} };
3158
+ }
3159
+ function parseLicenseField(value) {
3160
+ if (!value) return null;
3161
+ if (typeof value === "string") return value.trim() || null;
3162
+ if (Array.isArray(value)) {
3163
+ const parts = value.map(parseLicenseField).filter((s) => Boolean(s));
3164
+ if (parts.length === 0) return null;
3165
+ return parts.length === 1 ? parts[0] : `(${parts.join(" OR ")})`;
3166
+ }
3167
+ if (typeof value === "object") {
3168
+ const obj = value;
3169
+ if (typeof obj.type === "string") return obj.type.trim() || null;
3170
+ if (typeof obj.spdx === "string") return obj.spdx.trim() || null;
3171
+ }
3172
+ return null;
3081
3173
  }
3082
3174
  var BATCH_SIZE = 24;
3083
3175
  var WINDOWS_MAX_COMMAND_CHARS = 7e3;
@@ -3144,7 +3236,7 @@ var NpmCache = class {
3144
3236
  if (pending.length === 0) return;
3145
3237
  const batchPromise = this.sem.run(() => this.fetchBatch(pending));
3146
3238
  for (const pkg2 of pending) {
3147
- const promise = batchPromise.then((batch) => batch.get(pkg2) ?? { latest: null, stableVersions: [], latestStableOverall: null });
3239
+ const promise = batchPromise.then((batch) => batch.get(pkg2) ?? { latest: null, stableVersions: [], latestStableOverall: null, license: null });
3148
3240
  this.meta.set(pkg2, promise);
3149
3241
  }
3150
3242
  await batchPromise;
@@ -3167,10 +3259,12 @@ var NpmCache = class {
3167
3259
  out.set(pkg2, {
3168
3260
  latest: manifestEntry.latest ?? latestStableOverall,
3169
3261
  stableVersions: stable,
3170
- latestStableOverall
3262
+ latestStableOverall,
3263
+ license: manifestEntry.license ?? null,
3264
+ ...manifestEntry.releaseDates ? { releaseDates: manifestEntry.releaseDates } : {}
3171
3265
  });
3172
3266
  } else if (this.offline) {
3173
- out.set(pkg2, { latest: null, stableVersions: [], latestStableOverall: null });
3267
+ out.set(pkg2, { latest: null, stableVersions: [], latestStableOverall: null, license: null });
3174
3268
  } else {
3175
3269
  remote.push(pkg2);
3176
3270
  }
@@ -3191,22 +3285,22 @@ var NpmCache = class {
3191
3285
  return out;
3192
3286
  }
3193
3287
  try {
3194
- const data = await npmViewJson([...pkgs, "dist-tags.latest", "versions"], this.cwd);
3288
+ const data = await npmViewJson([...pkgs, "dist-tags.latest", "versions", "license", "licenses", "time"], this.cwd);
3195
3289
  if (Array.isArray(data)) {
3196
3290
  for (let i = 0; i < data.length && i < pkgs.length; i++) {
3197
3291
  out.set(pkgs[i], parseNpmMetaPayload(data[i]));
3198
3292
  }
3199
3293
  } else if (data && typeof data === "object") {
3200
- const record = data;
3294
+ const record2 = data;
3201
3295
  let keyedByPkg = 0;
3202
3296
  for (const pkg2 of pkgs) {
3203
- if (pkg2 in record) {
3204
- out.set(pkg2, parseNpmMetaPayload(record[pkg2]));
3297
+ if (pkg2 in record2) {
3298
+ out.set(pkg2, parseNpmMetaPayload(record2[pkg2]));
3205
3299
  keyedByPkg++;
3206
3300
  }
3207
3301
  }
3208
3302
  if (keyedByPkg === 0 && pkgs.length === 1) {
3209
- out.set(pkgs[0], parseNpmMetaPayload(record));
3303
+ out.set(pkgs[0], parseNpmMetaPayload(record2));
3210
3304
  }
3211
3305
  }
3212
3306
  } catch {
@@ -3226,17 +3320,19 @@ var NpmCache = class {
3226
3320
  return {
3227
3321
  latest: manifestEntry.latest ?? latestStableOverall,
3228
3322
  stableVersions: stable,
3229
- latestStableOverall
3323
+ latestStableOverall,
3324
+ license: manifestEntry.license ?? null,
3325
+ ...manifestEntry.releaseDates ? { releaseDates: manifestEntry.releaseDates } : {}
3230
3326
  };
3231
3327
  }
3232
3328
  if (this.offline) {
3233
- return { latest: null, stableVersions: [], latestStableOverall: null };
3329
+ return { latest: null, stableVersions: [], latestStableOverall: null, license: null };
3234
3330
  }
3235
3331
  return this.fetchOneRemote(pkg2);
3236
3332
  }
3237
3333
  async fetchOneRemote(pkg2) {
3238
3334
  try {
3239
- const data = await npmViewJson([pkg2, "dist-tags.latest", "versions"], this.cwd);
3335
+ const data = await npmViewJson([pkg2, "dist-tags.latest", "versions", "license", "licenses", "time"], this.cwd);
3240
3336
  return parseNpmMetaPayload(data);
3241
3337
  } catch {
3242
3338
  let latest = null;
@@ -3257,7 +3353,7 @@ var NpmCache = class {
3257
3353
  const stable = stableOnly(versions);
3258
3354
  const latestStableOverall = maxStable(stable);
3259
3355
  if (!latest && latestStableOverall) latest = latestStableOverall;
3260
- return { latest, stableVersions: stable, latestStableOverall };
3356
+ return { latest, stableVersions: stable, latestStableOverall, license: null };
3261
3357
  }
3262
3358
  }
3263
3359
  };
@@ -3292,6 +3388,449 @@ function isSemverSpec(spec) {
3292
3388
  if (s === "*" || s.toLowerCase() === "latest") return true;
3293
3389
  return semver.validRange(s) !== null;
3294
3390
  }
3391
+ function riskForCategory(category) {
3392
+ switch (category) {
3393
+ case "public-domain":
3394
+ case "permissive":
3395
+ return "low";
3396
+ case "weak-copyleft":
3397
+ return "medium";
3398
+ case "copyleft":
3399
+ case "network-copyleft":
3400
+ case "proprietary":
3401
+ return "high";
3402
+ case "unknown":
3403
+ default:
3404
+ return "medium";
3405
+ }
3406
+ }
3407
+ var NO_OBLIGATIONS = {
3408
+ attribution: false,
3409
+ copyleft: false,
3410
+ networkCopyleft: false,
3411
+ discloseSource: false,
3412
+ patentGrant: false,
3413
+ commercialRestriction: false
3414
+ };
3415
+ function record(seed) {
3416
+ return {
3417
+ spdxId: seed.id,
3418
+ name: seed.name,
3419
+ family: seed.family,
3420
+ category: seed.category,
3421
+ osiApproved: seed.osi ?? false,
3422
+ fsfLibre: seed.fsf ?? false,
3423
+ deprecated: seed.deprecated ?? false,
3424
+ referenceUrl: seed.url ?? `https://spdx.org/licenses/${seed.id}.html`,
3425
+ riskLevel: riskForCategory(seed.category),
3426
+ obligations: { ...NO_OBLIGATIONS, ...seed.obligations ?? {} }
3427
+ };
3428
+ }
3429
+ var ATTRIB = { attribution: true };
3430
+ var ATTRIB_PATENT = { attribution: true, patentGrant: true };
3431
+ var WEAK = { attribution: true, copyleft: true, discloseSource: true };
3432
+ var STRONG = { attribution: true, copyleft: true, discloseSource: true };
3433
+ var NETWORK = {
3434
+ attribution: true,
3435
+ copyleft: true,
3436
+ networkCopyleft: true,
3437
+ discloseSource: true
3438
+ };
3439
+ var SEEDS = [
3440
+ // ── Public domain / unlicensed ──
3441
+ { id: "CC0-1.0", name: "Creative Commons Zero v1.0 Universal", family: "CC", category: "public-domain", fsf: true },
3442
+ { id: "Unlicense", name: "The Unlicense", family: "Public Domain", category: "public-domain", osi: true, fsf: true },
3443
+ { id: "0BSD", name: "BSD Zero Clause License", family: "BSD", category: "permissive", osi: true },
3444
+ { id: "WTFPL", name: "Do What The F*ck You Want To Public License", family: "WTFPL", category: "permissive", fsf: true },
3445
+ // ── Permissive ──
3446
+ { id: "MIT", name: "MIT License", family: "MIT", category: "permissive", osi: true, fsf: true, obligations: ATTRIB },
3447
+ { id: "MIT-0", name: "MIT No Attribution", family: "MIT", category: "permissive", osi: true },
3448
+ { id: "ISC", name: "ISC License", family: "ISC", category: "permissive", osi: true, fsf: true, obligations: ATTRIB },
3449
+ { id: "BSD-2-Clause", name: 'BSD 2-Clause "Simplified" License', family: "BSD", category: "permissive", osi: true, fsf: true, obligations: ATTRIB },
3450
+ { id: "BSD-3-Clause", name: 'BSD 3-Clause "New" or "Revised" License', family: "BSD", category: "permissive", osi: true, fsf: true, obligations: ATTRIB },
3451
+ { id: "BSD-3-Clause-Clear", name: "BSD 3-Clause Clear License", family: "BSD", category: "permissive", osi: true, obligations: ATTRIB },
3452
+ { id: "Apache-2.0", name: "Apache License 2.0", family: "Apache", category: "permissive", osi: true, fsf: true, obligations: ATTRIB_PATENT },
3453
+ { id: "Apache-1.1", name: "Apache Software License 1.1", family: "Apache", category: "permissive", osi: true, obligations: ATTRIB },
3454
+ { id: "Zlib", name: "zlib License", family: "Zlib", category: "permissive", osi: true, fsf: true },
3455
+ { id: "BSL-1.0", name: "Boost Software License 1.0", family: "Boost", category: "permissive", osi: true, fsf: true, obligations: ATTRIB },
3456
+ { id: "PostgreSQL", name: "PostgreSQL License", family: "BSD", category: "permissive", osi: true, obligations: ATTRIB },
3457
+ { id: "Python-2.0", name: "Python License 2.0", family: "Python", category: "permissive", osi: true, obligations: ATTRIB },
3458
+ { id: "PSF-2.0", name: "Python Software Foundation License 2.0", family: "Python", category: "permissive", obligations: ATTRIB },
3459
+ { id: "Artistic-2.0", name: "Artistic License 2.0", family: "Artistic", category: "permissive", osi: true, fsf: true, obligations: ATTRIB },
3460
+ { id: "NCSA", name: "University of Illinois/NCSA Open Source License", family: "NCSA", category: "permissive", osi: true, fsf: true, obligations: ATTRIB },
3461
+ { id: "Unicode-DFS-2016", name: "Unicode License Agreement - Data Files and Software (2016)", family: "Unicode", category: "permissive", osi: true, obligations: ATTRIB },
3462
+ { id: "BlueOak-1.0.0", name: "Blue Oak Model License 1.0.0", family: "BlueOak", category: "permissive", obligations: ATTRIB },
3463
+ // ── Weak / file-level copyleft ──
3464
+ { id: "LGPL-2.0-only", name: "GNU Library General Public License v2 only", family: "LGPL", category: "weak-copyleft", osi: true, fsf: true, obligations: WEAK },
3465
+ { id: "LGPL-2.1-only", name: "GNU Lesser General Public License v2.1 only", family: "LGPL", category: "weak-copyleft", osi: true, fsf: true, obligations: WEAK },
3466
+ { id: "LGPL-2.1-or-later", name: "GNU Lesser General Public License v2.1 or later", family: "LGPL", category: "weak-copyleft", osi: true, fsf: true, obligations: WEAK },
3467
+ { id: "LGPL-3.0-only", name: "GNU Lesser General Public License v3.0 only", family: "LGPL", category: "weak-copyleft", osi: true, fsf: true, obligations: { ...WEAK, patentGrant: true } },
3468
+ { id: "LGPL-3.0-or-later", name: "GNU Lesser General Public License v3.0 or later", family: "LGPL", category: "weak-copyleft", osi: true, fsf: true, obligations: { ...WEAK, patentGrant: true } },
3469
+ { id: "MPL-2.0", name: "Mozilla Public License 2.0", family: "MPL", category: "weak-copyleft", osi: true, fsf: true, obligations: { ...WEAK, patentGrant: true } },
3470
+ { id: "MPL-1.1", name: "Mozilla Public License 1.1", family: "MPL", category: "weak-copyleft", osi: true, fsf: true, obligations: WEAK },
3471
+ { id: "EPL-1.0", name: "Eclipse Public License 1.0", family: "EPL", category: "weak-copyleft", osi: true, fsf: true, obligations: { ...WEAK, patentGrant: true } },
3472
+ { id: "EPL-2.0", name: "Eclipse Public License 2.0", family: "EPL", category: "weak-copyleft", osi: true, fsf: true, obligations: { ...WEAK, patentGrant: true } },
3473
+ { id: "CDDL-1.0", name: "Common Development and Distribution License 1.0", family: "CDDL", category: "weak-copyleft", osi: true, fsf: true, obligations: WEAK },
3474
+ { id: "CDDL-1.1", name: "Common Development and Distribution License 1.1", family: "CDDL", category: "weak-copyleft", obligations: WEAK },
3475
+ { id: "CPL-1.0", name: "Common Public License 1.0", family: "CPL", category: "weak-copyleft", osi: true, fsf: true, obligations: WEAK },
3476
+ { id: "MS-PL", name: "Microsoft Public License", family: "Microsoft", category: "weak-copyleft", osi: true, fsf: true, obligations: ATTRIB },
3477
+ { id: "MS-RL", name: "Microsoft Reciprocal License", family: "Microsoft", category: "weak-copyleft", osi: true, fsf: true, obligations: WEAK },
3478
+ // ── Strong copyleft ──
3479
+ { id: "GPL-2.0-only", name: "GNU General Public License v2.0 only", family: "GPL", category: "copyleft", osi: true, fsf: true, obligations: STRONG },
3480
+ { id: "GPL-2.0-or-later", name: "GNU General Public License v2.0 or later", family: "GPL", category: "copyleft", osi: true, fsf: true, obligations: STRONG },
3481
+ { id: "GPL-3.0-only", name: "GNU General Public License v3.0 only", family: "GPL", category: "copyleft", osi: true, fsf: true, obligations: { ...STRONG, patentGrant: true } },
3482
+ { id: "GPL-3.0-or-later", name: "GNU General Public License v3.0 or later", family: "GPL", category: "copyleft", osi: true, fsf: true, obligations: { ...STRONG, patentGrant: true } },
3483
+ { id: "EUPL-1.1", name: "European Union Public License 1.1", family: "EUPL", category: "copyleft", osi: true, fsf: true, obligations: STRONG },
3484
+ { id: "EUPL-1.2", name: "European Union Public License 1.2", family: "EUPL", category: "copyleft", osi: true, fsf: true, obligations: STRONG },
3485
+ { id: "OSL-3.0", name: "Open Software License 3.0", family: "OSL", category: "copyleft", osi: true, fsf: true, obligations: { ...STRONG, patentGrant: true } },
3486
+ { id: "CECILL-2.1", name: "CeCILL Free Software License Agreement v2.1", family: "CeCILL", category: "copyleft", osi: true, fsf: true, obligations: STRONG },
3487
+ { id: "CC-BY-SA-4.0", name: "Creative Commons Attribution Share Alike 4.0 International", family: "CC", category: "copyleft", obligations: { attribution: true, copyleft: true } },
3488
+ // ── Network copyleft ──
3489
+ { id: "AGPL-3.0-only", name: "GNU Affero General Public License v3.0 only", family: "AGPL", category: "network-copyleft", osi: true, fsf: true, obligations: { ...NETWORK, patentGrant: true } },
3490
+ { id: "AGPL-3.0-or-later", name: "GNU Affero General Public License v3.0 or later", family: "AGPL", category: "network-copyleft", osi: true, fsf: true, obligations: { ...NETWORK, patentGrant: true } },
3491
+ // ── Source-available / proprietary (non-OSI) ──
3492
+ { id: "SSPL-1.0", name: "Server Side Public License 1.0", family: "SSPL", category: "network-copyleft", obligations: NETWORK },
3493
+ { id: "BUSL-1.1", name: "Business Source License 1.1", family: "BUSL", category: "proprietary", obligations: { commercialRestriction: true, discloseSource: true } },
3494
+ { id: "Elastic-2.0", name: "Elastic License 2.0", family: "Elastic", category: "proprietary", obligations: { commercialRestriction: true } },
3495
+ { id: "RPL-1.5", name: "Reciprocal Public License 1.5", family: "RPL", category: "network-copyleft", osi: true, obligations: NETWORK },
3496
+ { id: "Commons-Clause", name: "Commons Clause License Condition v1.0", family: "Commons-Clause", category: "proprietary", obligations: { commercialRestriction: true } },
3497
+ { id: "LicenseRef-Proprietary", name: "Proprietary / Commercial", family: "Proprietary", category: "proprietary", obligations: { commercialRestriction: true } },
3498
+ // ── Non-commercial Creative Commons (restrictive) ──
3499
+ { id: "CC-BY-NC-4.0", name: "Creative Commons Attribution Non Commercial 4.0 International", family: "CC", category: "proprietary", obligations: { attribution: true, commercialRestriction: true } },
3500
+ { id: "CC-BY-4.0", name: "Creative Commons Attribution 4.0 International", family: "CC", category: "permissive", obligations: ATTRIB }
3501
+ ];
3502
+ var RECORDS = SEEDS.map(record);
3503
+ var BY_ID = /* @__PURE__ */ new Map();
3504
+ for (const rec of RECORDS) {
3505
+ BY_ID.set(rec.spdxId.toLowerCase(), rec);
3506
+ }
3507
+ function getLicenseRecord(spdxId) {
3508
+ return BY_ID.get(spdxId.trim().toLowerCase());
3509
+ }
3510
+ function unknownLicenseRecord(name = "Unknown") {
3511
+ return {
3512
+ spdxId: "NOASSERTION",
3513
+ name,
3514
+ family: "Unknown",
3515
+ category: "unknown",
3516
+ osiApproved: false,
3517
+ fsfLibre: false,
3518
+ deprecated: false,
3519
+ referenceUrl: "",
3520
+ riskLevel: "medium",
3521
+ obligations: { ...NO_OBLIGATIONS }
3522
+ };
3523
+ }
3524
+ function normalizeAliasKey(raw) {
3525
+ return raw.trim().toLowerCase().replace(/^\(+|\)+$/g, "").replace(/[._]/g, "-").replace(/\s+/g, " ").replace(/\s*-\s*/g, "-").replace(/\bversion\b/g, "v").replace(/\blicen[sc]e\b/g, "").replace(/\s+/g, " ").trim();
3526
+ }
3527
+ var RAW_ALIASES = {
3528
+ // MIT
3529
+ "mit": "MIT",
3530
+ "mit license": "MIT",
3531
+ "the mit license": "MIT",
3532
+ "expat": "MIT",
3533
+ // ISC
3534
+ "isc": "ISC",
3535
+ // Apache
3536
+ "apache": "Apache-2.0",
3537
+ "apache 2": "Apache-2.0",
3538
+ "apache2": "Apache-2.0",
3539
+ "apache 2.0": "Apache-2.0",
3540
+ "apache-2": "Apache-2.0",
3541
+ "apache license 2.0": "Apache-2.0",
3542
+ "apache license version 2.0": "Apache-2.0",
3543
+ "asl 2.0": "Apache-2.0",
3544
+ "al2": "Apache-2.0",
3545
+ // BSD
3546
+ "bsd": "BSD-3-Clause",
3547
+ "bsd license": "BSD-3-Clause",
3548
+ "bsd-2": "BSD-2-Clause",
3549
+ "bsd 2-clause": "BSD-2-Clause",
3550
+ "simplified bsd": "BSD-2-Clause",
3551
+ "freebsd": "BSD-2-Clause",
3552
+ "bsd-3": "BSD-3-Clause",
3553
+ "bsd 3-clause": "BSD-3-Clause",
3554
+ "new bsd": "BSD-3-Clause",
3555
+ "modified bsd": "BSD-3-Clause",
3556
+ "revised bsd": "BSD-3-Clause",
3557
+ // GPL
3558
+ "gpl": "GPL-3.0-or-later",
3559
+ "gplv2": "GPL-2.0-only",
3560
+ "gpl2": "GPL-2.0-only",
3561
+ "gpl-2": "GPL-2.0-only",
3562
+ "gpl 2.0": "GPL-2.0-only",
3563
+ "gpl-2.0": "GPL-2.0-only",
3564
+ "gpl-2.0+": "GPL-2.0-or-later",
3565
+ "gnu gpl v2": "GPL-2.0-only",
3566
+ "gplv3": "GPL-3.0-only",
3567
+ "gpl3": "GPL-3.0-only",
3568
+ "gpl-3": "GPL-3.0-only",
3569
+ "gpl 3.0": "GPL-3.0-only",
3570
+ "gpl-3.0": "GPL-3.0-only",
3571
+ "gpl-3.0+": "GPL-3.0-or-later",
3572
+ "gnu gpl v3": "GPL-3.0-only",
3573
+ "gnu general public v3": "GPL-3.0-only",
3574
+ // LGPL
3575
+ "lgpl": "LGPL-3.0-or-later",
3576
+ "lgplv2.1": "LGPL-2.1-only",
3577
+ "lgpl-2.1": "LGPL-2.1-only",
3578
+ "lgpl 2.1": "LGPL-2.1-only",
3579
+ "lgplv3": "LGPL-3.0-only",
3580
+ "lgpl-3": "LGPL-3.0-only",
3581
+ "lgpl-3.0": "LGPL-3.0-only",
3582
+ "lgpl 3.0": "LGPL-3.0-only",
3583
+ // AGPL
3584
+ "agpl": "AGPL-3.0-or-later",
3585
+ "agplv3": "AGPL-3.0-only",
3586
+ "agpl-3": "AGPL-3.0-only",
3587
+ "agpl-3.0": "AGPL-3.0-only",
3588
+ "agpl 3.0": "AGPL-3.0-only",
3589
+ "gnu agpl v3": "AGPL-3.0-only",
3590
+ // MPL
3591
+ "mpl": "MPL-2.0",
3592
+ "mpl 2.0": "MPL-2.0",
3593
+ "mpl-2": "MPL-2.0",
3594
+ "mozilla public 2.0": "MPL-2.0",
3595
+ // EPL
3596
+ "epl": "EPL-2.0",
3597
+ "eclipse public 2.0": "EPL-2.0",
3598
+ "epl-2": "EPL-2.0",
3599
+ // Source-available / proprietary
3600
+ "sspl": "SSPL-1.0",
3601
+ "server side public": "SSPL-1.0",
3602
+ "busl": "BUSL-1.1",
3603
+ "business source": "BUSL-1.1",
3604
+ "elastic": "Elastic-2.0",
3605
+ "elastic-2": "Elastic-2.0",
3606
+ "commons clause": "Commons-Clause",
3607
+ "proprietary": "LicenseRef-Proprietary",
3608
+ "commercial": "LicenseRef-Proprietary",
3609
+ "see license in license": "LicenseRef-Proprietary",
3610
+ "unlicensed": "LicenseRef-Proprietary",
3611
+ "all rights reserved": "LicenseRef-Proprietary",
3612
+ // Public domain
3613
+ "cc0": "CC0-1.0",
3614
+ "cc0 1.0": "CC0-1.0",
3615
+ "public domain": "CC0-1.0",
3616
+ "the unlicense": "Unlicense",
3617
+ "wtfpl": "WTFPL",
3618
+ // Boost / zlib / others
3619
+ "boost": "BSL-1.0",
3620
+ "bsl": "BSL-1.0",
3621
+ "zlib/libpng": "Zlib",
3622
+ "python": "Python-2.0",
3623
+ "psf": "PSF-2.0",
3624
+ "artistic": "Artistic-2.0"
3625
+ };
3626
+ var ALIASES = /* @__PURE__ */ new Map();
3627
+ for (const [raw, spdx] of Object.entries(RAW_ALIASES)) {
3628
+ ALIASES.set(normalizeAliasKey(raw), spdx);
3629
+ }
3630
+ function resolveAlias(raw) {
3631
+ return ALIASES.get(normalizeAliasKey(raw));
3632
+ }
3633
+ var TOKEN_RE = /\(|\)|\bWITH\b|\bAND\b|\bOR\b|[^\s()]+/gi;
3634
+ function parseLicenseExpression(raw) {
3635
+ const input = (raw ?? "").trim();
3636
+ const licenseIds = [];
3637
+ const exceptions = [];
3638
+ let hasOr = false;
3639
+ let hasAnd = false;
3640
+ let orLater = false;
3641
+ let expectException = false;
3642
+ const tokens = input.match(TOKEN_RE) ?? [];
3643
+ for (const token of tokens) {
3644
+ const upper = token.toUpperCase();
3645
+ if (token === "(" || token === ")") continue;
3646
+ if (upper === "OR") {
3647
+ hasOr = true;
3648
+ expectException = false;
3649
+ continue;
3650
+ }
3651
+ if (upper === "AND") {
3652
+ hasAnd = true;
3653
+ expectException = false;
3654
+ continue;
3655
+ }
3656
+ if (upper === "WITH") {
3657
+ expectException = true;
3658
+ continue;
3659
+ }
3660
+ if (expectException) {
3661
+ if (!exceptions.includes(token)) exceptions.push(token);
3662
+ expectException = false;
3663
+ continue;
3664
+ }
3665
+ let id = token;
3666
+ if (id.endsWith("+")) {
3667
+ orLater = true;
3668
+ id = id.slice(0, -1);
3669
+ }
3670
+ if (id && !licenseIds.includes(id)) licenseIds.push(id);
3671
+ }
3672
+ const operator = hasOr ? "OR" : hasAnd ? "AND" : "SINGLE";
3673
+ return {
3674
+ licenseIds,
3675
+ exceptions,
3676
+ operator,
3677
+ orLater,
3678
+ normalized: serialise(licenseIds, exceptions, operator, orLater, input)
3679
+ };
3680
+ }
3681
+ function serialise(ids, exceptions, operator, orLater, original) {
3682
+ if (ids.length === 0) return original.trim();
3683
+ if (ids.length === 1) {
3684
+ let out = ids[0];
3685
+ if (orLater && !out.endsWith("+")) out += "+";
3686
+ if (exceptions.length > 0) out += ` WITH ${exceptions.join(" WITH ")}`;
3687
+ return out;
3688
+ }
3689
+ const joiner = operator === "AND" ? " AND " : " OR ";
3690
+ return ids.join(joiner);
3691
+ }
3692
+ function isCompoundExpression(raw) {
3693
+ return /\b(?:AND|OR|WITH)\b|\+\s*$|\(/i.test(raw);
3694
+ }
3695
+ var CATEGORY_RESTRICTIVENESS = {
3696
+ "public-domain": 0,
3697
+ permissive: 1,
3698
+ "weak-copyleft": 2,
3699
+ copyleft: 3,
3700
+ "network-copyleft": 4,
3701
+ proprietary: 5,
3702
+ unknown: 2
3703
+ };
3704
+ function verdictFromRecord(rec, matchStatus, confidence, expression, components) {
3705
+ return {
3706
+ spdxId: rec.spdxId,
3707
+ name: rec.name,
3708
+ expression: expression ?? rec.spdxId,
3709
+ family: rec.family,
3710
+ category: rec.category,
3711
+ riskLevel: rec.riskLevel,
3712
+ osiApproved: rec.osiApproved,
3713
+ fsfLibre: rec.fsfLibre,
3714
+ obligations: rec.obligations,
3715
+ matchStatus,
3716
+ confidence,
3717
+ components: components ?? [rec.spdxId]
3718
+ };
3719
+ }
3720
+ function fuzzyMatch(raw) {
3721
+ const s = raw.toLowerCase();
3722
+ if (/\bagpl/.test(s)) return getLicenseRecord("AGPL-3.0-or-later");
3723
+ if (/\bsspl/.test(s)) return getLicenseRecord("SSPL-1.0");
3724
+ if (/\bbusl|business source/.test(s)) return getLicenseRecord("BUSL-1.1");
3725
+ if (/\blgpl/.test(s)) return getLicenseRecord("LGPL-3.0-or-later");
3726
+ if (/\bgpl/.test(s)) return getLicenseRecord("GPL-3.0-or-later");
3727
+ if (/\bmpl|mozilla/.test(s)) return getLicenseRecord("MPL-2.0");
3728
+ if (/\bepl|eclipse/.test(s)) return getLicenseRecord("EPL-2.0");
3729
+ if (/\bapache/.test(s)) return getLicenseRecord("Apache-2.0");
3730
+ if (/\bbsd/.test(s)) return getLicenseRecord("BSD-3-Clause");
3731
+ if (/\bmit\b/.test(s)) return getLicenseRecord("MIT");
3732
+ if (/\bisc\b/.test(s)) return getLicenseRecord("ISC");
3733
+ if (/commons clause/.test(s)) return getLicenseRecord("Commons-Clause");
3734
+ if (/proprietary|commercial|all rights reserved/.test(s)) return getLicenseRecord("LicenseRef-Proprietary");
3735
+ if (/public domain|cc0/.test(s)) return getLicenseRecord("CC0-1.0");
3736
+ return void 0;
3737
+ }
3738
+ function normalizeLicense(raw) {
3739
+ const input = (raw ?? "").trim();
3740
+ if (!input || /^(unknown|noassertion|none|n\/a)$/i.test(input)) {
3741
+ return verdictFromRecord(unknownLicenseRecord(), "unknown", 0);
3742
+ }
3743
+ const exact = getLicenseRecord(input);
3744
+ if (exact) return verdictFromRecord(exact, "exact", 1);
3745
+ if (isCompoundExpression(input)) {
3746
+ return resolveExpression(input);
3747
+ }
3748
+ const aliasId = resolveAlias(input);
3749
+ if (aliasId) {
3750
+ const rec = getLicenseRecord(aliasId);
3751
+ if (rec) return verdictFromRecord(rec, "alias", 0.95);
3752
+ }
3753
+ const fuzzy = fuzzyMatch(input);
3754
+ if (fuzzy) {
3755
+ return { ...verdictFromRecord(fuzzy, "fuzzy", 0.6), name: input.slice(0, 120) };
3756
+ }
3757
+ return verdictFromRecord(unknownLicenseRecord(input.slice(0, 120)), "unknown", 0);
3758
+ }
3759
+ function resolveExpression(input) {
3760
+ const parsed = parseLicenseExpression(input);
3761
+ if (parsed.licenseIds.length === 0) {
3762
+ return verdictFromRecord(unknownLicenseRecord(input.slice(0, 120)), "unknown", 0);
3763
+ }
3764
+ const componentVerdicts = parsed.licenseIds.map((id) => {
3765
+ const exact = getLicenseRecord(id);
3766
+ if (exact) return verdictFromRecord(exact, "exact", 1);
3767
+ const aliasId = resolveAlias(id);
3768
+ if (aliasId) {
3769
+ const rec = getLicenseRecord(aliasId);
3770
+ if (rec) return verdictFromRecord(rec, "alias", 0.95);
3771
+ }
3772
+ const fuzzy = fuzzyMatch(id);
3773
+ if (fuzzy) return verdictFromRecord(fuzzy, "fuzzy", 0.6);
3774
+ return verdictFromRecord(unknownLicenseRecord(id), "unknown", 0);
3775
+ });
3776
+ const pickMostRestrictive = parsed.operator !== "OR";
3777
+ let chosen = componentVerdicts[0];
3778
+ for (const v of componentVerdicts) {
3779
+ const more = CATEGORY_RESTRICTIVENESS[v.category] > CATEGORY_RESTRICTIVENESS[chosen.category];
3780
+ if (pickMostRestrictive ? more : !more) chosen = v;
3781
+ }
3782
+ const obligations = unionObligations(
3783
+ pickMostRestrictive ? componentVerdicts : [chosen]
3784
+ );
3785
+ const components = componentVerdicts.map((v) => v.spdxId);
3786
+ const category = chosen.category;
3787
+ return {
3788
+ spdxId: chosen.spdxId,
3789
+ name: parsed.normalized,
3790
+ expression: parsed.normalized,
3791
+ family: chosen.family,
3792
+ category,
3793
+ riskLevel: riskForCategory(category),
3794
+ osiApproved: componentVerdicts.every((v) => v.osiApproved),
3795
+ fsfLibre: componentVerdicts.every((v) => v.fsfLibre),
3796
+ obligations,
3797
+ matchStatus: "expression",
3798
+ confidence: Math.min(...componentVerdicts.map((v) => v.confidence)) || 0.5,
3799
+ components
3800
+ };
3801
+ }
3802
+ function unionObligations(verdicts) {
3803
+ const out = {
3804
+ attribution: false,
3805
+ copyleft: false,
3806
+ networkCopyleft: false,
3807
+ discloseSource: false,
3808
+ patentGrant: false,
3809
+ commercialRestriction: false
3810
+ };
3811
+ for (const v of verdicts) {
3812
+ out.attribution ||= v.obligations.attribution;
3813
+ out.copyleft ||= v.obligations.copyleft;
3814
+ out.networkCopyleft ||= v.obligations.networkCopyleft;
3815
+ out.discloseSource ||= v.obligations.discloseSource;
3816
+ out.patentGrant ||= v.obligations.patentGrant;
3817
+ out.commercialRestriction ||= v.obligations.commercialRestriction;
3818
+ }
3819
+ return out;
3820
+ }
3821
+ function buildDependencyLicense(raw, source) {
3822
+ const trimmed = (raw ?? "").trim();
3823
+ if (!trimmed) {
3824
+ return { raw: null, spdxId: null, source: "none", confidence: 0 };
3825
+ }
3826
+ const verdict = normalizeLicense(trimmed);
3827
+ return {
3828
+ raw: trimmed.slice(0, 200),
3829
+ spdxId: verdict.matchStatus === "unknown" ? null : verdict.spdxId,
3830
+ source,
3831
+ confidence: verdict.confidence
3832
+ };
3833
+ }
3295
3834
  var KNOWN_FRAMEWORKS = {
3296
3835
  // ── Frontend ──
3297
3836
  "react": "React",
@@ -3544,6 +4083,8 @@ async function scanOnePackageJson(packageJsonPath, rootDir, npmCache, cache) {
3544
4083
  } else {
3545
4084
  buckets.unknown++;
3546
4085
  }
4086
+ const ageDays = ageDaysBetween(resolvedVersion, latestStable, meta.releaseDates);
4087
+ const libyears = daysToLibyears(ageDays);
3547
4088
  dependencies.push({
3548
4089
  package: pkg2,
3549
4090
  section,
@@ -3551,7 +4092,10 @@ async function scanOnePackageJson(packageJsonPath, rootDir, npmCache, cache) {
3551
4092
  resolvedVersion,
3552
4093
  latestStable,
3553
4094
  majorsBehind,
3554
- drift
4095
+ drift,
4096
+ license: buildDependencyLicense(meta.license, "registry"),
4097
+ ageDays,
4098
+ libyears
3555
4099
  });
3556
4100
  if (pkg2 in KNOWN_FRAMEWORKS) {
3557
4101
  frameworks.push({
@@ -3573,7 +4117,8 @@ async function scanOnePackageJson(packageJsonPath, rootDir, npmCache, cache) {
3573
4117
  resolvedVersion: null,
3574
4118
  latestStable: null,
3575
4119
  majorsBehind: null,
3576
- drift: "unknown"
4120
+ drift: "unknown",
4121
+ license: { raw: null, spdxId: null, source: "none", confidence: 0 }
3577
4122
  });
3578
4123
  buckets.unknown++;
3579
4124
  }
@@ -3599,6 +4144,7 @@ async function scanOnePackageJson(packageJsonPath, rootDir, npmCache, cache) {
3599
4144
  frameworks,
3600
4145
  dependencies,
3601
4146
  dependencyAgeBuckets: buckets,
4147
+ libyears: aggregateLibyears(dependencies.map((d) => d.libyears)) ?? void 0,
3602
4148
  fileCount
3603
4149
  };
3604
4150
  }
@@ -11139,6 +11685,19 @@ function detectDbBrand(raw) {
11139
11685
  if (value.includes("cosmosdb") || value.includes("@azure/cosmos") || value.includes("cosmos")) return { kind: "nosql", brand: "CosmosDB", version: null, evidence: raw };
11140
11686
  if (value.includes("dynamodb")) return { kind: "nosql", brand: "DynamoDB", version: null, evidence: raw };
11141
11687
  if (value.includes("neo4j")) return { kind: "nosql", brand: "Neo4j", version: null, evidence: raw };
11688
+ if (value.includes("clickhouse")) return { kind: "sql", brand: "ClickHouse", version: null, evidence: raw };
11689
+ if (value.includes("snowflake")) return { kind: "sql", brand: "Snowflake", version: null, evidence: raw };
11690
+ if (value.includes("bigquery")) return { kind: "sql", brand: "BigQuery", version: null, evidence: raw };
11691
+ if (value.includes("firestore")) return { kind: "nosql", brand: "Firestore", version: null, evidence: raw };
11692
+ if (value.includes("cockroach")) return { kind: "sql", brand: "CockroachDB", version: null, evidence: raw };
11693
+ if (value.includes("planetscale")) return { kind: "sql", brand: "PlanetScale", version: null, evidence: raw };
11694
+ if (value.includes("supabase")) return { kind: "sql", brand: "Supabase", version: null, evidence: raw };
11695
+ if (value.includes("tidb")) return { kind: "sql", brand: "TiDB", version: null, evidence: raw };
11696
+ if (value.includes("couchdb")) return { kind: "nosql", brand: "CouchDB", version: null, evidence: raw };
11697
+ if (value.includes("elasticsearch") || value.includes("opensearch")) return { kind: "nosql", brand: "Elasticsearch", version: null, evidence: raw };
11698
+ if (value.includes("influxdb")) return { kind: "nosql", brand: "InfluxDB", version: null, evidence: raw };
11699
+ if (value.includes("memcached")) return { kind: "nosql", brand: "Memcached", version: null, evidence: raw };
11700
+ if (value.includes("cockroachdb")) return { kind: "sql", brand: "CockroachDB", version: null, evidence: raw };
11142
11701
  return null;
11143
11702
  }
11144
11703
  async function scanTextFiles(rootDir, fileCache) {
@@ -11215,12 +11774,12 @@ async function scanDataStores(rootDir, fileCache) {
11215
11774
  otherServices: []
11216
11775
  };
11217
11776
  for (const file of files) {
11218
- const connStrings = extract(file.content, /\b(?:postgres(?:ql)?:\/\/[^\s'"`]+|mysql:\/\/[^\s'"`]+|mongodb(?:\+srv)?:\/\/[^\s'"`]+|redis:\/\/[^\s'"`]+)\b/gi, file.relPath);
11777
+ const connStrings = extract(file.content, /\b(?:postgres(?:ql)?:\/\/[^\s'"`]+|mysql:\/\/[^\s'"`]+|mongodb(?:\+srv)?:\/\/[^\s'"`]+|redis:\/\/[^\s'"`]+|clickhouse:\/\/[^\s'"`]+|cockroachdb:\/\/[^\s'"`]+|snowflake:\/\/[^\s'"`]+)\b/gi, file.relPath);
11219
11778
  result.connectionStrings.push(...connStrings);
11220
11779
  const isLockFile = LOCKFILE_NAMES.has(path27.basename(file.relPath));
11221
11780
  if (!isLockFile) {
11222
11781
  const dbEvidence = [
11223
- ...extract(file.content, /\b(?:postgres|postgresql|mysql|mariadb|mssql|sqlserver|oracle|sqlite|mongodb|redis|cassandra|cosmosdb|cosmos|dynamodb|neo4j)\b/gi, file.relPath),
11782
+ ...extract(file.content, /\b(?:postgres|postgresql|mysql|mariadb|mssql|sqlserver|oracle|sqlite|mongodb|redis|cassandra|cosmosdb|cosmos|dynamodb|neo4j|clickhouse|snowflake|bigquery|firestore|cockroach|cockroachdb|planetscale|supabase|tidb|couchdb|elasticsearch|opensearch|influxdb|memcached)\b/gi, file.relPath),
11224
11783
  ...connStrings
11225
11784
  ];
11226
11785
  for (const evidence of dbEvidence) {
@@ -11506,7 +12065,9 @@ async function scanOssGovernance(rootDir, fileCache) {
11506
12065
  });
11507
12066
  }
11508
12067
  vulns.push(...extract(file.content, /\b(?:CVE-\d{4}-\d{4,}|critical vulnerability|high severity)\b[^\n]*/gi, file.relPath));
11509
- licenseRisks.push(...extract(file.content, /\b(?:GPL-3\.0|AGPL|SSPL|BUSL|source[- ]available)\b[^\n]*/gi, file.relPath));
12068
+ if (/package(-lock)?\.json$|pnpm-lock\.yaml$|yarn\.lock$|poetry\.lock$|pom\.xml$|build\.gradle(\.kts)?$|Cargo\.(toml|lock)$|composer\.(json|lock)$|requirements\.txt$|\.csproj$|\.nuspec$/i.test(file.relPath)) {
12069
+ licenseRisks.push(...extract(file.content, /\b(?:GPL-3\.0|AGPL|SSPL|BUSL|source[- ]available)\b[^\n]*/gi, file.relPath));
12070
+ }
11510
12071
  }
11511
12072
  return {
11512
12073
  directDependencies: directDeps.size,
@@ -13227,19 +13788,19 @@ async function loadScanHistory(rootDir) {
13227
13788
  return null;
13228
13789
  }
13229
13790
  }
13230
- async function saveScanHistory(rootDir, record) {
13791
+ async function saveScanHistory(rootDir, record2) {
13231
13792
  const dir = path32.join(rootDir, ".vibgrate");
13232
13793
  const filePath = path32.join(dir, HISTORY_FILENAME);
13233
13794
  let history;
13234
13795
  const existing = await loadScanHistory(rootDir);
13235
13796
  if (existing) {
13236
13797
  history = existing;
13237
- history.records.push(record);
13798
+ history.records.push(record2);
13238
13799
  if (history.records.length > MAX_RECORDS) {
13239
13800
  history.records = history.records.slice(-MAX_RECORDS);
13240
13801
  }
13241
13802
  } else {
13242
- history = { version: 1, records: [record] };
13803
+ history = { version: 1, records: [record2] };
13243
13804
  }
13244
13805
  try {
13245
13806
  await fs7.mkdir(dir, { recursive: true });
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  pathExists,
7
7
  readJsonFile,
8
8
  writeTextFile
9
- } from "./chunk-734OP6JR.js";
9
+ } from "./chunk-7CIAP6ZD.js";
10
10
  import {
11
11
  computeRepoFingerprint,
12
12
  detectVcs,
@@ -16,7 +16,7 @@ import {
16
16
  resolveRepositoryName,
17
17
  runScan,
18
18
  writeDefaultConfig
19
- } from "./chunk-K2D6JXLA.js";
19
+ } from "./chunk-7J3LXQEA.js";
20
20
  import {
21
21
  require_semver
22
22
  } from "./chunk-74ZJFYEM.js";
@@ -48,7 +48,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
48
48
  console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
49
49
  }
50
50
  if (opts.baseline) {
51
- const { runBaseline } = await import("./baseline-MJNJICDN.js");
51
+ const { runBaseline } = await import("./baseline-IGEWCPFI.js");
52
52
  await runBaseline(rootDir);
53
53
  }
54
54
  console.log("");
@@ -74,10 +74,38 @@ import * as crypto from "crypto";
74
74
  import * as path2 from "path";
75
75
  import { Command as Command2 } from "commander";
76
76
  import chalk2 from "chalk";
77
- var REGION_HOSTS = {
78
- us: "us.ingest.vibgrate.com",
79
- eu: "eu.ingest.vibgrate.com"
80
- };
77
+
78
+ // src/regions.ts
79
+ var REGIONS = [
80
+ {
81
+ id: "us",
82
+ label: "United States",
83
+ ingestHost: "us.ingest.vibgrate.com",
84
+ dashHost: "dash.vibgrate.com",
85
+ available: true
86
+ },
87
+ {
88
+ id: "eu",
89
+ label: "European Union",
90
+ ingestHost: "eu.ingest.vibgrate.com",
91
+ dashHost: "dash.vibgrate.eu",
92
+ available: true
93
+ },
94
+ {
95
+ id: "apac",
96
+ label: "Asia-Pacific (coming soon)",
97
+ ingestHost: "apac.ingest.vibgrate.com",
98
+ dashHost: "dash.vibgrate.com",
99
+ available: false
100
+ }
101
+ ];
102
+ var DEFAULT_REGION = "us";
103
+ function availableRegionIds() {
104
+ return REGIONS.filter((r) => r.available).map((r) => r.id);
105
+ }
106
+ function findRegion(id) {
107
+ return REGIONS.find((r) => r.id === id);
108
+ }
81
109
  function resolveIngestHost(region, ingest) {
82
110
  if (ingest) {
83
111
  try {
@@ -86,14 +114,23 @@ function resolveIngestHost(region, ingest) {
86
114
  throw new Error(`Invalid ingest URL: ${ingest}`);
87
115
  }
88
116
  }
89
- const r = (region ?? "us").toLowerCase();
90
- const host = REGION_HOSTS[r];
91
- if (!host) {
92
- throw new Error(`Unknown region "${r}". Supported: ${Object.keys(REGION_HOSTS).join(", ")}`);
117
+ const id = (region ?? DEFAULT_REGION).toLowerCase();
118
+ const match = findRegion(id);
119
+ if (!match) {
120
+ throw new Error(`Unknown region "${id}". Supported: ${availableRegionIds().join(", ")}`);
121
+ }
122
+ if (!match.available) {
123
+ throw new Error(`Region "${id}" (${match.label}) is not yet available. Supported: ${availableRegionIds().join(", ")}`);
93
124
  }
94
- return host;
125
+ return match.ingestHost;
126
+ }
127
+ function dashHostForIngestHost(ingestHost) {
128
+ const match = REGIONS.find((r) => r.ingestHost === ingestHost);
129
+ return match?.dashHost ?? "dash.vibgrate.com";
95
130
  }
96
- async function provisionDsn(keyId, secret, workspaceId, ingestHost) {
131
+
132
+ // src/commands/dsn.ts
133
+ async function provisionDsn(keyId, secret, workspaceId, ingestHost, region) {
97
134
  const url = `https://${ingestHost}/v1/provision`;
98
135
  try {
99
136
  const response = await fetch(url, {
@@ -103,7 +140,10 @@ async function provisionDsn(keyId, secret, workspaceId, ingestHost) {
103
140
  "Connection": "close"
104
141
  // Prevent keep-alive delays on exit
105
142
  },
106
- body: JSON.stringify({ keyId, secret, workspaceId })
143
+ // Pin the workspace to the selected region. The API rejects the request
144
+ // if `region` doesn't match the endpoint host (residency guard). For a
145
+ // custom --ingest host we omit it and let the API derive it from the host.
146
+ body: JSON.stringify(region ? { keyId, secret, workspaceId, region } : { keyId, secret, workspaceId })
107
147
  });
108
148
  if (!response.ok) {
109
149
  const result = await response.json();
@@ -115,7 +155,7 @@ async function provisionDsn(keyId, secret, workspaceId, ingestHost) {
115
155
  }
116
156
  }
117
157
  var dsnCommand = new Command2("dsn").description("Manage DSN tokens");
118
- dsnCommand.command("create").description("Create a new DSN token").option("--ingest <url>", "Ingest API URL (overrides --region)").option("--region <region>", "Data residency region (us, eu)", "us").requiredOption("--workspace <id>", 'Workspace ID (use "new" to auto-generate)').option("--write <path>", "Write DSN to file").action(async (opts) => {
158
+ dsnCommand.command("create").description("Create a new DSN token").option("--ingest <url>", "Ingest API URL (overrides --region)").option("--region <region>", `Data residency region (${availableRegionIds().join(", ")})`, "us").requiredOption("--workspace <id>", 'Workspace ID (use "new" to auto-generate)').option("--write <path>", "Write DSN to file").action(async (opts) => {
119
159
  const keyId = crypto.randomBytes(12).toString("hex");
120
160
  const secret = crypto.randomBytes(32).toString("hex");
121
161
  let ingestHost;
@@ -131,8 +171,9 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
131
171
  workspaceId = crypto.randomBytes(8).toString("hex");
132
172
  console.log(chalk2.dim(`Provisioning new workspace ${workspaceId}...`));
133
173
  }
174
+ const region = opts.ingest ? void 0 : opts.region.toLowerCase();
134
175
  if (isNewWorkspace) {
135
- const result = await provisionDsn(keyId, secret, workspaceId, ingestHost);
176
+ const result = await provisionDsn(keyId, secret, workspaceId, ingestHost, region);
136
177
  if (!result.success) {
137
178
  console.error(chalk2.red(`Failed to provision DSN: ${result.error}`));
138
179
  process.exit(1);
@@ -141,6 +182,9 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
141
182
  const dsn = `vibgrate+https://${keyId}:${secret}@${ingestHost}/${workspaceId}`;
142
183
  console.log(chalk2.green("\u2714") + " DSN created");
143
184
  console.log("");
185
+ console.log(chalk2.bold("Region:"));
186
+ console.log(` ${region ?? "custom"} (${ingestHost})`);
187
+ console.log("");
144
188
  console.log(chalk2.bold("DSN:"));
145
189
  console.log(` ${dsn}`);
146
190
  console.log("");
@@ -1379,7 +1423,7 @@ function parseDsn2(dsn) {
1379
1423
  function computeHmac(body, secret) {
1380
1424
  return crypto2.createHmac("sha256", secret).update(body).digest("base64");
1381
1425
  }
1382
- var pushCommand = new Command5("push").description("Push scan results to Vibgrate API").option("--dsn <dsn>", "DSN token (or use VIBGRATE_DSN env)").option("--region <region>", "Override data residency region (us, eu)").option("--file <file>", "Scan artifact file", ".vibgrate/scan_result.json").option("--strict", "Fail on upload errors").action(async (opts) => {
1426
+ var pushCommand = new Command5("push").description("Push scan results to Vibgrate API").option("--dsn <dsn>", "DSN token (or use VIBGRATE_DSN env)").option("--region <region>", `Override data residency region (${availableRegionIds().join(", ")})`).option("--file <file>", "Scan artifact file", ".vibgrate/scan_result.json").option("--strict", "Fail on upload errors").action(async (opts) => {
1383
1427
  const dsn = opts.dsn || process.env.VIBGRATE_DSN;
1384
1428
  if (!dsn) {
1385
1429
  console.error(chalk6.red("No DSN provided."));
@@ -1442,7 +1486,7 @@ var pushCommand = new Command5("push").description("Push scan results to Vibgrat
1442
1486
  console.log(chalk6.dim("Processing continues in the background. Results available shortly."));
1443
1487
  console.log();
1444
1488
  if (result.ingestId) {
1445
- const dashHost = host.includes("eu.") ? "dash.vibgrate.eu" : "dash.vibgrate.com";
1489
+ const dashHost = dashHostForIngestHost(host);
1446
1490
  const reportUrl = `https://${dashHost}/${parsed.workspaceId}/scan/${result.ingestId}`;
1447
1491
  console.log(chalk6.dim("View report: ") + chalk6.underline(reportUrl));
1448
1492
  }
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  formatText,
6
6
  generateFindings,
7
7
  runScan
8
- } from "./chunk-K2D6JXLA.js";
8
+ } from "./chunk-7J3LXQEA.js";
9
9
  import "./chunk-74ZJFYEM.js";
10
10
  import "./chunk-HAT4W7NZ.js";
11
11
  import "./chunk-JSBRDJBE.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibgrate/cli",
3
- "version": "2026.606.2",
3
+ "version": "2026.609.1",
4
4
  "description": "CLI for measuring upgrade drift across Node, .NET, Python & Java projects",
5
5
  "type": "module",
6
6
  "bin": {