@suss/extractor 0.25.0 → 0.27.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/index.cjs CHANGED
@@ -43,6 +43,7 @@ __export(index_exports, {
43
43
  assembleSummary: () => assembleSummary,
44
44
  assessConfidence: () => assessConfidence,
45
45
  buildUngatedExtractionReport: () => buildUngatedExtractionReport,
46
+ commonDirectoryOf: () => commonDirectoryOf,
46
47
  composeWrappers: () => composeWrappers,
47
48
  computeContentHash: () => computeContentHash,
48
49
  computeDistHashFrom: () => computeDistHashFrom,
@@ -295,8 +296,8 @@ var catchEntryCond = () => fixedCond(
295
296
  "positive",
296
297
  "catchBlock"
297
298
  );
298
- function classify(path3, home, isAncestorOrSelf) {
299
- return path3.map((cond) => {
299
+ function classify(path4, home, isAncestorOrSelf) {
300
+ return path4.map((cond) => {
300
301
  if (cond.branchStmt === null) {
301
302
  return cond.info;
302
303
  }
@@ -361,9 +362,9 @@ function chargeBudget(state) {
361
362
  );
362
363
  }
363
364
  }
364
- function recordTerminal(ctx, terminal, home, path3) {
365
+ function recordTerminal(ctx, terminal, home, path4) {
365
366
  chargeBudget(ctx.state);
366
- const info = classify(path3, home, ctx.isAncestorOrSelf);
367
+ const info = classify(path4, home, ctx.isAncestorOrSelf);
367
368
  const existing = ctx.state.byTerminal.get(terminal) ?? [];
368
369
  existing.push(info);
369
370
  ctx.state.byTerminal.set(terminal, existing);
@@ -374,15 +375,15 @@ function containsTerminal(ctx, stmt) {
374
375
  }
375
376
  return childrenOf(stmt).some((child) => containsTerminal(ctx, child));
376
377
  }
377
- function stepIf(stmt, path3, ctx) {
378
+ function stepIf(stmt, path4, ctx) {
378
379
  const thenExit = exitKindOfList(stmt.thenBody);
379
380
  const elseExit = stmt.elseBody === null ? null : exitKindOfList(stmt.elseBody);
380
381
  const thenPath = [
381
- ...path3,
382
+ ...path4,
382
383
  branchCond(stmt, stmt.condition, "positive", elseExit)
383
384
  ];
384
385
  const elsePath = [
385
- ...path3,
386
+ ...path4,
386
387
  branchCond(
387
388
  stmt,
388
389
  stmt.condition,
@@ -399,7 +400,7 @@ function stepIf(stmt, path3, ctx) {
399
400
  }
400
401
  return out;
401
402
  }
402
- function stepSwitch(stmt, path3, ctx) {
403
+ function stepSwitch(stmt, path4, ctx) {
403
404
  const out = [];
404
405
  const negations = [];
405
406
  let defaultRanBody = false;
@@ -409,7 +410,7 @@ function stepSwitch(stmt, path3, ctx) {
409
410
  if (hasStrayBreak(group.body)) {
410
411
  throw new UnmodeledFlow("non-trailing break in switch clause");
411
412
  }
412
- const groupPath = group.condition === null ? [...path3, ...negations] : [...path3, fixedCond(group.condition, "positive", "explicit")];
413
+ const groupPath = group.condition === null ? [...path4, ...negations] : [...path4, fixedCond(group.condition, "positive", "explicit")];
413
414
  const conts = enumerate(ctx, group.body, groupPath);
414
415
  if (conts.length > 0) {
415
416
  if (!group.hasTrailingBreak && !fallthroughSafe) {
@@ -426,24 +427,24 @@ function stepSwitch(stmt, path3, ctx) {
426
427
  }
427
428
  }
428
429
  if (!defaultRanBody) {
429
- out.push([...path3, ...negations]);
430
+ out.push([...path4, ...negations]);
430
431
  }
431
432
  return out;
432
433
  }
433
- function stepLoop(stmt, path3, ctx) {
434
+ function stepLoop(stmt, path4, ctx) {
434
435
  const bodyExit = exitKindOfList(stmt.body);
435
436
  if (containsTerminal(ctx, stmt)) {
436
437
  enumerate(ctx, stmt.body, [
437
- ...path3,
438
+ ...path4,
438
439
  loopIterationCond(stmt.condition.sourceText)
439
440
  ]);
440
441
  }
441
442
  if (bodyExit !== null) {
442
- return [[...path3, loopCompletedCond(stmt.condition.sourceText, bodyExit)]];
443
+ return [[...path4, loopCompletedCond(stmt.condition.sourceText, bodyExit)]];
443
444
  }
444
- return [path3];
445
+ return [path4];
445
446
  }
446
- function stepTry(stmt, path3, ctx) {
447
+ function stepTry(stmt, path4, ctx) {
447
448
  if (stmt.finallyBody !== null) {
448
449
  const finallyHasTerminal = stmt.finallyBody.some(
449
450
  (s) => containsTerminal(ctx, s)
@@ -453,22 +454,22 @@ function stepTry(stmt, path3, ctx) {
453
454
  }
454
455
  }
455
456
  const out = [];
456
- out.push(...enumerate(ctx, stmt.tryBody, path3));
457
+ out.push(...enumerate(ctx, stmt.tryBody, path4));
457
458
  if (stmt.catchBody !== null) {
458
- const catchPath = [...path3, catchEntryCond()];
459
+ const catchPath = [...path4, catchEntryCond()];
459
460
  out.push(...enumerate(ctx, stmt.catchBody, catchPath));
460
461
  }
461
462
  return out;
462
463
  }
463
- function stepExit(stmt, path3, ctx) {
464
+ function stepExit(stmt, path4, ctx) {
464
465
  const collector = ctx.state.callbackExits[ctx.state.callbackExits.length - 1];
465
466
  if (collector !== void 0 && (stmt.exit === "return" || stmt.exit === "throw")) {
466
- collector.push(path3);
467
+ collector.push(path4);
467
468
  }
468
469
  return [];
469
470
  }
470
- function stepOpaque(_stmt, path3, _ctx) {
471
- return [path3];
471
+ function stepOpaque(_stmt, path4, _ctx) {
472
+ return [path4];
472
473
  }
473
474
  var stepHandlers = {
474
475
  if: stepIf,
@@ -478,20 +479,20 @@ var stepHandlers = {
478
479
  exit: stepExit,
479
480
  opaque: stepOpaque
480
481
  };
481
- function stepCallback(ctx, body, path3) {
482
+ function stepCallback(ctx, body, path4) {
482
483
  const exits = [];
483
484
  ctx.state.callbackExits.push(exits);
484
485
  let fellThrough;
485
486
  try {
486
- fellThrough = enumerate(ctx, body, path3);
487
+ fellThrough = enumerate(ctx, body, path4);
487
488
  } finally {
488
489
  ctx.state.callbackExits.pop();
489
490
  }
490
491
  const out = [...fellThrough, ...exits];
491
- return out.length === 0 ? [path3] : out;
492
+ return out.length === 0 ? [path4] : out;
492
493
  }
493
- function stepStatement(ctx, stmt, path3) {
494
- let paths = [path3];
494
+ function stepStatement(ctx, stmt, path4) {
495
+ let paths = [path4];
495
496
  for (const body of stmt.callbacks ?? []) {
496
497
  paths = paths.flatMap((each) => stepCallback(ctx, body, each));
497
498
  }
@@ -572,8 +573,8 @@ function enumerate(ctx, stmts, prefix) {
572
573
  let frontiers = [[...prefix]];
573
574
  for (const stmt of stmts) {
574
575
  const nextFrontiers = [];
575
- for (const path3 of mergeRejoined(frontiers)) {
576
- nextFrontiers.push(...stepStatement(ctx, stmt, path3));
576
+ for (const path4 of mergeRejoined(frontiers)) {
577
+ nextFrontiers.push(...stepStatement(ctx, stmt, path4));
577
578
  }
578
579
  frontiers = nextFrontiers;
579
580
  if (frontiers.length > MAX_PATHS) {
@@ -624,9 +625,9 @@ function enumerateStructuredPaths(input) {
624
625
  isAncestorOrSelf
625
626
  };
626
627
  const continuations = withRejoined(enumerate(ctx, input.statements, []));
627
- for (const path3 of continuations) {
628
+ for (const path4 of continuations) {
628
629
  chargeBudget(state);
629
- state.fallthrough.push(classify(path3, null, isAncestorOrSelf));
630
+ state.fallthrough.push(classify(path4, null, isAncestorOrSelf));
630
631
  }
631
632
  return { byTerminal: state.byTerminal, fallthrough: state.fallthrough };
632
633
  }
@@ -672,9 +673,12 @@ function composeWrappers(summaries, options = {}) {
672
673
  const byKey = /* @__PURE__ */ new Map();
673
674
  for (const summary of summaries) {
674
675
  const key = summaryKey(summary.location.file, summary.identity.name);
675
- if (!byKey.has(key)) {
676
- byKey.set(key, summary);
676
+ const sharing = byKey.get(key);
677
+ if (sharing === void 0) {
678
+ byKey.set(key, [summary]);
679
+ continue;
677
680
  }
681
+ sharing.push(summary);
678
682
  }
679
683
  const keepGaps = options.gapHandling !== "silent";
680
684
  return summaries.map((summary) => composeOne(summary, byKey, keepGaps));
@@ -682,6 +686,13 @@ function composeWrappers(summaries, options = {}) {
682
686
  function summaryKey(file, name) {
683
687
  return `${file}::${name}`;
684
688
  }
689
+ function summaryOf(reference, byKey) {
690
+ const sharing = byKey.get(summaryKey(reference.file, reference.name)) ?? [];
691
+ if (reference.line === void 0) {
692
+ return sharing[0];
693
+ }
694
+ return sharing.find((one) => one.location.range.start === reference.line) ?? sharing[0];
695
+ }
685
696
  function composeOne(summary, byKey, keepGaps) {
686
697
  const recorded = (0, import_behavioral_ir3.readWrapperMetadata)(summary);
687
698
  const applied = recorded?.applied ?? [];
@@ -695,7 +706,7 @@ function composeOne(summary, byKey, keepGaps) {
695
706
  })
696
707
  };
697
708
  const wrappers = covering.flatMap((reference) => {
698
- const found = byKey.get(summaryKey(reference.file, reference.name));
709
+ const found = summaryOf(reference, byKey);
699
710
  return found === void 0 ? [] : [{ reference, summary: found }];
700
711
  });
701
712
  if (wrappers.length === 0) {
@@ -775,8 +786,8 @@ function applyWrapper(wrapper, inner) {
775
786
  wrapper.summary.transitions.filter((t) => t.output.type !== "delegate"),
776
787
  wrapper.reference
777
788
  );
778
- const passThroughs = wrapper.summary.transitions.filter(
779
- (t) => t.output.type === "delegate"
789
+ const passThroughs = withoutCoveredPassThroughs(
790
+ wrapper.summary.transitions.filter((t) => t.output.type === "delegate")
780
791
  );
781
792
  if (passThroughs.length === 0 || shortCircuits.length + passThroughs.length * inner.transitions.length > MAX_PATHS) {
782
793
  return {
@@ -789,6 +800,20 @@ function applyWrapper(wrapper, inner) {
789
800
  );
790
801
  return { transitions: [...shortCircuits, ...continued], degraded: false };
791
802
  }
803
+ function withoutCoveredPassThroughs(passThroughs) {
804
+ return passThroughs.filter(
805
+ (candidate, i) => !passThroughs.some(
806
+ (other, j) => j !== i && covers(other, candidate) && (j < i || !covers(candidate, other))
807
+ )
808
+ );
809
+ }
810
+ function covers(wider, narrower) {
811
+ if (JSON.stringify(wider.effects) !== JSON.stringify(narrower.effects)) {
812
+ return false;
813
+ }
814
+ const required = new Set(narrower.conditions.map((c) => JSON.stringify(c)));
815
+ return wider.conditions.every((c) => required.has(JSON.stringify(c)));
816
+ }
792
817
  function applyThrowWrapper(wrapper, inner) {
793
818
  const thrown = inner.transitions.filter((t) => t.output.type === "throw");
794
819
  if (thrown.length === 0) {
@@ -1114,8 +1139,8 @@ function sharedGatingConditions(paths, readPredicate) {
1114
1139
  return [];
1115
1140
  }
1116
1141
  const shared = new Set(first.map(keyOf2));
1117
- for (const path3 of rest) {
1118
- const here = new Set(path3.map(keyOf2));
1142
+ for (const path4 of rest) {
1143
+ const here = new Set(path4.map(keyOf2));
1119
1144
  for (const value of shared) {
1120
1145
  if (!here.has(value)) {
1121
1146
  shared.delete(value);
@@ -1191,6 +1216,27 @@ function walkDescendants(root, carried, visitor) {
1191
1216
  }
1192
1217
  }
1193
1218
 
1219
+ // src/projectRoot.ts
1220
+ var import_node_path = __toESM(require("path"), 1);
1221
+ function commonDirectoryOf(files) {
1222
+ const absolute = files.filter((f) => import_node_path.default.isAbsolute(f));
1223
+ const first = absolute[0];
1224
+ if (first === void 0) {
1225
+ return void 0;
1226
+ }
1227
+ let shared = import_node_path.default.dirname(first).split(import_node_path.default.sep);
1228
+ for (const file of absolute.slice(1)) {
1229
+ const parts = import_node_path.default.dirname(file).split(import_node_path.default.sep);
1230
+ let i = 0;
1231
+ while (i < shared.length && i < parts.length && shared[i] === parts[i]) {
1232
+ i += 1;
1233
+ }
1234
+ shared = shared.slice(0, i);
1235
+ }
1236
+ const joined = shared.join(import_node_path.default.sep);
1237
+ return joined.length > 1 ? joined : void 0;
1238
+ }
1239
+
1194
1240
  // src/reading.ts
1195
1241
  function writtenReading(value, range) {
1196
1242
  return { kind: "written", value, range };
@@ -1248,7 +1294,7 @@ function valueToReadFurtherFrom(reading) {
1248
1294
  var import_node_crypto = require("crypto");
1249
1295
  var import_node_fs = __toESM(require("fs"), 1);
1250
1296
  var import_node_module = require("module");
1251
- var import_node_path = __toESM(require("path"), 1);
1297
+ var import_node_path2 = __toESM(require("path"), 1);
1252
1298
  var import_node_url = require("url");
1253
1299
  var import_meta = {};
1254
1300
  var ANALYSIS_PACKAGES = [
@@ -1291,14 +1337,14 @@ function createAdapterStamp(config) {
1291
1337
  function readAdapterCodeStamp(moduleUrl) {
1292
1338
  try {
1293
1339
  const selfPath = (0, import_node_url.fileURLToPath)(moduleUrl);
1294
- const hash = computeDistHashFrom(import_node_path.default.dirname(selfPath));
1340
+ const hash = computeDistHashFrom(import_node_path2.default.dirname(selfPath));
1295
1341
  return hash.length > 0 ? { kind: "bundle", hash } : SOURCE_STAMP;
1296
1342
  } catch {
1297
1343
  return SOURCE_STAMP;
1298
1344
  }
1299
1345
  }
1300
1346
  function computeDistHashFrom(dir) {
1301
- const candidates = [import_node_path.default.join(dir, "index.js"), import_node_path.default.join(dir, "index.cjs")];
1347
+ const candidates = [import_node_path2.default.join(dir, "index.js"), import_node_path2.default.join(dir, "index.cjs")];
1302
1348
  for (const candidate of candidates) {
1303
1349
  if (!import_node_fs.default.existsSync(candidate)) {
1304
1350
  continue;
@@ -1356,7 +1402,7 @@ function analysisBundles() {
1356
1402
  // src/cache.ts
1357
1403
  var import_node_crypto2 = require("crypto");
1358
1404
  var import_promises = __toESM(require("fs/promises"), 1);
1359
- var import_node_path2 = __toESM(require("path"), 1);
1405
+ var import_node_path3 = __toESM(require("path"), 1);
1360
1406
  var SCHEMA_VERSION = "7";
1361
1407
  var MAX_ENTRIES = 2;
1362
1408
  var ENTRY_PREFIX = "key-";
@@ -1382,7 +1428,7 @@ function createCacheLayer(cacheDir) {
1382
1428
  async lookup(input) {
1383
1429
  const entryDir = entryDirFor(cacheDir, input);
1384
1430
  const manifest = await readManifest(
1385
- import_node_path2.default.join(entryDir, "manifest.json")
1431
+ import_node_path3.default.join(entryDir, "manifest.json")
1386
1432
  );
1387
1433
  if (manifest === null) {
1388
1434
  return missDiag(await describeAbsentEntry(cacheDir));
@@ -1405,7 +1451,7 @@ function createCacheLayer(cacheDir) {
1405
1451
  async plan(input) {
1406
1452
  const entryDir = entryDirFor(cacheDir, input);
1407
1453
  const manifest = await readManifest(
1408
- import_node_path2.default.join(entryDir, "manifest.json")
1454
+ import_node_path3.default.join(entryDir, "manifest.json")
1409
1455
  );
1410
1456
  if (manifest === null || manifest.roots === void 0 || manifest.rootMeta === void 0 || manifest.owners === void 0) {
1411
1457
  return null;
@@ -1420,7 +1466,7 @@ function createCacheLayer(cacheDir) {
1420
1466
  async write(input, summaries, attribution) {
1421
1467
  const entryDir = entryDirFor(cacheDir, input);
1422
1468
  const previous = await readManifest(
1423
- import_node_path2.default.join(entryDir, "manifest.json")
1469
+ import_node_path3.default.join(entryDir, "manifest.json")
1424
1470
  );
1425
1471
  const configStamp = await stampConfigFile(input.configPath);
1426
1472
  const files = await hashStamps(await resolveFileStamps(input), previous);
@@ -1434,7 +1480,7 @@ function createCacheLayer(cacheDir) {
1434
1480
  };
1435
1481
  await import_promises.default.mkdir(entryDir, { recursive: true });
1436
1482
  await import_promises.default.writeFile(
1437
- import_node_path2.default.join(entryDir, "manifest.json"),
1483
+ import_node_path3.default.join(entryDir, "manifest.json"),
1438
1484
  JSON.stringify(manifest)
1439
1485
  );
1440
1486
  await evictOldEntries(cacheDir, entryDir);
@@ -1613,7 +1659,7 @@ function entryDirFor(cacheDir, input) {
1613
1659
  input.configPath ?? ""
1614
1660
  ].join(" ");
1615
1661
  const name = (0, import_node_crypto2.createHash)("sha256").update(key).digest("hex").slice(0, 16);
1616
- return import_node_path2.default.join(cacheDir, `${ENTRY_PREFIX}${name}`);
1662
+ return import_node_path3.default.join(cacheDir, `${ENTRY_PREFIX}${name}`);
1617
1663
  }
1618
1664
  function isEntryDir(name) {
1619
1665
  return ENTRY_DIR_NAME.test(name);
@@ -1627,11 +1673,11 @@ async function markUsed(entryDir) {
1627
1673
  }
1628
1674
  async function evictOldEntries(cacheDir, keepDir) {
1629
1675
  try {
1630
- await import_promises.default.rm(import_node_path2.default.join(cacheDir, "manifest.json"), { force: true });
1676
+ await import_promises.default.rm(import_node_path3.default.join(cacheDir, "manifest.json"), { force: true });
1631
1677
  const entries = await import_promises.default.readdir(cacheDir, { withFileTypes: true });
1632
1678
  const dirs = await Promise.all(
1633
1679
  entries.filter((e) => e.isDirectory() && isEntryDir(e.name)).map(async (e) => {
1634
- const dir = import_node_path2.default.join(cacheDir, e.name);
1680
+ const dir = import_node_path3.default.join(cacheDir, e.name);
1635
1681
  const stat = await import_promises.default.stat(dir);
1636
1682
  return { dir, mtimeMs: stat.mtimeMs };
1637
1683
  })
@@ -2458,6 +2504,7 @@ function paramToInput(param) {
2458
2504
  assembleSummary,
2459
2505
  assessConfidence,
2460
2506
  buildUngatedExtractionReport,
2507
+ commonDirectoryOf,
2461
2508
  composeWrappers,
2462
2509
  computeContentHash,
2463
2510
  computeDistHashFrom,
package/dist/index.d.cts CHANGED
@@ -173,6 +173,12 @@ type DiscoveryMatch = {
173
173
  * `axios.get(...)`.
174
174
  */
175
175
  factoryMethods?: string[];
176
+ /**
177
+ * The import and every instance built from it can be called as a
178
+ * function, so `axios(config)` and `api(config)` are requests the
179
+ * same as a call through a method in `methodFilter`.
180
+ */
181
+ callable?: boolean;
176
182
  } | {
177
183
  /**
178
184
  * A constructor or factory call that takes a configuration object
@@ -2272,6 +2278,16 @@ interface NodeVisitor<T, C> {
2272
2278
  */
2273
2279
  declare function walkDescendants<T extends WalkableNode<T>, C>(root: T, carried: C, visitor: NodeVisitor<T, C>): void;
2274
2280
 
2281
+ /**
2282
+ * The project root when nothing declares one.
2283
+ *
2284
+ * Resolution from anywhere inside a tree finds the same `node_modules`,
2285
+ * and a recursive reader pointed at the directory every file shares
2286
+ * reaches all of them, so the deepest common directory serves both.
2287
+ */
2288
+ /** The deepest directory containing every file, or undefined when they share no absolute root. */
2289
+ declare function commonDirectoryOf(files: ReadonlyArray<string>): string | undefined;
2290
+
2275
2291
  /**
2276
2292
  * The small surface every language adapter implements, whatever that
2277
2293
  * language's own tooling looks like underneath.
@@ -3434,4 +3450,4 @@ declare function rawConditionToPredicate(c: RawCondition): Predicate;
3434
3450
  declare function effectToIR(effect: RawEffect): Effect;
3435
3451
  declare function paramToInput(param: RawParameter): Input;
3436
3452
 
3437
- export { type AccessRecognizer, type AdapterCodeStamp, type AdapterStamp, type AstCapableOps, type BindingExtraction, type BodyContent, type CacheAttribution, type CacheDiagnostic, type CacheInput, type CacheLayer, type CacheLookup, type CallOps, type CaseGroup, type ChannelSource, type ChosenReading, type ConditionHandle, type ConditionInfo, type ConditionSource, type ConstructedFrom, type ContractPattern, type DeclaredBinding, type DeclaredBy, type DeclaredMatch, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type EmptyStage, type ExitKind, type ExtractionReport, type ExtractorOptions, type FailureDelivery, type HealthCheck, type HealthViolation, type HelperDeclarations, type HelperSearch, type HelperSink, type HelperValue, IdMap, IdSet, type Identified, type InputMappingPattern, type InputRead, type InvocationRecognizer, type LanguageAdapter, type LoweredStatementParts, MAX_ENTRIES, MAX_PATHS, type ModuleInitOptions, type NodeVisitor, type OpsCarrier, type PackDeclarations, type PackFailure, type PackFunnel, type PackGradient, type PackTally, type PartialPlan, PathBudgetExceeded, type PatternPack, type ProjectHelper, type ProjectHelpers, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type Reading, type ReceiverOrigin, type RegistrationHelper, type ResponsePropertyMapping, type ResponsePropertyMeaning, type RootRecord, SKIP_CHILDREN, type SourceRange, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type Timer, type TimingReport, type TransparentWrapper, UnmodeledFlow, type UnsettledName, type ValueEntry, type ValueOps, type WalkableNode, type WrapperMethodRegistration, type WrapperOptionRegistration, type WrapperRegistration, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, buildUngatedExtractionReport, composeWrappers, computeContentHash, computeDistHashFrom, configuredCallOption, createAdapterStamp, createCacheLayer, createPackTallies, createTimer, detectGaps, effectToIR, emptyTally, enumerateOrDegrade, enumerateStructuredPaths, evaluatePackHealth, firstWrittenReading, formatPackHealth, guardsHoldOn, httpRouteDiscovery, makeTransitionId, mapReading, moduleInitStructure, noopTimer, packGradients, paramToInput, projectFileStamp, rawConditionToPredicate, recordPackFailure, registrationHelperDiscovery, routeHelperIndex, runDigest, runsBefore, scopeOption, sharedGatingConditions, stampModuleImports, storageSystemOption, summaryCountsByPack, tallyUnit, terminalToOutput, unreadableReading, unwrapJsonStringify, valueToReadFurtherFrom, walkDescendants, wrapperDiscovery, writtenReading };
3453
+ export { type AccessRecognizer, type AdapterCodeStamp, type AdapterStamp, type AstCapableOps, type BindingExtraction, type BodyContent, type CacheAttribution, type CacheDiagnostic, type CacheInput, type CacheLayer, type CacheLookup, type CallOps, type CaseGroup, type ChannelSource, type ChosenReading, type ConditionHandle, type ConditionInfo, type ConditionSource, type ConstructedFrom, type ContractPattern, type DeclaredBinding, type DeclaredBy, type DeclaredMatch, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type EmptyStage, type ExitKind, type ExtractionReport, type ExtractorOptions, type FailureDelivery, type HealthCheck, type HealthViolation, type HelperDeclarations, type HelperSearch, type HelperSink, type HelperValue, IdMap, IdSet, type Identified, type InputMappingPattern, type InputRead, type InvocationRecognizer, type LanguageAdapter, type LoweredStatementParts, MAX_ENTRIES, MAX_PATHS, type ModuleInitOptions, type NodeVisitor, type OpsCarrier, type PackDeclarations, type PackFailure, type PackFunnel, type PackGradient, type PackTally, type PartialPlan, PathBudgetExceeded, type PatternPack, type ProjectHelper, type ProjectHelpers, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type Reading, type ReceiverOrigin, type RegistrationHelper, type ResponsePropertyMapping, type ResponsePropertyMeaning, type RootRecord, SKIP_CHILDREN, type SourceRange, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type Timer, type TimingReport, type TransparentWrapper, UnmodeledFlow, type UnsettledName, type ValueEntry, type ValueOps, type WalkableNode, type WrapperMethodRegistration, type WrapperOptionRegistration, type WrapperRegistration, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, buildUngatedExtractionReport, commonDirectoryOf, composeWrappers, computeContentHash, computeDistHashFrom, configuredCallOption, createAdapterStamp, createCacheLayer, createPackTallies, createTimer, detectGaps, effectToIR, emptyTally, enumerateOrDegrade, enumerateStructuredPaths, evaluatePackHealth, firstWrittenReading, formatPackHealth, guardsHoldOn, httpRouteDiscovery, makeTransitionId, mapReading, moduleInitStructure, noopTimer, packGradients, paramToInput, projectFileStamp, rawConditionToPredicate, recordPackFailure, registrationHelperDiscovery, routeHelperIndex, runDigest, runsBefore, scopeOption, sharedGatingConditions, stampModuleImports, storageSystemOption, summaryCountsByPack, tallyUnit, terminalToOutput, unreadableReading, unwrapJsonStringify, valueToReadFurtherFrom, walkDescendants, wrapperDiscovery, writtenReading };
package/dist/index.d.ts CHANGED
@@ -173,6 +173,12 @@ type DiscoveryMatch = {
173
173
  * `axios.get(...)`.
174
174
  */
175
175
  factoryMethods?: string[];
176
+ /**
177
+ * The import and every instance built from it can be called as a
178
+ * function, so `axios(config)` and `api(config)` are requests the
179
+ * same as a call through a method in `methodFilter`.
180
+ */
181
+ callable?: boolean;
176
182
  } | {
177
183
  /**
178
184
  * A constructor or factory call that takes a configuration object
@@ -2272,6 +2278,16 @@ interface NodeVisitor<T, C> {
2272
2278
  */
2273
2279
  declare function walkDescendants<T extends WalkableNode<T>, C>(root: T, carried: C, visitor: NodeVisitor<T, C>): void;
2274
2280
 
2281
+ /**
2282
+ * The project root when nothing declares one.
2283
+ *
2284
+ * Resolution from anywhere inside a tree finds the same `node_modules`,
2285
+ * and a recursive reader pointed at the directory every file shares
2286
+ * reaches all of them, so the deepest common directory serves both.
2287
+ */
2288
+ /** The deepest directory containing every file, or undefined when they share no absolute root. */
2289
+ declare function commonDirectoryOf(files: ReadonlyArray<string>): string | undefined;
2290
+
2275
2291
  /**
2276
2292
  * The small surface every language adapter implements, whatever that
2277
2293
  * language's own tooling looks like underneath.
@@ -3434,4 +3450,4 @@ declare function rawConditionToPredicate(c: RawCondition): Predicate;
3434
3450
  declare function effectToIR(effect: RawEffect): Effect;
3435
3451
  declare function paramToInput(param: RawParameter): Input;
3436
3452
 
3437
- export { type AccessRecognizer, type AdapterCodeStamp, type AdapterStamp, type AstCapableOps, type BindingExtraction, type BodyContent, type CacheAttribution, type CacheDiagnostic, type CacheInput, type CacheLayer, type CacheLookup, type CallOps, type CaseGroup, type ChannelSource, type ChosenReading, type ConditionHandle, type ConditionInfo, type ConditionSource, type ConstructedFrom, type ContractPattern, type DeclaredBinding, type DeclaredBy, type DeclaredMatch, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type EmptyStage, type ExitKind, type ExtractionReport, type ExtractorOptions, type FailureDelivery, type HealthCheck, type HealthViolation, type HelperDeclarations, type HelperSearch, type HelperSink, type HelperValue, IdMap, IdSet, type Identified, type InputMappingPattern, type InputRead, type InvocationRecognizer, type LanguageAdapter, type LoweredStatementParts, MAX_ENTRIES, MAX_PATHS, type ModuleInitOptions, type NodeVisitor, type OpsCarrier, type PackDeclarations, type PackFailure, type PackFunnel, type PackGradient, type PackTally, type PartialPlan, PathBudgetExceeded, type PatternPack, type ProjectHelper, type ProjectHelpers, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type Reading, type ReceiverOrigin, type RegistrationHelper, type ResponsePropertyMapping, type ResponsePropertyMeaning, type RootRecord, SKIP_CHILDREN, type SourceRange, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type Timer, type TimingReport, type TransparentWrapper, UnmodeledFlow, type UnsettledName, type ValueEntry, type ValueOps, type WalkableNode, type WrapperMethodRegistration, type WrapperOptionRegistration, type WrapperRegistration, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, buildUngatedExtractionReport, composeWrappers, computeContentHash, computeDistHashFrom, configuredCallOption, createAdapterStamp, createCacheLayer, createPackTallies, createTimer, detectGaps, effectToIR, emptyTally, enumerateOrDegrade, enumerateStructuredPaths, evaluatePackHealth, firstWrittenReading, formatPackHealth, guardsHoldOn, httpRouteDiscovery, makeTransitionId, mapReading, moduleInitStructure, noopTimer, packGradients, paramToInput, projectFileStamp, rawConditionToPredicate, recordPackFailure, registrationHelperDiscovery, routeHelperIndex, runDigest, runsBefore, scopeOption, sharedGatingConditions, stampModuleImports, storageSystemOption, summaryCountsByPack, tallyUnit, terminalToOutput, unreadableReading, unwrapJsonStringify, valueToReadFurtherFrom, walkDescendants, wrapperDiscovery, writtenReading };
3453
+ export { type AccessRecognizer, type AdapterCodeStamp, type AdapterStamp, type AstCapableOps, type BindingExtraction, type BodyContent, type CacheAttribution, type CacheDiagnostic, type CacheInput, type CacheLayer, type CacheLookup, type CallOps, type CaseGroup, type ChannelSource, type ChosenReading, type ConditionHandle, type ConditionInfo, type ConditionSource, type ConstructedFrom, type ContractPattern, type DeclaredBinding, type DeclaredBy, type DeclaredMatch, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type EmptyStage, type ExitKind, type ExtractionReport, type ExtractorOptions, type FailureDelivery, type HealthCheck, type HealthViolation, type HelperDeclarations, type HelperSearch, type HelperSink, type HelperValue, IdMap, IdSet, type Identified, type InputMappingPattern, type InputRead, type InvocationRecognizer, type LanguageAdapter, type LoweredStatementParts, MAX_ENTRIES, MAX_PATHS, type ModuleInitOptions, type NodeVisitor, type OpsCarrier, type PackDeclarations, type PackFailure, type PackFunnel, type PackGradient, type PackTally, type PartialPlan, PathBudgetExceeded, type PatternPack, type ProjectHelper, type ProjectHelpers, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type Reading, type ReceiverOrigin, type RegistrationHelper, type ResponsePropertyMapping, type ResponsePropertyMeaning, type RootRecord, SKIP_CHILDREN, type SourceRange, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type Timer, type TimingReport, type TransparentWrapper, UnmodeledFlow, type UnsettledName, type ValueEntry, type ValueOps, type WalkableNode, type WrapperMethodRegistration, type WrapperOptionRegistration, type WrapperRegistration, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, buildUngatedExtractionReport, commonDirectoryOf, composeWrappers, computeContentHash, computeDistHashFrom, configuredCallOption, createAdapterStamp, createCacheLayer, createPackTallies, createTimer, detectGaps, effectToIR, emptyTally, enumerateOrDegrade, enumerateStructuredPaths, evaluatePackHealth, firstWrittenReading, formatPackHealth, guardsHoldOn, httpRouteDiscovery, makeTransitionId, mapReading, moduleInitStructure, noopTimer, packGradients, paramToInput, projectFileStamp, rawConditionToPredicate, recordPackFailure, registrationHelperDiscovery, routeHelperIndex, runDigest, runsBefore, scopeOption, sharedGatingConditions, stampModuleImports, storageSystemOption, summaryCountsByPack, tallyUnit, terminalToOutput, unreadableReading, unwrapJsonStringify, valueToReadFurtherFrom, walkDescendants, wrapperDiscovery, writtenReading };
package/dist/index.js CHANGED
@@ -218,8 +218,8 @@ var catchEntryCond = () => fixedCond(
218
218
  "positive",
219
219
  "catchBlock"
220
220
  );
221
- function classify(path3, home, isAncestorOrSelf) {
222
- return path3.map((cond) => {
221
+ function classify(path4, home, isAncestorOrSelf) {
222
+ return path4.map((cond) => {
223
223
  if (cond.branchStmt === null) {
224
224
  return cond.info;
225
225
  }
@@ -284,9 +284,9 @@ function chargeBudget(state) {
284
284
  );
285
285
  }
286
286
  }
287
- function recordTerminal(ctx, terminal, home, path3) {
287
+ function recordTerminal(ctx, terminal, home, path4) {
288
288
  chargeBudget(ctx.state);
289
- const info = classify(path3, home, ctx.isAncestorOrSelf);
289
+ const info = classify(path4, home, ctx.isAncestorOrSelf);
290
290
  const existing = ctx.state.byTerminal.get(terminal) ?? [];
291
291
  existing.push(info);
292
292
  ctx.state.byTerminal.set(terminal, existing);
@@ -297,15 +297,15 @@ function containsTerminal(ctx, stmt) {
297
297
  }
298
298
  return childrenOf(stmt).some((child) => containsTerminal(ctx, child));
299
299
  }
300
- function stepIf(stmt, path3, ctx) {
300
+ function stepIf(stmt, path4, ctx) {
301
301
  const thenExit = exitKindOfList(stmt.thenBody);
302
302
  const elseExit = stmt.elseBody === null ? null : exitKindOfList(stmt.elseBody);
303
303
  const thenPath = [
304
- ...path3,
304
+ ...path4,
305
305
  branchCond(stmt, stmt.condition, "positive", elseExit)
306
306
  ];
307
307
  const elsePath = [
308
- ...path3,
308
+ ...path4,
309
309
  branchCond(
310
310
  stmt,
311
311
  stmt.condition,
@@ -322,7 +322,7 @@ function stepIf(stmt, path3, ctx) {
322
322
  }
323
323
  return out;
324
324
  }
325
- function stepSwitch(stmt, path3, ctx) {
325
+ function stepSwitch(stmt, path4, ctx) {
326
326
  const out = [];
327
327
  const negations = [];
328
328
  let defaultRanBody = false;
@@ -332,7 +332,7 @@ function stepSwitch(stmt, path3, ctx) {
332
332
  if (hasStrayBreak(group.body)) {
333
333
  throw new UnmodeledFlow("non-trailing break in switch clause");
334
334
  }
335
- const groupPath = group.condition === null ? [...path3, ...negations] : [...path3, fixedCond(group.condition, "positive", "explicit")];
335
+ const groupPath = group.condition === null ? [...path4, ...negations] : [...path4, fixedCond(group.condition, "positive", "explicit")];
336
336
  const conts = enumerate(ctx, group.body, groupPath);
337
337
  if (conts.length > 0) {
338
338
  if (!group.hasTrailingBreak && !fallthroughSafe) {
@@ -349,24 +349,24 @@ function stepSwitch(stmt, path3, ctx) {
349
349
  }
350
350
  }
351
351
  if (!defaultRanBody) {
352
- out.push([...path3, ...negations]);
352
+ out.push([...path4, ...negations]);
353
353
  }
354
354
  return out;
355
355
  }
356
- function stepLoop(stmt, path3, ctx) {
356
+ function stepLoop(stmt, path4, ctx) {
357
357
  const bodyExit = exitKindOfList(stmt.body);
358
358
  if (containsTerminal(ctx, stmt)) {
359
359
  enumerate(ctx, stmt.body, [
360
- ...path3,
360
+ ...path4,
361
361
  loopIterationCond(stmt.condition.sourceText)
362
362
  ]);
363
363
  }
364
364
  if (bodyExit !== null) {
365
- return [[...path3, loopCompletedCond(stmt.condition.sourceText, bodyExit)]];
365
+ return [[...path4, loopCompletedCond(stmt.condition.sourceText, bodyExit)]];
366
366
  }
367
- return [path3];
367
+ return [path4];
368
368
  }
369
- function stepTry(stmt, path3, ctx) {
369
+ function stepTry(stmt, path4, ctx) {
370
370
  if (stmt.finallyBody !== null) {
371
371
  const finallyHasTerminal = stmt.finallyBody.some(
372
372
  (s) => containsTerminal(ctx, s)
@@ -376,22 +376,22 @@ function stepTry(stmt, path3, ctx) {
376
376
  }
377
377
  }
378
378
  const out = [];
379
- out.push(...enumerate(ctx, stmt.tryBody, path3));
379
+ out.push(...enumerate(ctx, stmt.tryBody, path4));
380
380
  if (stmt.catchBody !== null) {
381
- const catchPath = [...path3, catchEntryCond()];
381
+ const catchPath = [...path4, catchEntryCond()];
382
382
  out.push(...enumerate(ctx, stmt.catchBody, catchPath));
383
383
  }
384
384
  return out;
385
385
  }
386
- function stepExit(stmt, path3, ctx) {
386
+ function stepExit(stmt, path4, ctx) {
387
387
  const collector = ctx.state.callbackExits[ctx.state.callbackExits.length - 1];
388
388
  if (collector !== void 0 && (stmt.exit === "return" || stmt.exit === "throw")) {
389
- collector.push(path3);
389
+ collector.push(path4);
390
390
  }
391
391
  return [];
392
392
  }
393
- function stepOpaque(_stmt, path3, _ctx) {
394
- return [path3];
393
+ function stepOpaque(_stmt, path4, _ctx) {
394
+ return [path4];
395
395
  }
396
396
  var stepHandlers = {
397
397
  if: stepIf,
@@ -401,20 +401,20 @@ var stepHandlers = {
401
401
  exit: stepExit,
402
402
  opaque: stepOpaque
403
403
  };
404
- function stepCallback(ctx, body, path3) {
404
+ function stepCallback(ctx, body, path4) {
405
405
  const exits = [];
406
406
  ctx.state.callbackExits.push(exits);
407
407
  let fellThrough;
408
408
  try {
409
- fellThrough = enumerate(ctx, body, path3);
409
+ fellThrough = enumerate(ctx, body, path4);
410
410
  } finally {
411
411
  ctx.state.callbackExits.pop();
412
412
  }
413
413
  const out = [...fellThrough, ...exits];
414
- return out.length === 0 ? [path3] : out;
414
+ return out.length === 0 ? [path4] : out;
415
415
  }
416
- function stepStatement(ctx, stmt, path3) {
417
- let paths = [path3];
416
+ function stepStatement(ctx, stmt, path4) {
417
+ let paths = [path4];
418
418
  for (const body of stmt.callbacks ?? []) {
419
419
  paths = paths.flatMap((each) => stepCallback(ctx, body, each));
420
420
  }
@@ -495,8 +495,8 @@ function enumerate(ctx, stmts, prefix) {
495
495
  let frontiers = [[...prefix]];
496
496
  for (const stmt of stmts) {
497
497
  const nextFrontiers = [];
498
- for (const path3 of mergeRejoined(frontiers)) {
499
- nextFrontiers.push(...stepStatement(ctx, stmt, path3));
498
+ for (const path4 of mergeRejoined(frontiers)) {
499
+ nextFrontiers.push(...stepStatement(ctx, stmt, path4));
500
500
  }
501
501
  frontiers = nextFrontiers;
502
502
  if (frontiers.length > MAX_PATHS) {
@@ -547,9 +547,9 @@ function enumerateStructuredPaths(input) {
547
547
  isAncestorOrSelf
548
548
  };
549
549
  const continuations = withRejoined(enumerate(ctx, input.statements, []));
550
- for (const path3 of continuations) {
550
+ for (const path4 of continuations) {
551
551
  chargeBudget(state);
552
- state.fallthrough.push(classify(path3, null, isAncestorOrSelf));
552
+ state.fallthrough.push(classify(path4, null, isAncestorOrSelf));
553
553
  }
554
554
  return { byTerminal: state.byTerminal, fallthrough: state.fallthrough };
555
555
  }
@@ -595,9 +595,12 @@ function composeWrappers(summaries, options = {}) {
595
595
  const byKey = /* @__PURE__ */ new Map();
596
596
  for (const summary of summaries) {
597
597
  const key = summaryKey(summary.location.file, summary.identity.name);
598
- if (!byKey.has(key)) {
599
- byKey.set(key, summary);
598
+ const sharing = byKey.get(key);
599
+ if (sharing === void 0) {
600
+ byKey.set(key, [summary]);
601
+ continue;
600
602
  }
603
+ sharing.push(summary);
601
604
  }
602
605
  const keepGaps = options.gapHandling !== "silent";
603
606
  return summaries.map((summary) => composeOne(summary, byKey, keepGaps));
@@ -605,6 +608,13 @@ function composeWrappers(summaries, options = {}) {
605
608
  function summaryKey(file, name) {
606
609
  return `${file}::${name}`;
607
610
  }
611
+ function summaryOf(reference, byKey) {
612
+ const sharing = byKey.get(summaryKey(reference.file, reference.name)) ?? [];
613
+ if (reference.line === void 0) {
614
+ return sharing[0];
615
+ }
616
+ return sharing.find((one) => one.location.range.start === reference.line) ?? sharing[0];
617
+ }
608
618
  function composeOne(summary, byKey, keepGaps) {
609
619
  const recorded = readWrapperMetadata2(summary);
610
620
  const applied = recorded?.applied ?? [];
@@ -618,7 +628,7 @@ function composeOne(summary, byKey, keepGaps) {
618
628
  })
619
629
  };
620
630
  const wrappers = covering.flatMap((reference) => {
621
- const found = byKey.get(summaryKey(reference.file, reference.name));
631
+ const found = summaryOf(reference, byKey);
622
632
  return found === void 0 ? [] : [{ reference, summary: found }];
623
633
  });
624
634
  if (wrappers.length === 0) {
@@ -698,8 +708,8 @@ function applyWrapper(wrapper, inner) {
698
708
  wrapper.summary.transitions.filter((t) => t.output.type !== "delegate"),
699
709
  wrapper.reference
700
710
  );
701
- const passThroughs = wrapper.summary.transitions.filter(
702
- (t) => t.output.type === "delegate"
711
+ const passThroughs = withoutCoveredPassThroughs(
712
+ wrapper.summary.transitions.filter((t) => t.output.type === "delegate")
703
713
  );
704
714
  if (passThroughs.length === 0 || shortCircuits.length + passThroughs.length * inner.transitions.length > MAX_PATHS) {
705
715
  return {
@@ -712,6 +722,20 @@ function applyWrapper(wrapper, inner) {
712
722
  );
713
723
  return { transitions: [...shortCircuits, ...continued], degraded: false };
714
724
  }
725
+ function withoutCoveredPassThroughs(passThroughs) {
726
+ return passThroughs.filter(
727
+ (candidate, i) => !passThroughs.some(
728
+ (other, j) => j !== i && covers(other, candidate) && (j < i || !covers(candidate, other))
729
+ )
730
+ );
731
+ }
732
+ function covers(wider, narrower) {
733
+ if (JSON.stringify(wider.effects) !== JSON.stringify(narrower.effects)) {
734
+ return false;
735
+ }
736
+ const required = new Set(narrower.conditions.map((c) => JSON.stringify(c)));
737
+ return wider.conditions.every((c) => required.has(JSON.stringify(c)));
738
+ }
715
739
  function applyThrowWrapper(wrapper, inner) {
716
740
  const thrown = inner.transitions.filter((t) => t.output.type === "throw");
717
741
  if (thrown.length === 0) {
@@ -1037,8 +1061,8 @@ function sharedGatingConditions(paths, readPredicate) {
1037
1061
  return [];
1038
1062
  }
1039
1063
  const shared = new Set(first.map(keyOf2));
1040
- for (const path3 of rest) {
1041
- const here = new Set(path3.map(keyOf2));
1064
+ for (const path4 of rest) {
1065
+ const here = new Set(path4.map(keyOf2));
1042
1066
  for (const value of shared) {
1043
1067
  if (!here.has(value)) {
1044
1068
  shared.delete(value);
@@ -1114,6 +1138,27 @@ function walkDescendants(root, carried, visitor) {
1114
1138
  }
1115
1139
  }
1116
1140
 
1141
+ // src/projectRoot.ts
1142
+ import path from "path";
1143
+ function commonDirectoryOf(files) {
1144
+ const absolute = files.filter((f) => path.isAbsolute(f));
1145
+ const first = absolute[0];
1146
+ if (first === void 0) {
1147
+ return void 0;
1148
+ }
1149
+ let shared = path.dirname(first).split(path.sep);
1150
+ for (const file of absolute.slice(1)) {
1151
+ const parts = path.dirname(file).split(path.sep);
1152
+ let i = 0;
1153
+ while (i < shared.length && i < parts.length && shared[i] === parts[i]) {
1154
+ i += 1;
1155
+ }
1156
+ shared = shared.slice(0, i);
1157
+ }
1158
+ const joined = shared.join(path.sep);
1159
+ return joined.length > 1 ? joined : void 0;
1160
+ }
1161
+
1117
1162
  // src/reading.ts
1118
1163
  function writtenReading(value, range) {
1119
1164
  return { kind: "written", value, range };
@@ -1171,7 +1216,7 @@ function valueToReadFurtherFrom(reading) {
1171
1216
  import { createHash } from "crypto";
1172
1217
  import fs from "fs";
1173
1218
  import { createRequire } from "module";
1174
- import path from "path";
1219
+ import path2 from "path";
1175
1220
  import { fileURLToPath } from "url";
1176
1221
  var ANALYSIS_PACKAGES = [
1177
1222
  "@suss/extractor",
@@ -1213,14 +1258,14 @@ function createAdapterStamp(config) {
1213
1258
  function readAdapterCodeStamp(moduleUrl) {
1214
1259
  try {
1215
1260
  const selfPath = fileURLToPath(moduleUrl);
1216
- const hash = computeDistHashFrom(path.dirname(selfPath));
1261
+ const hash = computeDistHashFrom(path2.dirname(selfPath));
1217
1262
  return hash.length > 0 ? { kind: "bundle", hash } : SOURCE_STAMP;
1218
1263
  } catch {
1219
1264
  return SOURCE_STAMP;
1220
1265
  }
1221
1266
  }
1222
1267
  function computeDistHashFrom(dir) {
1223
- const candidates = [path.join(dir, "index.js"), path.join(dir, "index.cjs")];
1268
+ const candidates = [path2.join(dir, "index.js"), path2.join(dir, "index.cjs")];
1224
1269
  for (const candidate of candidates) {
1225
1270
  if (!fs.existsSync(candidate)) {
1226
1271
  continue;
@@ -1278,7 +1323,7 @@ function analysisBundles() {
1278
1323
  // src/cache.ts
1279
1324
  import { createHash as createHash2 } from "crypto";
1280
1325
  import fs2 from "fs/promises";
1281
- import path2 from "path";
1326
+ import path3 from "path";
1282
1327
  var SCHEMA_VERSION = "7";
1283
1328
  var MAX_ENTRIES = 2;
1284
1329
  var ENTRY_PREFIX = "key-";
@@ -1304,7 +1349,7 @@ function createCacheLayer(cacheDir) {
1304
1349
  async lookup(input) {
1305
1350
  const entryDir = entryDirFor(cacheDir, input);
1306
1351
  const manifest = await readManifest(
1307
- path2.join(entryDir, "manifest.json")
1352
+ path3.join(entryDir, "manifest.json")
1308
1353
  );
1309
1354
  if (manifest === null) {
1310
1355
  return missDiag(await describeAbsentEntry(cacheDir));
@@ -1327,7 +1372,7 @@ function createCacheLayer(cacheDir) {
1327
1372
  async plan(input) {
1328
1373
  const entryDir = entryDirFor(cacheDir, input);
1329
1374
  const manifest = await readManifest(
1330
- path2.join(entryDir, "manifest.json")
1375
+ path3.join(entryDir, "manifest.json")
1331
1376
  );
1332
1377
  if (manifest === null || manifest.roots === void 0 || manifest.rootMeta === void 0 || manifest.owners === void 0) {
1333
1378
  return null;
@@ -1342,7 +1387,7 @@ function createCacheLayer(cacheDir) {
1342
1387
  async write(input, summaries, attribution) {
1343
1388
  const entryDir = entryDirFor(cacheDir, input);
1344
1389
  const previous = await readManifest(
1345
- path2.join(entryDir, "manifest.json")
1390
+ path3.join(entryDir, "manifest.json")
1346
1391
  );
1347
1392
  const configStamp = await stampConfigFile(input.configPath);
1348
1393
  const files = await hashStamps(await resolveFileStamps(input), previous);
@@ -1356,7 +1401,7 @@ function createCacheLayer(cacheDir) {
1356
1401
  };
1357
1402
  await fs2.mkdir(entryDir, { recursive: true });
1358
1403
  await fs2.writeFile(
1359
- path2.join(entryDir, "manifest.json"),
1404
+ path3.join(entryDir, "manifest.json"),
1360
1405
  JSON.stringify(manifest)
1361
1406
  );
1362
1407
  await evictOldEntries(cacheDir, entryDir);
@@ -1535,7 +1580,7 @@ function entryDirFor(cacheDir, input) {
1535
1580
  input.configPath ?? ""
1536
1581
  ].join(" ");
1537
1582
  const name = createHash2("sha256").update(key).digest("hex").slice(0, 16);
1538
- return path2.join(cacheDir, `${ENTRY_PREFIX}${name}`);
1583
+ return path3.join(cacheDir, `${ENTRY_PREFIX}${name}`);
1539
1584
  }
1540
1585
  function isEntryDir(name) {
1541
1586
  return ENTRY_DIR_NAME.test(name);
@@ -1549,11 +1594,11 @@ async function markUsed(entryDir) {
1549
1594
  }
1550
1595
  async function evictOldEntries(cacheDir, keepDir) {
1551
1596
  try {
1552
- await fs2.rm(path2.join(cacheDir, "manifest.json"), { force: true });
1597
+ await fs2.rm(path3.join(cacheDir, "manifest.json"), { force: true });
1553
1598
  const entries = await fs2.readdir(cacheDir, { withFileTypes: true });
1554
1599
  const dirs = await Promise.all(
1555
1600
  entries.filter((e) => e.isDirectory() && isEntryDir(e.name)).map(async (e) => {
1556
- const dir = path2.join(cacheDir, e.name);
1601
+ const dir = path3.join(cacheDir, e.name);
1557
1602
  const stat = await fs2.stat(dir);
1558
1603
  return { dir, mtimeMs: stat.mtimeMs };
1559
1604
  })
@@ -2379,6 +2424,7 @@ export {
2379
2424
  assembleSummary,
2380
2425
  assessConfidence,
2381
2426
  buildUngatedExtractionReport,
2427
+ commonDirectoryOf,
2382
2428
  composeWrappers,
2383
2429
  computeContentHash,
2384
2430
  computeDistHashFrom,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/extractor",
3
- "version": "0.25.0",
3
+ "version": "0.27.0",
4
4
  "type": "module",
5
5
  "description": "The suss extraction pipeline, which turns what a language adapter read into the summaries the checker pairs across a boundary.",
6
6
  "keywords": [
@@ -46,7 +46,7 @@
46
46
  "typecheck": "tsc --noEmit"
47
47
  },
48
48
  "dependencies": {
49
- "@suss/behavioral-ir": "0.25.0",
49
+ "@suss/behavioral-ir": "0.27.0",
50
50
  "zod": "^4.0.0"
51
51
  },
52
52
  "devDependencies": {