@suss/extractor 0.35.1 → 0.37.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 +350 -91
- package/dist/index.d.cts +305 -33
- package/dist/index.d.ts +305 -33
- package/dist/index.js +312 -60
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { createHash as
|
|
2
|
+
import { createHash as createHash8 } from "crypto";
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
displayLabel as displayLabel2,
|
|
5
|
+
effectKey as effectKey2,
|
|
5
6
|
exchangesHttpResponses as exchangesHttpResponses2,
|
|
6
7
|
foldRepeatedEffects,
|
|
7
|
-
|
|
8
|
+
isUnsettledJob,
|
|
9
|
+
normalizeCalleeText as normalizeCalleeText2,
|
|
10
|
+
unfollowedCallGap,
|
|
8
11
|
withGraphqlMetadata,
|
|
9
12
|
withHttpMetadata,
|
|
10
13
|
withMountMetadata,
|
|
@@ -165,6 +168,45 @@ function mergeInputReads(derived, syntactic) {
|
|
|
165
168
|
return [...found.values()];
|
|
166
169
|
}
|
|
167
170
|
|
|
171
|
+
// src/callSiteGroup.ts
|
|
172
|
+
import { createHash } from "crypto";
|
|
173
|
+
import { effectKey, normalizeCalleeText } from "@suss/behavioral-ir";
|
|
174
|
+
function callSiteKey(effect) {
|
|
175
|
+
const callee = calleeOf(effect);
|
|
176
|
+
const written = callee === void 0 ? effect : { ...effect, callee: normalizeCalleeText(callee) };
|
|
177
|
+
const hash = createHash("sha1").update(effectKey(written)).digest("hex").slice(0, 7);
|
|
178
|
+
return callee === void 0 ? hash : `${normalizeCalleeText(callee)}:${hash}`;
|
|
179
|
+
}
|
|
180
|
+
function calleeOf(effect) {
|
|
181
|
+
return effect.type === "interaction" || effect.type === "invocation" ? effect.callee : void 0;
|
|
182
|
+
}
|
|
183
|
+
function numberRepeatedCallSites(sites) {
|
|
184
|
+
const taken = /* @__PURE__ */ new Map();
|
|
185
|
+
return sites.map((entries) => {
|
|
186
|
+
const ids = /* @__PURE__ */ new Map();
|
|
187
|
+
for (const key of new Set(
|
|
188
|
+
entries.flatMap((entry) => groupIdOf(entry.effect))
|
|
189
|
+
)) {
|
|
190
|
+
const count = (taken.get(key) ?? 0) + 1;
|
|
191
|
+
taken.set(key, count);
|
|
192
|
+
ids.set(key, count === 1 ? key : `${key}#${count}`);
|
|
193
|
+
}
|
|
194
|
+
return entries.map((entry) => ({
|
|
195
|
+
...entry,
|
|
196
|
+
effect: withGroupId(entry.effect, ids)
|
|
197
|
+
}));
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
function groupIdOf(effect) {
|
|
201
|
+
return effect.type === "interaction" && effect.groupId !== void 0 ? [effect.groupId] : [];
|
|
202
|
+
}
|
|
203
|
+
function withGroupId(effect, ids) {
|
|
204
|
+
if (effect.type !== "interaction" || effect.groupId === void 0) {
|
|
205
|
+
return effect;
|
|
206
|
+
}
|
|
207
|
+
return { ...effect, groupId: ids.get(effect.groupId) ?? effect.groupId };
|
|
208
|
+
}
|
|
209
|
+
|
|
168
210
|
// src/composeWrappers.ts
|
|
169
211
|
import {
|
|
170
212
|
exchangesHttpResponses,
|
|
@@ -472,6 +514,22 @@ function modulesStamp(modules) {
|
|
|
472
514
|
// src/effectGuards.ts
|
|
473
515
|
var keyOf = (condition) => `${condition.polarity}:${condition.sourceText}`;
|
|
474
516
|
var opposite = (condition) => `${condition.polarity === "positive" ? "negative" : "positive"}:${condition.sourceText}`;
|
|
517
|
+
function firesOn(site, branch) {
|
|
518
|
+
const loop = branch.loopAround;
|
|
519
|
+
if (loop !== void 0 && site.line >= loop.start && site.line <= loop.end) {
|
|
520
|
+
return true;
|
|
521
|
+
}
|
|
522
|
+
if (!guardsHoldOn(site.preconditions, branch.conditions)) {
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
return runsBefore(
|
|
526
|
+
site.cleansUpFrom ?? site.line,
|
|
527
|
+
branch.terminal.location.end
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
function effectsFiringOn(placed, branch) {
|
|
531
|
+
return placed.filter((one) => firesOn(one.site, branch)).map((one) => one.effect);
|
|
532
|
+
}
|
|
475
533
|
function guardsHoldOn(preconditions, conditions) {
|
|
476
534
|
if (preconditions === void 0 || preconditions.length === 0) {
|
|
477
535
|
return true;
|
|
@@ -539,16 +597,21 @@ function throughHelper(branch, at, helperBranches) {
|
|
|
539
597
|
isDefault: branch.isDefault && helper.isDefault
|
|
540
598
|
};
|
|
541
599
|
}
|
|
600
|
+
const { loopAround, ...rest } = branch;
|
|
542
601
|
return {
|
|
543
|
-
...
|
|
602
|
+
...rest,
|
|
544
603
|
...reads,
|
|
545
604
|
conditions,
|
|
546
605
|
terminal: { ...helper.terminal, location: at },
|
|
547
606
|
location: at,
|
|
607
|
+
...loopAround !== void 0 && within(at, loopAround) ? { loopAround } : {},
|
|
548
608
|
isDefault: false
|
|
549
609
|
};
|
|
550
610
|
});
|
|
551
611
|
}
|
|
612
|
+
function within(inner, outer) {
|
|
613
|
+
return inner.start >= outer.start && inner.end <= outer.end;
|
|
614
|
+
}
|
|
552
615
|
function mergeReadShapes(a, b) {
|
|
553
616
|
if (a === null || b === null) {
|
|
554
617
|
return a ?? b;
|
|
@@ -566,12 +629,11 @@ function mergeReadShapes(a, b) {
|
|
|
566
629
|
function splitAtHandOffs(branch, sites, from = 0) {
|
|
567
630
|
for (let index = from; index < sites.length; index++) {
|
|
568
631
|
const site = sites[index];
|
|
569
|
-
|
|
570
|
-
if (!reached || !guardsHoldOn(site.preconditions, branch.conditions)) {
|
|
632
|
+
if (!firesOn(site, branch)) {
|
|
571
633
|
continue;
|
|
572
634
|
}
|
|
573
635
|
return throughHelper(branch, site.at, site.helper).flatMap(
|
|
574
|
-
(
|
|
636
|
+
(path11) => path11.terminal === branch.terminal ? splitAtHandOffs(path11, sites, index + 1) : [path11]
|
|
575
637
|
);
|
|
576
638
|
}
|
|
577
639
|
return [branch];
|
|
@@ -618,6 +680,34 @@ function rootName(ref) {
|
|
|
618
680
|
return ref.type === "dependency" ? ref.name : null;
|
|
619
681
|
}
|
|
620
682
|
|
|
683
|
+
// src/libraryEnvReads.ts
|
|
684
|
+
import { functionCallBinding } from "@suss/behavioral-ir";
|
|
685
|
+
function libraryEnvReadMarker(recognition, declared, importer) {
|
|
686
|
+
return {
|
|
687
|
+
kind: "library",
|
|
688
|
+
location: { file: importer, range: { start: 0, end: 0 }, exportName: null },
|
|
689
|
+
identity: {
|
|
690
|
+
name: `${declared.module} env reads`,
|
|
691
|
+
exportPath: null,
|
|
692
|
+
boundaryBinding: functionCallBinding({
|
|
693
|
+
transport: "in-process",
|
|
694
|
+
recognition
|
|
695
|
+
})
|
|
696
|
+
},
|
|
697
|
+
inputs: [],
|
|
698
|
+
transitions: [],
|
|
699
|
+
gaps: [],
|
|
700
|
+
confidence: { source: "declared", level: "high" },
|
|
701
|
+
metadata: {
|
|
702
|
+
libraryEnvReads: {
|
|
703
|
+
module: declared.module,
|
|
704
|
+
...declared.prefixes !== void 0 ? { prefixes: declared.prefixes } : {},
|
|
705
|
+
...declared.names !== void 0 ? { names: declared.names } : {}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
|
|
621
711
|
// src/moduleImports.ts
|
|
622
712
|
function stampModuleImports(summaries, importsOf2) {
|
|
623
713
|
const byFile = /* @__PURE__ */ new Map();
|
|
@@ -919,8 +1009,8 @@ var catchEntryCond = () => fixedCond(
|
|
|
919
1009
|
"positive",
|
|
920
1010
|
"catchBlock"
|
|
921
1011
|
);
|
|
922
|
-
function classify(
|
|
923
|
-
return
|
|
1012
|
+
function classify(path11, home, isAncestorOrSelf) {
|
|
1013
|
+
return path11.map((cond) => {
|
|
924
1014
|
if (cond.branchStmt === null) {
|
|
925
1015
|
return cond.info;
|
|
926
1016
|
}
|
|
@@ -992,9 +1082,9 @@ function limitOpenPaths(paths) {
|
|
|
992
1082
|
);
|
|
993
1083
|
}
|
|
994
1084
|
}
|
|
995
|
-
function recordTerminal(ctx, terminal, home,
|
|
1085
|
+
function recordTerminal(ctx, terminal, home, path11) {
|
|
996
1086
|
chargeBudget(ctx.state);
|
|
997
|
-
const info = classify(
|
|
1087
|
+
const info = classify(path11, home, ctx.isAncestorOrSelf);
|
|
998
1088
|
const existing = ctx.state.byTerminal.get(terminal) ?? [];
|
|
999
1089
|
existing.push(info);
|
|
1000
1090
|
ctx.state.byTerminal.set(terminal, existing);
|
|
@@ -1005,15 +1095,15 @@ function containsTerminal(ctx, stmt) {
|
|
|
1005
1095
|
}
|
|
1006
1096
|
return childrenOf(stmt).some((child) => containsTerminal(ctx, child));
|
|
1007
1097
|
}
|
|
1008
|
-
function stepIf(stmt,
|
|
1098
|
+
function stepIf(stmt, path11, ctx) {
|
|
1009
1099
|
const thenExit = exitKindOfList(stmt.thenBody);
|
|
1010
1100
|
const elseExit = stmt.elseBody === null ? null : exitKindOfList(stmt.elseBody);
|
|
1011
1101
|
const thenPath = [
|
|
1012
|
-
...
|
|
1102
|
+
...path11,
|
|
1013
1103
|
branchCond(stmt, stmt.condition, "positive", elseExit)
|
|
1014
1104
|
];
|
|
1015
1105
|
const elsePath = [
|
|
1016
|
-
...
|
|
1106
|
+
...path11,
|
|
1017
1107
|
branchCond(
|
|
1018
1108
|
stmt,
|
|
1019
1109
|
stmt.condition,
|
|
@@ -1030,7 +1120,7 @@ function stepIf(stmt, path10, ctx) {
|
|
|
1030
1120
|
}
|
|
1031
1121
|
return out;
|
|
1032
1122
|
}
|
|
1033
|
-
function stepSwitch(stmt,
|
|
1123
|
+
function stepSwitch(stmt, path11, ctx) {
|
|
1034
1124
|
const out = [];
|
|
1035
1125
|
const negations = [];
|
|
1036
1126
|
let defaultRanBody = false;
|
|
@@ -1040,7 +1130,7 @@ function stepSwitch(stmt, path10, ctx) {
|
|
|
1040
1130
|
if (hasStrayBreak(group.body)) {
|
|
1041
1131
|
throw new UnmodeledFlow("non-trailing break in switch clause");
|
|
1042
1132
|
}
|
|
1043
|
-
const groupPath = group.condition === null ? [...
|
|
1133
|
+
const groupPath = group.condition === null ? [...path11, ...negations] : [...path11, fixedCond(group.condition, "positive", "explicit")];
|
|
1044
1134
|
const conts = enumerate(ctx, group.body, groupPath);
|
|
1045
1135
|
if (conts.length > 0) {
|
|
1046
1136
|
if (!group.hasTrailingBreak && !fallthroughSafe) {
|
|
@@ -1057,24 +1147,24 @@ function stepSwitch(stmt, path10, ctx) {
|
|
|
1057
1147
|
}
|
|
1058
1148
|
}
|
|
1059
1149
|
if (!defaultRanBody) {
|
|
1060
|
-
out.push([...
|
|
1150
|
+
out.push([...path11, ...negations]);
|
|
1061
1151
|
}
|
|
1062
1152
|
return out;
|
|
1063
1153
|
}
|
|
1064
|
-
function stepLoop(stmt,
|
|
1154
|
+
function stepLoop(stmt, path11, ctx) {
|
|
1065
1155
|
const bodyExit = exitKindOfList(stmt.body);
|
|
1066
1156
|
if (containsTerminal(ctx, stmt)) {
|
|
1067
1157
|
enumerate(ctx, stmt.body, [
|
|
1068
|
-
...
|
|
1158
|
+
...path11,
|
|
1069
1159
|
loopIterationCond(stmt.condition.sourceText)
|
|
1070
1160
|
]);
|
|
1071
1161
|
}
|
|
1072
1162
|
if (bodyExit !== null) {
|
|
1073
|
-
return [[...
|
|
1163
|
+
return [[...path11, loopCompletedCond(stmt.condition.sourceText, bodyExit)]];
|
|
1074
1164
|
}
|
|
1075
|
-
return [
|
|
1165
|
+
return [path11];
|
|
1076
1166
|
}
|
|
1077
|
-
function stepTry(stmt,
|
|
1167
|
+
function stepTry(stmt, path11, ctx) {
|
|
1078
1168
|
if (stmt.finallyBody !== null) {
|
|
1079
1169
|
const finallyHasTerminal = stmt.finallyBody.some(
|
|
1080
1170
|
(s) => containsTerminal(ctx, s)
|
|
@@ -1084,22 +1174,22 @@ function stepTry(stmt, path10, ctx) {
|
|
|
1084
1174
|
}
|
|
1085
1175
|
}
|
|
1086
1176
|
const out = [];
|
|
1087
|
-
out.push(...enumerate(ctx, stmt.tryBody,
|
|
1177
|
+
out.push(...enumerate(ctx, stmt.tryBody, path11));
|
|
1088
1178
|
if (stmt.catchBody !== null) {
|
|
1089
|
-
const catchPath = [...
|
|
1179
|
+
const catchPath = [...path11, catchEntryCond()];
|
|
1090
1180
|
out.push(...enumerate(ctx, stmt.catchBody, catchPath));
|
|
1091
1181
|
}
|
|
1092
1182
|
return out;
|
|
1093
1183
|
}
|
|
1094
|
-
function stepExit(stmt,
|
|
1184
|
+
function stepExit(stmt, path11, ctx) {
|
|
1095
1185
|
const collector = ctx.state.callbackExits[ctx.state.callbackExits.length - 1];
|
|
1096
1186
|
if (collector !== void 0 && (stmt.exit === "return" || stmt.exit === "throw")) {
|
|
1097
|
-
collector.push(
|
|
1187
|
+
collector.push(path11);
|
|
1098
1188
|
}
|
|
1099
1189
|
return [];
|
|
1100
1190
|
}
|
|
1101
|
-
function stepOpaque(_stmt,
|
|
1102
|
-
return [
|
|
1191
|
+
function stepOpaque(_stmt, path11, _ctx) {
|
|
1192
|
+
return [path11];
|
|
1103
1193
|
}
|
|
1104
1194
|
var stepHandlers = {
|
|
1105
1195
|
if: stepIf,
|
|
@@ -1109,20 +1199,20 @@ var stepHandlers = {
|
|
|
1109
1199
|
exit: stepExit,
|
|
1110
1200
|
opaque: stepOpaque
|
|
1111
1201
|
};
|
|
1112
|
-
function stepCallback(ctx, body,
|
|
1202
|
+
function stepCallback(ctx, body, path11) {
|
|
1113
1203
|
const exits = [];
|
|
1114
1204
|
ctx.state.callbackExits.push(exits);
|
|
1115
1205
|
let fellThrough;
|
|
1116
1206
|
try {
|
|
1117
|
-
fellThrough = enumerate(ctx, body,
|
|
1207
|
+
fellThrough = enumerate(ctx, body, path11);
|
|
1118
1208
|
} finally {
|
|
1119
1209
|
ctx.state.callbackExits.pop();
|
|
1120
1210
|
}
|
|
1121
1211
|
const out = [...fellThrough, ...exits];
|
|
1122
|
-
return out.length === 0 ? [
|
|
1212
|
+
return out.length === 0 ? [path11] : out;
|
|
1123
1213
|
}
|
|
1124
|
-
function stepStatement(ctx, stmt,
|
|
1125
|
-
let paths = [
|
|
1214
|
+
function stepStatement(ctx, stmt, path11) {
|
|
1215
|
+
let paths = [path11];
|
|
1126
1216
|
for (const body of stmt.callbacks ?? []) {
|
|
1127
1217
|
paths = paths.flatMap((each) => stepCallback(ctx, body, each));
|
|
1128
1218
|
limitOpenPaths(paths);
|
|
@@ -1209,8 +1299,8 @@ function enumerate(ctx, stmts, prefix) {
|
|
|
1209
1299
|
let frontiers = [[...prefix]];
|
|
1210
1300
|
for (const stmt of stmts) {
|
|
1211
1301
|
const nextFrontiers = [];
|
|
1212
|
-
for (const
|
|
1213
|
-
nextFrontiers.push(...stepStatement(ctx, stmt,
|
|
1302
|
+
for (const path11 of mergeRejoined(frontiers, ctx.keepsArms)) {
|
|
1303
|
+
nextFrontiers.push(...stepStatement(ctx, stmt, path11));
|
|
1214
1304
|
}
|
|
1215
1305
|
frontiers = nextFrontiers;
|
|
1216
1306
|
limitOpenPaths(frontiers);
|
|
@@ -1234,6 +1324,39 @@ function buildParentMap(statements) {
|
|
|
1234
1324
|
}
|
|
1235
1325
|
return parentOf;
|
|
1236
1326
|
}
|
|
1327
|
+
function placeTerminals(input, parentOf = buildParentMap(input.statements)) {
|
|
1328
|
+
const placement = /* @__PURE__ */ new Map();
|
|
1329
|
+
for (const [home, terminals] of input.terminalsByStmt) {
|
|
1330
|
+
const placed = placementOf(home, parentOf);
|
|
1331
|
+
if (placed.loopAround === void 0 && placed.cleansUpFrom === void 0) {
|
|
1332
|
+
continue;
|
|
1333
|
+
}
|
|
1334
|
+
for (const terminal of terminals) {
|
|
1335
|
+
placement.set(terminal, placed);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
return placement;
|
|
1339
|
+
}
|
|
1340
|
+
function placementOf(home, parentOf) {
|
|
1341
|
+
let loopAround = home.kind === "loop" ? home.lines : void 0;
|
|
1342
|
+
let cleansUpFrom;
|
|
1343
|
+
let child = home;
|
|
1344
|
+
let parent = parentOf.get(home) ?? null;
|
|
1345
|
+
while (parent !== null) {
|
|
1346
|
+
if (parent.kind === "loop" && parent.lines !== void 0) {
|
|
1347
|
+
loopAround = parent.lines;
|
|
1348
|
+
}
|
|
1349
|
+
if (parent.kind === "try" && parent.lines !== void 0 && (parent.finallyBody ?? []).includes(child)) {
|
|
1350
|
+
cleansUpFrom = parent.lines.start;
|
|
1351
|
+
}
|
|
1352
|
+
child = parent;
|
|
1353
|
+
parent = parentOf.get(parent) ?? null;
|
|
1354
|
+
}
|
|
1355
|
+
return {
|
|
1356
|
+
...loopAround === void 0 ? {} : { loopAround: { start: loopAround.start, end: loopAround.end } },
|
|
1357
|
+
...cleansUpFrom === void 0 ? {} : { cleansUpFrom }
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1237
1360
|
function enumerateStructuredPaths(input) {
|
|
1238
1361
|
const parentOf = buildParentMap(input.statements);
|
|
1239
1362
|
const isAncestorOrSelf = (a, b) => {
|
|
@@ -1259,11 +1382,15 @@ function enumerateStructuredPaths(input) {
|
|
|
1259
1382
|
isAncestorOrSelf
|
|
1260
1383
|
};
|
|
1261
1384
|
const continuations = withRejoined(enumerate(ctx, input.statements, []));
|
|
1262
|
-
for (const
|
|
1385
|
+
for (const path11 of continuations) {
|
|
1263
1386
|
chargeBudget(state);
|
|
1264
|
-
state.fallthrough.push(classify(
|
|
1387
|
+
state.fallthrough.push(classify(path11, null, isAncestorOrSelf));
|
|
1265
1388
|
}
|
|
1266
|
-
return {
|
|
1389
|
+
return {
|
|
1390
|
+
byTerminal: state.byTerminal,
|
|
1391
|
+
fallthrough: state.fallthrough,
|
|
1392
|
+
placement: placeTerminals(input, parentOf)
|
|
1393
|
+
};
|
|
1267
1394
|
}
|
|
1268
1395
|
function enumerateOrDegrade(input, terminals) {
|
|
1269
1396
|
try {
|
|
@@ -1283,9 +1410,28 @@ function enumerateOrDegrade(input, terminals) {
|
|
|
1283
1410
|
for (const terminal of terminals) {
|
|
1284
1411
|
byTerminal.set(terminal, [[marker]]);
|
|
1285
1412
|
}
|
|
1286
|
-
return {
|
|
1413
|
+
return {
|
|
1414
|
+
byTerminal,
|
|
1415
|
+
fallthrough: [[marker]],
|
|
1416
|
+
placement: placeTerminals(input),
|
|
1417
|
+
degraded: reason
|
|
1418
|
+
};
|
|
1287
1419
|
}
|
|
1288
1420
|
}
|
|
1421
|
+
function enumerateCallStatements(input, terminals) {
|
|
1422
|
+
const placement = placeTerminals(input);
|
|
1423
|
+
const enumerable = new Map(
|
|
1424
|
+
[...input.terminalsByStmt].filter(
|
|
1425
|
+
([, found]) => found.every(
|
|
1426
|
+
(terminal) => placement.get(terminal)?.cleansUpFrom === void 0
|
|
1427
|
+
)
|
|
1428
|
+
)
|
|
1429
|
+
);
|
|
1430
|
+
return {
|
|
1431
|
+
...enumerateOrDegrade({ ...input, terminalsByStmt: enumerable }, terminals),
|
|
1432
|
+
placement
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1289
1435
|
function reasonToDegrade(error) {
|
|
1290
1436
|
if (error instanceof PathBudgetExceeded) {
|
|
1291
1437
|
return error.message === "" ? "path budget exceeded" : error.message;
|
|
@@ -1304,8 +1450,8 @@ function sharedGatingConditions(paths, readPredicate) {
|
|
|
1304
1450
|
return [];
|
|
1305
1451
|
}
|
|
1306
1452
|
const shared = new Set(first.map(keyOf2));
|
|
1307
|
-
for (const
|
|
1308
|
-
const here = new Set(
|
|
1453
|
+
for (const path11 of rest) {
|
|
1454
|
+
const here = new Set(path11.map(keyOf2));
|
|
1309
1455
|
for (const value of shared) {
|
|
1310
1456
|
if (!here.has(value)) {
|
|
1311
1457
|
shared.delete(value);
|
|
@@ -1423,6 +1569,43 @@ function walkDescendants(root, carried, childrenOf2, visitor) {
|
|
|
1423
1569
|
}
|
|
1424
1570
|
}
|
|
1425
1571
|
|
|
1572
|
+
// src/paths/statementSites.ts
|
|
1573
|
+
function statementSites(nodes, reading) {
|
|
1574
|
+
const statements = new IdMap();
|
|
1575
|
+
const statementOfNode = new IdMap();
|
|
1576
|
+
for (const node of nodes) {
|
|
1577
|
+
const written = reading.statementOf(node);
|
|
1578
|
+
if (written === null) {
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
const statement = statements.get(written) ?? written;
|
|
1582
|
+
statements.set(statement, statement);
|
|
1583
|
+
statementOfNode.set(node, statement);
|
|
1584
|
+
}
|
|
1585
|
+
const terminals = [...statements].map(([statement]) => statement);
|
|
1586
|
+
const enumerated = enumerateCallStatements(
|
|
1587
|
+
reading.lower(terminals),
|
|
1588
|
+
terminals
|
|
1589
|
+
);
|
|
1590
|
+
const pathsTo = (node) => {
|
|
1591
|
+
const statement = statementOfNode.get(node);
|
|
1592
|
+
return statement === void 0 ? void 0 : enumerated.byTerminal.get(statement);
|
|
1593
|
+
};
|
|
1594
|
+
const siteOf = (node, preconditions) => {
|
|
1595
|
+
const statement = statementOfNode.get(node);
|
|
1596
|
+
return {
|
|
1597
|
+
line: reading.firstLine(statement ?? node),
|
|
1598
|
+
preconditions,
|
|
1599
|
+
cleansUpFrom: statement === void 0 ? void 0 : enumerated.placement.get(statement)?.cleansUpFrom
|
|
1600
|
+
};
|
|
1601
|
+
};
|
|
1602
|
+
return {
|
|
1603
|
+
pathsTo,
|
|
1604
|
+
siteOf,
|
|
1605
|
+
plainSiteOf: (node) => siteOf(node, sharedGatingConditions(pathsTo(node)))
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1426
1609
|
// src/projectRoot.ts
|
|
1427
1610
|
import path3 from "path";
|
|
1428
1611
|
function commonDirectoryOf(files) {
|
|
@@ -1571,13 +1754,13 @@ function quoted(text, key) {
|
|
|
1571
1754
|
}
|
|
1572
1755
|
|
|
1573
1756
|
// src/adapterStamp.ts
|
|
1574
|
-
import { createHash as
|
|
1757
|
+
import { createHash as createHash3 } from "crypto";
|
|
1575
1758
|
import fs3 from "fs";
|
|
1576
1759
|
import path5 from "path";
|
|
1577
1760
|
import { fileURLToPath } from "url";
|
|
1578
1761
|
|
|
1579
1762
|
// src/codeHash.ts
|
|
1580
|
-
import { createHash } from "crypto";
|
|
1763
|
+
import { createHash as createHash2 } from "crypto";
|
|
1581
1764
|
import fs2 from "fs";
|
|
1582
1765
|
import { createRequire } from "module";
|
|
1583
1766
|
import path4 from "path";
|
|
@@ -1623,7 +1806,7 @@ function createCodeHasher() {
|
|
|
1623
1806
|
if (files.length === 0) {
|
|
1624
1807
|
return "";
|
|
1625
1808
|
}
|
|
1626
|
-
const hash =
|
|
1809
|
+
const hash = createHash2("sha256");
|
|
1627
1810
|
for (const file of files) {
|
|
1628
1811
|
hash.update(read.get(file)?.digest ?? "");
|
|
1629
1812
|
}
|
|
@@ -1640,7 +1823,7 @@ function readCodeFile(file) {
|
|
|
1640
1823
|
return null;
|
|
1641
1824
|
}
|
|
1642
1825
|
return {
|
|
1643
|
-
digest:
|
|
1826
|
+
digest: createHash2("sha256").update(text).digest("hex"),
|
|
1644
1827
|
imports: importsOf(file, text.toString("utf8"))
|
|
1645
1828
|
};
|
|
1646
1829
|
}
|
|
@@ -1798,7 +1981,7 @@ function computeContentHash(paths) {
|
|
|
1798
1981
|
if (paths.length === 0) {
|
|
1799
1982
|
return "";
|
|
1800
1983
|
}
|
|
1801
|
-
const hash =
|
|
1984
|
+
const hash = createHash3("sha256");
|
|
1802
1985
|
for (const file of paths) {
|
|
1803
1986
|
try {
|
|
1804
1987
|
hash.update(fs3.readFileSync(file));
|
|
@@ -1812,7 +1995,7 @@ function projectFileStamp(paths) {
|
|
|
1812
1995
|
if (paths.length === 0) {
|
|
1813
1996
|
return "none";
|
|
1814
1997
|
}
|
|
1815
|
-
const hash =
|
|
1998
|
+
const hash = createHash3("sha256");
|
|
1816
1999
|
for (const file of [...paths].sort()) {
|
|
1817
2000
|
hash.update(file);
|
|
1818
2001
|
hash.update("\0");
|
|
@@ -1847,7 +2030,7 @@ function runDigest(packsDigest, packs, files) {
|
|
|
1847
2030
|
}
|
|
1848
2031
|
|
|
1849
2032
|
// src/cache.ts
|
|
1850
|
-
import { createHash as
|
|
2033
|
+
import { createHash as createHash6 } from "crypto";
|
|
1851
2034
|
import fs7 from "fs/promises";
|
|
1852
2035
|
import path8 from "path";
|
|
1853
2036
|
import { readJsonFile as readJsonFile3, writeDocumentFile as writeDocumentFile2 } from "@suss/behavioral-ir/files";
|
|
@@ -1955,7 +2138,7 @@ function keyOf3(binding) {
|
|
|
1955
2138
|
}
|
|
1956
2139
|
|
|
1957
2140
|
// src/cacheShards.ts
|
|
1958
|
-
import { createHash as
|
|
2141
|
+
import { createHash as createHash4 } from "crypto";
|
|
1959
2142
|
import fs4 from "fs";
|
|
1960
2143
|
import path6 from "path";
|
|
1961
2144
|
import {
|
|
@@ -2021,7 +2204,7 @@ function namesIn(dir) {
|
|
|
2021
2204
|
}
|
|
2022
2205
|
function writeOnce(root, value) {
|
|
2023
2206
|
const pieces = [...documentPieces(value)];
|
|
2024
|
-
const hash =
|
|
2207
|
+
const hash = createHash4("sha256");
|
|
2025
2208
|
for (const piece of pieces) {
|
|
2026
2209
|
hash.update(piece);
|
|
2027
2210
|
}
|
|
@@ -2125,7 +2308,7 @@ function copyOfJson(value) {
|
|
|
2125
2308
|
}
|
|
2126
2309
|
|
|
2127
2310
|
// src/cacheStamps.ts
|
|
2128
|
-
import { createHash as
|
|
2311
|
+
import { createHash as createHash5 } from "crypto";
|
|
2129
2312
|
import fs5 from "fs/promises";
|
|
2130
2313
|
var RECENT_CHANGE_MS = 2e3;
|
|
2131
2314
|
function stampMayMissAWrite(stat, statAt) {
|
|
@@ -2215,7 +2398,7 @@ function reusableHash(prior, stamp) {
|
|
|
2215
2398
|
async function hashFile(filePath) {
|
|
2216
2399
|
try {
|
|
2217
2400
|
const content = await fs5.readFile(filePath);
|
|
2218
|
-
return
|
|
2401
|
+
return createHash5("sha256").update(content).digest("hex").slice(0, 16);
|
|
2219
2402
|
} catch {
|
|
2220
2403
|
return null;
|
|
2221
2404
|
}
|
|
@@ -2671,7 +2854,7 @@ function entryDirFor(cacheDir, input) {
|
|
|
2671
2854
|
input.adapterPacksDigest,
|
|
2672
2855
|
input.configPath ?? ""
|
|
2673
2856
|
].join(" ");
|
|
2674
|
-
const name =
|
|
2857
|
+
const name = createHash6("sha256").update(key).digest("hex").slice(0, 16);
|
|
2675
2858
|
return path8.join(cacheDir, `${ENTRY_PREFIX}${name}`);
|
|
2676
2859
|
}
|
|
2677
2860
|
function isEntryDir(name) {
|
|
@@ -2745,7 +2928,7 @@ async function readManifest2(manifestPath) {
|
|
|
2745
2928
|
}
|
|
2746
2929
|
|
|
2747
2930
|
// src/cacheLocation.ts
|
|
2748
|
-
import { createHash as
|
|
2931
|
+
import { createHash as createHash7 } from "crypto";
|
|
2749
2932
|
import fs8 from "fs";
|
|
2750
2933
|
import path9 from "path";
|
|
2751
2934
|
var CACHE_DIR_ENV = "SUSS_CACHE_DIR";
|
|
@@ -2764,7 +2947,7 @@ function realPathOrSelf(dir) {
|
|
|
2764
2947
|
}
|
|
2765
2948
|
}
|
|
2766
2949
|
function projectKey(projectDir) {
|
|
2767
|
-
return
|
|
2950
|
+
return createHash7("sha256").update(realPathOrSelf(path9.resolve(projectDir))).digest("hex").slice(0, 16);
|
|
2768
2951
|
}
|
|
2769
2952
|
function extractionCacheDirFor(projectDir) {
|
|
2770
2953
|
const root = chosenCacheRoot();
|
|
@@ -2773,6 +2956,13 @@ function extractionCacheDirFor(projectDir) {
|
|
|
2773
2956
|
}
|
|
2774
2957
|
return path9.join(root, "cache", projectKey(projectDir));
|
|
2775
2958
|
}
|
|
2959
|
+
function mapFolderFor(projectDir) {
|
|
2960
|
+
const root = chosenCacheRoot();
|
|
2961
|
+
if (root === null) {
|
|
2962
|
+
return path9.join(projectDir, ".suss");
|
|
2963
|
+
}
|
|
2964
|
+
return path9.join(root, "map", projectKey(projectDir));
|
|
2965
|
+
}
|
|
2776
2966
|
function liveRecordFor(projectDir) {
|
|
2777
2967
|
const root = chosenCacheRoot();
|
|
2778
2968
|
if (root === null) {
|
|
@@ -2921,6 +3111,7 @@ var KeptParses = class {
|
|
|
2921
3111
|
};
|
|
2922
3112
|
|
|
2923
3113
|
// src/packHealth.ts
|
|
3114
|
+
import path10 from "path";
|
|
2924
3115
|
function stagesOf(funnel) {
|
|
2925
3116
|
const stages = [];
|
|
2926
3117
|
const gateSaysSomething = funnel.gates.length > 0 && funnel.unresolvedGates.length === 0;
|
|
@@ -3068,6 +3259,30 @@ function packConfigCheck(needs) {
|
|
|
3068
3259
|
}))
|
|
3069
3260
|
};
|
|
3070
3261
|
}
|
|
3262
|
+
function describeMissingPath(missing) {
|
|
3263
|
+
const where = `${missing.option} is ${missing.value} in ${missing.configFile}`;
|
|
3264
|
+
if (path10.isAbsolute(missing.value)) {
|
|
3265
|
+
return {
|
|
3266
|
+
problem: `${where}, and nothing exists there.`,
|
|
3267
|
+
fix: `Set ${missing.option} to a path that exists.`
|
|
3268
|
+
};
|
|
3269
|
+
}
|
|
3270
|
+
return {
|
|
3271
|
+
problem: `${where}, and nothing exists at ${missing.resolved}. A relative path is read against the config file, not the project.`,
|
|
3272
|
+
fix: missing.inProject === void 0 ? `Set ${missing.option} to an absolute path, or move the config file into the project.` : `Set ${missing.option} to ${missing.inProject} in the config file, or move the config file into the project.`
|
|
3273
|
+
};
|
|
3274
|
+
}
|
|
3275
|
+
function packPathCheck(missing) {
|
|
3276
|
+
return {
|
|
3277
|
+
name: "every path a pack config gives exists",
|
|
3278
|
+
code: "no-path",
|
|
3279
|
+
audience: "run",
|
|
3280
|
+
violations: missing.map((one) => {
|
|
3281
|
+
const { problem, fix } = describeMissingPath(one);
|
|
3282
|
+
return { label: one.pack, detail: `${problem} ${fix}` };
|
|
3283
|
+
})
|
|
3284
|
+
};
|
|
3285
|
+
}
|
|
3071
3286
|
function evaluatePackHealth(report) {
|
|
3072
3287
|
const gradients = packGradients(report);
|
|
3073
3288
|
return [
|
|
@@ -3220,7 +3435,7 @@ function makeTransitionId(functionName, branch) {
|
|
|
3220
3435
|
const { terminal } = branch;
|
|
3221
3436
|
const statusKey = terminal.statusCode === null ? "none" : terminal.statusCode.type === "literal" ? String(terminal.statusCode.value) : `dyn:${terminal.statusCode.sourceText}`;
|
|
3222
3437
|
const conditionSig = branch.conditions.map((c) => `${c.polarity}:${c.sourceText}`).join(";");
|
|
3223
|
-
const conditionHash =
|
|
3438
|
+
const conditionHash = createHash8("sha1").update(conditionSig).digest("hex").slice(0, 7);
|
|
3224
3439
|
return `${functionName}:${terminal.kind}:${statusKey}:${conditionHash}`;
|
|
3225
3440
|
}
|
|
3226
3441
|
function collapseReading(table, reading) {
|
|
@@ -3295,11 +3510,11 @@ function provenanceOf(raw, effects) {
|
|
|
3295
3510
|
if (raw.length === 0) {
|
|
3296
3511
|
return [];
|
|
3297
3512
|
}
|
|
3298
|
-
const positionOf = new Map(effects.map((one, at) => [
|
|
3513
|
+
const positionOf = new Map(effects.map((one, at) => [effectKey2(one), at]));
|
|
3299
3514
|
const bySlot = /* @__PURE__ */ new Map();
|
|
3300
3515
|
const said = /* @__PURE__ */ new Map();
|
|
3301
3516
|
for (const one of raw) {
|
|
3302
|
-
const effect = positionOf.get(
|
|
3517
|
+
const effect = positionOf.get(effectKey2(withNormalizedCallee(one.effect)));
|
|
3303
3518
|
if (effect === void 0) {
|
|
3304
3519
|
continue;
|
|
3305
3520
|
}
|
|
@@ -3331,7 +3546,7 @@ function withNormalizedCallee(effect) {
|
|
|
3331
3546
|
if (effect.callee === void 0) {
|
|
3332
3547
|
return effect;
|
|
3333
3548
|
}
|
|
3334
|
-
return { ...effect, callee:
|
|
3549
|
+
return { ...effect, callee: normalizeCalleeText2(effect.callee) };
|
|
3335
3550
|
}
|
|
3336
3551
|
function assembleSummary(raw, options = DEFAULT_OPTIONS) {
|
|
3337
3552
|
const transitions = raw.branches.map((rawBranch) => {
|
|
@@ -3419,6 +3634,9 @@ function buildMetadata(raw) {
|
|
|
3419
3634
|
if (raw.test !== void 0) {
|
|
3420
3635
|
metadata = withTestMetadata(metadata, raw.test);
|
|
3421
3636
|
}
|
|
3637
|
+
if (raw.packMetadata !== void 0) {
|
|
3638
|
+
metadata = { ...metadata, ...raw.packMetadata };
|
|
3639
|
+
}
|
|
3422
3640
|
return Object.keys(metadata).length > 0 ? metadata : null;
|
|
3423
3641
|
}
|
|
3424
3642
|
function buildHttpMetadataValue(raw) {
|
|
@@ -3470,6 +3688,12 @@ function buildGraphqlMetadataValue(raw) {
|
|
|
3470
3688
|
if (raw.graphqlUnresolvedDocument !== void 0) {
|
|
3471
3689
|
graphql.unresolvedDocument = raw.graphqlUnresolvedDocument;
|
|
3472
3690
|
}
|
|
3691
|
+
if (raw.graphqlClient !== void 0) {
|
|
3692
|
+
graphql.client = raw.graphqlClient;
|
|
3693
|
+
}
|
|
3694
|
+
if (raw.graphqlUnresolvedClient !== void 0) {
|
|
3695
|
+
graphql.unresolvedClient = raw.graphqlUnresolvedClient;
|
|
3696
|
+
}
|
|
3473
3697
|
return Object.keys(graphql).length > 0 ? graphql : null;
|
|
3474
3698
|
}
|
|
3475
3699
|
function answersWithHttpResponses(raw) {
|
|
@@ -3512,8 +3736,23 @@ function detectGaps(raw, transitions, options) {
|
|
|
3512
3736
|
if (raw.declaredContract && answersWithHttpResponses(raw)) {
|
|
3513
3737
|
gaps.push(...contractStatusGaps(raw.declaredContract, transitions));
|
|
3514
3738
|
}
|
|
3739
|
+
gaps.push(...unsettledJobGaps(transitions));
|
|
3515
3740
|
return gaps;
|
|
3516
3741
|
}
|
|
3742
|
+
function unsettledJobGaps(transitions) {
|
|
3743
|
+
const callees = /* @__PURE__ */ new Set();
|
|
3744
|
+
for (const transition of transitions) {
|
|
3745
|
+
for (const effect of transition.effects) {
|
|
3746
|
+
if (effect.type !== "interaction" || effect.interaction.class !== "message-send" || !isUnsettledJob(effect.binding)) {
|
|
3747
|
+
continue;
|
|
3748
|
+
}
|
|
3749
|
+
callees.add(effect.callee ?? displayLabel2(effect.binding));
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
return [...callees].map(
|
|
3753
|
+
(callee) => unfollowedCallGap({ callee, reason: "unsettledJob" })
|
|
3754
|
+
);
|
|
3755
|
+
}
|
|
3517
3756
|
function bodyWentUnread(raw) {
|
|
3518
3757
|
if (raw.readFailure !== void 0) {
|
|
3519
3758
|
return true;
|
|
@@ -3661,7 +3900,10 @@ var effectConverters = {
|
|
|
3661
3900
|
stateChange: (e) => ({ type: "stateChange", variable: e.variable })
|
|
3662
3901
|
};
|
|
3663
3902
|
function effectToIR(effect) {
|
|
3664
|
-
|
|
3903
|
+
const converted = effectConverters[effect.type](
|
|
3904
|
+
effect
|
|
3905
|
+
);
|
|
3906
|
+
return effect.location === void 0 ? converted : { ...converted, location: effect.location };
|
|
3665
3907
|
}
|
|
3666
3908
|
function paramToInput(param) {
|
|
3667
3909
|
return {
|
|
@@ -3692,6 +3934,7 @@ export {
|
|
|
3692
3934
|
assembleSummary,
|
|
3693
3935
|
assessConfidence,
|
|
3694
3936
|
buildUngatedExtractionReport,
|
|
3937
|
+
callSiteKey,
|
|
3695
3938
|
chosenCacheRoot,
|
|
3696
3939
|
commonDirectoryOf,
|
|
3697
3940
|
composeWrappers,
|
|
@@ -3703,23 +3946,28 @@ export {
|
|
|
3703
3946
|
createCodeHasher,
|
|
3704
3947
|
createPackTallies,
|
|
3705
3948
|
createTimer,
|
|
3949
|
+
describeMissingPath,
|
|
3706
3950
|
detectGaps,
|
|
3707
3951
|
effectToIR,
|
|
3952
|
+
effectsFiringOn,
|
|
3708
3953
|
emptyTally,
|
|
3709
3954
|
enumerateOrDegrade,
|
|
3710
3955
|
enumerateStructuredPaths,
|
|
3711
3956
|
evaluatePackHealth,
|
|
3712
3957
|
extractionCacheDirFor,
|
|
3713
3958
|
extractionConfigStamp,
|
|
3959
|
+
firesOn,
|
|
3714
3960
|
firstWrittenReading,
|
|
3715
3961
|
formatPackHealth,
|
|
3716
3962
|
guardsHoldOn,
|
|
3717
3963
|
helperPathsOnVariables,
|
|
3718
3964
|
httpRouteDiscovery,
|
|
3719
3965
|
isListedTestFile,
|
|
3966
|
+
libraryEnvReadMarker,
|
|
3720
3967
|
liveRecordFor,
|
|
3721
3968
|
makeIgnoredDirectory,
|
|
3722
3969
|
makeTransitionId,
|
|
3970
|
+
mapFolderFor,
|
|
3723
3971
|
mapReading,
|
|
3724
3972
|
markReturnsAsExitCode,
|
|
3725
3973
|
matchesTestFileName,
|
|
@@ -3727,10 +3975,13 @@ export {
|
|
|
3727
3975
|
moduleInitStructure,
|
|
3728
3976
|
moduleOfFile,
|
|
3729
3977
|
noopTimer,
|
|
3978
|
+
numberRepeatedCallSites,
|
|
3730
3979
|
ownersWhereWritten,
|
|
3731
3980
|
packConfigCheck,
|
|
3732
3981
|
packGradients,
|
|
3982
|
+
packPathCheck,
|
|
3733
3983
|
paramToInput,
|
|
3984
|
+
placeTerminals,
|
|
3734
3985
|
processCodeHasher,
|
|
3735
3986
|
projectFileStamp,
|
|
3736
3987
|
rawConditionToPredicate,
|
|
@@ -3748,6 +3999,7 @@ export {
|
|
|
3748
3999
|
stampMayMissAWrite,
|
|
3749
4000
|
stampModuleImports,
|
|
3750
4001
|
stampModules,
|
|
4002
|
+
statementSites,
|
|
3751
4003
|
storageSystemOption,
|
|
3752
4004
|
summaryCountsByPack,
|
|
3753
4005
|
tallyUnit,
|